summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore13
-rw-r--r--.travis.yml55
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--HOWTO/INSTALL-ANDROID.md115
-rw-r--r--HOWTO/INSTALL.md11
-rw-r--r--HOWTO/SYSTEMTAP.md2
-rw-r--r--HOWTO/TESTING.md46
-rw-r--r--Makefile.in17
-rw-r--r--OTP_VERSION2
-rw-r--r--bootstrap/bin/no_dot_erlang.bootbin6602 -> 6639 bytes
-rw-r--r--bootstrap/bin/start.bootbin6602 -> 6639 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin6602 -> 6639 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin3332 -> 3080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11052 -> 10944 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin3444 -> 3832 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_call_types.beambin0 -> 13336 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin3516 -> 3688 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin4644 -> 4464 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_digraph.beambin0 -> 3492 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin20868 -> 20740 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin4248 -> 0 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin1928 -> 1924 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin10012 -> 9880 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beambin28684 -> 27020 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_listing.beambin1580 -> 1604 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_opcodes.beambin7548 -> 7648 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin3588 -> 3540 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa.beambin12252 -> 12104 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_bool.beambin0 -> 22676 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_bsm.beambin17936 -> 17328 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_codegen.beambin37932 -> 37876 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_dead.beambin12424 -> 12528 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_funs.beambin2556 -> 2460 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_lint.beambin7528 -> 7676 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_opt.beambin40080 -> 43152 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pp.beambin5500 -> 5512 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beambin46768 -> 45932 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_recv.beambin4396 -> 4208 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_share.beambin5372 -> 5224 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_type.beambin28480 -> 33008 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin8676 -> 8696 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_types.beambin0 -> 15312 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin3548 -> 3664 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin50844 -> 48476 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin3604 -> 3952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin28236 -> 28076 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin2808 -> 2696 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin34712 -> 33496 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_sets.beambin2728 -> 2400 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin20408 -> 19876 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin41604 -> 41152 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app10
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin3720 -> 3528 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12472 -> 12224 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin63064 -> 61092 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11452 -> 11348 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6248 -> 6176 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2080 -> 2112 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/rec_env.beambin4468 -> 4244 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_alias.beambin5556 -> 5352 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_bsm.beambin1672 -> 1648 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin45228 -> 39872 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold_lists.beambin4020 -> 3996 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin3908 -> 3412 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_prepare.beambin0 -> 1704 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2468 -> 2216 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin50688 -> 59048 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin50920 -> 41324 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12052 -> 10844 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3852 -> 3840 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30032 -> 31540 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6108 -> 5968 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1180 -> 1172 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6148 -> 6260 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin12920 -> 12884 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin22544 -> 22128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin29556 -> 28956 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin22348 -> 21840 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6136 -> 5996 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_sup.beambin556 -> 552 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin23356 -> 22872 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin12304 -> 14704 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5572 -> 5560 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_compile_server.beambin5052 -> 4936 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin2744 -> 2680 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_distribution.beambin1872 -> 1852 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7040 -> 7112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_reply.beambin888 -> 856 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_signal_handler.beambin1100 -> 1096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1576 -> 1576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin6112 -> 6204 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin9228 -> 8956 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin13348 -> 13200 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15292 -> 15264 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin4912 -> 4876 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_sctp.beambin3212 -> 3216 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_tcp.beambin2096 -> 2096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_udp.beambin1636 -> 1640 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin28700 -> 27976 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin15708 -> 15584 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_search.beambin2920 -> 2916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14196 -> 14000 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin5496 -> 5656 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5140 -> 5092 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12340 -> 11896 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23648 -> 23372 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_sctp.beambin1440 -> 1436 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin3016 -> 3012 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin864 -> 864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_udp.beambin2104 -> 2096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7236 -> 7180 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin25360 -> 24744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin18564 -> 17184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin9704 -> 9608 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_hosts.beambin1896 -> 1888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13424 -> 13096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin13192 -> 12828 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_sctp.beambin2148 -> 2136 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2784 -> 2784 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7476 -> 7404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin2188 -> 2176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app6
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin3764 -> 3836 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2604 -> 2616 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_refc.beambin2288 -> 2212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_tcp.beambin2180 -> 2124 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_udp.beambin1388 -> 1332 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger.beambin15084 -> 14876 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_backend.beambin2544 -> 2532 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_config.beambin3176 -> 3832 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_disk_log_h.beambin3348 -> 3280 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_filters.beambin1748 -> 1720 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_formatter.beambin9060 -> 8856 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_h_common.beambin7696 -> 7500 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_handler_watcher.beambin1344 -> 1336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_olp.beambin8308 -> 8004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_proxy.beambin2884 -> 2888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_server.beambin11352 -> 10948 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_simple_h.beambin4212 -> 4176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_std_h.beambin9604 -> 9396 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_sup.beambin636 -> 632 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net.beambin3276 -> 3360 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin2824 -> 2812 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin24504 -> 24744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5112 -> 4992 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg.beambin0 -> 7796 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7568 -> 7496 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/ram_file.beambin6008 -> 5952 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io.beambin1660 -> 1652 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_compressed.beambin2336 -> 2376 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_deflate.beambin2592 -> 2584 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_delayed.beambin5224 -> 5232 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_inflate.beambin4140 -> 4124 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_list.beambin2460 -> 2564 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_raw.beambin396 -> 396 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin7704 -> 7764 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/seq_trace.beambin1600 -> 1588 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/standard_error.beambin3724 -> 3692 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin10980 -> 10864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin10948 -> 10764 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_sup.beambin1704 -> 1696 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin2940 -> 2916 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist.hrl13
-rw-r--r--bootstrap/lib/kernel/include/dist_util.hrl5
-rw-r--r--bootstrap/lib/kernel/include/eep48.hrl14
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11568 -> 11468 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/base64.beambin6496 -> 6084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin18852 -> 18560 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2844 -> 2796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin16988 -> 16716 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/calendar.beambin8104 -> 8084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin45120 -> 44636 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6444 -> 6344 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_sup.beambin544 -> 540 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin25696 -> 25272 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin45356 -> 44104 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin8836 -> 8616 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin7500 -> 7400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph_utils.beambin6736 -> 6276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin10472 -> 10332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3748 -> 3592 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin28328 -> 27452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_abstract_code.beambin968 -> 960 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_anno.beambin3484 -> 3456 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2348 -> 2340 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin6808 -> 6712 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_error.beambin8252 -> 8872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin35024 -> 34452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin19344 -> 18436 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin6732 -> 6748 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin86552 -> 84016 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin96136 -> 93352 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_posix_msg.beambin5168 -> 5192 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin27160 -> 26652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin25692 -> 25496 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin30976 -> 30268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin3944 -> 3908 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4776 -> 4744 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin15692 -> 15136 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin21608 -> 20876 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin7660 -> 7156 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin27268 -> 26792 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin10364 -> 10184 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin14772 -> 14464 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin7712 -> 7564 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin5084 -> 5084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin4896 -> 5612 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13012 -> 14672 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin12424 -> 14004 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin15328 -> 17752 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin21284 -> 23320 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin5792 -> 5776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin13636 -> 13228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin14868 -> 14612 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_fread.beambin6508 -> 6328 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin21136 -> 20852 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29272 -> 28740 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2352 -> 2356 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin3188 -> 3172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/math.beambin1288 -> 1288 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin18528 -> 18000 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/orddict.beambin2900 -> 2884 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ordsets.beambin1848 -> 1844 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin8256 -> 7948 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3596 -> 3564 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin12624 -> 14272 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4596 -> 4344 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin65048 -> 63132 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin70588 -> 67164 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin5908 -> 5928 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/rand.beambin29048 -> 28564 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/random.beambin1768 -> 1788 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12388 -> 12060 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin6152 -> 6120 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin28396 -> 28176 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell_default.beambin4064 -> 4064 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell_docs.beambin0 -> 13036 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4740 -> 4732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin35284 -> 34628 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app5
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin35740 -> 33896 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin21588 -> 23308 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2332 -> 5304 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin9116 -> 9108 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/timer.beambin5272 -> 5228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin14076 -> 12836 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode_util.beambin200320 -> 200960 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/uri_string.beambin25072 -> 24708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5120 -> 5060 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin24208 -> 23632 bytes
-rw-r--r--configure.src16
-rw-r--r--erts/.gitignore1
-rw-r--r--erts/Makefile2
-rw-r--r--erts/aclocal.m485
-rwxr-xr-xerts/autoconf/configure.vxworks142
-rw-r--r--erts/autoconf/vxworks/sed.general132
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_cpu3247
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc3258
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc60353
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall53
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc86052
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simlinux66
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simso70
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_sparc40
-rw-r--r--erts/configure.in34
-rw-r--r--erts/doc/src/.gitignore3
-rw-r--r--erts/doc/src/Makefile113
-rw-r--r--erts/doc/src/alt_dist.xml6
-rw-r--r--erts/doc/src/epmd_cmd.xml (renamed from erts/doc/src/epmd.xml)13
-rw-r--r--erts/doc/src/erl_cmd.xml (renamed from erts/doc/src/erl.xml)37
-rw-r--r--erts/doc/src/erl_dist_protocol.xml458
-rw-r--r--erts/doc/src/erl_ext_dist.xml48
-rw-r--r--erts/doc/src/erl_ext_fig.gifbin3834 -> 3840 bytes
-rw-r--r--erts/doc/src/erlang.xml736
-rw-r--r--erts/doc/src/erlc_cmd.xml (renamed from erts/doc/src/erlc.xml)3
-rw-r--r--erts/doc/src/erlsrv_cmd.xml (renamed from erts/doc/src/erlsrv.xml)13
-rw-r--r--erts/doc/src/escript_cmd.xml (renamed from erts/doc/src/escript.xml)3
-rw-r--r--erts/doc/src/init.xml25
-rw-r--r--erts/doc/src/ref_man.xml.src18
-rw-r--r--erts/doc/src/run_erl_cmd.xml (renamed from erts/doc/src/run_erl.xml)5
-rw-r--r--erts/doc/src/socket.xml68
-rw-r--r--erts/doc/src/start_cmd.xml (renamed from erts/doc/src/start.xml)5
-rw-r--r--erts/doc/src/start_erl_cmd.xml (renamed from erts/doc/src/start_erl.xml)7
-rw-r--r--erts/doc/src/werl_cmd.xml (renamed from erts/doc/src/werl.xml)9
-rw-r--r--erts/emulator/Makefile.in44
-rw-r--r--erts/emulator/beam/arith_instrs.tab18
-rw-r--r--erts/emulator/beam/atom.names21
-rw-r--r--erts/emulator/beam/beam_bif_load.c102
-rw-r--r--erts/emulator/beam/beam_bp.c395
-rw-r--r--erts/emulator/beam/beam_bp.h16
-rw-r--r--erts/emulator/beam/beam_debug.c5
-rw-r--r--erts/emulator/beam/beam_emu.c582
-rw-r--r--erts/emulator/beam/beam_load.c475
-rw-r--r--erts/emulator/beam/beam_load.h2
-rw-r--r--erts/emulator/beam/bif.c414
-rw-r--r--erts/emulator/beam/bif.tab39
-rw-r--r--erts/emulator/beam/bif_instrs.tab159
-rw-r--r--erts/emulator/beam/big.c18
-rw-r--r--erts/emulator/beam/big.h2
-rw-r--r--erts/emulator/beam/binary.c12
-rw-r--r--erts/emulator/beam/code_ix.c74
-rw-r--r--erts/emulator/beam/code_ix.h9
-rw-r--r--erts/emulator/beam/dist.c1846
-rw-r--r--erts/emulator/beam/dist.h158
-rw-r--r--erts/emulator/beam/erl_alloc.c6
-rw-r--r--erts/emulator/beam/erl_alloc.types8
-rw-r--r--erts/emulator/beam/erl_alloc_util.c1204
-rw-r--r--erts/emulator/beam/erl_alloc_util.h43
-rw-r--r--erts/emulator/beam/erl_async.c38
-rw-r--r--erts/emulator/beam/erl_bif_binary.c2
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c10
-rw-r--r--erts/emulator/beam/erl_bif_guard.c2
-rw-r--r--erts/emulator/beam/erl_bif_info.c327
-rw-r--r--erts/emulator/beam/erl_bif_lists.c31
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c590
-rw-r--r--erts/emulator/beam/erl_bif_port.c17
-rw-r--r--erts/emulator/beam/erl_bif_trace.c193
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c13
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h5
-rw-r--r--erts/emulator/beam/erl_db.c807
-rw-r--r--erts/emulator/beam/erl_db.h19
-rw-r--r--erts/emulator/beam/erl_db_catree.c43
-rw-r--r--erts/emulator/beam/erl_db_hash.c783
-rw-r--r--erts/emulator/beam/erl_db_hash.h12
-rw-r--r--erts/emulator/beam/erl_db_tree.c232
-rw-r--r--erts/emulator/beam/erl_db_tree_util.h7
-rw-r--r--erts/emulator/beam/erl_db_util.c70
-rw-r--r--erts/emulator/beam/erl_db_util.h54
-rw-r--r--erts/emulator/beam/erl_flxctr.c80
-rw-r--r--erts/emulator/beam/erl_flxctr.h18
-rw-r--r--erts/emulator/beam/erl_fun.c145
-rw-r--r--erts/emulator/beam/erl_fun.h1
-rw-r--r--erts/emulator/beam/erl_gc.c27
-rw-r--r--erts/emulator/beam/erl_init.c27
-rw-r--r--erts/emulator/beam/erl_io_queue.c2
-rw-r--r--erts/emulator/beam/erl_lock_check.c2
-rw-r--r--erts/emulator/beam/erl_lock_count.h11
-rw-r--r--erts/emulator/beam/erl_lock_flags.h4
-rw-r--r--erts/emulator/beam/erl_map.c12
-rw-r--r--erts/emulator/beam/erl_message.c20
-rw-r--r--erts/emulator/beam/erl_message.h1
-rw-r--r--erts/emulator/beam/erl_monitor_link.c95
-rw-r--r--erts/emulator/beam/erl_monitor_link.h18
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.c89
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.h144
-rw-r--r--erts/emulator/beam/erl_nif.c763
-rw-r--r--erts/emulator/beam/erl_node_tables.c21
-rw-r--r--erts/emulator/beam/erl_node_tables.h39
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c684
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.h12
-rw-r--r--erts/emulator/beam/erl_process.c862
-rw-r--r--erts/emulator/beam/erl_process.h110
-rw-r--r--erts/emulator/beam/erl_trace.c17
-rw-r--r--erts/emulator/beam/erl_trace.h11
-rw-r--r--erts/emulator/beam/erl_utils.h47
-rw-r--r--erts/emulator/beam/erlang_dtrace.d39
-rw-r--r--erts/emulator/beam/erlang_lttng.h44
-rw-r--r--erts/emulator/beam/error.h16
-rw-r--r--erts/emulator/beam/export.c66
-rw-r--r--erts/emulator/beam/export.h83
-rw-r--r--erts/emulator/beam/external.c2057
-rw-r--r--erts/emulator/beam/external.h30
-rw-r--r--erts/emulator/beam/global.h22
-rw-r--r--erts/emulator/beam/hash.c107
-rw-r--r--erts/emulator/beam/hash.h66
-rw-r--r--erts/emulator/beam/index.c45
-rw-r--r--erts/emulator/beam/instrs.tab248
-rw-r--r--erts/emulator/beam/macros.tab134
-rw-r--r--erts/emulator/beam/msg_instrs.tab3
-rw-r--r--erts/emulator/beam/ops.tab347
-rw-r--r--erts/emulator/beam/packet_parser.c7
-rw-r--r--erts/emulator/beam/packet_parser.h6
-rw-r--r--erts/emulator/beam/register.c118
-rw-r--r--erts/emulator/beam/register.h1
-rw-r--r--erts/emulator/beam/sys.h55
-rw-r--r--erts/emulator/beam/trace_instrs.tab22
-rw-r--r--erts/emulator/beam/utils.c937
-rw-r--r--erts/emulator/drivers/common/inet_drv.c18
-rw-r--r--erts/emulator/drivers/win32/win_con.c58
-rw-r--r--erts/emulator/hipe/hipe_bif2.c1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m42
-rw-r--r--erts/emulator/hipe/hipe_debug.c1
-rw-r--r--erts/emulator/hipe/hipe_instrs.tab9
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c8
-rw-r--r--erts/emulator/internal_doc/AutomaticYieldingOfCCode.md62
-rw-r--r--erts/emulator/internal_doc/beam_makeops.md24
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c56
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.h15
-rw-r--r--erts/emulator/nifs/common/prim_net_nif.c27
-rw-r--r--erts/emulator/nifs/common/socket_nif.c715
-rw-r--r--erts/emulator/nifs/common/socket_util.c115
-rw-r--r--erts/emulator/nifs/common/socket_util.h20
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c153
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c233
-rw-r--r--erts/emulator/sys/common/erl_check_io.c2
-rw-r--r--erts/emulator/sys/common/erl_osenv.h28
-rw-r--r--erts/emulator/sys/common/erl_poll.h11
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c34
-rw-r--r--erts/emulator/sys/unix/sys.c11
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c1
-rw-r--r--erts/emulator/sys/unix/sys_uds.c2
-rw-r--r--erts/emulator/sys/win32/sys.c1
-rw-r--r--erts/emulator/sys/win32/sys_env.c1
-rw-r--r--erts/emulator/test/Makefile4
-rw-r--r--erts/emulator/test/alloc_SUITE.erl79
-rw-r--r--erts/emulator/test/beam_SUITE.erl3
-rw-r--r--erts/emulator/test/bif_SUITE.erl83
-rw-r--r--erts/emulator/test/binary_SUITE.erl92
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl72
-rw-r--r--erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl3
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl74
-rw-r--r--erts/emulator/test/dirty_bif_SUITE.erl4
-rw-r--r--erts/emulator/test/distribution_SUITE.erl137
-rw-r--r--erts/emulator/test/emulator.spec1
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/erts_test_destructor.erl41
-rw-r--r--erts/emulator/test/exception_SUITE.erl138
-rw-r--r--erts/emulator/test/hash_SUITE.erl618
-rw-r--r--erts/emulator/test/hash_property_test_SUITE.erl103
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl9
-rw-r--r--erts/emulator/test/hipe_SUITE.erl47
-rw-r--r--erts/emulator/test/hipe_SUITE_data/trycatch_1.erl5
-rw-r--r--erts/emulator/test/list_bif_SUITE.erl68
-rw-r--r--erts/emulator/test/lttng_SUITE.erl70
-rw-r--r--erts/emulator/test/map_SUITE.erl16
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl3
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src4
-rw-r--r--erts/emulator/test/nif_SUITE.erl64
-rw-r--r--erts/emulator/test/nif_SUITE_data/Makefile.src7
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c1
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h4
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl5
-rw-r--r--erts/emulator/test/persistent_term_SUITE.erl137
-rw-r--r--erts/emulator/test/persistent_term_SUITE_data/Makefile.src8
-rw-r--r--erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c83
-rw-r--r--erts/emulator/test/port_SUITE.erl8
-rw-r--r--erts/emulator/test/process_SUITE.erl1031
-rw-r--r--erts/emulator/test/property_test/phash2_properties.erl63
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl191
-rw-r--r--erts/emulator/test/send_term_SUITE.erl24
-rw-r--r--erts/emulator/test/small_SUITE.erl12
-rw-r--r--erts/emulator/test/socket_SUITE.erl1926
-rw-r--r--erts/emulator/test/trace_SUITE.erl7
-rw-r--r--erts/emulator/test/trace_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/trace_SUITE_data/slow_drv.c102
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl91
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl9
-rwxr-xr-xerts/emulator/utils/beam_makeops293
-rwxr-xr-xerts/emulator/utils/make_alloc_types3
-rwxr-xr-xerts/emulator/utils/make_preload10
-rwxr-xr-xerts/emulator/utils/make_tables78
-rw-r--r--erts/epmd/Makefile2
-rw-r--r--erts/epmd/epmd.mk2
-rw-r--r--erts/epmd/src/Makefile.in4
-rw-r--r--erts/epmd/src/epmd.c51
-rw-r--r--erts/epmd/src/epmd.h1
-rw-r--r--erts/epmd/src/epmd_int.h34
-rw-r--r--erts/epmd/src/epmd_srv.c115
-rw-r--r--erts/epmd/test/Makefile2
-rw-r--r--erts/etc/common/Makefile.in3
-rw-r--r--erts/etc/common/erlexec.c153
-rw-r--r--erts/etc/unix/cerl.src6
-rw-r--r--erts/etc/unix/etp-commands.in32
-rw-r--r--erts/etc/unix/run_erl.c12
-rw-r--r--erts/etc/win32/cygwin_tools/reg_query.sh24
-rwxr-xr-xerts/etc/win32/cygwin_tools/w32_path.sh59
-rw-r--r--erts/etc/win32/erl.c21
-rw-r--r--erts/etc/win32/msys_tools/reg_query.sh2
-rwxr-xr-xerts/etc/win32/msys_tools/w32_path.sh59
-rw-r--r--erts/etc/win32/nsis/Makefile49
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh19
-rwxr-xr-xerts/etc/win32/nsis/find_redist.sh50
-rw-r--r--erts/etc/win32/wsl_tools/SetupWSLcross.bat66
-rwxr-xr-xerts/etc/win32/wsl_tools/erl45
-rwxr-xr-xerts/etc/win32/wsl_tools/erlc60
-rwxr-xr-xerts/etc/win32/wsl_tools/javac.sh53
-rwxr-xr-x[-rw-r--r--]erts/etc/win32/wsl_tools/make_bootstrap_ini.sh (renamed from lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.first)33
-rwxr-xr-x[-rw-r--r--]erts/etc/win32/wsl_tools/make_local_ini.sh (renamed from erts/doc/Makefile)45
-rwxr-xr-xerts/etc/win32/wsl_tools/reg_query.sh19
-rwxr-xr-x[-rw-r--r--]erts/etc/win32/wsl_tools/vc/ar.sh (renamed from lib/erl_interface/test/erl_format_SUITE_data/Makefile.first)38
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/cc.sh382
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/coffix.c161
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/emu_cc.sh100
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/ld.sh210
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/mc.sh96
-rwxr-xr-xerts/etc/win32/wsl_tools/vc/rc.sh94
-rwxr-xr-xerts/etc/win32/wsl_tools/w32_path.sh70
-rw-r--r--erts/include/internal/erl_misc_utils.h1
-rw-r--r--erts/include/internal/erl_printf_format.h4
-rw-r--r--erts/include/internal/ethread.h3
-rw-r--r--erts/lib_src/Makefile.in3
-rw-r--r--erts/lib_src/common/erl_misc_utils.c260
-rw-r--r--erts/lib_src/common/erl_printf.c5
-rw-r--r--erts/lib_src/common/erl_printf_format.c5
-rw-r--r--erts/lib_src/common/ethr_aux.c3
-rw-r--r--erts/lib_src/yielding_c_fun/.gitignore16
-rw-r--r--erts/lib_src/yielding_c_fun/GNUmakefile212
-rw-r--r--erts/lib_src/yielding_c_fun/Makefile54
-rw-r--r--erts/lib_src/yielding_c_fun/README.md610
-rw-r--r--erts/lib_src/yielding_c_fun/TODO4
-rwxr-xr-xerts/lib_src/yielding_c_fun/bin/yielding_c_fun42
-rw-r--r--erts/lib_src/yielding_c_fun/doc/thread_tutorial.md222
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore11
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt10
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION73
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE201
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile121
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md69
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h307
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c310
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h57
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h370
-rw-r--r--erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c53
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION468
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE24
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile106
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md138
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c470
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h54
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py307
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py77
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py82
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c141
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c2097
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c21
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c28
-rw-r--r--erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c29
-rw-r--r--erts/lib_src/yielding_c_fun/main_target.mk26
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/auto_yield.c124
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out173
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/const_defenition.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/consume_reds.c85
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/control_statements.c289
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out45
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c129
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c131
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations.c91
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c83
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out211
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c92
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out33
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c54
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c93
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out9
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c91
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c104
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out74
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore25
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE191
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile34
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md23
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile83
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore3
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION265
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE24
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile11
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md97
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c224
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h4
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c210
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c238
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c173
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config12
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src14
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl27
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl32
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c92
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield.c58
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c61
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c111
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out18
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/stack_array.c135
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out4
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c71
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c (renamed from lib/erl_interface/src/legacy/erl_internal.h)68
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c71
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out302
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c60
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/test_trap.c42
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/thread_example.c88
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out47
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_param.c79
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_param.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c85
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c126
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out15
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c67
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out2
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c68
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out10
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c104
-rw-r--r--erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out302
-rwxr-xr-xerts/lib_src/yielding_c_fun/test/test.sh176
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_helpers.h (renamed from lib/erl_interface/src/legacy/portability.h)34
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_lexer.c483
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_lists.h316
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_main.c330
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_node.c915
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_node.h455
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_parser.c1498
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_parser.h (renamed from lib/erl_interface/src/legacy/erl_connect.h)18
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_printers.c493
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_printers.h30
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_string.c111
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_string.h89
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_symbol.c166
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_symbol.h110
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_utils.c106
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_utils.h (renamed from lib/erl_interface/src/legacy/erl_marshal.h)34
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_yield_fun.c1625
-rw-r--r--erts/lib_src/yielding_c_fun/ycf_yield_fun.h116
-rw-r--r--erts/preloaded/ebin/atomics.beambin3316 -> 3316 bytes
-rw-r--r--erts/preloaded/ebin/counters.beambin3116 -> 3112 bytes
-rw-r--r--erts/preloaded/ebin/erl_init.beambin2312 -> 2304 bytes
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin52732 -> 52304 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2228 -> 2228 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin100440 -> 108992 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin10996 -> 10904 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_signal_handler.beambin2784 -> 2760 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin20984 -> 22900 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin3272 -> 3260 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin50236 -> 50828 bytes
-rw-r--r--erts/preloaded/ebin/persistent_term.beambin1864 -> 1864 bytes
-rw-r--r--erts/preloaded/ebin/prim_buffer.beambin3620 -> 3620 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1544 -> 1544 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin28016 -> 28740 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin81480 -> 81252 bytes
-rw-r--r--erts/preloaded/ebin/prim_net.beambin5144 -> 5144 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin22460 -> 22308 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin79020 -> 80780 bytes
-rw-r--r--erts/preloaded/ebin/socket_registry.beambin5676 -> 5592 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin19732 -> 19680 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl24
-rw-r--r--erts/preloaded/src/erlang.erl686
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--erts/preloaded/src/erts_internal.erl98
-rw-r--r--erts/preloaded/src/init.erl37
-rw-r--r--erts/preloaded/src/prim_file.erl38
-rw-r--r--erts/preloaded/src/socket.erl102
-rw-r--r--erts/test/erl_print_SUITE.erl41
-rw-r--r--erts/test/erlexec_SUITE.erl40
-rw-r--r--erts/test/erlexec_SUITE_data/erlexec_tests.c2
-rw-r--r--erts/test/nt_SUITE_data/nt_info.c7
-rw-r--r--erts/test/otp_SUITE.erl27
-rw-r--r--erts/test/upgrade_SUITE.erl11
-rw-r--r--erts/test/z_SUITE.erl99
-rw-r--r--lib/asn1/Makefile9
-rw-r--r--lib/asn1/doc/src/Makefile79
-rw-r--r--lib/asn1/doc/users_guide/Makefile3
-rw-r--r--lib/asn1/src/asn1ct.erl5
-rw-r--r--lib/asn1/src/asn1ct_tok.erl55
-rw-r--r--lib/asn1/src/asn1rt.erl34
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ValueTest.asn2
-rw-r--r--lib/asn1/test/testValueTest.erl2
-rw-r--r--lib/common_test/Makefile4
-rw-r--r--lib/common_test/doc/src/Makefile89
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml14
-rw-r--r--lib/common_test/doc/src/ct_run_cmd.xml (renamed from lib/common_test/doc/src/ct_run.xml)3
-rw-r--r--lib/common_test/doc/src/ref_man.xml2
-rw-r--r--lib/common_test/src/ct_config.erl9
-rw-r--r--lib/common_test/src/ct_property_test.erl45
-rw-r--r--lib/common_test/src/cth_log_redirect.erl12
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl138
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl33
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_formatter_SUITE.erl124
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_unexpect_SUITE.erl129
-rw-r--r--lib/common_test/test_server/conf_vars.in1
-rw-r--r--lib/common_test/test_server/configure.in8
-rw-r--r--lib/common_test/test_server/ts_autoconf_win32.erl14
-rw-r--r--lib/compiler/Makefile3
-rw-r--r--lib/compiler/doc/src/Makefile83
-rw-r--r--lib/compiler/scripts/.gitignore6
-rw-r--r--lib/compiler/scripts/smoke-build/mix.lock9
-rw-r--r--lib/compiler/scripts/smoke-mix.exs8
-rw-r--r--lib/compiler/src/Makefile12
-rw-r--r--lib/compiler/src/beam_a.erl10
-rw-r--r--lib/compiler/src/beam_asm.erl23
-rw-r--r--lib/compiler/src/beam_block.erl39
-rw-r--r--lib/compiler/src/beam_call_types.erl983
-rw-r--r--lib/compiler/src/beam_clean.erl52
-rw-r--r--lib/compiler/src/beam_dict.erl43
-rw-r--r--lib/compiler/src/beam_digraph.erl308
-rw-r--r--lib/compiler/src/beam_disasm.erl9
-rw-r--r--lib/compiler/src/beam_except.erl256
-rw-r--r--lib/compiler/src/beam_jump.erl254
-rw-r--r--lib/compiler/src/beam_kernel_to_ssa.erl729
-rw-r--r--lib/compiler/src/beam_ssa.erl81
-rw-r--r--lib/compiler/src/beam_ssa.hrl12
-rw-r--r--lib/compiler/src/beam_ssa_bool.erl1625
-rw-r--r--lib/compiler/src/beam_ssa_bsm.erl40
-rw-r--r--lib/compiler/src/beam_ssa_codegen.erl208
-rw-r--r--lib/compiler/src/beam_ssa_dead.erl120
-rw-r--r--lib/compiler/src/beam_ssa_lint.erl198
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl964
-rw-r--r--lib/compiler/src/beam_ssa_opt.hrl24
-rw-r--r--lib/compiler/src/beam_ssa_pp.erl83
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl692
-rw-r--r--lib/compiler/src/beam_ssa_recv.erl18
-rw-r--r--lib/compiler/src/beam_ssa_share.erl25
-rw-r--r--lib/compiler/src/beam_ssa_type.erl3239
-rw-r--r--lib/compiler/src/beam_trim.erl18
-rw-r--r--lib/compiler/src/beam_types.erl1127
-rw-r--r--lib/compiler/src/beam_types.hrl154
-rw-r--r--lib/compiler/src/beam_utils.erl10
-rw-r--r--lib/compiler/src/beam_validator.erl2468
-rw-r--r--lib/compiler/src/beam_z.erl19
-rw-r--r--lib/compiler/src/cerl_inline.erl72
-rw-r--r--lib/compiler/src/cerl_sets.erl69
-rw-r--r--lib/compiler/src/cerl_trees.erl11
-rw-r--r--lib/compiler/src/compile.erl121
-rw-r--r--lib/compiler/src/compiler.app.src8
-rw-r--r--lib/compiler/src/core_lib.erl105
-rw-r--r--lib/compiler/src/core_lint.erl54
-rw-r--r--lib/compiler/src/core_pp.erl12
-rw-r--r--lib/compiler/src/erl_bifs.erl5
-rwxr-xr-xlib/compiler/src/genop.tab12
-rw-r--r--lib/compiler/src/sys_core_fold.erl692
-rw-r--r--lib/compiler/src/sys_core_fold_lists.erl36
-rw-r--r--lib/compiler/src/sys_core_inline.erl20
-rw-r--r--lib/compiler/src/sys_core_prepare.erl130
-rw-r--r--lib/compiler/src/v3_core.erl1217
-rw-r--r--lib/compiler/src/v3_kernel.erl1718
-rw-r--r--lib/compiler/src/v3_kernel.hrl24
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl89
-rw-r--r--lib/compiler/test/Makefile20
-rw-r--r--lib/compiler/test/andor_SUITE.erl30
-rw-r--r--lib/compiler/test/beam_except_SUITE.erl15
-rw-r--r--lib/compiler/test/beam_ssa_SUITE.erl179
-rw-r--r--lib/compiler/test/beam_type_SUITE.erl5
-rw-r--r--lib/compiler/test/beam_types_SUITE.erl166
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl78
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S48
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/call_last.S21
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S21
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S2
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S77
-rw-r--r--lib/compiler/test/bs_bincomp_SUITE.erl6
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl50
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl512
-rw-r--r--lib/compiler/test/bs_size_expr_SUITE.erl286
-rw-r--r--lib/compiler/test/compile_SUITE.erl44
-rw-r--r--lib/compiler/test/core_SUITE.erl62
-rw-r--r--lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core10
-rw-r--r--lib/compiler/test/core_SUITE_data/receive_tests.core1761
-rw-r--r--lib/compiler/test/core_alias_SUITE.erl31
-rw-r--r--lib/compiler/test/error_SUITE.erl3
-rw-r--r--lib/compiler/test/guard_SUITE.erl258
-rw-r--r--lib/compiler/test/lfe_andor_SUITE.core2
-rw-r--r--lib/compiler/test/lfe_guard_SUITE.core2
-rw-r--r--lib/compiler/test/map_SUITE.erl96
-rw-r--r--lib/compiler/test/match_SUITE.erl22
-rw-r--r--lib/compiler/test/misc_SUITE.erl42
-rw-r--r--lib/compiler/test/property_test/beam_types_prop.erl315
-rw-r--r--lib/compiler/test/receive_SUITE.erl90
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_19.erl15
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_20.erl16
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_21.erl16
-rw-r--r--lib/compiler/test/record_SUITE.erl36
-rw-r--r--lib/compiler/test/test_lib.erl21
-rw-r--r--lib/compiler/test/trycatch_SUITE.erl12
-rw-r--r--lib/compiler/test/warnings_SUITE.erl4
-rw-r--r--lib/crypto/Makefile1
-rw-r--r--lib/crypto/c_src/Makefile.in7
-rw-r--r--lib/crypto/c_src/api_ng.c417
-rw-r--r--lib/crypto/c_src/api_ng.h2
-rw-r--r--lib/crypto/c_src/atoms.c16
-rw-r--r--lib/crypto/c_src/atoms.h8
-rw-r--r--lib/crypto/c_src/cipher.h6
-rw-r--r--lib/crypto/c_src/crypto.c18
-rw-r--r--lib/crypto/c_src/crypto_callback.c24
-rw-r--r--lib/crypto/c_src/crypto_callback.h2
-rw-r--r--lib/crypto/c_src/evp.c27
-rw-r--r--lib/crypto/c_src/openssl_config.h4
-rw-r--r--lib/crypto/c_src/otp_test_engine.c2
-rw-r--r--lib/crypto/configure.in98
-rw-r--r--lib/crypto/doc/src/Makefile79
-rw-r--r--lib/crypto/doc/src/crypto.xml142
-rw-r--r--lib/crypto/doc/src/insidecover.xml26
-rw-r--r--lib/crypto/doc/src/new_api.xml15
-rw-r--r--lib/crypto/src/crypto.erl385
-rw-r--r--lib/crypto/test/crypto_SUITE.erl98
-rw-r--r--lib/crypto/test/crypto_property_test_SUITE.erl2
-rw-r--r--lib/crypto/test/property_test/crypto_ng_api.erl126
-rw-r--r--lib/crypto/test/property_test/crypto_prop_generators.erl4
-rw-r--r--lib/crypto/test/property_test/crypto_prop_generators.hrl1
-rw-r--r--lib/debugger/Makefile4
-rw-r--r--lib/debugger/doc/src/Makefile84
-rw-r--r--lib/debugger/doc/src/attach.jpg (renamed from lib/debugger/doc/src/images/attach.jpg)bin56341 -> 56341 bytes
-rw-r--r--lib/debugger/doc/src/cond_break_dialog.jpg (renamed from lib/debugger/doc/src/images/cond_break_dialog.jpg)bin21770 -> 21770 bytes
-rw-r--r--lib/debugger/doc/src/debugger_chapter.xml14
-rw-r--r--lib/debugger/doc/src/function_break_dialog.jpg (renamed from lib/debugger/doc/src/images/function_break_dialog.jpg)bin13532 -> 13532 bytes
-rw-r--r--lib/debugger/doc/src/interpret.jpg (renamed from lib/debugger/doc/src/images/interpret.jpg)bin28924 -> 28924 bytes
-rw-r--r--lib/debugger/doc/src/line_break_dialog.jpg (renamed from lib/debugger/doc/src/images/line_break_dialog.jpg)bin14414 -> 14414 bytes
-rw-r--r--lib/debugger/doc/src/monitor.jpg (renamed from lib/debugger/doc/src/images/monitor.jpg)bin40742 -> 40742 bytes
-rw-r--r--lib/debugger/doc/src/view.jpg (renamed from lib/debugger/doc/src/images/view.jpg)bin34504 -> 34504 bytes
-rw-r--r--lib/debugger/src/dbg_ieval.erl32
-rw-r--r--lib/debugger/src/dbg_iload.erl6
-rw-r--r--lib/debugger/test/Makefile1
-rw-r--r--lib/debugger/test/bs_size_expr_SUITE.erl273
-rw-r--r--lib/debugger/test/exception_SUITE.erl81
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/stacktrace.erl13
-rw-r--r--lib/debugger/test/line_number_SUITE.erl4
-rw-r--r--lib/debugger/test/map_SUITE.erl52
-rw-r--r--lib/dialyzer/Makefile3
-rw-r--r--lib/dialyzer/doc/src/Makefile76
-rw-r--r--lib/dialyzer/doc/src/ref_man.xml3
-rw-r--r--lib/dialyzer/doc/src/typer_cmd.xml (renamed from lib/dialyzer/doc/src/typer.xml)12
-rw-r--r--lib/dialyzer/src/Makefile1
-rw-r--r--lib/dialyzer/src/dialyzer.app.src1
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl107
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_clean_core.erl225
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl76
-rw-r--r--lib/dialyzer/src/dialyzer_dep.erl82
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl43
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl4
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/maps_remove4
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/maps_remove.erl23
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/stacktrace4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl12
-rw-r--r--lib/diameter/Makefile5
-rw-r--r--lib/diameter/doc/src/Makefile116
-rw-r--r--lib/diameter/doc/src/diameterc_cmd.xml (renamed from lib/diameter/doc/src/diameterc.xml)0
-rw-r--r--lib/diameter/doc/src/files.mk4
-rw-r--r--lib/diameter/doc/src/ref_man.xml2
-rw-r--r--lib/diameter/src/Makefile8
-rw-r--r--lib/edoc/Makefile9
-rw-r--r--lib/edoc/doc/src/Makefile97
-rw-r--r--lib/edoc/src/edoc.app.src2
-rw-r--r--lib/edoc/src/edoc.erl7
-rw-r--r--lib/edoc/src/edoc_data.erl17
-rw-r--r--lib/edoc/src/edoc_lib.erl140
-rw-r--r--lib/eldap/Makefile3
-rw-r--r--lib/eldap/doc/src/Makefile76
-rw-r--r--lib/eldap/doc/src/eldap.xml2
-rw-r--r--lib/erl_docgen/Makefile2
-rw-r--r--lib/erl_docgen/doc/src/Makefile78
-rw-r--r--lib/erl_docgen/doc/src/block_tags.xml16
-rw-r--r--lib/erl_docgen/doc/src/doc_storage.xml61
-rw-r--r--lib/erl_docgen/doc/src/inline_tags.xml40
-rw-r--r--lib/erl_docgen/doc/src/part.xml1
-rw-r--r--lib/erl_docgen/priv/bin/chunk.escript30
-rw-r--r--lib/erl_docgen/priv/bin/specs_gen.escript5
-rw-r--r--lib/erl_docgen/priv/css/Makefile9
-rw-r--r--lib/erl_docgen/priv/dtd/Makefile7
-rw-r--r--lib/erl_docgen/priv/dtd/bookinsidecover.dtd37
-rw-r--r--lib/erl_docgen/priv/dtd/cites.dtd36
-rw-r--r--lib/erl_docgen/priv/dtd/common.dtd31
-rw-r--r--lib/erl_docgen/priv/dtd/fascicules.dtd36
-rw-r--r--lib/erl_docgen/priv/dtd/report.dtd141
-rw-r--r--lib/erl_docgen/priv/dtd/terms.dtd37
-rw-r--r--lib/erl_docgen/priv/images/Makefile9
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/Makefile10
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl136
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl2
-rw-r--r--lib/erl_docgen/src/Makefile3
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl2
-rw-r--r--lib/erl_docgen/src/docgen_xml_to_chunk.erl757
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src3
-rw-r--r--lib/erl_interface/Makefile2
-rw-r--r--lib/erl_interface/configure.in4
-rw-r--r--lib/erl_interface/doc/src/Makefile79
-rw-r--r--lib/erl_interface/doc/src/ei.xml51
-rw-r--r--lib/erl_interface/doc/src/ei_connect.xml25
-rw-r--r--lib/erl_interface/doc/src/ei_global.xml (renamed from lib/erl_interface/doc/src/erl_global.xml)43
-rw-r--r--lib/erl_interface/doc/src/ei_users_guide.xml438
-rw-r--r--lib/erl_interface/doc/src/erl_call_cmd.xml (renamed from lib/erl_interface/doc/src/erl_call.xml)25
-rw-r--r--lib/erl_interface/doc/src/erl_connect.xml662
-rw-r--r--lib/erl_interface/doc/src/erl_error.xml145
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml776
-rw-r--r--lib/erl_interface/doc/src/erl_format.xml138
-rw-r--r--lib/erl_interface/doc/src/erl_interface.xml637
-rw-r--r--lib/erl_interface/doc/src/erl_malloc.xml212
-rw-r--r--lib/erl_interface/doc/src/erl_marshal.xml276
-rw-r--r--lib/erl_interface/doc/src/part_erl_interface.xml33
-rw-r--r--lib/erl_interface/doc/src/ref_man.xml16
-rw-r--r--lib/erl_interface/doc/src/ref_man_ei.xml53
-rw-r--r--lib/erl_interface/doc/src/ref_man_erl_interface.xml60
-rw-r--r--lib/erl_interface/include/ei.h74
-rw-r--r--lib/erl_interface/include/ei_global.h (renamed from lib/erl_interface/src/legacy/erl_global.h)15
-rw-r--r--lib/erl_interface/include/erl_interface.h473
-rw-r--r--lib/erl_interface/src/Makefile.in196
-rw-r--r--lib/erl_interface/src/README4
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c716
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h27
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c257
-rw-r--r--lib/erl_interface/src/connect/send.c7
-rw-r--r--lib/erl_interface/src/connect/send_reg.c4
-rw-r--r--lib/erl_interface/src/decode/decode_big.c7
-rw-r--r--lib/erl_interface/src/encode/encode_pid.c11
-rw-r--r--lib/erl_interface/src/encode/encode_port.c11
-rw-r--r--lib/erl_interface/src/encode/encode_ref.c10
-rw-r--r--lib/erl_interface/src/epmd/ei_epmd.h8
-rw-r--r--lib/erl_interface/src/epmd/epmd_port.c22
-rw-r--r--lib/erl_interface/src/epmd/epmd_publish.c38
-rw-r--r--lib/erl_interface/src/epmd/epmd_unpublish.c10
-rw-r--r--lib/erl_interface/src/global/global_names.c (renamed from lib/erl_interface/src/legacy/global_names.c)8
-rw-r--r--lib/erl_interface/src/global/global_register.c (renamed from lib/erl_interface/src/legacy/global_register.c)16
-rw-r--r--lib/erl_interface/src/global/global_unregister.c (renamed from lib/erl_interface/src/legacy/global_unregister.c)8
-rw-r--r--lib/erl_interface/src/global/global_whereis.c (renamed from lib/erl_interface/src/legacy/global_whereis.c)29
-rw-r--r--lib/erl_interface/src/legacy/decode_term.c144
-rw-r--r--lib/erl_interface/src/legacy/encode_term.c54
-rw-r--r--lib/erl_interface/src/legacy/erl_connect.c467
-rw-r--r--lib/erl_interface/src/legacy/erl_error.c181
-rw-r--r--lib/erl_interface/src/legacy/erl_error.h26
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c1413
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.h64
-rw-r--r--lib/erl_interface/src/legacy/erl_fix_alloc.c198
-rw-r--r--lib/erl_interface/src/legacy/erl_fix_alloc.h27
-rw-r--r--lib/erl_interface/src/legacy/erl_format.c742
-rw-r--r--lib/erl_interface/src/legacy/erl_format.h23
-rw-r--r--lib/erl_interface/src/legacy/erl_malloc.c252
-rw-r--r--lib/erl_interface/src/legacy/erl_malloc.h27
-rw-r--r--lib/erl_interface/src/legacy/erl_marshal.c2267
-rw-r--r--lib/erl_interface/src/legacy/erl_resolve.c107
-rw-r--r--lib/erl_interface/src/legacy/erl_timeout.c163
-rw-r--r--lib/erl_interface/src/legacy/erl_timeout.h75
-rw-r--r--lib/erl_interface/src/misc/ei_format.c4
-rw-r--r--lib/erl_interface/src/misc/ei_locking.c25
-rw-r--r--lib/erl_interface/src/misc/ei_locking.h11
-rw-r--r--lib/erl_interface/src/misc/ei_portio.c26
-rw-r--r--lib/erl_interface/src/misc/ei_portio.h2
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c4
-rw-r--r--lib/erl_interface/src/misc/ei_pthreads.c25
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c4
-rw-r--r--lib/erl_interface/src/misc/eidef.h5
-rw-r--r--lib/erl_interface/src/not_used/ei_send.c105
-rw-r--r--lib/erl_interface/src/not_used/ei_send_reg.c108
-rw-r--r--lib/erl_interface/src/not_used/send_link.c104
-rw-r--r--lib/erl_interface/src/not_used/whereis.c71
-rw-r--r--lib/erl_interface/src/prog/ei_fake_prog.c10
-rw-r--r--lib/erl_interface/src/prog/erl_call.c139
-rw-r--r--lib/erl_interface/src/prog/erl_fake_prog.c251
-rw-r--r--lib/erl_interface/src/prog/erl_start.c119
-rw-r--r--lib/erl_interface/src/registry/reg_get.c4
-rw-r--r--lib/erl_interface/src/registry/reg_set.c4
-rw-r--r--lib/erl_interface/test/Makefile7
-rw-r--r--lib/erl_interface/test/all_SUITE_data/Makefile.src2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/init_tc.erl4
-rw-r--r--lib/erl_interface/test/all_SUITE_data/my_ussi.c198
-rw-r--r--lib/erl_interface/test/all_SUITE_data/my_ussi.h (renamed from lib/erl_interface/src/legacy/erl_config.h)19
-rw-r--r--lib/erl_interface/test/all_SUITE_data/runner.c459
-rw-r--r--lib/erl_interface/test/all_SUITE_data/runner.h51
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE.erl50
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/Makefile.src1
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c28
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c60
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE.erl68
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/Makefile.src6
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c51
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/einode.c23
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl35
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c18
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c4
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE.erl84
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c12
-rw-r--r--lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c4
-rw-r--r--lib/erl_interface/test/ei_global_SUITE.erl (renamed from lib/erl_interface/test/erl_global_SUITE.erl)93
-rw-r--r--lib/erl_interface/test/ei_global_SUITE_data/Makefile.first (renamed from lib/erl_interface/test/erl_global_SUITE_data/Makefile.first)4
-rw-r--r--lib/erl_interface/test/ei_global_SUITE_data/Makefile.src (renamed from lib/erl_interface/test/erl_global_SUITE_data/Makefile.src)19
-rw-r--r--lib/erl_interface/test/ei_global_SUITE_data/ei_global_test.c239
-rw-r--r--lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c4
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE.erl311
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE_data/Makefile.src3
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c172
-rw-r--r--lib/erl_interface/test/erl_call_SUITE.erl97
-rw-r--r--lib/erl_interface/test/erl_connect_SUITE.erl131
-rw-r--r--lib/erl_interface/test/erl_connect_SUITE_data/Makefile.first22
-rw-r--r--lib/erl_interface/test/erl_connect_SUITE_data/Makefile.src41
-rw-r--r--lib/erl_interface/test/erl_connect_SUITE_data/erl_connect_test.c203
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE.erl1084
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.src50
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c173
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c1604
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c131
-rw-r--r--lib/erl_interface/test/erl_format_SUITE.erl135
-rw-r--r--lib/erl_interface/test/erl_format_SUITE_data/Makefile.src43
-rw-r--r--lib/erl_interface/test/erl_format_SUITE_data/format_test.c133
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c264
-rw-r--r--lib/erl_interface/test/erl_match_SUITE.erl280
-rw-r--r--lib/erl_interface/test/erl_match_SUITE_data/Makefile.first22
-rw-r--r--lib/erl_interface/test/erl_match_SUITE_data/Makefile.src42
-rw-r--r--lib/erl_interface/test/erl_match_SUITE_data/match_test.c114
-rw-r--r--lib/erl_interface/test/port_call_SUITE_data/Makefile.src3
-rw-r--r--lib/erl_interface/test/port_call_SUITE_data/port_call_drv.c2
-rw-r--r--lib/et/Makefile3
-rw-r--r--lib/et/doc/src/Makefile80
-rw-r--r--lib/et/doc/src/et_collector.xml24
-rw-r--r--lib/et/doc/src/files.mk2
-rw-r--r--lib/et/src/et_collector.erl2
-rw-r--r--lib/eunit/Makefile10
-rw-r--r--lib/eunit/doc/overview.edoc20
-rw-r--r--lib/eunit/doc/src/Makefile127
-rw-r--r--lib/eunit/include/eunit.hrl10
-rw-r--r--lib/eunit/src/eunit_proc.erl22
-rw-r--r--lib/eunit/src/eunit_tests.erl7
-rw-r--r--lib/ftp/Makefile40
-rw-r--r--lib/ftp/doc/src/Makefile102
-rw-r--r--lib/ftp/doc/src/ftp.xml2
-rw-r--r--lib/ftp/test/Makefile2
-rw-r--r--lib/ftp/test/ftp_bench.spec1
-rw-r--r--lib/hipe/Makefile3
-rw-r--r--lib/hipe/cerl/cerl_closurean.erl4
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl8
-rw-r--r--lib/hipe/cerl/erl_types.erl22
-rw-r--r--lib/hipe/doc/src/Makefile76
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl59
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl10
-rw-r--r--lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl26
-rw-r--r--lib/inets/Makefile40
-rw-r--r--lib/inets/doc/src/Makefile100
-rw-r--r--lib/inets/doc/src/http_server.xml224
-rw-r--r--lib/inets/doc/src/http_uri.xml9
-rw-r--r--lib/inets/doc/src/httpd.xml114
-rw-r--r--lib/inets/doc/src/httpd_util.xml8
-rw-r--r--lib/inets/doc/src/introduction.xml2
-rw-r--r--lib/inets/doc/src/part.xml2
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl2
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl35
-rw-r--r--lib/inets/src/http_lib/http_uri.erl8
-rw-r--r--lib/inets/src/http_server/Makefile2
-rw-r--r--lib/inets/src/http_server/httpd.erl31
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl496
-rw-r--r--lib/inets/src/http_server/httpd_example.erl151
-rw-r--r--lib/inets/src/http_server/httpd_instance_sup.erl45
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl21
-rw-r--r--lib/inets/src/http_server/httpd_request.erl30
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl8
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl55
-rw-r--r--lib/inets/src/http_server/httpd_util.erl115
-rw-r--r--lib/inets/src/http_server/mod_actions.erl19
-rw-r--r--lib/inets/src/http_server/mod_alias.erl66
-rw-r--r--lib/inets/src/http_server/mod_auth.erl93
-rw-r--r--lib/inets/src/http_server/mod_browser.erl251
-rw-r--r--lib/inets/src/http_server/mod_dir.erl4
-rw-r--r--lib/inets/src/http_server/mod_esi.erl180
-rw-r--r--lib/inets/src/http_server/mod_htaccess.erl1071
-rw-r--r--lib/inets/src/inets_app/inets.app.src6
-rw-r--r--lib/inets/test/http_format_SUITE.erl52
-rw-r--r--lib/inets/test/httpd_SUITE.erl384
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl4
-rw-r--r--lib/inets/test/httpd_bench_SUITE.erl3
-rw-r--r--lib/inets/test/httpd_mod_SUITE.erl2
-rw-r--r--lib/inets/test/httpd_test_lib.erl12
-rw-r--r--lib/inets/test/inets_SUITE.erl90
-rw-r--r--lib/jinterface/Makefile1
-rw-r--r--lib/jinterface/doc/src/Makefile68
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java197
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java11
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java12
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java14
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java50
-rw-r--r--lib/kernel/Makefile2
-rw-r--r--lib/kernel/doc/src/Makefile110
-rw-r--r--lib/kernel/doc/src/code.xml49
-rw-r--r--lib/kernel/doc/src/disk_log.xml14
-rw-r--r--lib/kernel/doc/src/erpc.xml513
-rw-r--r--lib/kernel/doc/src/file.xml28
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml6
-rw-r--r--lib/kernel/doc/src/gen_udp.xml1
-rw-r--r--lib/kernel/doc/src/kernel_app.xml10
-rw-r--r--lib/kernel/doc/src/net.xml2
-rw-r--r--lib/kernel/doc/src/pg.xml195
-rw-r--r--lib/kernel/doc/src/pg2.xml10
-rw-r--r--lib/kernel/doc/src/ref_man.xml2
-rw-r--r--lib/kernel/doc/src/rpc.xml153
-rw-r--r--lib/kernel/doc/src/seq_trace.xml139
-rw-r--r--lib/kernel/doc/src/specs.xml2
-rw-r--r--lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl7
-rw-r--r--lib/kernel/include/dist.hrl13
-rw-r--r--lib/kernel/include/dist_util.hrl5
-rw-r--r--lib/kernel/include/eep48.hrl14
-rw-r--r--lib/kernel/src/Makefile5
-rw-r--r--lib/kernel/src/application_controller.erl152
-rw-r--r--lib/kernel/src/auth.erl6
-rw-r--r--lib/kernel/src/code.erl163
-rw-r--r--lib/kernel/src/code_server.erl33
-rw-r--r--lib/kernel/src/disk_log_server.erl6
-rw-r--r--lib/kernel/src/dist_util.erl325
-rw-r--r--lib/kernel/src/erl_epmd.erl13
-rw-r--r--lib/kernel/src/erpc.erl473
-rw-r--r--lib/kernel/src/error_logger.erl13
-rw-r--r--lib/kernel/src/erts_debug.erl64
-rw-r--r--lib/kernel/src/file.erl28
-rw-r--r--lib/kernel/src/file_io_server.erl8
-rw-r--r--lib/kernel/src/global.erl2
-rw-r--r--lib/kernel/src/group.erl10
-rw-r--r--lib/kernel/src/group_history.erl30
-rw-r--r--lib/kernel/src/inet_db.erl455
-rw-r--r--lib/kernel/src/inet_hosts.erl26
-rw-r--r--lib/kernel/src/kernel.app.src8
-rw-r--r--lib/kernel/src/kernel.erl19
-rw-r--r--lib/kernel/src/logger.erl21
-rw-r--r--lib/kernel/src/logger_backend.erl3
-rw-r--r--lib/kernel/src/logger_config.erl150
-rw-r--r--lib/kernel/src/logger_formatter.erl4
-rw-r--r--lib/kernel/src/logger_h_common.erl24
-rw-r--r--lib/kernel/src/logger_handler_watcher.erl29
-rw-r--r--lib/kernel/src/logger_internal.hrl17
-rw-r--r--lib/kernel/src/logger_olp.erl97
-rw-r--r--lib/kernel/src/logger_olp.hrl19
-rw-r--r--lib/kernel/src/logger_proxy.erl12
-rw-r--r--lib/kernel/src/logger_server.erl40
-rw-r--r--lib/kernel/src/logger_simple_h.erl15
-rw-r--r--lib/kernel/src/logger_std_h.erl18
-rw-r--r--lib/kernel/src/net.erl12
-rw-r--r--lib/kernel/src/pg.erl507
-rw-r--r--lib/kernel/src/pg2.erl4
-rw-r--r--lib/kernel/src/raw_file_io_compressed.erl6
-rw-r--r--lib/kernel/src/raw_file_io_delayed.erl6
-rw-r--r--lib/kernel/src/raw_file_io_list.erl7
-rw-r--r--lib/kernel/src/rpc.erl523
-rw-r--r--lib/kernel/src/seq_trace.erl16
-rw-r--r--lib/kernel/src/user.erl53
-rw-r--r--lib/kernel/test/Makefile2
-rw-r--r--lib/kernel/test/application_SUITE.erl214
-rw-r--r--lib/kernel/test/code_SUITE.erl55
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl332
-rw-r--r--lib/kernel/test/erl_distribution_wb_SUITE.erl269
-rw-r--r--lib/kernel/test/erpc_SUITE.erl796
-rw-r--r--lib/kernel/test/file_SUITE.erl232
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl7
-rw-r--r--lib/kernel/test/global_SUITE.erl14
-rw-r--r--lib/kernel/test/inet_SUITE.erl225
-rw-r--r--lib/kernel/test/init_SUITE.erl20
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl28
-rw-r--r--lib/kernel/test/kernel_SUITE.erl3
-rw-r--r--lib/kernel/test/logger_SUITE.erl42
-rw-r--r--lib/kernel/test/logger_formatter_SUITE.erl7
-rw-r--r--lib/kernel/test/logger_legacy_SUITE.erl6
-rw-r--r--lib/kernel/test/logger_std_h_SUITE.erl15
-rw-r--r--lib/kernel/test/os_SUITE.erl10
-rw-r--r--lib/kernel/test/pg_SUITE.erl619
-rw-r--r--lib/kernel/test/rpc_SUITE.erl190
-rw-r--r--lib/kernel/test/seq_trace_SUITE.erl452
-rw-r--r--lib/megaco/Makefile42
-rw-r--r--lib/megaco/doc/src/Makefile122
-rw-r--r--lib/megaco/doc/src/definitions/term.defs.xml1518
-rw-r--r--lib/megaco/doc/src/files.mk2
-rw-r--r--lib/megaco/doc/src/megaco.xml17
-rw-r--r--lib/megaco/src/app/megaco.erl3
-rw-r--r--lib/megaco/test/megaco_mess_SUITE.erl4
-rw-r--r--lib/mnesia/Makefile1
-rw-r--r--lib/mnesia/doc/misc/Makefile2
-rw-r--r--lib/mnesia/doc/src/Makefile81
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap4.xmlsrc8
-rw-r--r--lib/mnesia/doc/src/mnesia.xml14
-rw-r--r--lib/mnesia/src/Makefile1
-rw-r--r--lib/mnesia/src/mnesia.app.src48
-rw-r--r--lib/mnesia/src/mnesia.erl12
-rw-r--r--lib/mnesia/src/mnesia.hrl2
-rw-r--r--lib/mnesia/src/mnesia_event.erl8
-rw-r--r--lib/mnesia/src/mnesia_kernel_sup.erl1
-rw-r--r--lib/mnesia/src/mnesia_lib.erl56
-rw-r--r--lib/mnesia/src/mnesia_loader.erl4
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl8
-rw-r--r--lib/mnesia/src/mnesia_rpc.erl86
-rw-r--r--lib/mnesia/src/mnesia_schema.erl2
-rw-r--r--lib/mnesia/src/mnesia_sp.erl13
-rw-r--r--lib/mnesia/src/mnesia_sup.erl2
-rw-r--r--lib/mnesia/src/mnesia_text.erl4
-rw-r--r--lib/mnesia/src/mnesia_tm.erl6
-rw-r--r--lib/observer/Makefile2
-rw-r--r--lib/observer/doc/src/Makefile88
-rw-r--r--lib/observer/doc/src/cdv_cmd.xml (renamed from lib/observer/doc/src/cdv.xml)2
-rw-r--r--lib/observer/doc/src/ref_man.xml2
-rw-r--r--lib/observer/doc/src/ttb.xml17
-rw-r--r--lib/observer/doc/src/ttb_ug.xml4
-rw-r--r--lib/observer/src/observer.app.src4
-rw-r--r--lib/observer/src/observer_alloc_wx.erl21
-rw-r--r--lib/observer/test/crashdump_helper.erl2
-rw-r--r--lib/observer/test/ttb_SUITE.erl26
-rw-r--r--lib/odbc/Makefile8
-rw-r--r--lib/odbc/c_src/Makefile.in4
-rw-r--r--lib/odbc/doc/src/Makefile76
-rw-r--r--lib/os_mon/Makefile1
-rw-r--r--lib/os_mon/doc/src/Makefile71
-rw-r--r--lib/os_mon/src/Makefile2
-rw-r--r--lib/os_mon/src/os_mon.app.src2
-rw-r--r--lib/os_mon/src/os_mon_mib.erl27
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl76
-rw-r--r--lib/parsetools/Makefile1
-rw-r--r--lib/parsetools/doc/src/Makefile71
-rw-r--r--lib/parsetools/doc/src/leex.xml18
-rw-r--r--lib/parsetools/doc/src/ref_man.xml6
-rw-r--r--lib/parsetools/src/yecc.erl30
-rw-r--r--lib/public_key/Makefile3
-rw-r--r--lib/public_key/doc/src/Makefile105
-rw-r--r--lib/public_key/src/pubkey_pbe.erl44
-rw-r--r--lib/public_key/test/pbe_SUITE.erl15
-rw-r--r--lib/public_key/test/pkits_SUITE.erl3
-rw-r--r--lib/reltool/Makefile3
-rw-r--r--lib/reltool/doc/src/Makefile77
-rw-r--r--lib/reltool/doc/src/files.mk3
-rw-r--r--lib/runtime_tools/Makefile2
-rw-r--r--lib/runtime_tools/doc/src/LTTng.xml25
-rw-r--r--lib/runtime_tools/doc/src/Makefile77
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml2
-rw-r--r--lib/runtime_tools/examples/function-calls.d46
-rw-r--r--lib/runtime_tools/examples/function-calls.systemtap46
-rw-r--r--lib/runtime_tools/src/erts_alloc_config.erl25
-rw-r--r--lib/runtime_tools/src/observer_backend.erl23
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src4
-rw-r--r--lib/sasl/Makefile3
-rw-r--r--lib/sasl/doc/src/Makefile76
-rw-r--r--lib/sasl/src/systools_lib.erl6
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/src/m.erl1
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/src/m.erl1
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/src/m.erl1
-rw-r--r--lib/sasl/test/sasl_SUITE.erl4
-rw-r--r--lib/sasl/test/sasl_report_SUITE.erl42
-rw-r--r--lib/snmp/Makefile50
-rw-r--r--lib/snmp/doc/src/Makefile126
-rw-r--r--lib/snmp/doc/src/files.mk16
-rw-r--r--lib/snmp/doc/src/snmp_app.xml2
-rw-r--r--lib/snmp/doc/src/snmp_pdus.xml1
-rw-r--r--lib/snmp/src/agent/snmpa.erl2
-rw-r--r--lib/snmp/src/app/snmp.erl94
-rw-r--r--lib/snmp/src/compile/snmpc_misc.erl5
-rw-r--r--lib/snmp/src/misc/snmp_conf.erl3
-rw-r--r--lib/snmp/src/misc/snmp_config.erl3
-rw-r--r--lib/snmp/src/misc/snmp_usm.erl31
-rw-r--r--lib/ssh/Makefile3
-rw-r--r--lib/ssh/doc/src/Makefile87
-rw-r--r--lib/ssh/doc/src/ssh.xml70
-rw-r--r--lib/ssh/src/Makefile8
-rw-r--r--lib/ssh/src/ssh.app.src6
-rw-r--r--lib/ssh/src/ssh.erl171
-rw-r--r--lib/ssh/src/ssh.hrl6
-rw-r--r--lib/ssh/src/ssh_auth.erl8
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl90
-rw-r--r--lib/ssh/src/ssh_connection.erl247
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl221
-rw-r--r--lib/ssh/src/ssh_file.erl694
-rw-r--r--lib/ssh/src/ssh_info.erl35
-rw-r--r--lib/ssh/src/ssh_message.erl150
-rw-r--r--lib/ssh/src/ssh_options.erl12
-rw-r--r--lib/ssh/src/ssh_sftp.erl8
-rw-r--r--lib/ssh/src/ssh_sftpd.erl5
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl48
-rw-r--r--lib/ssh/src/ssh_system_sup.erl27
-rw-r--r--lib/ssh/src/ssh_tcpip_forward_acceptor.erl116
-rw-r--r--lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl (renamed from lib/ssh/src/ssh_server_channel_sup.erl)44
-rw-r--r--lib/ssh/src/ssh_tcpip_forward_client.erl84
-rw-r--r--lib/ssh/src/ssh_tcpip_forward_srv.erl75
-rw-r--r--lib/ssh/src/ssh_transport.erl78
-rw-r--r--lib/ssh/src/ssh_xfer.erl23
-rw-r--r--lib/ssh/src/sshc_sup.erl46
-rw-r--r--lib/ssh/src/sshd_sup.erl2
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_encode_decode.erl2
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl79
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE.erl269
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa21
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa9
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed255197
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa27
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key21
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key9
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key7
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa12
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa5
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa27
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key5
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa15
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa8
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa30
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key5
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl75
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl85
-rw-r--r--lib/ssh/test/ssh_test_lib.erl11
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl236
-rw-r--r--lib/ssl/Makefile2
-rw-r--r--lib/ssl/doc/src/Makefile86
-rw-r--r--lib/ssl/doc/src/ssl.xml19
-rw-r--r--lib/ssl/doc/src/standards_compliance.xml1
-rw-r--r--lib/ssl/src/Makefile12
-rw-r--r--lib/ssl/src/dtls_handshake.erl2
-rw-r--r--lib/ssl/src/dtls_packet_demux.erl12
-rw-r--r--lib/ssl/src/inet_tls_dist.erl6
-rw-r--r--lib/ssl/src/ssl.app.src1
-rw-r--r--lib/ssl/src/ssl.erl56
-rw-r--r--lib/ssl/src/ssl_cipher.erl112
-rw-r--r--lib/ssl/src/ssl_handshake.erl28
-rw-r--r--lib/ssl/src/ssl_internal.hrl6
-rw-r--r--lib/ssl/src/ssl_v3.erl200
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl37
-rw-r--r--lib/ssl/src/tls_record.erl47
-rw-r--r--lib/ssl/src/tls_sender.erl22
-rw-r--r--lib/ssl/src/tls_v1.erl2
-rw-r--r--lib/ssl/test/inet_crypto_dist.erl144
-rw-r--r--lib/ssl/test/openssl_alpn_SUITE.erl1
-rw-r--r--lib/ssl/test/openssl_cipher_suite_SUITE.erl2
-rw-r--r--lib/ssl/test/openssl_client_cert_SUITE.erl2
-rw-r--r--lib/ssl/test/openssl_reject_SUITE.erl49
-rw-r--r--lib/ssl/test/openssl_renegotiate_SUITE.erl9
-rw-r--r--lib/ssl/test/openssl_server_cert_SUITE.erl2
-rw-r--r--lib/ssl/test/openssl_session_SUITE.erl9
-rw-r--r--lib/ssl/test/property_test/ssl_eqc_cipher_format.erl7
-rw-r--r--lib/ssl/test/property_test/ssl_eqc_handshake.erl21
-rw-r--r--lib/ssl/test/ssl_alpn_SUITE.erl30
-rw-r--r--lib/ssl/test/ssl_api_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_app_env_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl5
-rw-r--r--lib/ssl/test/ssl_bench_test_lib.erl8
-rw-r--r--lib/ssl/test/ssl_cert_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_cipher_suite_SUITE.erl20
-rw-r--r--lib/ssl/test/ssl_engine_SUITE.erl15
-rw-r--r--lib/ssl/test/ssl_npn_SUITE.erl35
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl14
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl6
-rw-r--r--lib/ssl/test/ssl_renegotiate_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_session_SUITE.erl18
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl77
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl21
-rw-r--r--lib/ssl/test/ssl_test_lib.erl56
-rw-r--r--lib/ssl/test/tls_api_SUITE.erl39
-rw-r--r--lib/stdlib/Makefile4
-rw-r--r--lib/stdlib/doc/src/Makefile72
-rw-r--r--lib/stdlib/doc/src/c.xml58
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml19
-rw-r--r--lib/stdlib/doc/src/erl_pp.xml4
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml17
-rw-r--r--lib/stdlib/doc/src/ets.xml75
-rw-r--r--lib/stdlib/doc/src/filelib.xml30
-rw-r--r--lib/stdlib/doc/src/filename.xml4
-rw-r--r--lib/stdlib/doc/src/gen_event.xml165
-rw-r--r--lib/stdlib/doc/src/gen_server.xml188
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml186
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml43
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml96
-rw-r--r--lib/stdlib/doc/src/ref_man.xml1
-rw-r--r--lib/stdlib/doc/src/shell_docs.xml120
-rw-r--r--lib/stdlib/doc/src/specs.xml1
-rw-r--r--lib/stdlib/doc/src/supervisor.xml14
-rw-r--r--lib/stdlib/doc/src/uri_string.xml6
-rw-r--r--lib/stdlib/doc/src/zip.xml4
-rwxr-xr-xlib/stdlib/scripts/update_deprecations149
-rw-r--r--lib/stdlib/src/Makefile11
-rw-r--r--lib/stdlib/src/beam_lib.erl1
-rw-r--r--lib/stdlib/src/c.erl82
-rw-r--r--lib/stdlib/src/calendar.erl3
-rw-r--r--lib/stdlib/src/edlin.erl5
-rw-r--r--lib/stdlib/src/edlin_expand.erl78
-rw-r--r--lib/stdlib/src/erl_error.erl219
-rw-r--r--lib/stdlib/src/erl_eval.erl26
-rw-r--r--lib/stdlib/src/erl_expand_records.erl8
-rw-r--r--lib/stdlib/src/erl_internal.erl13
-rw-r--r--lib/stdlib/src/erl_lint.erl412
-rw-r--r--lib/stdlib/src/erl_parse.yrl23
-rw-r--r--lib/stdlib/src/erl_pp.erl78
-rw-r--r--lib/stdlib/src/erl_scan.erl160
-rw-r--r--lib/stdlib/src/erl_tar.erl85
-rw-r--r--lib/stdlib/src/escript.erl38
-rw-r--r--lib/stdlib/src/ets.erl4
-rw-r--r--lib/stdlib/src/eval_bits.erl25
-rw-r--r--lib/stdlib/src/filelib.erl109
-rw-r--r--lib/stdlib/src/filename.erl4
-rw-r--r--lib/stdlib/src/gen.erl97
-rw-r--r--lib/stdlib/src/gen_event.erl256
-rw-r--r--lib/stdlib/src/gen_fsm.erl343
-rw-r--r--lib/stdlib/src/gen_server.erl339
-rw-r--r--lib/stdlib/src/gen_statem.erl441
-rw-r--r--lib/stdlib/src/io.erl12
-rw-r--r--lib/stdlib/src/io_lib.erl29
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl3
-rw-r--r--lib/stdlib/src/maps.erl3
-rw-r--r--lib/stdlib/src/otp_internal.erl988
-rw-r--r--lib/stdlib/src/otp_internal.hrl36
-rw-r--r--lib/stdlib/src/proc_lib.erl411
-rw-r--r--lib/stdlib/src/proplists.erl20
-rw-r--r--lib/stdlib/src/qlc.erl4
-rw-r--r--lib/stdlib/src/qlc_pt.erl28
-rw-r--r--lib/stdlib/src/queue.erl2
-rw-r--r--lib/stdlib/src/random.erl2
-rw-r--r--lib/stdlib/src/shell_default.erl15
-rw-r--r--lib/stdlib/src/shell_docs.erl684
-rw-r--r--lib/stdlib/src/stdlib.app.src3
-rw-r--r--lib/stdlib/src/supervisor.erl261
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl176
-rw-r--r--lib/stdlib/src/sys.erl5
-rw-r--r--lib/stdlib/src/zip.erl148
-rw-r--r--lib/stdlib/test/Makefile6
-rw-r--r--lib/stdlib/test/dict_test_lib.erl5
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl32
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl (renamed from lib/stdlib/test/ExpandTestCaps.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl (renamed from lib/stdlib/test/ExpandTestCaps1.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl (renamed from lib/stdlib/test/expand_test.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl (renamed from lib/stdlib/test/expand_test1.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl (renamed from lib/stdlib/test/unicode_expand.erl)0
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl52
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl112
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl34
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl111
-rw-r--r--lib/stdlib/test/ets_SUITE.erl607
-rw-r--r--lib/stdlib/test/ets_SUITE_data/visualize_throughput.html137
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl170
-rw-r--r--lib/stdlib/test/filename_SUITE.erl7
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl302
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl239
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl344
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl377
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl20
-rw-r--r--lib/stdlib/test/maps_SUITE.erl33
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl372
-rw-r--r--lib/stdlib/test/shell_SUITE.erl16
-rw-r--r--lib/stdlib/test/shell_docs_SUITE.erl88
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl4
-rw-r--r--lib/stdlib/test/string_SUITE.erl20
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl251
-rw-r--r--lib/stdlib/test/supervisor_bridge_SUITE.erl206
-rw-r--r--lib/stdlib/test/tar_SUITE.erl28
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt78
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt6
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt34
-rw-r--r--lib/stdlib/test/win32reg_SUITE.erl38
-rw-r--r--lib/stdlib/test/zip_SUITE.erl203
-rw-r--r--lib/stdlib/test/zip_SUITE_data/zip-latin1.zipbin0 -> 115 bytes
-rw-r--r--lib/stdlib/uc_spec/CaseFolding.txt13
-rw-r--r--lib/stdlib/uc_spec/CompositionExclusions.txt6
-rw-r--r--lib/stdlib/uc_spec/GraphemeBreakProperty.txt39
-rw-r--r--lib/stdlib/uc_spec/PropList.txt77
-rw-r--r--lib/stdlib/uc_spec/README-UPDATE.txt10
-rw-r--r--lib/stdlib/uc_spec/SpecialCasing.txt6
-rw-r--r--lib/stdlib/uc_spec/UnicodeData.txt565
-rw-r--r--lib/stdlib/uc_spec/emoji-data.txt305
-rw-r--r--lib/stdlib/uc_spec/gen_unicode_mod.escript2
-rw-r--r--lib/syntax_tools/Makefile2
-rw-r--r--lib/syntax_tools/doc/src/Makefile101
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl2
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl88
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl97
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl5
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl53
-rw-r--r--lib/syntax_tools/src/prettypr.erl5
-rw-r--r--lib/tftp/Makefile34
-rw-r--r--lib/tftp/doc/src/Makefile100
-rw-r--r--lib/tftp/test/Makefile2
-rw-r--r--lib/tftp/test/tftp_bench.spec1
-rw-r--r--lib/tools/Makefile3
-rw-r--r--lib/tools/c_src/Makefile.in1
-rw-r--r--lib/tools/doc/src/Makefile77
-rw-r--r--lib/tools/doc/src/cprof.xml2
-rw-r--r--lib/tools/doc/src/instrument.xml10
-rw-r--r--lib/tools/doc/src/xref.xml5
-rw-r--r--lib/tools/emacs/Makefile16
-rw-r--r--lib/tools/emacs/erlang.el10
-rw-r--r--lib/tools/src/eprof.erl2
-rw-r--r--lib/tools/src/instrument.erl7
-rw-r--r--lib/tools/src/tools.app.src2
-rw-r--r--lib/tools/src/xref_base.erl15
-rw-r--r--lib/tools/test/cprof_SUITE.erl12
-rw-r--r--lib/tools/test/instrument_SUITE.erl22
-rw-r--r--lib/tools/test/xref_SUITE.erl15
-rw-r--r--lib/wx/Makefile2
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl8
-rw-r--r--lib/wx/configure.in74
-rw-r--r--lib/wx/doc/src/Makefile95
-rw-r--r--lib/wx/src/gen/wxCalendarCtrl.erl5
-rw-r--r--lib/wx/src/gen/wxClientDC.erl2
-rw-r--r--lib/wx/src/gen/wxCursor.erl3
-rw-r--r--lib/wx/src/gen/wxDC.erl4
-rw-r--r--lib/wx/src/gen/wxGraphicsRenderer.erl3
-rw-r--r--lib/wx/src/gen/wxGridCellEditor.erl3
-rw-r--r--lib/wx/src/gen/wxIdleEvent.erl2
-rw-r--r--lib/wx/src/gen/wxMDIClientWindow.erl3
-rw-r--r--lib/wx/src/gen/wxPaintDC.erl2
-rw-r--r--lib/wx/src/gen/wxPostScriptDC.erl3
-rw-r--r--lib/wx/src/gen/wxWindowDC.erl2
-rw-r--r--lib/wx/src/wx_object.erl32
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl11
-rw-r--r--lib/xmerl/Makefile8
-rw-r--r--lib/xmerl/doc/src/Makefile118
-rw-r--r--lib/xmerl/doc/src/xmerl_sax_parser.xml8
-rw-r--r--make/app_targets.mk66
-rw-r--r--make/configure.in4
-rw-r--r--make/doc.mk215
-rw-r--r--make/otp.mk.in30
-rw-r--r--make/otp_release_targets.mk18
-rw-r--r--make/otp_version_tickets_in_merge3
-rw-r--r--make/target.mk3
-rwxr-xr-xmake/test_target_script.sh254
-rwxr-xr-xotp_build334
-rwxr-xr-xscripts/build-otp2
-rwxr-xr-xscripts/pre-push92
-rwxr-xr-xscripts/run-dialyzer1
-rw-r--r--system/doc/definitions/term.defs.xml1525
-rw-r--r--system/doc/design_principles/Makefile13
-rw-r--r--system/doc/design_principles/distributed_applications.xml2
-rw-r--r--system/doc/design_principles/statem.xml19
-rw-r--r--system/doc/efficiency_guide/Makefile14
-rw-r--r--system/doc/embedded/Makefile16
-rw-r--r--system/doc/general_info/Makefile15
-rw-r--r--system/doc/general_info/deprecations.xml84
-rw-r--r--system/doc/general_info/scheduled_for_removal.xml67
-rw-r--r--system/doc/getting_started/Makefile14
-rw-r--r--system/doc/installation_guide/Makefile13
-rw-r--r--system/doc/oam/Makefile14
-rw-r--r--system/doc/programming_examples/Makefile16
-rw-r--r--system/doc/programming_examples/bit_syntax.xml32
-rw-r--r--system/doc/reference_manual/Makefile30
-rw-r--r--system/doc/reference_manual/data_types.xml25
-rw-r--r--system/doc/reference_manual/errors.xml7
-rw-r--r--system/doc/reference_manual/expressions.xml71
-rw-r--r--system/doc/system_architecture_intro/Makefile16
-rw-r--r--system/doc/system_principles/Makefile15
-rw-r--r--system/doc/top/Makefile41
-rw-r--r--system/doc/top/print.html2
-rw-r--r--system/doc/tutorial/Makefile12
-rw-r--r--system/doc/tutorial/cnode.xmlsrc20
-rw-r--r--system/doc/tutorial/distribution.xml2
-rw-r--r--system/doc/tutorial/ei.c75
-rw-r--r--system/doc/tutorial/erl_comm.c59
-rw-r--r--system/doc/tutorial/erl_interface.xmlsrc79
-rw-r--r--system/doc/tutorial/overview.xml35
-rw-r--r--xcomp/erl-xcomp-arm-android.conf52
-rw-r--r--xcomp/erl-xcomp-arm64-android.conf (renamed from xcomp/erl-xcomp-vxworks_ppc32.conf)63
1564 files changed, 86459 insertions, 49323 deletions
diff --git a/.gitignore b/.gitignore
index b14ec86df6..f06f8291f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,8 @@ autom4te.cache
# Do not use too creative wildcards.
# Those might ignore files that should not be ignored.
+aarch64-unknown-linux-android
+arm-unknown-linux-androideabi
armv7l-unknown-linux-gnueabihf
i686-pc-linux-gnu
x86_64-unknown-linux-gnu
@@ -54,8 +56,10 @@ a.out.dSYM/
# Windows
*.pdb
+local.static.config.cache
tcltk85_win32_bin.tar.gz
erts/autoconf/win32.config.cache
+erts/autoconf/win64.config.cache
erts/emulator/obj/
erts/emulator/pcre/obj/
erts/emulator/pcre/win32/
@@ -66,6 +70,9 @@ erts/epmd/src/win32/
erts/etc/common/Install.ini
erts/etc/common/win32/
erts/etc/win32/cygwin_tools/vc/coffix.exe
+erts/etc/win32/wsl_tools/vc/coffix.exe
+erts/etc/win32/nsis/helper.*
+erts/etc/win32/nsis/hello.*
erts/include/internal/win32/
erts/include/win32/
erts/lib/internal/win32/
@@ -178,6 +185,8 @@ JAVADOC-GENERATED
/lib/*/doc/pdf/*.pdf
/lib/*/doc/src/*.fo
/lib/*/doc/xml/*.xml
+/lib/*/doc/xml/*.ent
+/lib/*/doc/chunks/*.chunk
/lib/config.log
/lib/config.status
@@ -250,6 +259,8 @@ JAVADOC-GENERATED
/lib/compiler/test/*_inline_SUITE.erl
/lib/compiler/test/*_r21_SUITE.erl
/lib/compiler/test/*_no_module_opt_SUITE.erl
+/lib/compiler/test/*_no_type_opt_SUITE.erl
+/lib/compiler/test/*_dialyzer_SUITE.erl
# crypto
/lib/crypto/test/crypto_SUITE_data/*.rsp
@@ -274,6 +285,8 @@ JAVADOC-GENERATED
/erts/doc/pdf/*.pdf
/erts/doc/src/*.fo
/erts/doc/xml/*.xml
+/erts/doc/xml/figures/*.png
+/erts/doc/chunks/*.chunk
/erts/doc/man[0-9]/*.[0-9]
/erts/doc/CONF_INFO
diff --git a/.travis.yml b/.travis.yml
index 5a87bd3fd1..b94af2281a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -36,7 +36,24 @@ matrix:
script:
- ./scripts/build-otp
- ./scripts/run-dialyzer
-
+ # Doc build second as it also takes a long time to run
+ - env: Linux64Docbuild
+ script:
+ - ./scripts/build-otp docs
+ deploy:
+ provider: pages
+ repo: erlang/cd
+ target-branch: master
+ skip-cleanup: true
+ keep-history: false
+ verbose: true
+ github-token: $ERLANG_CD_GITHUB_TOKEN
+ on:
+ # We only deploy on pushes to branches
+ all_branches: true
+ tags: false
+ condition: $TRAVIS_PULL_REQUEST = "false"
+ repo: erlang/otp
- env: Linux32
services:
- docker
@@ -70,23 +87,27 @@ matrix:
- xsltproc
- libxml2-utils
- - env: Linux64Docbuild
+ - env: Linux-s390x-SmokeTest
+ arch: s390x
script:
- - ./scripts/build-otp docs
- deploy:
- provider: pages
- repo: erlang/cd
- target-branch: master
- skip-cleanup: true
- keep-history: false
- verbose: true
- github-token: $ERLANG_CD_GITHUB_TOKEN
- on:
- # We only deploy on pushes to branches
- all_branches: true
- tags: false
- condition: $TRAVIS_PULL_REQUEST = "false"
- repo: erlang/otp
+ - ./scripts/build-otp
+ - ./otp_build tests
+ - ./scripts/run-smoke-tests
+ addons:
+ apt:
+ update: true
+ packages:
+ - autoconf
+ - libncurses-dev
+ - build-essential
+ - libssl-dev
+ - libwxgtk3.0-dev
+ - libglu1-mesa-dev
+ - default-jdk
+ - g++
+ - xsltproc
+ - libxml2-utils
+
# This stage publishes a otp bundle that contains multiple
# Erlang/OTP source repositories
- stage: deploy
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8814e506f9..b754fbd662 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ By making a contribution to this project, I certify that:
Erlang/otp is licensed under the
Apache License 2.0
-As stated in: LICENSE.txt
+As stated in: [LICENSE.txt](LICENSE.txt)
http://developercertificate.org/
@@ -95,6 +95,9 @@ a discussion on the mailing list.
* Make sure existing test cases don't fail. It is not necessary to run all tests (that would take many hours),
but you should at least run the tests for the application you have changed.
See [Running tests](https://github.com/erlang/otp/wiki/Running-tests).
+* Make sure the documentation builds and is according to the dtd. eg. `make xmllint` or `cd lib/stdlib/ && make xmllint`
+* Make sure no new dialyzer warnings have been added. eg. `make dialyzer` or `cd lib/stdlib/ && make dialyzer`
+* Make sure that travis passes, if you go to https://travis-ci.org/$YOUR_GITHUB_USER/otp/ you can enable travis builds for you otp fork.
Make sure that your branch contains clean commits:
diff --git a/HOWTO/INSTALL-ANDROID.md b/HOWTO/INSTALL-ANDROID.md
index 31698d4ce3..ad6aed95e7 100644
--- a/HOWTO/INSTALL-ANDROID.md
+++ b/HOWTO/INSTALL-ANDROID.md
@@ -4,48 +4,121 @@ Cross Compiling Erlang/OTP - ANDROID
Introduction
------------
-This document describes how to cross compile Erlang OTP to Android/Rasberry Pi platforms.
+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
+https://developer.android.com/ndk
+
### 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
+ $ export NDK_ROOT=/path/to/android-ndk
+ $ export PATH=$NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
+ $ # export PATH=$NDK_ROOT/toolchains/lvvm/prebuilt/darwin-x86_64/bin:$PATH
+
+
+### Configure Erlang/OTP ###
+
+If you are building Erlang/OTP from git, you will need to run this
+to generate the configure scripts.
+
+ $ ./otp_build autoconf
+
+
+Use the following when compiling a 64-bit version.
+
+ $ export NDK_ABI_PLAT=android21 # When targeting Android 5.0 Lollipop
+ $ ./otp_build configure \
+ --xcomp-conf=./xcomp/erl-xcomp-arm64-android.conf \
+ --without-ssl
+
+
+Use the following instead when compiling a 32-bit version.
+
+ $ export NDK_ABI_PLAT=androideabi16 # When targeting Android 4.1 Jelly Bean
+ $ ./otp_build configure \
+ --xcomp-conf=./xcomp/erl-xcomp-arm-android.conf \
+ --without-ssl
-### Configure OTP ###
-./otp_build configure \
- --xcomp-conf=./xcomp/erl-xcomp-arm-android.conf \
- --without-ssl
+### Compile Erlang/OTP ###
-### Compile OTP ###
+ $ # make noboot [-j4] # noboot doesn't work, is it a recent regression?
+ $ make [-j4]
-make noboot [-j4]
### Make Release ###
-./otp_build release -a /usr/local/otp_R16B03_arm
+ $ make RELEASE_ROOT=/path/to/release/erlang_23.0_arm release
-### 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
+### Target Deployment for Rasberry Pi ###
-./Install /usr/local/otp_R16B03_arm
+Make a tarball out of /path/to/release/erlang_23.0_arm and copy it to target
+device. Extract it and install.
-Android SDK (adb tool) is used to deploy OTP/Erlang to target device for
-evaluation purpose only.
+ $ ./Install /usr/local/erlang_23.0_arm
+
+
+### Target Deployment for Android testing ###
+
+The adb tool from the Android SDK can be used to deploy Erlang/OTP to a target
+Android device, for testing purpose mainly, as the /data/local/tmp path used
+for installation below is executable only from the adb shell command, but not
+from other local applications due to Android sandbox security model.
+
+ $ cd /path/to/release/erlang_23.0_arm
+ $ # For testing purpose, configure the Erlang/OTP scripts to use the target
+ $ # installation path in /data/local/tmp which is executable from adb shell
+ $ ./Install -cross -minimal /data/local/tmp/erlang_23.0
+
+To properly integrate into an Android application, the installation would have
+to target /data/data/[your/app/package/name]/files/[erlang/dir/once/unpacked]
+as shown in https://github.com/JeromeDeBretagne/erlanglauncher as an example.
+
+TODO: Propose a permanent fix for the following issue.
+Adapt the installation specifically for Android, by replacing manually /bin/sh
+into /system/bin/sh in the various Erlang/OTP release scripts, such as:
+ - bin/erl
+ - bin/start
+ - bin/start_erl
+ - erts-X.Y.Z/bin/erl
+ - erts-X.Y.Z/bin/erl.src
+ - erts-X.Y.Z/bin/start
+ - erts-X.Y.Z/bin/start_erl.src
+ - erts-X.Y.Z/bin/start.src
+ - etc.
+
+WARNING: adb has issues with symlinks (and java.util.zip too). There is only
+one symlink for epmd in recent Erlang/OTP releases (20 to master-based 23) so
+it has to be removed before using adb push, and then recreated manually on the
+target device itself if needed (or epmd can simply be duplicated instead).
+
+ $ # Make sure that the epmd symlink is not present before adb push
+ $ rm bin/epmd
+ $ cp erts-X.Y.Z/bin/epmd bin/epmd
+ $ cd ..
+ $ # The release can now be deployed in the pre-configured target directory
+ $ adb push erlang_23.0_arm /data/local/tmp/erlang_23.0
+
+Start an interactive shell onto the target Android device, and launch erl.
+
+ $ adb shell
+ :/ $ /data/local/tmp/erlang_23.0/bin/erl
+ Eshel VX.Y.Z (abort with ^G)
+ 1> q().
+ ok
+ 2> :/ $ # Erlang/OTP is running on Android, congratulations! :-)
-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)
+ * 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 ###
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 674454bc8e..3af9091468 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -217,6 +217,12 @@ Build the documentation.
$ make docs
+It is possible to limit which types of documentation is build by passing the `DOC_TARGETS`
+environment variable to `make docs`. The currently available types are: `html`, `pdf`, `man` and
+`chunks`. Example:
+
+ $ make docs DOC_TARGETS=chunks
+
#### Build Issues ####
We have sometimes experienced problems with Oracle's `java` running out of
@@ -247,6 +253,8 @@ or using the `release_docs` target.
$ make release_docs RELEASE_ROOT=<release dir>
+It is possible to limit which types of documentation is released using the same `DOC_TARGETS`
+environment variable as when building documentation.
### Accessing the Documentation ###
@@ -261,6 +269,8 @@ After installation you can access the documentation by
* 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.
+* Read the embedded documentation by using the built-in shell functions `h/1,2,3` or
+ `ht/1,2,3`.
### How to Install the Pre-formatted Documentation ###
@@ -353,6 +363,7 @@ Some of the available `configure` options are:
`(g)cc`
* `--enable-m32-build` - Build 32-bit binaries using the `-m32` flag to
`(g)cc`
+* `--{enable,disable}-pie` - Build position independent executable binaries.
* `--with-assumed-cache-line-size=SIZE` - Set assumed cache-line size in
bytes. Default is 64. Valid values are powers of two between and
including 16 and 8192. The runtime system use this value in order to
diff --git a/HOWTO/SYSTEMTAP.md b/HOWTO/SYSTEMTAP.md
index ce9c0b2f0c..76deac97e5 100644
--- a/HOWTO/SYSTEMTAP.md
+++ b/HOWTO/SYSTEMTAP.md
@@ -5,7 +5,7 @@ Introduction
------------
SystemTap is DTrace for Linux. In fact Erlang's SystemTap support
-is build using SystemTap's DTrace compatibility's layer. For an
+is built using SystemTap's DTrace compatibility's layer. For an
introduction to Erlang DTrace support read [$ERL_TOP/HOWTO/DTRACE.md][].
Requisites
diff --git a/HOWTO/TESTING.md b/HOWTO/TESTING.md
index ad59319efa..020be0309c 100644
--- a/HOWTO/TESTING.md
+++ b/HOWTO/TESTING.md
@@ -130,6 +130,52 @@ i.e.
Running [ct_run][] from the command line still requires you to do the
`ts:install()` step above.
+### Convenience for running tests without the release and configuration steps
+
+It can be convenient to run tests with a single command. This way, one
+do not need to worry about missing to run `make release_tests` after
+changing a test suite. The `make test` command can be used for this
+purpose. The `make test` command works when the current directory
+contains a directory called test and in the root directory of the
+source code tree.
+
+*(Waring)* Some test cases do not run correctly or cannot be run at
+all through the `make test` command (typically test cases that require
+test specific C code to be compiled) because `make test` runs tests
+directly by invoking the `ct_run` command instead of using the `ts`
+wrapper. One has to follow the procedure described above to run test
+cases that do not work with `make test`.
+
+Below are some examples that illustrate how `make test` can be
+used:
+
+ # ERL_TOP needs to be set correctly
+ cd /path/to/otp
+ export ERL_TOP=`pwd`
+
+ # Build Erlang/OTP
+ #
+ # Note that make test will only compile test code except when
+ # make test is executed from $ERL_TOP.
+ ./otp_build setup -a
+
+ # Run a test case (The ARGS variable is passed to ct_run)
+ (cd $ERL_TOP/erts/emulator && make ARGS="-suite binary_SUITE -case deep_bitstr_lists" test)
+
+ # Run a test suite
+ (cd $ERL_TOP/lib/stdlib && make ARGS="-suite ets_SUITE" test)
+
+ # Run all test suites for an application
+ (cd $ERL_TOP/lib/asn1 && make test)
+
+ # Run all tests
+ #
+ # When executed from $ERL_TOP, "make test" will first release and
+ # configure all tests and then attempt to run all tests with `ts:run`.
+ # This will take several hours.
+ (cd $ERL_TOP && make test)
+
+
Examining the results
---------------------
diff --git a/Makefile.in b/Makefile.in
index 622f5a4a01..58535524aa 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -518,6 +518,15 @@ $(APPS):
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) $(TYPE) BUILD_ALL=true
+##
+## Generate `otp_internal` from the -deprecated() attributes in source.
+##
+deprecations: all
+ $(ERL_TOP)/lib/stdlib/scripts/update_deprecations \
+ $(ERL_TOP)/lib/*/ebin $(ERL_TOP)/erts/preloaded/ebin \
+ > $(ERL_TOP)/lib/stdlib/src/otp_internal.erl \
+ && $(MAKE) stdlib
+
preloaded:
$(make_verbose)cd erts/preloaded/src && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
@@ -1206,3 +1215,11 @@ bootstrap_clean:
|| $(MAKE) BOOTSTRAP_ROOT=$(BOOTSTRAP_ROOT) bootstrap_root_clean
# ----------------------------------------------------------------------
+
+.PHONY: test dialyzer
+
+test: all release release_tests
+ $(ERL_TOP)/make/test_target_script.sh $(ERL_TOP)
+
+dialyzer: all
+ $(ERL_TOP)/scripts/run-dialyzer
diff --git a/OTP_VERSION b/OTP_VERSION
index 3abee4573a..0af13d0821 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-22.2.8
+23.0-rc1
diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot
index 74de5dea07..d68b39a9a4 100644
--- a/bootstrap/bin/no_dot_erlang.boot
+++ b/bootstrap/bin/no_dot_erlang.boot
Binary files differ
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 74de5dea07..d68b39a9a4 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 74de5dea07..d68b39a9a4 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index 69f3c8e451..f9bcd13b57 100644
--- a/bootstrap/lib/compiler/ebin/beam_a.beam
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 4b8481dd11..b1a1631953 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index 1db56cb5f1..cdac62f730 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_call_types.beam b/bootstrap/lib/compiler/ebin/beam_call_types.beam
new file mode 100644
index 0000000000..ce52df622d
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_call_types.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 4d567eca88..3494688fbc 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index b9317a55ee..8ddfe50682 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_digraph.beam b/bootstrap/lib/compiler/ebin/beam_digraph.beam
new file mode 100644
index 0000000000..eab1e25cdb
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_digraph.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index eb2a13d620..facbe9d261 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_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam
deleted file mode 100644
index e0a0e4ca6f..0000000000
--- a/bootstrap/lib/compiler/ebin/beam_except.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index d7780861c2..93a34eda47 100644
--- a/bootstrap/lib/compiler/ebin/beam_flatten.beam
+++ b/bootstrap/lib/compiler/ebin/beam_flatten.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 289c08630a..6feff53f2e 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_kernel_to_ssa.beam b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
index 96362c8cdd..970c66b301 100644
--- a/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_listing.beam b/bootstrap/lib/compiler/ebin/beam_listing.beam
index 097ba5e7ff..d67ccd1854 100644
--- a/bootstrap/lib/compiler/ebin/beam_listing.beam
+++ b/bootstrap/lib/compiler/ebin/beam_listing.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 52a23f7f6c..601c6881a8 100644
--- a/bootstrap/lib/compiler/ebin/beam_opcodes.beam
+++ b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index 9246bd1c27..66e11fbf58 100644
--- a/bootstrap/lib/compiler/ebin/beam_peep.beam
+++ b/bootstrap/lib/compiler/ebin/beam_peep.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa.beam b/bootstrap/lib/compiler/ebin/beam_ssa.beam
index 0327230a58..b0e55d0cf4 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_bool.beam b/bootstrap/lib/compiler/ebin/beam_ssa_bool.beam
new file mode 100644
index 0000000000..2cfe66142d
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_bool.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
index 3e1bb229ed..3ef519d6c0 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
index cb06630012..76b03b5114 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
index 782073698e..7e3d84ddb2 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
index 6370e1eb78..1d5c58725c 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
index e0a43057fc..b6ca3d8a78 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
index b8588725b4..bba369f0bd 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
index f76f15aa70..5c75c3afec 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
index 7b68634bb3..63a0f52c48 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
index 6a5b747025..8dfde56ce4 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
index d975cebf48..58ce110308 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
index 0403230604..20d4873090 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 00f3224106..06f05be6ac 100644
--- a/bootstrap/lib/compiler/ebin/beam_trim.beam
+++ b/bootstrap/lib/compiler/ebin/beam_trim.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_types.beam b/bootstrap/lib/compiler/ebin/beam_types.beam
new file mode 100644
index 0000000000..b139ec4c1c
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_types.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 996525efc9..1b06a5e2b8 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index b08f629092..d939baca71 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 21ca7bcf46..aed5028582 100644
--- a/bootstrap/lib/compiler/ebin/beam_z.beam
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 948c4759b0..c8cee8870e 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 a20a4ca43b..34e810e7d0 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 7bf92a0dd7..9438c6c038 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_sets.beam b/bootstrap/lib/compiler/ebin/cerl_sets.beam
index bda70130a6..cfb8b7289f 100644
--- a/bootstrap/lib/compiler/ebin/cerl_sets.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_sets.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index 831eddc405..0340c77ad1 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 540672bb22..3f13db253c 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 9e2b619667..a7b43d788d 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -19,15 +19,16 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "7.4.9"},
+ {vsn, "7.5.1"},
{modules, [
beam_a,
beam_asm,
beam_block,
+ beam_call_types,
beam_clean,
beam_dict,
+ beam_digraph,
beam_disasm,
- beam_except,
beam_flatten,
beam_jump,
beam_kernel_to_ssa,
@@ -35,6 +36,7 @@
beam_opcodes,
beam_peep,
beam_ssa,
+ beam_ssa_bool,
beam_ssa_bsm,
beam_ssa_codegen,
beam_ssa_dead,
@@ -47,6 +49,7 @@
beam_ssa_share,
beam_ssa_type,
beam_trim,
+ beam_types,
beam_utils,
beam_validator,
beam_z,
@@ -68,6 +71,7 @@
sys_core_fold,
sys_core_fold_lists,
sys_core_inline,
+ sys_core_prepare,
sys_pre_attributes,
v3_core,
v3_kernel,
@@ -76,5 +80,5 @@
{registered, []},
{applications, [kernel, stdlib]},
{env, []},
- {runtime_dependencies, ["stdlib-2.5","kernel-4.0","hipe-3.12","erts-9.0",
+ {runtime_dependencies, ["stdlib-@OTP-15251@","kernel-@OTP-15251@","hipe-3.12","erts-@OTP-15251@",
"crypto-3.6"]}]}.
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index 19b0c3dfcb..aebf95b9ce 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 ecad4f33e0..c86fa94e21 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 f14357cf02..4d6fb708ec 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 a2b3167e10..4755d0dc87 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/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 8e44464cd7..5901eb80aa 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index e626e91f4f..06e497b1d2 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/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam
index 6d0c5baa04..64119fb5a0 100644
--- a/bootstrap/lib/compiler/ebin/rec_env.beam
+++ b/bootstrap/lib/compiler/ebin/rec_env.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_alias.beam b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
index b769b6be41..bbb40e4b4d 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_alias.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam b/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
index 6f4204e0a1..50903e68f3 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_bsm.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 d4746e3664..3542c4a403 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_core_fold_lists.beam b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
index f4aac9ac13..7a6d5fb047 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_inline.beam b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
index 5647c8fcb5..404b4ce9bf 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_inline.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_prepare.beam b/bootstrap/lib/compiler/ebin/sys_core_prepare.beam
new file mode 100644
index 0000000000..d973d575b7
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/sys_core_prepare.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
index 002b4f2914..03b9744ca5 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 3cbbdfd88b..73a679e59a 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 4be5b827e4..2418bdcbde 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 5177b65198..d6daf52522 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/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index 84c5b7636e..4d04b88ec5 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/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index b5d6545ca7..5e8da1c458 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index 77bb9544e2..08c6be7cd7 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_starter.beam b/bootstrap/lib/kernel/ebin/application_starter.beam
index 0daab463ab..c703c9ee0f 100644
--- a/bootstrap/lib/kernel/ebin/application_starter.beam
+++ b/bootstrap/lib/kernel/ebin/application_starter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index c77178bf7c..8799b21abd 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index 204b15fcf2..cf05fe7a6e 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index 637e40f5d8..f7c7141352 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index 25142ee575..4296e55bba 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index 0a781001dc..6406bdb7cb 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index 3b961dcefa..67582bf7b6 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/disk_log_sup.beam b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
index 67099f7212..570af6d495 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_sup.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index e82fc7dc75..e4606dbf30 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index 8fa128dd23..99b21b8189 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
index 801e3d8b88..0f6e93795a 100644
--- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_compile_server.beam b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
index 28d79c2efb..97c108b30d 100644
--- a/bootstrap/lib/kernel/ebin/erl_compile_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index c04bc43872..a4555a39ab 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_distribution.beam b/bootstrap/lib/kernel/ebin/erl_distribution.beam
index 4490f9c81f..216ff87678 100644
--- a/bootstrap/lib/kernel/ebin/erl_distribution.beam
+++ b/bootstrap/lib/kernel/ebin/erl_distribution.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 57dd1b8735..5c0ec49ea6 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_reply.beam b/bootstrap/lib/kernel/ebin/erl_reply.beam
index 7658a35642..e74194895d 100644
--- a/bootstrap/lib/kernel/ebin/erl_reply.beam
+++ b/bootstrap/lib/kernel/ebin/erl_reply.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
index da03769e2e..f15afbdc68 100644
--- a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
+++ b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index 19739b0043..7e06588ea2 100644
--- a/bootstrap/lib/kernel/ebin/error_handler.beam
+++ b/bootstrap/lib/kernel/ebin/error_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index 10b13c35b6..686fdf4cb3 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index e0b759eee3..09a6864c09 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index a7a82a1f12..cccc8f13be 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam
index b8f1272be4..dd010f72aa 100644
--- a/bootstrap/lib/kernel/ebin/file_io_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_io_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam
index cc2face79c..76852ac2c4 100644
--- a/bootstrap/lib/kernel/ebin/file_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam
index 9fc5800f0c..31d1cc9185 100644
--- a/bootstrap/lib/kernel/ebin/gen_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_tcp.beam b/bootstrap/lib/kernel/ebin/gen_tcp.beam
index 736b28c68f..9d33150e1e 100644
--- a/bootstrap/lib/kernel/ebin/gen_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_udp.beam b/bootstrap/lib/kernel/ebin/gen_udp.beam
index 0d6541ee53..de97446ecc 100644
--- a/bootstrap/lib/kernel/ebin/gen_udp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index aadb5bf080..1838386e3a 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 83885cdeb5..2f4b03e228 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_search.beam b/bootstrap/lib/kernel/ebin/global_search.beam
index a94d9da454..6adc30b7bb 100644
--- a/bootstrap/lib/kernel/ebin/global_search.beam
+++ b/bootstrap/lib/kernel/ebin/global_search.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index dcec55421c..4a41056760 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group_history.beam b/bootstrap/lib/kernel/ebin/group_history.beam
index b9df7cfc14..e07fdffc5a 100644
--- a/bootstrap/lib/kernel/ebin/group_history.beam
+++ b/bootstrap/lib/kernel/ebin/group_history.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index 1b63142893..55ba5a65cc 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index de7d9c2e57..ad23e72f58 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 45d7b10040..3af51ac479 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_sctp.beam b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
index a6220a9d69..d1f974115f 100644
--- a/bootstrap/lib/kernel/ebin/inet6_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
index 7cacde3399..8e9bfc1fc8 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index d1ca0b4f0d..aa53ec19db 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_udp.beam b/bootstrap/lib/kernel/ebin/inet6_udp.beam
index ac4be100e8..0bfdaa353b 100644
--- a/bootstrap/lib/kernel/ebin/inet6_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index c44b389b75..25b2df43de 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/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index a835fa1a74..63d35729a5 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index d88fa2185d..2ff0230379 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
index 8166eca5de..5b9460572b 100644
--- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
+++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_hosts.beam b/bootstrap/lib/kernel/ebin/inet_hosts.beam
index 6797eecb29..c85ca9cf8e 100644
--- a/bootstrap/lib/kernel/ebin/inet_hosts.beam
+++ b/bootstrap/lib/kernel/ebin/inet_hosts.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index e8935134b5..880e065055 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index c964152d79..5c1a210ede 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam
index 0e659bd738..9157154021 100644
--- a/bootstrap/lib/kernel/ebin/inet_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam
index c273d47012..4658fa2f4e 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index d09aa250c8..c6460c28a8 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam
index 5853bde54f..5ca6c7adc2 100644
--- a/bootstrap/lib/kernel/ebin/inet_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index 2f6252bbb0..324bd512ad 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -22,7 +22,7 @@
{application, kernel,
[
{description, "ERTS CXC 138 10"},
- {vsn, "6.5"},
+ {vsn, "6.5.1"},
{modules, [application,
application_controller,
application_master,
@@ -103,6 +103,7 @@
inet_tcp,
inet_udp,
inet_sctp,
+ pg,
pg2,
raw_file_io,
raw_file_io_compressed,
@@ -143,12 +144,13 @@
ddll_server,
erl_epmd,
inet_db,
+ pg,
pg2]},
{applications, []},
{env, [{logger_level, notice},
{logger_sasl_compatible, false}
]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-@OTP-16216@", "stdlib-3.5", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-@OTP-15251@", "stdlib-@OTP-15251@", "sasl-3.0"]}
]
}.
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 5b93fe805b..3d97857282 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index e749cc6c96..f0793bff3b 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_refc.beam b/bootstrap/lib/kernel/ebin/kernel_refc.beam
index 04306c6e2c..96da03d795 100644
--- a/bootstrap/lib/kernel/ebin/kernel_refc.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_refc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_tcp.beam b/bootstrap/lib/kernel/ebin/local_tcp.beam
index 2958d19524..49ff2386b3 100644
--- a/bootstrap/lib/kernel/ebin/local_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/local_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_udp.beam b/bootstrap/lib/kernel/ebin/local_udp.beam
index 33ed76e5cd..13584f7024 100644
--- a/bootstrap/lib/kernel/ebin/local_udp.beam
+++ b/bootstrap/lib/kernel/ebin/local_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger.beam b/bootstrap/lib/kernel/ebin/logger.beam
index 8cd5613a2d..163d589709 100644
--- a/bootstrap/lib/kernel/ebin/logger.beam
+++ b/bootstrap/lib/kernel/ebin/logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_backend.beam b/bootstrap/lib/kernel/ebin/logger_backend.beam
index 40a3c4d80a..576d49603b 100644
--- a/bootstrap/lib/kernel/ebin/logger_backend.beam
+++ b/bootstrap/lib/kernel/ebin/logger_backend.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_config.beam b/bootstrap/lib/kernel/ebin/logger_config.beam
index 51c7f78237..8dba727792 100644
--- a/bootstrap/lib/kernel/ebin/logger_config.beam
+++ b/bootstrap/lib/kernel/ebin/logger_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
index dd0ca5f204..89eab84dbc 100644
--- a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_filters.beam b/bootstrap/lib/kernel/ebin/logger_filters.beam
index 2d8035278d..ac28d8c9d0 100644
--- a/bootstrap/lib/kernel/ebin/logger_filters.beam
+++ b/bootstrap/lib/kernel/ebin/logger_filters.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_formatter.beam b/bootstrap/lib/kernel/ebin/logger_formatter.beam
index e3e40460f0..c6fd2abc6c 100644
--- a/bootstrap/lib/kernel/ebin/logger_formatter.beam
+++ b/bootstrap/lib/kernel/ebin/logger_formatter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_h_common.beam b/bootstrap/lib/kernel/ebin/logger_h_common.beam
index 9e8c25b6b9..f40cbb8702 100644
--- a/bootstrap/lib/kernel/ebin/logger_h_common.beam
+++ b/bootstrap/lib/kernel/ebin/logger_h_common.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam b/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
index fb20fd1818..e64ad85427 100644
--- a/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
+++ b/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_olp.beam b/bootstrap/lib/kernel/ebin/logger_olp.beam
index ed06b6ae5e..68a9095277 100644
--- a/bootstrap/lib/kernel/ebin/logger_olp.beam
+++ b/bootstrap/lib/kernel/ebin/logger_olp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_proxy.beam b/bootstrap/lib/kernel/ebin/logger_proxy.beam
index ed0ed0f56b..b81289c73e 100644
--- a/bootstrap/lib/kernel/ebin/logger_proxy.beam
+++ b/bootstrap/lib/kernel/ebin/logger_proxy.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_server.beam b/bootstrap/lib/kernel/ebin/logger_server.beam
index dcff9beac7..282a823526 100644
--- a/bootstrap/lib/kernel/ebin/logger_server.beam
+++ b/bootstrap/lib/kernel/ebin/logger_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_simple_h.beam b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
index 583c77b290..5eb5c69513 100644
--- a/bootstrap/lib/kernel/ebin/logger_simple_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_std_h.beam b/bootstrap/lib/kernel/ebin/logger_std_h.beam
index 679e2573c2..d96c3f0e2b 100644
--- a/bootstrap/lib/kernel/ebin/logger_std_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_std_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_sup.beam b/bootstrap/lib/kernel/ebin/logger_sup.beam
index 60192ef4a1..d866563a79 100644
--- a/bootstrap/lib/kernel/ebin/logger_sup.beam
+++ b/bootstrap/lib/kernel/ebin/logger_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net.beam b/bootstrap/lib/kernel/ebin/net.beam
index 434c08737f..21b60335c6 100644
--- a/bootstrap/lib/kernel/ebin/net.beam
+++ b/bootstrap/lib/kernel/ebin/net.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 5810577c6a..9206927979 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 853c8c561d..279889d701 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index f05007041b..06c4c97c37 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/pg.beam b/bootstrap/lib/kernel/ebin/pg.beam
new file mode 100644
index 0000000000..b7153d5100
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/pg.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index d293ea18f3..65faafc80d 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/ram_file.beam b/bootstrap/lib/kernel/ebin/ram_file.beam
index 02c4c6454a..b75394e007 100644
--- a/bootstrap/lib/kernel/ebin/ram_file.beam
+++ b/bootstrap/lib/kernel/ebin/ram_file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io.beam b/bootstrap/lib/kernel/ebin/raw_file_io.beam
index 16dc314ba5..3802b11701 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
index 4f42134f4a..4562900b86 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
index 11b8f4a6db..2af151bdb5 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
index ec7492f62a..643f928d7d 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
index 7c47f2cf10..2b3dc87ddc 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_list.beam b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
index b544fd394e..1185a1798f 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam b/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
index b47a67055d..ef11ec329c 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index b53525e2ca..799986228e 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/seq_trace.beam b/bootstrap/lib/kernel/ebin/seq_trace.beam
index f81a39ddc0..3781fee0d9 100644
--- a/bootstrap/lib/kernel/ebin/seq_trace.beam
+++ b/bootstrap/lib/kernel/ebin/seq_trace.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/standard_error.beam b/bootstrap/lib/kernel/ebin/standard_error.beam
index 18582d22f2..bb945e48ee 100644
--- a/bootstrap/lib/kernel/ebin/standard_error.beam
+++ b/bootstrap/lib/kernel/ebin/standard_error.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index 8214d9d6aa..2342a29a54 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 43df5afa36..df15a88223 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_sup.beam b/bootstrap/lib/kernel/ebin/user_sup.beam
index c82fca70da..b0de6cf7d1 100644
--- a/bootstrap/lib/kernel/ebin/user_sup.beam
+++ b/bootstrap/lib/kernel/ebin/user_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index ceb8dd9a78..55060bfc79 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/dist.hrl b/bootstrap/lib/kernel/include/dist.hrl
index f06fc328d7..3cc825fca6 100644
--- a/bootstrap/lib/kernel/include/dist.hrl
+++ b/bootstrap/lib/kernel/include/dist.hrl
@@ -44,7 +44,18 @@
-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000).
%% -define(DFLAG_NO_MAGIC, 16#200000). %% Used internally only
-define(DFLAG_EXIT_PAYLOAD, 16#400000).
--define(DFLAG_FRAGMENTS, 16#800000).
+-define(DFLAG_FRAGMENTS, 16#00800000).
+-define(DFLAG_HANDSHAKE_23, 16#01000000).
+-define(DFLAG_RESERVED, 16#fe000000).
+-define(DFLAG_SPAWN, 16#100000000).
%% Also update dflag2str() in ../src/dist_util.erl
%% when adding flags...
+
+
+-define(ERL_DIST_VER_5, 5). % OTP-22 or (much) older
+-define(ERL_DIST_VER_6, 6). % OTP-23 (or maybe newer?)
+
+-define(ERL_DIST_VER_LOW, ?ERL_DIST_VER_5).
+-define(ERL_DIST_VER_HIGH, ?ERL_DIST_VER_6).
+
diff --git a/bootstrap/lib/kernel/include/dist_util.hrl b/bootstrap/lib/kernel/include/dist_util.hrl
index 56f775f060..05c7eee795 100644
--- a/bootstrap/lib/kernel/include/dist_util.hrl
+++ b/bootstrap/lib/kernel/include/dist_util.hrl
@@ -84,7 +84,10 @@
f_handshake_complete, %% Notify handshake complete
add_flags, %% dflags to add
reject_flags, %% dflags not to use (not all can be rejected)
- require_flags %% dflags that are required
+ require_flags, %% dflags that are required
+
+ %% New in kernel-@master@ (OTP-23.0)
+ other_creation
}).
diff --git a/bootstrap/lib/kernel/include/eep48.hrl b/bootstrap/lib/kernel/include/eep48.hrl
new file mode 100644
index 0000000000..2ce9a1430a
--- /dev/null
+++ b/bootstrap/lib/kernel/include/eep48.hrl
@@ -0,0 +1,14 @@
+-define(NATIVE_FORMAT,<<"application/erlang+html">>).
+-define(CURR_DOC_VERSION, {1,0,0}).
+-record(docs_v1, {anno,
+ beam_language = erlang,
+ format = ?NATIVE_FORMAT,
+ module_doc,
+ metadata = #{ otp_doc_vsn => ?CURR_DOC_VERSION },
+ docs}).
+
+-record(docs_v1_entry, {kind_name_arity,
+ anno,
+ signature,
+ doc,
+ metadata}).
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 9e783452f5..e734a9967b 100644
--- a/bootstrap/lib/stdlib/ebin/array.beam
+++ b/bootstrap/lib/stdlib/ebin/array.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/base64.beam b/bootstrap/lib/stdlib/ebin/base64.beam
index 5872970da7..3893851b25 100644
--- a/bootstrap/lib/stdlib/ebin/base64.beam
+++ b/bootstrap/lib/stdlib/ebin/base64.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam
index a96ccb6f8c..f3d5afe210 100644
--- a/bootstrap/lib/stdlib/ebin/beam_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index fc1bbe163a..a5022bed68 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/c.beam b/bootstrap/lib/stdlib/ebin/c.beam
index 17e061b1d0..afedd7d0a8 100644
--- a/bootstrap/lib/stdlib/ebin/c.beam
+++ b/bootstrap/lib/stdlib/ebin/c.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/calendar.beam b/bootstrap/lib/stdlib/ebin/calendar.beam
index 89102adda8..5aa3968415 100644
--- a/bootstrap/lib/stdlib/ebin/calendar.beam
+++ b/bootstrap/lib/stdlib/ebin/calendar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 2eadde21ff..ad90a53078 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 8cd247e3be..967267b9c2 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_sup.beam b/bootstrap/lib/stdlib/ebin/dets_sup.beam
index 0b9fb6379f..c14efa6c28 100644
--- a/bootstrap/lib/stdlib/ebin/dets_sup.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_sup.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index e38eb384a1..8d7dab605e 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam
index f73509ae3f..3a4806a008 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v9.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dict.beam b/bootstrap/lib/stdlib/ebin/dict.beam
index dc390024a4..234e1eb851 100644
--- a/bootstrap/lib/stdlib/ebin/dict.beam
+++ b/bootstrap/lib/stdlib/ebin/dict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index 60abe020fd..aeca4eaf86 100644
--- a/bootstrap/lib/stdlib/ebin/digraph.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
index fabe782d90..bdd382fec7 100644
--- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index b6a7ecc9a5..6135a214d9 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index 865066c3ac..8c6340221b 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index e3d8c21edf..d1cc97aadc 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_abstract_code.beam b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
index 973ad9fb80..8046b67672 100644
--- a/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam
index a40fcb3f92..f422046b72 100644
--- a/bootstrap/lib/stdlib/ebin/erl_anno.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 1beb0d5ebb..80daf0155a 100644
--- a/bootstrap/lib/stdlib/ebin/erl_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index 5dd5691b3b..9c93ed3076 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_error.beam b/bootstrap/lib/stdlib/ebin/erl_error.beam
index 5d1b91aeef..5ebe9584cf 100644
--- a/bootstrap/lib/stdlib/ebin/erl_error.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_error.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 5ef1521371..55b8e34ae6 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 e527f98776..c942d68d59 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_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 9015ad8936..0b64bc5345 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 3004cbf251..d2275702d4 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 41369ca147..2ce37c7d18 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_posix_msg.beam b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
index 5f6c03bcae..1b3edde848 100644
--- a/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 1965b9e656..b9358bda26 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/erl_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 18538620b7..f1b505b111 100644
--- a/bootstrap/lib/stdlib/ebin/erl_scan.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_scan.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index 871ccd82d4..dd457db874 100644
--- a/bootstrap/lib/stdlib/ebin/erl_tar.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_tar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 64de26f7a4..0d6208ce45 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index e7ee41cc2e..4094f0d739 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index b1c6f58180..069ee49e16 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index 350316dfcc..21254b5522 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index 08f86f86ff..c4941fa92f 100644
--- a/bootstrap/lib/stdlib/ebin/eval_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/eval_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 0a42a40d7e..b11e38927c 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam
index fd719cd85d..24e605dd77 100644
--- a/bootstrap/lib/stdlib/ebin/filelib.beam
+++ b/bootstrap/lib/stdlib/ebin/filelib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index d353a11f3e..456a07b279 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index 8d1e2c11e0..0db05e4956 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index 0aae509cb6..3c21af6bd9 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index 85a9c25618..7b6b4e69f7 100644
--- a/bootstrap/lib/stdlib/ebin/gen.beam
+++ b/bootstrap/lib/stdlib/ebin/gen.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index f3376d2ee2..7ad62fcc5c 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 5c4a0aa3d5..a58e1256f4 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 06c3d6de19..d9703c5c65 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index 38633cd911..83c3429d77 100644
--- a/bootstrap/lib/stdlib/ebin/gen_statem.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_statem.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam
index 631863b587..3eff6444fc 100644
--- a/bootstrap/lib/stdlib/ebin/io.beam
+++ b/bootstrap/lib/stdlib/ebin/io.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index 45692978e4..95e3c862dc 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
index 66047b5070..c19f475970 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
index 22690ce685..140327af25 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index b0f4177d71..7f420c9eb4 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 5a330bece0..6c63b8d3b7 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index b812621c0a..743487b523 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 193a06241d..3aec184bf5 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/math.beam b/bootstrap/lib/stdlib/ebin/math.beam
index 7e61673b35..e388b2c304 100644
--- a/bootstrap/lib/stdlib/ebin/math.beam
+++ b/bootstrap/lib/stdlib/ebin/math.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 750f8b9185..96036877a7 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/orddict.beam b/bootstrap/lib/stdlib/ebin/orddict.beam
index d6f267ad8f..cc32d43f7a 100644
--- a/bootstrap/lib/stdlib/ebin/orddict.beam
+++ b/bootstrap/lib/stdlib/ebin/orddict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ordsets.beam b/bootstrap/lib/stdlib/ebin/ordsets.beam
index 44f9e5d8b5..4f2465c9bb 100644
--- a/bootstrap/lib/stdlib/ebin/ordsets.beam
+++ b/bootstrap/lib/stdlib/ebin/ordsets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index 26d495af72..eeb9ec57d8 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/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index e8e339dfa0..698a8d3bd1 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proc_lib.beam b/bootstrap/lib/stdlib/ebin/proc_lib.beam
index b51898901d..86c31c02d5 100644
--- a/bootstrap/lib/stdlib/ebin/proc_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/proc_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proplists.beam b/bootstrap/lib/stdlib/ebin/proplists.beam
index a8ddeb2d57..247c7ca209 100644
--- a/bootstrap/lib/stdlib/ebin/proplists.beam
+++ b/bootstrap/lib/stdlib/ebin/proplists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index cca174e3cd..d4ffde656d 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 4d86fdfec2..91dd75fb46 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/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam
index 94728e30cc..a709b35e2b 100644
--- a/bootstrap/lib/stdlib/ebin/queue.beam
+++ b/bootstrap/lib/stdlib/ebin/queue.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/rand.beam b/bootstrap/lib/stdlib/ebin/rand.beam
index 7f1e6140bf..bdae4cd318 100644
--- a/bootstrap/lib/stdlib/ebin/rand.beam
+++ b/bootstrap/lib/stdlib/ebin/rand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/random.beam b/bootstrap/lib/stdlib/ebin/random.beam
index a4bc2b6128..a420647dbe 100644
--- a/bootstrap/lib/stdlib/ebin/random.beam
+++ b/bootstrap/lib/stdlib/ebin/random.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index e16327b906..51187f3fe0 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sets.beam b/bootstrap/lib/stdlib/ebin/sets.beam
index e3609ea527..963d5e81e1 100644
--- a/bootstrap/lib/stdlib/ebin/sets.beam
+++ b/bootstrap/lib/stdlib/ebin/sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index 319bb9aebf..a370e30981 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell_default.beam b/bootstrap/lib/stdlib/ebin/shell_default.beam
index afd0c65921..ff4d1071d5 100644
--- a/bootstrap/lib/stdlib/ebin/shell_default.beam
+++ b/bootstrap/lib/stdlib/ebin/shell_default.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell_docs.beam b/bootstrap/lib/stdlib/ebin/shell_docs.beam
new file mode 100644
index 0000000000..82bf544fc1
--- /dev/null
+++ b/bootstrap/lib/stdlib/ebin/shell_docs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index 259d300155..97bea03b29 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 3a90a07add..0b91e8e940 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index 59fc04f13f..8a98867c25 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -20,7 +20,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "3.10"},
+ {vsn, "3.11.2"},
{modules, [array,
base64,
beam_lib,
@@ -108,7 +108,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-16052@","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-@OTP-15251@","erts-@OTP-15251@","crypto-3.3",
"compiler-5.0"]}
]}.
-
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index ee19fb41a4..9a58fa2259 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 1816fe6c41..e1fcdfa9f4 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index 761907b40c..d11613b068 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index a3f46b5983..91f82a6067 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/timer.beam b/bootstrap/lib/stdlib/ebin/timer.beam
index 2953299e8b..b6b67e8f44 100644
--- a/bootstrap/lib/stdlib/ebin/timer.beam
+++ b/bootstrap/lib/stdlib/ebin/timer.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index d61ed589aa..9a66bac177 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode_util.beam b/bootstrap/lib/stdlib/ebin/unicode_util.beam
index c0523f2e52..6867a779e8 100644
--- a/bootstrap/lib/stdlib/ebin/unicode_util.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode_util.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/uri_string.beam b/bootstrap/lib/stdlib/ebin/uri_string.beam
index 7255fdca63..70b6213783 100644
--- a/bootstrap/lib/stdlib/ebin/uri_string.beam
+++ b/bootstrap/lib/stdlib/ebin/uri_string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/win32reg.beam b/bootstrap/lib/stdlib/ebin/win32reg.beam
index 136b65f9fb..c9c29747a0 100644
--- a/bootstrap/lib/stdlib/ebin/win32reg.beam
+++ b/bootstrap/lib/stdlib/ebin/win32reg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index 75c2289f47..608396886b 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/configure.src b/configure.src
index 4b748f2545..ff39a58aa9 100644
--- a/configure.src
+++ b/configure.src
@@ -37,6 +37,8 @@ unset CDPATH
default_cflags="-g -O2"
+pie_cflags=
+pie_ldflags=
mXY_build=
static_cache=
@@ -125,6 +127,14 @@ while test $# != 0; do
if test "$mXY_build" = "-m32"; then
mXY_build=
fi;;
+ --enable-pie)
+ pie_cflags="-fPIE"
+ pie_ldflags="-pie"
+ ;;
+ --disable-pie)
+ pie_cflags="-fno-PIE"
+ pie_ldflags="-no-pie"
+ ;;
CFLAGS=* | LDFLAGS=*)
flgs_var=`expr "$1" : '\([^=]*\)=.*'`
flgs_val=`expr "$1" : '[^=]*=\(.*\)'`
@@ -263,7 +273,7 @@ case "$help" in
exit 0;;
esac
-if test "$mXY_build" = ""; then
+if test "$mXY_build" = "" && test "$pie_cflags" = ""; then
if test "$CFLAGS" != ""; then
config_arguments="$config_arguments CFLAGS='$CFLAGS'"
unset CFLAGS
@@ -277,9 +287,9 @@ else
if test "$CFLAGS" = ""; then
CFLAGS=$default_cflags
fi
- config_arguments="$config_arguments CFLAGS='$mXY_build $CFLAGS'"
+ config_arguments="$config_arguments CFLAGS='$mXY_build $pie_cflags $CFLAGS'"
unset CFLAGS
- config_arguments="$config_arguments LDFLAGS='$mXY_build $LDFLAGS'"
+ config_arguments="$config_arguments LDFLAGS='$mXY_build $pie_ldflags $LDFLAGS'"
unset LDFLAGS
case $mXY_build in
-m32)
diff --git a/erts/.gitignore b/erts/.gitignore
index e515dc8811..6631fc883e 100644
--- a/erts/.gitignore
+++ b/erts/.gitignore
@@ -21,3 +21,4 @@
/emulator/test/*_no_opt_SUITE.erl
/emulator/pcre/pcre_exec_loop_break_cases.inc
+/emulator/beam/erl_db_insert_list.ycf.h
diff --git a/erts/Makefile b/erts/Makefile
index e62c896170..12ac50f7d5 100644
--- a/erts/Makefile
+++ b/erts/Makefile
@@ -153,3 +153,5 @@ release_docs:
.PHONY: xmllint
xmllint:
$(MAKE) -C doc/src $@
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 5d274e69c3..022e22fa62 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -116,8 +116,8 @@ dnl
dnl LM_WINDOWS_ENVIRONMENT
dnl
dnl
-dnl Tries to determine thw windows build environment, i.e.
-dnl MIXED_CYGWIN_VC or MIXED_MSYS_VC
+dnl Tries to determine the windows build environment, i.e.
+dnl MIXED_VC or MIXED_MINGW
dnl
AC_DEFUN(LM_WINDOWS_ENVIRONMENT,
@@ -127,36 +127,39 @@ if test "X$windows_environment_" != "Xchecked"; then
windows_environment_=checked
MIXED_CYGWIN=no
MIXED_MSYS=no
+MIXED_VSL=no
-AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
+dnl MIXED_VC is Microsoft Visual C++ used as standard compiler
+MIXED_VC=no
+dnl MIXED_MINGW is mingw(32|64) used as standard compiler
+MIXED_MINGW=no
+
+AC_MSG_CHECKING(for mixed mingw-gcc and native VC++ environment)
if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
if test -x /usr/bin/msys-?.0.dll; then
CFLAGS="$CFLAGS -O2"
MIXED_MSYS=yes
AC_MSG_RESULT([MSYS and VC])
- MIXED_MSYS_VC=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
elif test -x /usr/bin/cygpath; then
CFLAGS="$CFLAGS -O2"
MIXED_CYGWIN=yes
AC_MSG_RESULT([Cygwin and VC])
- MIXED_CYGWIN_VC=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC"
- else
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
+ elif test -x /bin/wslpath; then
+ CFLAGS="$CFLAGS -O2"
+ MIXED_WSL=yes
+ AC_MSG_RESULT([WSL and VC])
+ MIXED_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_VC"
+ else
AC_MSG_RESULT([undeterminable])
- AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
+ AC_MSG_ERROR(Seems to be mixed windows but not within any known env, cannot handle this!)
fi
else
AC_MSG_RESULT([no])
- MIXED_CYGWIN_VC=no
- MIXED_MSYS_VC=no
-fi
-AC_SUBST(MIXED_CYGWIN_VC)
-AC_SUBST(MIXED_MSYS_VC)
-
-MIXED_VC=no
-if test "x$MIXED_MSYS_VC" = "xyes" -o "x$MIXED_CYGWIN_VC" = "xyes" ; then
- MIXED_VC=yes
fi
AC_SUBST(MIXED_VC)
@@ -166,44 +169,58 @@ if test "x$MIXED_MSYS" != "xyes"; then
if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then
if test -x /usr/bin/cygpath; then
CFLAGS="$CFLAGS -O2"
- MIXED_CYGWIN=yes
AC_MSG_RESULT([yes])
- MIXED_CYGWIN_MINGW=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_MINGW"
+ MIXED_MINGW=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MINGW"
else
AC_MSG_RESULT([undeterminable])
AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
fi
else
AC_MSG_RESULT([no])
- MIXED_CYGWIN_MINGW=no
fi
else
- MIXED_CYGWIN_MINGW=no
-fi
-AC_SUBST(MIXED_CYGWIN_MINGW)
+ AC_MSG_CHECKING(for mixed MSYS and native MinGW environment)
+ if test "x$GCC" = x"yes"; then
+ if test -x /usr/bin/msys-=.0.dll; then
+ CFLAGS="$CFLAGS -O2"
+ AC_MSG_RESULT([yes])
+ MIXED_MINGW=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MINGW"
+ else
+ AC_MSG_RESULT([undeterminable])
+ AC_MSG_ERROR(Seems to be mixed windows but not with msys, cannot handle this!)
+ fi
+ else
+ AC_MSG_RESULT([no])
+ fi
+fi
+AC_SUBST(MIXED_MINGW)
AC_MSG_CHECKING(if we mix cygwin with any native compiler)
if test "X$MIXED_CYGWIN" = "Xyes"; then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
-AC_SUBST(MIXED_CYGWIN)
-
AC_MSG_CHECKING(if we mix msys with another native compiler)
if test "X$MIXED_MSYS" = "Xyes" ; then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
-AC_SUBST(MIXED_MSYS)
+AC_MSG_CHECKING(if we mix WSL with another native compiler)
+if test "X$MIXED_WSL" = "Xyes" ; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
fi
-])
-
+])
+
dnl ----------------------------------------------------------------------
dnl
dnl LM_FIND_EMU_CC
@@ -2943,6 +2960,8 @@ if test "x$GCC" = xyes; then
DED_STATIC_CFLAGS="$DED_CFLAGS"
DED_CFLAGS="$DED_CFLAGS -fPIC"
+ # Remove -fPIE and -fno-PIE
+ DED_CFLAGS=`echo $DED_CFLAGS | sed 's/-f\(no-\)\?PIE//g'`
fi
DED_EXT=so
@@ -2993,7 +3012,7 @@ case $host_os in
DED_LDFLAGS="-64 $DED_LDFLAGS"
fi
;;
- aix4*)
+ aix*|os400*)
DED_LDFLAGS="-G -bnoentry -bexpall"
;;
freebsd2*)
diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks
deleted file mode 100755
index c3bdfd0095..0000000000
--- a/erts/autoconf/configure.vxworks
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/sh
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author:
-# Patrik Winroth
-#
-
-
-# vxworks_ppc860 vxworks_ppc603 vxworks_ppc603_longcall vxworks_cpu32 vxworks_sparc
-# vxworks_ppc750 vxworks_simso
-
-case $# in
-1) host=$1 ;;
-*) echo "usage: configure.vxworks host-configuration"; exit 1 ;;
-esac
-
-case $1 in
-vxworks_cpu32) ;;
-vxworks_ppc750) ;;
-vxworks_ppc860) ;;
-vxworks_ppc603) ;;
-vxworks_ppc603_nolongcall) ;;
-vxworks_sparc) ;;
-vxworks_simso) ;;
-vxworks_simlinux) ;;
-vxworks_ppc32) ;;
-*) echo "usage: configure.vxworks TARGET";
- echo "where TARGET is one of vxworks_cpu32, vxworks_ppc750, vxworks_ppc860, vxworks_ppc603, vxworks_ppc603_nolongcall, vxworks_sparc, vxworks_simso, vxworks_simlinux, vxworks_ppc32"; exit 1;;
-esac
-
-if [ "x$ERL_TOP" = x ]; then
- echo "You need to set ERL_TOP!"
- exit 1
-fi
-
-
-target=$host
-
-# Find out the HOST and WIND_BASE environment
-HOST_TYPE=${HOST_TYPE:=sun4-solaris2}
-case $1 in
-vxworks_ppc750) VXTOP=Tornado2.2 ;;
-vxworks_simso) VXTOP=WindRiver ;;
-vxworks_simlinux) VXTOP=WindRiver ;;
-vxworks_ppc32) VXTOP=WindRiver ;;
-*) VXTOP=wind ;;
-esac
-
-WIND_BASE=${WIND_BASE:=`ypmatch tornado passwd | awk -F: '{print $6}'`/$VXTOP}
-
-# These are created by autoconf.
-MKDIRS="${ERL_TOP}/lib/os_mon/priv/bin/$target
- ${ERL_TOP}/lib/os_mon/priv/obj/$target
- ${ERL_TOP}/lib/asn1/priv/lib/$target
- ${ERL_TOP}/lib/asn1/priv/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj.debug/$target
- ${ERL_TOP}/lib/erl_interface/bin/$target
- ${ERL_TOP}/lib/runtime_tools/priv/lib/$target
- ${ERL_TOP}/lib/runtime_tools/priv/obj/$target
- ${ERL_TOP}/erts/obj/$target
- ${ERL_TOP}/erts/obj.debug/$target
- ${ERL_TOP}/bin/$target"
-
-for dir in $MKDIRS; do
- test ! -d "$dir" && mkdir -p "$dir"
-done
-
-#
-# Create Makefiles for vxWorks.
-#
-my_root=${ERL_TOP}/erts/emulator
-emu_test=$my_root/test
-beam=$my_root/beam
-erts_lib_src=${ERL_TOP}/erts/lib_src
-erts_incl=${ERL_TOP}/erts/include
-erts_incl_intrnl=${ERL_TOP}/erts/include/internal
-etcdir=${ERL_TOP}/erts/etc/common
-erlint_incl_dir=${ERL_TOP}/lib/erl_interface/include
-erlint_dir=${ERL_TOP}/lib/erl_interface/src
-epmd_dir=${ERL_TOP}/erts/epmd/src
-os_mon_dir=${ERL_TOP}/lib/os_mon/c_src
-internal_tools_dir=${ERL_TOP}
-libdir=${ERL_TOP}/lib
-tsdir=$libdir/test_server/src
-zlibdir=${ERL_TOP}/erts/emulator/zlib
-runtime_tools_dir=${ERL_TOP}/lib/runtime_tools/c_src
-tools_dir=${ERL_TOP}/lib/tools/c_src
-
-CONFIG_FILES="${ERL_TOP}/erts/emulator/$host/Makefile
- $erts_lib_src/$host/Makefile
- $erts_incl/$host/erl_int_sizes_config.h
- $erts_incl_intrnl/$host/ethread.mk
- $erts_incl_intrnl/$host/ethread_header_config.h
- $etcdir/$host/Makefile
- $erlint_incl_dir/$host/ei_config.h
- $erlint_dir/$host/Makefile
- $erlint_dir/$host/eidefs.mk
- $epmd_dir/$host/Makefile
- $internal_tools_dir/make/$host/otp.mk
- $internal_tools_dir/make/$host/otp_ded.mk
- $os_mon_dir/$host/Makefile
- $zlibdir/$host/Makefile
- $runtime_tools_dir/$host/Makefile
- $tools_dir/$host/Makefile"
-
-for file in $CONFIG_FILES; do
- new_name=`echo $file|sed "s%/$host/%/$target/%"`
- dir=`echo $new_name|sed 's%/[^/][^/]*$%%'`
- if test "$dir" != "$new_name" && test "$dir" != .; then
- test ! -d "$dir" && mkdir "$dir"
- fi
-
- sole_name=`echo $file|sed "s%.*$target/%%"`
- in_file=`echo $dir|sed "s%/[^/][^/]*$%/$sole_name.in%"`
- echo "creating $new_name"
- sed -f vxworks/sed.$target -f vxworks/sed.general \
- -e "s,@HOST_TYPE@,$HOST_TYPE,g" \
- -e "s,@WIND_BASE@,$WIND_BASE,g" \
- -e "s,@TARGET@,$target,g" \
- -e "s,@ENV_CFLAGS@,$CFLAGS,g" \
- $in_file > $new_name
-done
-
-
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
deleted file mode 100644
index ffd5a8133c..0000000000
--- a/erts/autoconf/vxworks/sed.general
+++ /dev/null
@@ -1,132 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles
-# for vxworks from the generic Makefile.in that is found in a number
-# of directories (see configure.vxworks).
-#
-# This is the general part that is common for all architectures.
-#
-
-# Size of data types.
-s|^#undef SIZEOF_CHAR|#define SIZEOF_CHAR 1|
-s|^#undef SIZEOF_SHORT|#define SIZEOF_SHORT 2|
-s|^#undef SIZEOF_INT|#define SIZEOF_INT 4|
-s|^#undef SIZEOF_LONG_LONG|#define SIZEOF_LONG_LONG 8|
-s|^#undef SIZEOF_LONG$|#define SIZEOF_LONG 4|
-s|^#undef SIZEOF_VOID_P$|#define SIZEOF_VOID_P 4|
-
-# General stuff.
-s|@erts_rootdir@|/clearcase/otp/erts|
-
-s|@LIBOBJS@|$(OBJDIR)/elib_malloc.o|
-s|@DLOAD_LIB@||
-s|@LDFLAGS@||
-# FIXME: A bit strange to clear out remaining DED_*
-s|@DED_LDFLAGS@||
-s|@DED_CFLAGS@||
-s|@DED_EMU_THR_DEFS@||
-s|@DED_THR_DEFS@||
-s|@DED_SYS_INCLUDE@||
-s|@WERRORFLAGS@||
-s|@DED_STATIC_CFLAGS@||
-s|@STATIC_CFLAGS@||
-s|@GCCLIB@|libgcc.a|
-s|@DEFS@||
-s|@DEXPORT@||
-s|@DCFLAGS@||
-s|@THR_DEFS@||
-s|@THR_LIBS@||
-s|@THR_LIB_NAME@||
-s|@THR_X_LIBS@||
-s|@ETHR_X_LIBS@||
-s|@ETHR_LIBS@||
-s|@ETHR_LIB_NAME@||
-s|@ETHR_DEFS@||
-s|@ETHR_THR_LIB_BASE@||
-s|@ETHR_THR_LIB_BASE_DIR@||
-s|@SYSTEMD_DAEMON_LIBS@||
-s|@EMU_THR_DEFS@||
-s|@EMU_THR_LIBS@||
-s|@EMU_THR_LIB_NAME@|ethread|
-s|@ERTS_ENABLE_KERNEL_POLL@|no|
-s|@ERTS_INTERNAL_X_LIBS@||
-s|@cc_root@|/clearcase/otp/|
-# Define VxWorks even though cross-compiling.
-s|@CROSS_COMPILING|yes|
-
-s|@HCFLAGS@|-DVXWORKS|
-s|@HCLIBS@||
-s|@ENABLE_ALLOC_TYPE_VARS@||
-s|@TERMCAP_LIB@||
-s|@ERTS_BUILD_SMP_EMU@|no|
-s|@HAVE_VALGRIND@|no|
-s|@EXEEXT@||
-s|@WITH_SCTP@||
-
-# HiPE
-s|@HIPE_ENABLED@||
-
-# m4
-s|@OPSYS@|noopsys|
-
-# Conditional inclusion of applications
-s|@HIPE_APP@||
-s|@SSL_APP@|ssl|
-s|@CRYPTO_APP@|crypto|
-s|@SSH_APP@|ssh|
-
-# The target tools prefix, prepended to all cc,ld,as etc commands
-s|@TTPREFIX@|GCC_EXEC_PREFIX=@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/host/@HOST_TYPE@/bin/|
-
-# Install programs etc
-s|@PERL@|perl|
-s|@INSTALL_PROGRAM@|${INSTALL}|
-s|@INSTALL_SCRIPT@|${INSTALL}|
-s|@INSTALL_DATA@|${INSTALL} -m 644|
-s|@INSTALL_DIR@|$(INSTALL) -d|
-s|@MKDIR@|/bin/mkdir|
-s|@ERLANG_OSTYPE@|vxworks|
-s|@vxworks_reclaim@|reclaim.h|
-s|@os_mon_programs@||
-s|@erlexec@|erl.exec|
-s|@EMU_LIBOBJS@||
-
-# General CFLAGS
-s|@GENERAL_CFLAGS@|@ENV_CFLAGS@ -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
-s|@WFLAGS@||
-
-# Thread flags for eidefs.mk (erl_interface)
-s|@EI_THREADS@|false|
-
-# Make java code compile although we don't test it on VxWorks (no license)
-s|@JAVAC@|javac|
-
-# What is this anyway?
-# Disable it and see what breaks.
-#s|@ded_soname@||
-
-# Only variable substituted directly
-s|$(LDFLAGS)|-r -d|
-s|@LIBRT@||
-# XXX What is EFFLAGS? Not used in the emulator Makefile.in anyway.
-s|$(EFLAGS)|-DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS|
-
diff --git a/erts/autoconf/vxworks/sed.vxworks_cpu32 b/erts/autoconf/vxworks/sed.vxworks_cpu32
deleted file mode 100644
index 26e4f4c7ad..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_cpu32
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_cpu32|
-s|@system_type@|vxworks_cpu32|
-s|@CC@|@TTPREFIX@cc68k|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ld68k|
-s|@LIBS@||
-s|@DED_LD@|@TTPREFIX@ld68k|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_FLAGS@|-g|
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlib68k|
-s|@AR@|@TTPREFIX@ar68k|
-s|@STRIP@|@TTPREFIX@strip68k|
-s|@SYMPREFIX@|_|
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float -lgcc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32
deleted file mode 100644
index 44697aadc2..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc32
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2006-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/bin/install -c|
-
-# other
-s|@host@|vxworks_ppc32|
-s|@system_type@|vxworks_ppc32|
-s|@ARCH@|ppc32|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/workbench-2.3/@HOST_TYPE@/bin/stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common -lgcc|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common/libgcc.a|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibppc|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/powerpc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
-
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -I@WIND_BASE@/vxworks-6.3/target/h -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603 b/erts/autoconf/vxworks/sed.vxworks_ppc603
deleted file mode 100644
index 4fdfd51273..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
deleted file mode 100644
index d86876e90e..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc860 b/erts/autoconf/vxworks/sed.vxworks_ppc860
deleted file mode 100644
index a5c4c2d5c3..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_ppc860
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc860|
-s|@system_type@|vxworks_ppc860|
-s|@ARCH@|ppc860|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@GCC@|yes|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option (go for dwarf)
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=860 -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=powerpc -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux
deleted file mode 100644
index 1a2bbd6236..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_simlinux
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2008-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/bin/install -c|
-
-# other
-s|@host@|vxworks_simlinux|
-s|@system_type@|vxworks_simlinux|
-s|@ARCH@|simlinux|
-
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccpentium|
-
-s|@HCC@|gcc|
-s|@GCC@|yes|
-
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-#s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/strip|
-s|@STRIP@||
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common -lgcc|
-
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common/libgcc.a|
-
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibpentium|
-
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arpentium|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/i586-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso
deleted file mode 100644
index 8d15e87a1b..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_simso
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2005-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-# Install programs etc
-s|@INSTALL@|/usr/ucb/install -c|
-
-# other
-s|@host@|vxworks_simso|
-s|@system_type@|vxworks_simso|
-s|@ARCH@|simso|
-
-# Tornado2.2: s|@CC@|@TTPREFIX@ccsimso|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccsparc|
-
-s|@HCC@|gcc|
-s|@GCC@|yes|
-
-# Tornado2.2: s|@LD@|@TTPREFIX@ldsimso|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-# Tornado2.2: s|@STRIP@|@TTPREFIX@stripsimso|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/stripsparc|
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common -lgcc|
-
-# Tornado2.2: s|@DED_LD@|@TTPREFIX@ldsimso|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common/libgcc.a|
-
-# Tornado2.2: s|@RANLIB@|@TTPREFIX@ranlibsimso|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibsparc|
-
-# Tornado2.2: s|@AR@|arsimso|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arsparc|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/sparc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/erts/autoconf/vxworks/sed.vxworks_sparc b/erts/autoconf/vxworks/sed.vxworks_sparc
deleted file mode 100644
index 118b01d16d..0000000000
--- a/erts/autoconf/vxworks/sed.vxworks_sparc
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2018. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-
-# ccsparc -O2 doesn't work when compiling "rundir"/gc.c - signal 11 is generated when trying
-# therefore it is compiled with -O1 instead, which works - get a new ccsparc !
-s/\$(COMPILE\.emu) -o \$@ -c gc\.c/$(CC) @CFLAGS@ @DEFS@ -O1 $(BEAM_MODE) -I$(SYSDIR) -I$(EMUDIR) -I. $(CPPFLAGS) -c -o $@ -c gc.c/
-s/@host@/vxworks_sparc/
-s/@system_type@/vxworks_sparc/
-s/@CC@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ccsparc/
-s/@HCC@/gcc/
-s/@GCC@/yes/
-s/@LD@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ldsparc/
-s/@DEBUG_FLAGS@/-g/
-s/@GCCLIB_PATH@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/lib\/gcc-lib\/sparc-wrs-vxworks\/cygnus-2.2.3.1\/libgcc.a/
-s/@RANLIB@/ranlibsparc/
-s/@AR@/arsparc/
-s/@CFLAGS@/-I\/home\/gandalf\/bsproj\/BS.2\/UOS\/vw\/5.2\/h -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DCPU=SPARC -DVXWORKS -fno-builtin -nostdinc/
-
diff --git a/erts/configure.in b/erts/configure.in
index 609c457393..46c90a152a 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -447,6 +447,12 @@ dnl ---------------------------------------------------------------------
dnl NOTE: CPPFLAGS will be included in CFLAGS at the end
case $host_os in
linux*) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE";;
+ aix*|os400*)
+ # * _ALL_SOURCE: Required to get the winsize structure for TIOCSWINSZ.
+ # * _LINUX_SOURCE_COMPAT: Not required, but makes some libc functions
+ # behave closer to glibc assumptions.
+ CPPFLAGS="$CPPFLAGS -D_ALL_SOURCE -D_LINUX_SOURCE_COMPAT"
+ ;;
win32)
# The ethread library requires _WIN32_WINNT of at least 0x0403.
# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
@@ -577,6 +583,17 @@ else
WERRORFLAGS=""
fi
+AC_MSG_CHECKING([C99 support])
+
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[
+#if __STDC_VERSION__ < 199901L
+ #error "Not C99"
+#endif])],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ CFLAGS="-std=gnu99 $CFLAGS"
+ DEBUG_CFLAGS="-std=gnu99 $DEBUG_CFLAGS"])
+
AC_MSG_CHECKING([CFLAGS for -O switch])
case "$CFLAGS" in
*-O*) AC_MSG_RESULT([yes]) ;;
@@ -918,13 +935,10 @@ dnl
HCC='$(CC)' AC_SUBST(HCC)
HCFLAGS="" AC_SUBST(HCFLAGS)
HCFLAGS="$HCFLAGS -I${ERL_TOP}/erts/$host"
-vxworks_reclaim="" AC_SUBST(vxworks_reclaim)
dnl We want to use $(CC) as linker for the emulator regardless of
dnl what the user say. This might not be the right way to do it, but
dnl for now that is the way we do it.
-USER_LD=$LD
-USER_LDFLAGS="$LDFLAGS"
LD='$(CC)'
case $host_os in
darwin*)
@@ -949,7 +963,7 @@ dnl AC_CYGWIN is deprecated
AC_EXEEXT
AC_OBJEXT
-dnl This is the os flavour, should be unix, ose, vxworks or win32
+dnl This is the os flavour, should be unix or win32
case $host in
win32)
ERLANG_OSTYPE=win32 ;;
@@ -972,7 +986,7 @@ AC_SUBST(ERLANG_OSTYPE)
AC_MSG_CHECKING(for extra flags needed to export symbols)
DEXPORT=""
case $host_os in
- aix4*)
+ aix*|os400*)
DEXPORT=-Wl,-bexpall,-brtl
;;
bsdi*)
@@ -1479,7 +1493,7 @@ if test "$have_gethostbyname_r" = yes; then
AC_DEFINE(HAVE_GETHOSTBYNAME_R, GHBN_R_SOLARIS,
[Define to flavour of gethostbyname_r])
;;
- aix4*)
+ aix*|os400*)
# AIX version also needs "struct hostent_data" defn
AC_TRY_COMPILE([#include <netdb.h>],
[struct hostent_data hd;],
@@ -1598,21 +1612,15 @@ AC_CHECK_HEADERS(fcntl.h limits.h unistd.h syslog.h dlfcn.h ieeefp.h \
AC_CHECK_MEMBERS([struct ifreq.ifr_hwaddr], [], [],
[#ifdef __WIN32__
#else
- #ifdef VXWORKS
- #else
#include <net/if.h>
#endif
- #endif
])
AC_CHECK_MEMBERS([struct ifreq.ifr_enaddr], [], [],
[#ifdef __WIN32__
#else
- #ifdef VXWORKS
- #else
#include <net/if.h>
#endif
- #endif
])
dnl ----------------------------------------------------------------------
@@ -2098,7 +2106,7 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
gethrtime localtime_r gmtime_r inet_pton mprotect \
mmap mremap memcpy mallopt sbrk _sbrk __sbrk brk _brk __brk \
flockfile fstat strlcpy strlcat setsid posix2time time2posix \
- setlocale nl_langinfo poll mlockall ppoll])
+ setlocale nl_langinfo poll mlockall ppoll vsyslog])
AC_MSG_CHECKING([for isfinite])
AC_TRY_LINK([#include <math.h>],
diff --git a/erts/doc/src/.gitignore b/erts/doc/src/.gitignore
new file mode 100644
index 0000000000..abe9a7d858
--- /dev/null
+++ b/erts/doc/src/.gitignore
@@ -0,0 +1,3 @@
+ref_man.xml
+specs.xml
+part.xml \ No newline at end of file
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index bb96293947..3e2eb80b50 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -37,15 +37,15 @@ RELSYSDIR = $(RELEASE_PATH)/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF1_FILES = epmd.xml \
- erl.xml \
- erlc.xml \
- escript.xml \
- werl.xml \
- erlsrv.xml \
- start_erl.xml \
- run_erl.xml \
- start.xml
+XML_REF1_FILES = epmd_cmd.xml \
+ erl_cmd.xml \
+ erlc_cmd.xml \
+ escript_cmd.xml \
+ werl_cmd.xml \
+ erlsrv_cmd.xml \
+ start_erl_cmd.xml \
+ run_erl_cmd.xml \
+ start_cmd.xml
ifeq ($(USE_ESOCK), yes)
XML_REF3_ESOCK_EFILES = socket.xml
@@ -72,13 +72,16 @@ XML_REF3_EFILES = \
zlib.xml \
$(XML_REF3_ESOCK_EFILES)
-XML_REF3_FILES = \
- $(XML_REF3_EFILES) \
+XML_REF3_CREF = \
driver_entry.xml \
erl_nif.xml \
erl_driver.xml \
erts_alloc.xml
+XML_REF3_FILES = \
+ $(XML_REF3_EFILES) \
+ $(XML_REF3_CREF)
+
XML_PART_FILES = \
part.xml internal.xml
@@ -96,7 +99,6 @@ XML_INTERNAL_FILES = \
SuperCarrier.xml \
CountingInstructions.xml
-
XML_CHAPTER_FILES = \
introduction.xml \
tty.xml \
@@ -118,32 +120,24 @@ TOPDOCDIR=../../../doc
BOOK_FILES = book.xml
-GIF_FILES = \
+IMAGE_FILES = \
erl_ext_fig.gif
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF1_FILES) $(XML_APPLICATION_FILES)
+HTML_EXTRA_FILES = $(ERL_TOP)/erts/example/time_compat.erl \
+ $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
+
XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%)
-# ----------------------------------------------------
+NO_CHUNKS = $(XML_REF3_CREF) erl_tracer.xml
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+# ----------------------------------------------------
-INFO_FILE = ../../info
INFO_FILE_SRC = ../../info.src
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_EFILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
XML_FIGURE_DIR = $(XMLDIR)/figures
@@ -153,60 +147,24 @@ PNG_FILES = $(notdir $(INTERNAL_DOC_PNG_FILES))
XMLDIR_PNG_FILES = $(PNG_FILES:%=$(XML_FIGURE_DIR)/%)
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-KERNEL_SRC=$(ERL_TOP)/lib/kernel/src
-KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include
-SPECS_FLAGS = -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
-
-# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR))
+include $(ERL_TOP)/make/doc.mk
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR))
$(XML_FIGURE_DIR)/%.png: ../../emulator/internal_doc/figures/%.png
$(INSTALL_DATA) $< $@
-docs: part ref_man specs figures man pdf html $(INFO_FILE)
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-man: $(MAN1_FILES) $(MAN3_FILES)
-
-ref_man: ref_man.xml
-part: part.xml
-specs: specs.xml
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+html: figures
$(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk
sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE)
figures: $(XMLDIR_PNG_FILES)
-debug opt:
-
-ldocs: xmllint local_docs
-
-clean:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
-
-$(SPECDIR)/specs_%.xml:
+## This rule generate dummy specs for all XML_REF3_CREF's
+$(XML_REF3_CREF:%.xml=$(SPECDIR)/specs_%.xml): $(@:%.xml=%.xml)
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@)
@@ -229,25 +187,10 @@ specs.xml: specs.xml.src
# ----------------------------------------------------
# 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"
+release_html_spec: release_figures
+
+release_figures:
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/figures"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(XMLDIR)/figures/* \
"$(RELSYSDIR)/doc/html/figures"
- $(INSTALL_DATA) $(ERL_TOP)/erts/example/time_compat.erl \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1"
-
-release_spec:
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index f72e8acd2c..f2155bcc4d 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -785,12 +785,14 @@
<taglist>
<tag><c>DFLAG_DIST_HDR_ATOM_CACHE</c></tag>
<item>Do not use atom cache over this connection.</item>
+ <tag><c>DFLAG_FRAGMENTS</c></tag>
+ <item>Split large distribution messages into multiple fragments.</item>
</taglist>
- <p>Use function <c>dist_util:strict_order_flags/0</c> to get all flags
- for features that require strict order delivery.</p>
<p>
This flag field is optional.
</p>
+ <p>See also <seealso marker="#distribution_data_delivery">
+ Distribution Data Delivery</seealso></p>
</item>
<tag><marker id="hs_data_require_flags"/><c>require_flags</c></tag>
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd_cmd.xml
index 75353cbc07..ee886bd68e 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,11 +19,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>epmd</title>
- <prepared>Claes Wikstr&ouml;m</prepared>
+ <prepared>Claes Wikstr&ouml;m</prepared>
<responsible></responsible>
<docno>1</docno>
<approved></approved>
@@ -295,9 +295,8 @@
<title>Logging</title>
<p>On some operating systems <em>syslog</em> will be used for
error reporting when <c>epmd</c> runs as a daemon. To enable
- the error logging, you must edit the
- <path unix="" windows="">/etc/syslog.conf</path> file and add an
- entry:</p>
+ the error logging, you must edit the /etc/syslog.conf file and
+ add an entry:</p>
<code type="none"><![CDATA[
!epmd
@@ -334,5 +333,3 @@
<p>To restrict access further, firewall software must be used.</p>
</section>
</comref>
-
-
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl_cmd.xml
index a37707f7f9..64663ce95e 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -181,7 +181,7 @@
which is used to start the system; see
<seealso marker="init"><c>init(3)</c></seealso>. Unless
<c><![CDATA[File]]></c> contains an absolute path, the system searches
- for <c><![CDATA[File.boot]]></c> in the current and
+ for <c><![CDATA[File.boot]]></c> in the current and
<c><![CDATA[$ROOT/bin]]></c> directories.</p>
<p>Defaults to <c><![CDATA[$ROOT/bin/start.boot]]></c>.</p>
</item>
@@ -207,9 +207,9 @@
<p>Not recommended; use <seealso marker="erlc"><c>erlc</c></seealso>
instead.</p>
</item>
- <tag><c><![CDATA[-config Config]]></c></tag>
+ <tag><c><![CDATA[-config Config [Config]]]></c></tag>
<item>
- <p>Specifies the name of a configuration file,
+ <p>Specifies the name of one or more configuration files,
<c><![CDATA[Config.config]]></c>, which is used to configure
applications; see
<seealso marker="kernel:app"><c>app(4)</c></seealso> and
@@ -575,8 +575,8 @@
<tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag>
<item>
<p>Sets the number of threads in async thread pool. Valid range
- is 0-1024. The async thread pool is used by linked-in drivers to
- handle work that may take a very long time. Since OTP-21 there are
+ is 1-1024. The async thread pool is used by linked-in drivers to
+ handle work that may take a very long time. Since OTP 21 there are
very few linked-in drivers in the default Erlang/OTP distribution
that uses the async thread pool. Most of them have been migrated to
dirty IO schedulers. Defaults to 1.</p>
@@ -852,7 +852,7 @@
<p>Sets the range of characters that the system considers printable in
heuristic detection of strings. This typically affects the shell,
debugger, and <c>io:format</c> functions (when <c>~tp</c> is used in
- the format string).</p>
+ the format string).</p>
<p>Two values are supported for <c>Range</c>:</p>
<taglist>
<tag><c>latin1</c></tag>
@@ -948,14 +948,19 @@
<c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
<item>
<p>Sets the number of scheduler threads to create and scheduler threads
- to set online. The maximum for both
- values is 1024. If the Erlang runtime system is able to determine the
- number of logical processors configured and logical processors
- available, <c>Schedulers</c> defaults to logical processors
- configured, and <c>SchedulersOnline</c> defaults to logical processors
- available; otherwise the default values are 1. <c>Schedulers</c> can
- be omitted if <c>:SchedulerOnline</c> is not and conversely. The
- number of schedulers online can be changed at runtime through
+ to set online. The maximum for both values is 1024. If the Erlang
+ runtime system is able to determine the number of logical processors
+ configured and logical processors available, <c>Schedulers</c>
+ defaults to logical processors configured, and
+ <c>SchedulersOnline</c> defaults to logical processors available;
+ otherwise the default values are 1. If the emulator detects that it
+ is subject to a <seealso marker="erlang#system_info_cpu_quota">CPU
+ quota</seealso>, the default value for <c>SchedulersOnline</c> will
+ be limited accordingly.</p>
+ <p>
+ <c>Schedulers</c> can be omitted if <c>:SchedulerOnline</c> is not
+ and conversely. The number of schedulers online can be changed at
+ runtime through
<seealso marker="erlang#system_flag_schedulers_online">
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
@@ -1270,7 +1275,7 @@
node identifiers can be omitted. If omitted, the thread ID
defaults to <c>t0</c>, the core ID defaults to <c>c0</c>,
the processor ID defaults to <c>p0</c>, and the node ID is
- left undefined. Either each logical processor must
+ left undefined. Either each logical processor must
belong to only one NUMA node, or no logical
processors must belong to any NUMA nodes.</p>
<p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index f924c8a70b..eeb0049f4f 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -109,7 +109,8 @@
<title>Register a Node in EPMD</title>
<p>When a distributed node is started it registers itself in the EPMD.
The message <c>ALIVE2_REQ</c> described below is sent from the node to
- the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p>
+ the EPMD. The response from the EPMD is <c>ALIVE2_X_RESP</c> (or
+ <c>ALIVE2_RESP</c>).</p>
<table align="left">
<row>
@@ -154,13 +155,13 @@
</item>
<tag><c>HighestVersion</c></tag>
<item>
- <p>The highest distribution version that this node can handle.
- The value in Erlang/OTP R6B and later is 5.</p>
+ <p>The highest distribution protocol version this node can handle.
+ The value in OTP 23 and later is 6. Older nodes only support version 5.</p>
</item>
<tag><c>LowestVersion</c></tag>
<item>
<p>The lowest distribution version that this node can handle.
- The value in Erlang/OTP R6B and later is 5.</p>
+ Should be 5 to support connections to nodes older than OTP 23.</p>
</item>
<tag><c>Nlen</c></tag>
<item>
@@ -184,7 +185,24 @@
node is a distributed node. When the connection is closed,
the node is automatically unregistered from the EPMD.</p>
- <p>The response message <c>ALIVE2_RESP</c> is as follows:</p>
+ <p>The response message is either <c>ALIVE2_X_RESP</c> or
+ <c>ALIVE2_RESP</c> depending on distribution version. If both the node
+ and EPMD support distribution version 6 then the response is
+ <c>ALIVE2_X_RESP</c> otherwise it is the older <c>ALIVE2_RESP</c>:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>118</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>ALIVE2_X_RESP (118) with 32 bit creation</tcaption>
+ </table>
<table align="left">
<row>
@@ -197,7 +215,7 @@
<cell align="center"><c>Result</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption>ALIVE2_RESP (121)</tcaption>
+ <tcaption>ALIVE2_RESP (121) with 16-bit creation</tcaption>
</table>
<p>Result = 0 -> ok, result &gt; 0 -> error.</p>
@@ -531,8 +549,14 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<section>
<marker id="distribution_handshake"/>
<title>Distribution Handshake</title>
- <p>This section describes the distribution handshake protocol introduced
- in Erlang/OTP R6. The handshake has remained almost the same since then.</p>
+ <p>
+ This section describes the distribution handshake protocol used between
+ nodes to establishing a connection. The protocol was introduced in
+ Erlang/OTP R6 and has remained unchanged until OTP 23. The changes made in
+ OTP 23 were designed to be compatible with the older protocol
+ version. That is an old node can still connect toward a new node and vice
+ versa.
+ </p>
<section>
<title>General</title>
@@ -599,19 +623,68 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
<tag>2) <c>send_name</c>/<c>receive_name</c></tag>
<item>
<p><c>A</c> sends an initial identification to <c>B</c>, which
- receives the message. The message looks as follows (every "square"
- is one byte and the packet header is removed):</p>
- <pre>
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
-|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
- <p>'n' is the message tag. 'Version0' and 'Version1' is the
- distribution version selected by <c>A</c>, based on information
- from the EPMD. (16-bit big-endian) 'Flag0' ... 'Flag3' are
- capability flags, the capabilities are defined in
- <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. (32-bit big-endian)
- 'Name0' ... 'NameN' is the full node name of <c>A</c>, as a string
- of bytes (the packet length denotes how long it is).</p>
+ receives the message. The message can have two different formats
+ which looks as follows (the packet headers are removed):
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">4</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'n'</c></cell>
+ <cell align="center"><c>Version=5</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>Old send_name ('n') for protocol version 5</tcaption>
+ </table>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">8</cell>
+ <cell align="center">4</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'N'</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>New send_name ('N') for protocol version 6</tcaption>
+ </table>
+
+ <p>
+ The old <c>send_name</c> format is sent from nodes only supporting version 5
+ or to nodes that might only support version 5. The <c>Version</c> is
+ a 16-bit big endian integer and <em>must</em> always have the value 5, even
+ if node <c>A</c> supports version 6. <c>Flags</c> are the
+ <seealso marker="#dflags">capability flags</seealso>
+ of node <c>A</c> in 32-bit big endian. The flag bit
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ should be set if node <c>A</c> supports version 6.
+ <c>Name</c> is the full node name of <c>A</c>, as a string of bytes
+ (the packet length denotes how long it is).
+ </p>
+ <p>
+ The new <c>send_name</c> is only sent from nodes supporting version 6 to
+ nodes known to support version 6. <c>Flags</c> are the
+ <seealso marker="#dflags">capability flags</seealso> of node
+ <c>A</c> in 64-bit big endian. The flag bit
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ must always be set. <c>Creation</c> is the node incarnation
+ identifier used by node <c>A</c> to create its pids, ports and
+ references. <c>Name</c> is the full node name of <c>A</c>, as a
+ string of bytes. <c>Nlen</c> is the byte length of the node name in
+ 16-bit big endian. Any extra data after the node <c>Name</c> must be
+ accepted and ignored.
+ </p>
</item>
<tag>3) <c>recv_status</c>/<c>send_status</c></tag>
<item>
@@ -648,13 +721,19 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
node <c>B</c>. See step 3B below.</p>
</item>
</taglist>
- <p>The format of the status message is as follows:</p>
- <pre>
-+---+-------+-------+-...-+-------+
-|'s'|Status0|Status1| ... |StatusN|
-+---+-------+-------+-...-+-------+</pre>
- <p>'s' is the message tag. 'Status0' ... 'StatusN' is the status as a
- string (not terminated).</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">Slen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'s'</c></cell>
+ <cell align="center"><c>Status</c></cell>
+ </row>
+ <tcaption>The format of the status message</tcaption>
+ </table>
+ <p>'s' is the message tag. <c>Status</c> is the status as a
+ string (not null terminated).</p>
</item>
<tag>3B) <c>send_status</c>/<c>recv_status</c></tag>
<item>
@@ -670,39 +749,136 @@ io:format("old/unused name ~ts at port ~p, fd = ~p ~n",
handshake continues with <c>B</c> sending <c>A</c> another message,
the challenge. The challenge contains the same type of information
as the "name" message initially sent from <c>A</c> to <c>B</c>, plus
- a 32-bit challenge:</p>
- <pre>
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+
-|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
- <p>'Chal0' ... 'Chal3' is the challenge as a 32-bit big-endian integer
- and the other fields are <c>B</c>'s version, flags, and full node
- name.</p>
+ a 32-bit challenge. The challenge message can have two different
+ formats:
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'n'</c></cell>
+ <cell align="center"><c>Version=5</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>The old challenge message format (version 5)</tcaption>
+ </table>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">8</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'N'</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>Name</c></cell>
+ </row>
+ <tcaption>The new challenge message format (version 6)</tcaption>
+ </table>
+ <p>
+ The old challenge message is sent from old <c>B</c> nodes
+ (supporting only version 5) or if node <c>A</c> had not capability flag
+ <seealso marker="#DFLAG_HANDSHAKE_23"><c>DFLAG_HANDSHAKE_23</c></seealso>
+ set. The <c>Version</c> is a 16-bit big endian integer and
+ <c>must</c> always have the value 5.
+ </p>
+ <p>
+ The new challenge message is sent from new <c>B</c> nodes if node
+ <c>A</c> had capability flag <seealso marker="#DFLAG_HANDSHAKE_23">
+ <c>DFLAG_HANDSHAKE_23</c></seealso> set. Any extra data after the
+ node <c>Name</c> must be accepted and ignored.
+ </p>
+ <p>
+ <c>Challenge</c> is a 32-bit big-endian integer. The other fields
+ are node <c>B</c>'s flags, creation and full node name, similar to
+ the <c>send_name</c> message.
+ </p>
+ </item>
+
+ <tag>4B) <c>send_complement</c>/<c>recv_complement</c></tag>
+ <item>
+ <p>
+ The complement message, from <c>A</c> to <c>B</c>, is only sent if
+ node <c>A</c> initially sent an old name message and received back a
+ new challenge message from node <c>B</c>. It contains complementary
+ information missing in the initial old name message from node <c>A</c>.
+ </p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'c'</c></cell>
+ <cell align="center"><c>FlagsHigh</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>The complement message</tcaption>
+ </table>
+ <p>
+ <c>FlagsHigh</c> are the high capability flags (bit 33-64) of node
+ <c>A</c> as a 32-bit big endian integer. <c>Creation</c> is the
+ incarnation identifier of node <c>A</c>.
+ </p>
</item>
+
<tag>5) <c>send_challenge_reply</c>/<c>recv_challenge_reply</c></tag>
<item>
<p>Now <c>A</c> has generated a digest and its own challenge. Those
are sent together in a package to <c>B</c>:</p>
- <pre>
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
-|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+</pre>
- <p>'r' is the tag. 'Chal0' ... 'Chal3' is <c>A</c>'s challenge for
- <c>B</c> to handle. 'Dige0' ... 'Dige15' is the digest that <c>A</c>
- constructed from the challenge <c>B</c> sent in the previous
- step.</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">16</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'r'</c></cell>
+ <cell align="center"><c>Challenge</c></cell>
+ <cell align="center"><c>Digest</c></cell>
+ </row>
+ <tcaption>The challenge_reply message</tcaption>
+ </table>
+ <p>
+ <c>Challenge</c> is <c>A</c>'s challenge for <c>B</c> to
+ handle. <c>Digest</c> is the MD5 digest that <c>A</c> constructed
+ from the challenge <c>B</c> sent in the previous step.
+ </p>
</item>
<tag>6) <c>recv_challenge_ack</c>/<c>send_challenge_ack</c></tag>
<item>
<p><c>B</c> checks that the digest received from <c>A</c> is correct
and generates a digest from the challenge received from <c>A</c>.
The digest is then sent to <c>A</c>. The message is as follows:</p>
- <pre>
-+---+-----+-----+-----+-----+-...-+------+
-|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-...-+------+</pre>
- <p>'a' is the tag. 'Dige0' ... 'Dige15' is the digest calculated by
- <c>B</c> for <c>A</c>'s challenge.</p>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">16</cell>
+ </row>
+ <row>
+ <cell align="center"><c>'a'</c></cell>
+ <cell align="center"><c>Digest</c></cell>
+ </row>
+ <tcaption>The challenge_ack message</tcaption>
+ </table>
+ <p>
+ <c>Digest</c> is the digest calculated by <c>B</c> for <c>A</c>'s
+ challenge.
+ </p>
</item>
<tag>7) check</tag>
<item>
@@ -728,10 +904,15 @@ recv_status
(if status was 'alive'
send_status - - - - - - - - - - - - - - - - - -&gt;
recv_status)
- ChB = gen_challenge()
- (ChB)
+
+ (ChB) ChB = gen_challenge()
&lt;---------------------------------------------- send_challenge
recv_challenge
+
+(if old send_name and new recv_challenge
+ send_complement - - - - - - - - - - - - - - - -&gt;
+ recv_complement)
+
ChA = gen_challenge(),
OCA = out_cookie(B),
DiA = gen_digest(ChB, OCA)
@@ -793,7 +974,8 @@ DiB == gen_digest(ChA, ICA)?
</item>
<tag><c>-define(DFLAG_NEW_FUN_TAGS,16#80).</c></tag>
<item>
- <p>The node understand new fun tags.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#NEW_FUN_EXT">
+ <c>NEW_FUN_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).</c></tag>
<item>
@@ -802,13 +984,18 @@ DiB == gen_digest(ChA, ICA)?
</item>
<tag><c>-define(DFLAG_EXPORT_PTR_TAG,16#200).</c></tag>
<item>
+ <p>The node understands the <seealso marker="erl_ext_dist#EXPORT_EXT">
+ <c>EXPORT_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_BIT_BINARIES,16#400).</c></tag>
<item>
+ <p>The node understands the <seealso marker="erl_ext_dist#BIT_BINARY_EXT">
+ <c>BIT_BINARY_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_NEW_FLOATS,16#800).</c></tag>
<item>
- <p>The node understands new float format.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#NEW_FLOAT_EXT">
+ <c>NEW_FLOAT_EXT</c></seealso> tag.</p>
</item>
<tag><c>-define(DFLAG_UNICODE_IO,16#1000).</c></tag>
<item>
@@ -817,21 +1004,34 @@ DiB == gen_digest(ChA, ICA)?
<item>
<p>The node implements atom cache in distribution header.</p>
</item>
+ <marker id="DFLAG_SMALL_ATOM_TAGS"/>
<tag><c>-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).</c></tag>
<item>
- <p>The node understand the <c>SMALL_ATOM_EXT</c> tag.</p>
+ <p>The node understands the <seealso marker="erl_ext_dist#SMALL_ATOM_EXT">
+ <c>SMALL_ATOM_EXT</c></seealso> tag.</p>
</item>
+ <marker id="DFLAG_UTF8_ATOMS"/>
<tag><c>-define(DFLAG_UTF8_ATOMS, 16#10000).</c></tag>
<item>
- <p>The node understand UTF-8 encoded atoms.</p>
+ <p>The node understands UTF-8 atoms encoded with
+ <seealso marker="erl_ext_dist#ATOM_UTF8_EXT">
+ <c>ATOM_UTF8_EXT</c></seealso> and
+ <seealso marker="erl_ext_dist#SMALL_ATOM_UTF8_EXT">
+ <c>SMALL ATOM_UTF8_EXT</c></seealso>.</p>
</item>
<tag><c>-define(DFLAG_MAP_TAG, 16#20000).</c></tag>
<item>
- <p>The node understand the map tag.</p>
+ <p>The node understands the map tag
+ <seealso marker="erl_ext_dist#MAP_EXT"><c>MAP_EXT</c></seealso>.</p>
</item>
+ <marker id="DFLAG_BIG_CREATION"/>
<tag><c>-define(DFLAG_BIG_CREATION, 16#40000).</c></tag>
<item>
- <p>The node understand big node creation.</p>
+ <p>The node understands big node creation tags
+ <seealso marker="erl_ext_dist#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>,
+ <seealso marker="erl_ext_dist#NEW_PORT_EXT"><c>NEW_PORT_EXT</c></seealso> and
+ <seealso marker="erl_ext_dist#NEWER_REFERENCE_EXT"><c>NEWER_REFERENCE_EXT</c></seealso>.
+ </p>
</item>
<tag><c>-define(DFLAG_SEND_SENDER, 16#80000).</c></tag>
<item>
@@ -855,11 +1055,26 @@ DiB == gen_digest(ChA, ICA)?
<seealso marker="#control_message">control message</seealso>s
instead of the non-PAYLOAD variants.</p>
</item>
+ <marker id="DFLAG_FRAGMENTS"/>
<tag><c>-define(DFLAG_FRAGMENTS, 16#800000).</c></tag>
<item>
<p>Use <seealso marker="erl_ext_dist#fragments">fragmented</seealso>
distribution messages to send large messages.</p>
</item>
+ <marker id="DFLAG_HANDSHAKE_23"/>
+ <tag><c>-define(DFLAG_HANDSHAKE_23, 16#1000000).</c></tag>
+ <item>
+ <p>The node supports the new connection setup handshake (version 6)
+ introduced in OTP 23.</p>
+ </item>
+ <tag><marker id="DFLAG_SPAWN"/><c>-define(DFLAG_SPAWN, 16#100000000).</c></tag>
+ <item>
+ <p>Set if the <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso>,
+ <seealso marker="#SPAWN_REQUEST_TT"><c>SPAWN_REQUEST_TT</c></seealso>,
+ <seealso marker="#SPAWN_REPLY"><c>SPAWN_REPLY</c></seealso>,
+ <seealso marker="#SPAWN_REPLY_TT"><c>SPAWN_REPLY_TT</c></seealso>
+ control messages are supported.</p>
+ </item>
</taglist>
<p>
There is also function <c>dist_util:strict_order_flags/0</c>
@@ -1009,12 +1224,6 @@ DiB == gen_digest(ChA, ICA)?
<p><c>{8, FromPid, ToPid, Reason}</c></p>
<p>This signal is sent by a call to the erlang:exit/2 bif</p>
</item>
- </taglist>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 1 (Erlang/OTP R4)</title>
- <taglist>
<tag><c>SEND_TT</c></tag>
<item>
<p><c>{12, Unused, ToPid, TraceToken}</c></p>
@@ -1035,24 +1244,6 @@ DiB == gen_digest(ChA, ICA)?
<item>
<p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p>
</item>
- </taglist>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 2</title>
- <p><c>distrvsn</c> 2 was never used.</p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 3 (Erlang/OTP R5C)</title>
- <p>None, but the version number was increased anyway.</p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 4 (Erlang/OTP R6)</title>
- <p>These are only recognized by Erlang nodes, not by hidden nodes.</p>
-
- <taglist>
<tag><c>MONITOR_P</c></tag>
<item>
<p><c>{19, FromPid, ToProc, Ref}</c>, where
@@ -1185,7 +1376,108 @@ DiB == gen_digest(ChA, ICA)?
has been negotiated in the connection setup handshake.
</p>
</item>
+
+ </taglist>
+ </section>
+ <section>
+ <title>New Ctrlmessages for Erlang/OTP 23</title>
+ <taglist>
+ <tag><marker id="SPAWN_REQUEST"/><c>SPAWN_REQUEST</c></tag>
+ <item>
+ <p><c>{29, ReqId, From, GroupLeader, {Module, Function, Arity}, OptList}</c></p>
+ <p>Followed by <c>ArgList</c>.</p>
+ <p>This signal is sent by the
+ <seealso marker="erlang#spawn_request/5"><c>spawn_request()</c></seealso> BIF.</p>
+ <taglist>
+ <tag><c>ReqId :: reference()</c></tag>
+ <item><p>Request identifier. Also used as monitor
+ reference in case the <c>monitor</c> option has been
+ passed.</p></item>
+ <tag><c>From :: pid()</c></tag>
+ <item><p>Process identifier of the process making the
+ request. That is, the parent process to be.</p></item>
+ <tag><c>GroupLeader :: pid()</c></tag>
+ <item><p>Process identifier of the group leader of the
+ newly created process.</p></item>
+ <tag><c>{Module :: atom(), Function :: atom(), Arity :: integer() >= 0}</c></tag>
+ <item><p>Entry point for the the new process.</p></item>
+ <tag><c>OptList :: [term()]</c></tag>
+ <item><p>A proper list of spawn options to use when spawning.</p></item>
+ <tag><c>ArgList :: [term()]</c></tag>
+ <item><p>A proper list of arguments to use in the call to the entry point.</p></item>
+ </taglist>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REQUEST_TT"/><c>SPAWN_REQUEST_TT</c></tag>
+ <item>
+ <p><c>{30, ReqId, From, GroupLeader, {Module, Function, Arity}, OptList, Token}</c></p>
+ <p>Followed by <c>ArgList</c>.</p>
+ <p>Same as <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso>, but also
+ with a sequential trace <c>Token</c>.
+ </p>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REPLY"/><c>SPAWN_REPLY</c></tag>
+ <item>
+ <p><c>{31, ReqId, To, Flags, Result}</c></p>
+ <p>This signal is sent as a reply to a process previously sending
+ a <seealso marker="#SPAWN_REQUEST"><c>SPAWN_REQUEST</c></seealso> signal.</p>
+ <taglist>
+ <tag><c>ReqId :: reference()</c></tag>
+ <item><p>Request identifier. Also used as monitor
+ reference in case the <c>monitor</c> option has been
+ passed.</p></item>
+ <tag><c>To :: pid()</c></tag>
+ <item><p>Process identifier of the process making the
+ spawn request.</p></item>
+ <tag><c>Flags :: integer() >= 0</c></tag>
+ <item><p>A bit flag field of bit flags bitwise or:ed together. Currently the
+ following flags are defined:</p>
+ <taglist>
+ <tag><c>1</c></tag>
+ <item><p>A link between <c>To</c> and <c>Result</c> was set up on
+ the node where <c>Result</c> resides.</p></item>
+ <tag><c>2</c></tag>
+ <item><p>A monitor from <c>To</c> to <c>Result</c> was set up on
+ the node where <c>Result</c> resides.</p></item>
+ </taglist>
+ </item>
+ <tag><c>Result :: pid() | atom()</c></tag>
+ <item><p>Result of the operation. If <c>Result</c> is a process
+ identifier, the operation succeeded and the process identifier
+ is the identifier of the newly created process. If <c>Result</c>
+ is an atom, the operation failed and the atom identifies failure
+ reason.</p></item>
+ </taglist>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
+ <tag><marker id="SPAWN_REPLY_TT"/><c>SPAWN_REPLY_TT</c></tag>
+ <item>
+ <p><c>{32, ReqId, To, Flags, Result, Token}</c></p>
+ <p>Same as <seealso marker="#SPAWN_REPLY"><c>SPAWN_REPLY</c></seealso>, but also
+ with a sequential trace <c>Token</c>.</p>
+ <p>
+ Only supported when the
+ <seealso marker="erl_dist_protocol#DFLAG_SPAWN"><c>DFLAG_SPAWN</c></seealso>
+ <seealso marker="erl_dist_protocol#dflags">distribution flag</seealso>
+ has been passed.
+ </p>
+ </item>
</taglist>
</section>
-
</chapter>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 2ba5994557..c5b2ce1a0a 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -264,7 +264,7 @@
consists of. Length is a 2 byte big-endian integer
if flag <c>LongAtoms</c> has been set, otherwise a 1 byte
integer. When distribution flag
- <seealso marker="erl_dist_protocol#dflags">
+ <seealso marker="erl_dist_protocol#DFLAG_UTF8_ATOMS">
<c>DFLAG_UTF8_ATOMS</c></seealso>
has been exchanged between both nodes in the
<seealso marker="erl_dist_protocol#distribution_handshake">
@@ -316,8 +316,8 @@
</p>
<p>Fragmented distribution messages are only used if the receiving node
signals that it supports them via the
- <seealso marker="erl_dist_protocol#dflags">DFLAG_FRAGMENTS</seealso> distribution
- flag.</p>
+ <seealso marker="erl_dist_protocol#DFLAG_FRAGMENTS">DFLAG_FRAGMENTS</seealso>
+ distribution flag.</p>
<p>A process must complete the sending of a fragmented message before it
can start sending any other message on the same distribution channel.</p>
@@ -637,11 +637,14 @@
<seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.
Port operations are not allowed across node boundaries.
</p>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local ports. Planned to supersede <seealso marker="#PORT_EXT">
- <c>PORT_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEW_PORT_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local ports.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All ports are now
+ encoded using <c>NEW_PORT_EXT</c>, even external ports received as <seealso
+ marker="#PORT_EXT"><c>PORT_EXT</c></seealso> from older nodes.
</p>
</section>
@@ -719,11 +722,14 @@
erlang:list_to_pid/1</seealso>).</p>
</item>
</taglist>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local processes. Planned to supersede <seealso marker="#PID_EXT">
- <c>PID_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEW_PID_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local processes.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All pids are now encoded using <c>NEW_PID_EXT</c>,
+ even external pids received as
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso> from older nodes.
</p>
</section>
@@ -1047,11 +1053,15 @@
<seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.</p>
</item>
</taglist>
- <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
- encoded for local references. Planned to supersede <seealso marker="#NEW_REFERENCE_EXT">
- <c>NEW_REFERENCE_EXT</c></seealso> in OTP 23 when
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
- becomes mandatory.
+ <p><c>NEWER_REFERENCE_EXT</c> was introduced in OTP 19, but only to be decoded
+ and echoed back. Not encoded for local references.
+ </p>
+ <p>In OTP 23 distribution flag
+ <seealso marker="erl_dist_protocol#DFLAG_BIG_CREATION"><c>DFLAG_BIG_CREATION</c></seealso>
+ became mandatory. All references are now encoded using
+ <c>NEWER_REFERENCE_EXT</c>, even external references received as
+ <seealso marker="#NEW_REFERENCE_EXT"><c>NEW_REFERENCE_EXT</c></seealso>
+ from older nodes.
</p>
</section>
@@ -1408,7 +1418,7 @@
<p>
<c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and
require an exchange of distribution flag
- <seealso marker="erl_dist_protocol#dflags">
+ <seealso marker="erl_dist_protocol#DFLAG_SMALL_ATOM_TAGS">
<c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the
<seealso marker="erl_dist_protocol#distribution_handshake">
distribution handshake</seealso>.
diff --git a/erts/doc/src/erl_ext_fig.gif b/erts/doc/src/erl_ext_fig.gif
index 14d6bbc871..40dd17bd5e 100644
--- a/erts/doc/src/erl_ext_fig.gif
+++ b/erts/doc/src/erl_ext_fig.gif
Binary files differ
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 2f3d2f9624..7c8591f719 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -60,6 +60,13 @@
</desc>
</datatype>
<datatype>
+ <name name="ext_iovec"/>
+ <desc>
+ <p>A term of type <seealso marker="#type-iovec"><c>iovec()</c></seealso>,
+ structured according to the Erlang external term format.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="iovec"/>
<desc>
<p>A list of binaries. This datatype is useful to use
@@ -205,6 +212,37 @@
</seealso>.</p>
</desc>
</datatype>
+ <datatype>
+ <name name="spawn_opt_option"></name>
+ <desc>
+ <p>Options for <seealso marker="#spawn_opt/4"><c>spawn_opt()</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="priority_level"></name>
+ <desc>
+ <p>Process priority level. For more info see
+ <seealso marker="#process_flag_priority"><c>process_flag(priority,
+ Level)</c></seealso> </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="max_heap_size"></name>
+ <desc>
+ <p>Process max heap size configuration. For more info see
+ <seealso marker="#process_flag_max_heap_size"><c>process_flag(max_heap_size,
+ MaxHeapSize)</c></seealso> </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="message_queue_data"></name>
+ <desc>
+ <p>Process message queue data configuration. For more info see
+ <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
+ MQD)</c></seealso> </p>
+ </desc>
+ </datatype>
+
</datatypes>
@@ -337,6 +375,16 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="atom_to_binary" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Return the binary representation of an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#atom_to_binary/2"><c>atom_to_binary</c>
+ </seealso><c>(<anno>Atom</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="atom_to_binary" arity="2" since=""/>
<fsummary>Return the binary representation of an atom.</fsummary>
<desc>
@@ -409,14 +457,21 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_atom/2"><c>binary_to_atom</c>
+ </seealso><c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
- <c><anno>Binary</anno></c>.
- If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
- translation of bytes in the binary is done.
- If <c><anno>Encoding</anno></c>
+ <c><anno>Binary</anno></c>. If <c><anno>Encoding</anno></c>
is <c>utf8</c> or <c>unicode</c>, the binary must contain
valid UTF-8 sequences.</p>
<note>
@@ -438,6 +493,17 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_existing_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_existing_atom/2">
+ <c>binary_to_existing_atom</c></seealso>
+ <c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_existing_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
@@ -1045,7 +1111,9 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
as atoms. Others are returned as strings. Strings of
unrecognized header fields are formatted with only
capital letters first and after hyphen characters, for
- example, <c>"Sec-Websocket-Key"</c>.</p>
+ example, <c>"Sec-Websocket-Key"</c>. Header field names
+ are also returned in <c><anno>UnmodifiedField</anno></c>
+ as strings, without any conversion or formatting.</p>
<p>The protocol type <c>http</c> is only to be used for
the first line when an <c><anno>HttpRequest</anno></c> or an
<c><anno>HttpResponse</anno></c> is expected.
@@ -1270,9 +1338,10 @@ end</code>
<seealso marker="erlang#dist_ctrl_get_data_notification/1"><c>erlang:dist_ctrl_get_data_notification(DHandle)</c></seealso>.
</p>
<p>The returned value when there are data available depends
- on the value of the <c>get_size</c> option configured on the
- distribution channel identified by <c><anno>DHandle</anno></c>.
- For more information see the documentation of the <c>get_size</c>
+ on the value of the <c>get_size</c>
+ option configured on the distribution channel identified
+ by <c><anno>DHandle</anno></c>. For more information see
+ the documentation of the <c>get_size</c>
option for the
<seealso marker="#dist_ctrl_set_opt/3"><c>erlang:dist_ctrl_set_opt/3</c></seealso>
function.</p>
@@ -1839,7 +1908,7 @@ true</pre>
<p>It might point to the <c>init</c> process if the
<c>Fun</c> was statically allocated when module was
loaded (this optimisation is performed for local
- functions that do not capture the enviornment).</p>
+ functions that do not capture the environment).</p>
</item>
<tag><c>{index, Index}</c></tag>
<item>
@@ -2082,8 +2151,9 @@ true</pre>
<fsummary>Get the call stack back-trace of the last exception.</fsummary>
<type name="stack_item"/>
<desc>
- <warning><p><c>erlang:get_stacktrace/0</c> is deprecated and will stop working
- in a future release.</p></warning>
+ <warning><p><c>erlang:get_stacktrace/0</c> is deprecated and
+ will be removed in OTP 24. Starting from OTP 23,
+ <c>erlang:get_stacktrace/0</c> returns <c>[]</c>.</p></warning>
<p>Instead of using <c>erlang:get_stacktrace/0</c> to retrieve
the call stack back-trace, use the following syntax:</p>
<pre>
@@ -2092,53 +2162,6 @@ catch
Class:Reason:Stacktrace ->
{Class,Reason,Stacktrace}
end</pre>
- <p><c>erlang:get_stacktrace/0</c> retrieves the call stack back-trace
- (<em>stacktrace</em>) for an exception that has just been
- caught in the calling process as a list of
- <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c>
- tuples. Field <c><anno>Arity</anno></c> in the first tuple can
- be the argument list of that function call instead of an arity
- integer, depending on the exception.</p>
- <p>If there has not been any exceptions in a process, the
- stacktrace is <c>[]</c>. After a code change for the process,
- the stacktrace can also be reset to <c>[]</c>.</p>
- <p>The stacktrace is the same data as operator <c>catch</c>
- returns, for example:</p>
- <pre>
-{'EXIT',{badarg,Stacktrace}} = catch abs(x)</pre>
- <p><c><anno>Location</anno></c> is a (possibly empty) list
- of two-tuples that
- can indicate the location in the source code of the function.
- The first element is an atom describing the type of
- information in the second element. The following
- items can occur:</p>
- <taglist>
- <tag><c>file</c></tag>
- <item>The second element of the tuple is a string (list of
- characters) representing the filename of the source file
- of the function.
- </item>
- <tag><c>line</c></tag>
- <item>The second element of the tuple is the line number
- (an integer &gt; 0) in the source file
- where the exception occurred or the function was called.
- </item>
- </taglist>
- <warning><p>Developers should rely on stacktrace entries only for
- debugging purposes.</p>
- <p>The VM performs tail call optimization, which
- does not add new entries to the stacktrace, and also limits stacktraces
- to a certain depth. Furthermore, compiler options, optimizations and
- future changes may add or remove stacktrace entries, causing any code
- that expects the stacktrace to be in a certain order or contain specific
- items to fail.</p>
- <p>The only exception to this rule is <c>error:undef</c> which
- guarantees to include the <anno>Module</anno>, <anno>Function</anno> and <anno>Arity</anno>
- of the attempted function as the first stacktrace entry.</p>
- </warning>
- <p>See also
- <seealso marker="#error/1"><c>error/1</c></seealso> and
- <seealso marker="#error/2"><c>error/2</c></seealso>.</p>
</desc>
</func>
@@ -2675,26 +2698,37 @@ false</code>
<name name="link" arity="1" since=""/>
<fsummary>Create a link to another process (or port).</fsummary>
<desc>
- <p>Creates a link between the calling process and another
- process (or port) <c><anno>PidOrPort</anno></c>, if there is
- not such a link
- already. If a process attempts to create a link to itself,
- nothing is done. Returns <c>true</c>.</p>
- <p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior
- of the BIF
- depends on if the calling process is trapping exits or not (see
- <seealso marker="#process_flag/2">
- <c>process_flag/2</c></seealso>):</p>
+
+ <p>Creates a link between the calling process and another process (or
+ port) <c><anno>PidOrPort</anno></c>. If the link already exists or a
+ process attempts to create a link to itself, nothing is done. Returns
+ <c>true</c> if the link is set up.</p>
+
+ <p>If <c><anno>PidOrPort</anno></c> does not exist and checking it is
+ cheap, a <c>noproc</c> error is raised. Currently, checking is cheap
+ if the <c><anno>PidOrPort</anno></c> is local and the caller does not
+ trap exits (see <seealso marker="#process_flag/2"><c>process_flag/2
+ </c></seealso>).</p>
+
+ <p>Apart from any exit signals from the linked process itself, two
+ special exit signals may be sent to the calling process:</p>
+
<list type="bulleted">
- <item><p>If the calling process is not trapping exits, and
- checking <c><anno>PidOrPort</anno></c> is cheap
- (that is, if <c><anno>PidOrPort</anno></c>
- is local), <c>link/1</c> fails with reason <c>noproc</c>.</p></item>
- <item><p>Otherwise, if the calling process is trapping exits,
- and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
- returns <c>true</c>, but an exit signal with reason <c>noproc</c>
- is sent to the calling process.</p></item>
+
+ <item><p><c>noproc</c> is sent immediately if
+ <c><anno>PidOrPort</anno></c> does not exist at the time of linking
+ (if the caller is trapping exits or <c><anno>PidOrPort</anno></c> is
+ remote).</p></item>
+
+ <item><p><c>noconnection</c> if <c><anno>PidOrPort</anno></c> is
+ remote and a connection between the nodes could not be established
+ or was severed.</p></item>
+
</list>
+
+ <p>See <seealso marker="doc/reference_manual:processes#links">Processes
+ ➜ Links</seealso> in the Erlang Reference Manual for more details.</p>
+
</desc>
</func>
@@ -5383,12 +5417,13 @@ RealSystem = system + MissedSystem</code>
</item>
<tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
- <p>Returns the current call stack back-trace (<em>stacktrace</em>)
- of the process. The stack has the same format as returned by
- <seealso marker="#get_stacktrace/0">
- <c>erlang:get_stacktrace/0</c></seealso>. The depth of the
- stacktrace is truncated according to the <c>backtrace_depth</c>
- system flag setting.</p>
+ <p>Returns the current call stack back-trace
+ (<em>stacktrace</em>) of the process. The stack has the
+ same format as in the <c>catch</c> part of a
+ <c>try</c>. See <seealso
+ marker="doc/reference_manual:errors#stacktrace">The call-stack back trace (stacktrace)</seealso>.
+ The depth of the stacktrace is truncated according to the
+ <c>backtrace_depth</c> system flag setting.</p>
</item>
<tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
@@ -6280,6 +6315,23 @@ true</pre>
</func>
<func>
+ <name name="spawn_monitor" arity="2" since="OTP @OTP-15251@"/>
+ <fsummary>Create and monitor a new process with a fun as entry point.
+ </fsummary>
+ <desc>
+ <p>Returns the process identifier of a new process, started by
+ the application of <c><anno>Fun</anno></c> to the empty list
+ <c>[]</c> on the node <c><anno>Node</anno></c>,
+ and a reference for a monitor created to the new process.
+ Otherwise works like
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
+ <p>If the node identified by <c><anno>Node</anno></c> does not
+ support distributed <c>spawn_monitor()</c>, the call will fail
+ with a <c>notsup</c> exception.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="spawn_monitor" arity="3" since=""/>
<fsummary>Create and monitor a new process with a function as entry point.
</fsummary>
@@ -6294,6 +6346,23 @@ true</pre>
</func>
<func>
+ <name name="spawn_monitor" arity="4" since="OTP @OTP-15251@"/>
+ <fsummary>Create and monitor a new process with a function as entry point.
+ </fsummary>
+ <desc>
+ <p>A new process is started by the application
+ of <c><anno>Module</anno>:<anno>Function</anno></c>
+ to <c><anno>Args</anno></c> on the node <c><anno>Node</anno></c>.
+ The process is monitored at the same time. Returns the process
+ identifier and a reference for the monitor. Otherwise works like
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
+ <p>If the node identified by <c><anno>Node</anno></c> does not
+ support distributed <c>spawn_monitor()</c>, the call will fail
+ with a <c>notsup</c> exception.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="spawn_opt" arity="2" since=""/>
<fsummary>Create a new process with a fun as entry point.</fsummary>
<type name="priority_level"/>
@@ -6315,17 +6384,22 @@ true</pre>
<name name="spawn_opt" arity="3" since=""/>
<fsummary>Create a new process with a fun as entry point on a specified
node.</fsummary>
- <type name="priority_level"/>
- <type name="max_heap_size"/>
- <type name="message_queue_data"/>
- <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>
+ Valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of
+ valid <c>Option</c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
</desc>
</func>
@@ -6469,10 +6543,6 @@ true</pre>
<name name="spawn_opt" arity="5" since=""/>
<fsummary>Create a new process with a function as entry point on a
specified node.</fsummary>
- <type name="priority_level"/>
- <type name="max_heap_size"/>
- <type name="message_queue_data"/>
- <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -6480,15 +6550,417 @@ true</pre>
<c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
- <note>
- <p>Option <c>monitor</c> is not supported by
- <c>spawn_opt/5</c>.</p>
- </note>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>
+ Valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of
+ valid <c>Option</c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(node(),<anno>Fun</anno>,[])</c></seealso>.
+ That is, a spawn request on the local node with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="2" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(node(),<anno>Fun</anno>,<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request on the local node.
+ </p>
</desc>
</func>
<func>
+ <name name="spawn_request" arity="2" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/3"><c>spawn_request(<anno>Node</anno>,<anno>Fun</anno>,[])</c></seealso>.
+ That is, a spawn request with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="3" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as
+ <seealso marker="#spawn_request/5"><c>spawn_request(<anno>Node</anno>,erlang,apply,[<anno>Fun</anno>,[]],<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request using the fun <c><anno>Fun</anno></c> of
+ arity zero as entry point.
+ </p>
+ <p>This function will fail with a <c>badarg</c> exception if:</p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Fun</anno></c> is not a fun of arity zero.</p></item>
+ <item><p><c><anno>Options</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="3" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(node(),<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,[])</c></seealso>.
+ That is, a spawn request on the local node with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="4" clause_i="1" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(<anno>Node</anno>,<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,[])</c></seealso>.
+ That is, a spawn request with no options.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="4" clause_i="2" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ The same as the call
+ <seealso marker="#spawn_request/5"><c>spawn_request(node(),<anno>Module</anno>,<anno>Function</anno>,<anno>Args</anno>,<anno>Options</anno>)</c></seealso>.
+ That is, a spawn request on the local node.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request" arity="5" since="OTP @OTP-15251@"/>
+ <fsummary>Asynchronously send a request to spawn a new process.</fsummary>
+ <desc>
+ <p>
+ Asynchronously send a spawn request. Returns a request
+ identifier <c><anno>ReqId</anno></c>.
+ </p>
+
+ <marker id="spawn_request_success_message"/>
+ <p>
+ If the spawn operation succeeds, a new process is
+ created on the node identified by <c><anno>Node</anno></c>.
+ When a spawn operation succeeds, the caller will by
+ default be sent a message on the form
+ <c>{<anno>ReplyTag</anno>, <anno>ReqId</anno>, ok, Pid}</c>
+ where <c>Pid</c> is the process identifier of the
+ newly created process. Such a message is referred to as a
+ <i>success message</i> below in the text.
+ <c><anno>ReplyTag</anno></c> is by default the atom
+ <c>spawn_reply</c> unless modified by
+ the <c>{reply_tag, <anno>ReplyTag</anno>}</c> option. The
+ new process is started by the application of
+ <c><anno>Module</anno>:<anno>Function</anno></c>
+ to <c><anno>Args</anno></c>.
+ </p>
+
+ <marker id="spawn_request_error_message"/>
+ <p>The spawn operation fails either if creation of a new process
+ failed or if the spawn operation was interrupted by a connection
+ failure. When a spawn operation fails, the caller will by default
+ be sent a message on the form
+ <c>{<anno>ReplyTag</anno>, <anno>ReqId</anno>, error, Reason}</c>
+ where <c>Reason</c> is the error reason. Such a message is
+ referred to as an <i>error message</i> below in the text.
+ Currently the following spawn error <c>Reason</c>s are defined,
+ but other reasons can appear at any time without prior notice:</p>
+ <taglist>
+ <tag><c>badopt</c></tag>
+ <item>
+ <p>
+ An invalid <c><anno>Option</anno></c> was passed as
+ argument. Note that different runtime systems may
+ support different options.
+ </p>
+ </item>
+ <tag><c>notsup</c></tag>
+ <item>
+ <p>
+ The node identified by <c><anno>Node</anno></c> does
+ not support spawn operations issued by
+ <c>spawn_request()</c>.
+ </p>
+ </item>
+ <tag><c>noconnection</c></tag>
+ <item>
+ <p>
+ Failure to set up a connection to the node
+ identified by <c><anno>Node</anno></c> or
+ the connection to that node was lost during
+ the spawn operation. In the case the
+ connection was lost, a process may or may
+ not have been created.
+ </p>
+ </item>
+ <tag><c>system_limit</c></tag>
+ <item>
+ <p>
+ Could not create a new process due to that some
+ system limit was reached. Typically the process table
+ was full.
+ </p>
+ </item>
+ </taglist>
+ <p>Valid <c><anno>Option</anno></c>s:</p>
+ <taglist>
+ <tag><c>monitor</c></tag>
+ <item>
+ <p>
+ In the absence of spawn operation failures, atomically
+ sets up a monitor to the newly created process. That
+ is, as if the calling process had called
+ <seealso marker="#monitor/2"><c>monitor(process, Pid)</c></seealso>
+ where <c>Pid</c> is the process identifier of the
+ newly created process. The <c><anno>ReqId</anno></c>
+ returned by <c>spawn_request()</c> is also used as
+ monitor reference as if it was returned from
+ <c>monitor(process, Pid)</c>.
+ </p>
+ <p>
+ The monitor will not be activated for the calling
+ process until the spawn operation has succeeded.
+ The monitor can not be
+ <seealso marker="#demonitor/1">demonitored</seealso>
+ before the operation has succeeded. A <c>'DOWN'</c>
+ message for the corresponding monitor is guaranteed
+ not to be delivered before a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> that corresponds to the spawn
+ operation. If the spawn operation fails, no
+ <c>'DOWN'</c> message will be delivered.
+ </p>
+ <p>
+ If the connection between the nodes involved in
+ the spawn operation is lost during the spawn
+ operation, the spawn operation will fail with an
+ error reason of <c>noconnection</c>. A new process
+ may or may not have been created.
+ </p>
+ </item>
+ <tag><c>link</c></tag>
+ <item>
+ <p>
+ In absence of spawn operation failures, atomically
+ sets up a link between the calling process and the
+ newly created process. That is, as if the calling
+ process had called
+ <seealso marker="#link/1"><c>link(Pid)</c></seealso>
+ where <c>Pid</c> is the process identifier of the
+ newly created process.
+ </p>
+ <p>
+ The link will not be activated for the calling
+ process until the spawn operation has succeeded.
+ The link can not be removed before the operation
+ has succeeded. An exit signal due to the link is
+ guaranteed not to be delivered before a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> that corresponds to the spawn
+ operation. If the spawn operation fails, no
+ exit signal due to the link will be delivered to
+ the caller of <c>spawn_request()</c>.
+ </p>
+ <p>
+ If the connection between the nodes involved in
+ the spawn operation is lost during the spawn
+ operation, the spawn operation will fail with
+ an error reason of <c>noconnection</c>. A new
+ process may or may not have been created. If it
+ has been created, it will be delivered an exit
+ signal with an exit reason of <c>noconnection</c>.
+ </p>
+ </item>
+ <tag><c>{reply, <anno>Reply</anno>}</c></tag>
+ <item>
+ <p>Valid <c>Reply</c> values:</p>
+ <taglist>
+ <tag><c>yes</c></tag>
+ <item><p>
+ A spawn reply message will be sent to the caller
+ regardless of whether the operation succeeds or
+ not. If the call to <c>spawn_request()</c> returns
+ without raising an exception and the <c>reply</c>
+ option is set to <c>yes</c>, the caller is
+ guaranteed to be delivered either a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> or an
+ <seealso marker="#spawn_request_error_message"><i>error
+ message</i></seealso>. The <c>reply</c> option is by
+ default set to <c>yes</c>.
+ </p></item>
+ <tag><c>no</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ when the spawn operation completes. This regardless of
+ whether the operation succeeds or not.
+ </p></item>
+ <tag><c>error_only</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ if the spawn operation succeeds, but an
+ <seealso marker="#spawn_request_error_message"><i>error
+ message</i></seealso> will be sent to the caller
+ if the operation fails.
+ </p></item>
+ <tag><c>success_only</c></tag>
+ <item><p>
+ No spawn reply message will be sent to the caller
+ if the spawn operation fails, but a
+ <seealso marker="#spawn_request_success_message"><i>success
+ message</i></seealso> will be sent to the caller
+ if the operation succeeds.
+ </p></item>
+ </taglist>
+ </item>
+ <tag><c>{reply_tag, <anno>ReplyTag</anno>}</c></tag>
+ <item>
+ <p>
+ Sets the reply tag to <c><anno>ReplyTag</anno></c> in
+ the reply message. That is, in the
+ <seealso marker="#spawn_request_success_message"><i>success</i></seealso>
+ or <seealso marker="#spawn_request_error_message"><i>error</i></seealso>
+ message that is sent to the caller due to the
+ spawn operation. The default reply tag is the atom
+ <c>spawn_reply</c>.
+ </p>
+ </item>
+ <tag><c><anno>OtherOption</anno></c></tag>
+ <item>
+ <p>
+ Other valid options depends on what options are
+ supported by the node identified by
+ <c><anno>Node</anno></c>. A description of other
+ valid <c><anno>Option</anno></c>s for the local node
+ of current OTP version can be found in the
+ documentation of
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.
+ </p>
+ </item>
+ </taglist>
+ <p>This function will fail with a <c>badarg</c> exception if:
+ </p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Module</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Function</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Args</anno></c> is not a proper list
+ of terms.</p></item>
+ <item><p><c><anno>Options</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ <p>
+ Note that not all individual <c><anno>Option</anno></c>s
+ are checked when the spawn request is sent. Some
+ <c><anno>Option</anno></c>s can only be checked on
+ reception of the request. Therefore an invalid option
+ does <em>not</em> cause a <c>badarg</c> exception, but
+ will cause the spawn operation to fail with an error
+ reason of <c>badopt</c>.
+ </p>
+ <p>
+ A spawn request can be abandoned by calling
+ <seealso marker="#spawn_request_abandon/1"><c>spawn_request_abandon/1</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="spawn_request_abandon" arity="1" since="OTP @OTP-15251@"/>
+ <fsummary>Abandon a previously issued spawn request.</fsummary>
+ <desc>
+ <p>
+ Abandon a previously issued spawn request. <c><anno>ReqId</anno></c>
+ corresponds to a request identifier previously returned by
+ <seealso marker="#spawn_request/5"><c>spawn_request()</c></seealso>
+ in a call from current process. That is, only the process that
+ has made the request can abandon the request.
+ </p>
+ <p>
+ A spawn request can only be successfully abandoned until the
+ spawn request has completed. When a spawn request has been
+ successfully abandoned, the caller will not be effected by
+ future direct effects of the spawn request itself.
+ For example, it will not receive a spawn reply message. The
+ request is however not withdrawn, so a new process may or may
+ not be created due to the request. If a new process is created
+ after the spawn request was abandoned, no monitors nor links
+ will be set up to the caller of <c>spawn_request_abandon/1</c>
+ due to the spawn request. If the spawn request included the
+ <c>link</c> option, the process created due to this request
+ will be sent an exit signal from its parent with the exit
+ reason <c>abandoned</c> when it is detected that the
+ spawn operation has succeeded.
+ </p>
+ <note><p>
+ A process created due to a spawn request that has been
+ abandoned may communicate with its parent as any other
+ process. It is <em>only</em> the direct effects on the
+ parent of the actual spawn request, that will be canceled
+ by abandoning a spawn request.
+ </p></note>
+ <p>Return values:</p>
+ <taglist>
+ <tag><c>true</c></tag>
+ <item><p>
+ The spawn request was successfully abandoned.
+ </p></item>
+ <tag><c>false</c></tag>
+ <item><p>
+ No spawn request was abandoned. The <c><anno>ReqId</anno></c>
+ request identifier did not correspond to an outstanding
+ spawn request issued by the calling process. The reason for
+ this is either:</p>
+ <list>
+ <item><p>
+ <c><anno>ReqId</anno></c> corresponds to a spawn request
+ previoulsy made by the calling process. The spawn operation
+ has completed and a spawn reply has already been delivered
+ to the calling process unless the spawn reply was disabled
+ in the request.
+ </p></item>
+ <item><p>
+ <c><anno>ReqId</anno></c> does not correspond to a spawn
+ request that has been made by the calling process.
+ </p></item>
+ </list>
+ </item>
+ </taglist>
+ <p>This function fail with a <c>badarg</c> exception if
+ <c><anno>ReqId</anno></c> is not a reference.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="split_binary" arity="2" since=""/>
<fsummary>Split a binary into two.</fsummary>
<type_desc variable="Pos">0..byte_size(Bin)</type_desc>
@@ -7799,7 +8271,7 @@ Metadata = #{ pid => pid(),
</func>
<func>
- <name name="system_info" arity="1" clause_i="76" since=""/>
+ <name name="system_info" arity="1" clause_i="77" since=""/>
<fsummary>System info overview.</fsummary>
<desc>
<p>Returns information about the current system.
@@ -8218,6 +8690,15 @@ Metadata = #{ pid => pid(),
<seealso marker="#system_info_logical_processors">logical processors
configured</seealso>.</p>
</item>
+ <tag><marker id="system_info_cpu_quota"/>
+ <c>cpu_quota</c></tag>
+ <item>
+ <p>Returns the detected CPU quota the emulator is limited by. The
+ return value is an integer saying how many processors' worth of
+ runtime we get (between 1 and the number of logical processors),
+ or the atom <c>unknown</c> if the emulator cannot detect a
+ quota.</p>
+ </item>
<tag><marker id="system_info_update_cpu_info"/>
<c>update_cpu_info</c></tag>
<item>
@@ -8227,8 +8708,9 @@ Metadata = #{ pid => pid(),
CPU topology</seealso> and the number of logical processors
<seealso marker="#system_info_logical_processors">configured</seealso>,
<seealso marker="#system_info_logical_processors_online">online</seealso>,
- and <seealso marker="#system_info_logical_processors_available">
- available</seealso>.</p>
+ <seealso marker="#system_info_logical_processors_available">available</seealso>,
+ and <seealso marker="#system_info_cpu_quota">cpu
+ quota</seealso>.</p>
<p>If the CPU information has changed since the last time
it was read, the atom <c>changed</c> is returned, otherwise
the atom <c>unchanged</c>. If the CPU information has changed,
@@ -9120,6 +9602,7 @@ Metadata = #{ pid => pid(),
<name name="system_info" arity="1" clause_i="75" since=""/> <!-- version -->
<name name="system_info" arity="1" clause_i="76" since=""/> <!-- wordsize -->
<!-- <name name="system_info" arity="1" clause_i="77"/> overview -->
+ <!-- When adding any entry, make sure to update the overview clause_i -->
<fsummary>Information about the system.</fsummary>
<desc>
<marker id="system_info_misc_tags"/>
@@ -9672,7 +10155,7 @@ hello
<func>
<name name="term_to_binary" arity="2" since=""/>
- <fsummary>Encode a term to en Erlang external term format binary.
+ <fsummary>Encode a term to an Erlang external term format binary.
</fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
@@ -9736,6 +10219,59 @@ hello
</func>
<func>
+ <name name="term_to_iovec" arity="1" since="OTP @OTP-15618@"/>
+ <fsummary>Encode a term to an Erlang external term format as iovec().
+ </fsummary>
+ <desc>
+ <p>Returns the encoding of <c><anno>Term</anno></c> according to
+ the Erlang external term format as
+ <seealso marker="#type-ext_iovec"><c>ext_iovec()</c></seealso>.</p>
+
+ <p>This function produce the same encoding as
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>,
+ but with another return type. The call
+ <c>iolist_to_binary(term_to_iovec(Term))</c> will produce
+ exactly the same result as the call <c>term_to_binary(Term)</c>.</p>
+
+ <p><c>term_to_iovec()</c> is a pure optimization of the functionality
+ <c>term_to_binary()</c> provide. <c>term_to_iovec()</c> can for example
+ refer directly to off heap binaries instead of copying the binary
+ data into the result.</p>
+
+ <p>See also
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="term_to_iovec" arity="2" since="OTP @OTP-15618@"/>
+ <fsummary>Encode a term to en Erlang external term format as iovec().
+ </fsummary>
+ <desc>
+ <p>Returns the encoding of <c><anno>Term</anno></c> according to
+ the Erlang external term format as
+ <seealso marker="#type-ext_iovec"><c>ext_iovec()</c></seealso>.</p>
+
+ <p>This function produce the same encoding as
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>,
+ but with another return type. The call
+ <c>iolist_to_binary(term_to_iovec(Term, Opts))</c> will produce
+ exactly the same result as <c>term_to_binary(Term, Opts)</c>.</p>
+
+ <p>Currently recognised options are all options recognised by
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>.</p>
+
+ <p><c>term_to_iovec()</c> is a pure optimization of the functionality
+ <c>term_to_binary()</c> provide. <c>term_to_iovec()</c> can for example
+ refer directly to off heap binaries instead of copying the binary
+ data into the result.</p>
+
+ <p>See also
+ <seealso marker="#term_to_binary/2"><c>term_to_binary/2</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="throw" arity="1" since=""/>
<fsummary>Throw an exception.</fsummary>
<desc>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc_cmd.xml
index f1f2c786da..55a4712b25 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -387,4 +387,3 @@ erlc +export_all file.erl</pre>
<seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv_cmd.xml
index 6c08b25220..1db5fcd9db 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -43,7 +43,7 @@
Windows services applet in a manner similar to other services.</p>
<p>Notice that <c>erlsrv</c> is not a general service utility for Windows,
- but designed for embedded Erlang systems.</p>
+ but designed for embedded Erlang systems.</p>
<p><c>erlsrv</c> also provides a command-line interface for registering,
changing, starting, and stopping services.</p>
@@ -471,10 +471,10 @@
<code type="none"><![CDATA[
#include <windows.h>
-/*
+/*
** A Console control handler that ignores the log off events,
** and lets the default handler take care of other events.
-*/
+*/
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
@@ -485,8 +485,8 @@ BOOL WINAPI service_aware_handler(DWORD ctrl){
void initialize_handler(void){
char buffer[2];
- /*
- * We assume we are running as a service if this
+ /*
+ * We assume we are running as a service if this
* environment variable is defined.
*/
if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,
@@ -530,4 +530,3 @@ void initialize_handler(void){
<c>release_handler(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript_cmd.xml
index be1664b39f..4ca7e46e6e 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -448,4 +448,3 @@ ok
</taglist>
</section>
</comref>
-
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index d6c73a02ec..c4b1478c18 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -48,6 +48,15 @@
the system.</p>
</description>
+ <datatypes>
+ <datatype>
+ <name name="mode"/>
+ <desc>
+ <p>Code loading mode.</p>
+ </desc>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name name="boot" arity="1" since=""/>
@@ -165,12 +174,24 @@
<name name="restart" arity="0" since=""/>
<fsummary>Restart the running Erlang node.</fsummary>
<desc>
+ <p>The same as
+ <seealso marker="#restart/1"><c>restart([])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="restart" arity="1" since="OTP 23.0"/>
+ <fsummary>Restart the running Erlang node.</fsummary>
+ <desc>
<p>The system is restarted <em>inside</em> the running Erlang
node, which means that the emulator is not restarted. All
applications are taken down smoothly, all code is unloaded,
and all ports are closed before the system is booted again in
- the same way as initially started. The same <c>BootArgs</c>
- are used again.</p>
+ the same way as initially started.</p>
+ <p>The same <c>BootArgs</c> are used when restarting the
+ system unless the <c>mode</c> option is given, allowing the
+ code loading mode to be set to either <c>embedded</c> or
+ <c>interactive</c>. All other <c>BootArgs</c> remain the same.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
to spend taking down applications, command-line flag
<c>-shutdown_time</c> is to be used.</p>
diff --git a/erts/doc/src/ref_man.xml.src b/erts/doc/src/ref_man.xml.src
index 7dd003763c..5b327165d4 100644
--- a/erts/doc/src/ref_man.xml.src
+++ b/erts/doc/src/ref_man.xml.src
@@ -34,24 +34,24 @@
<xi:include href="atomics.xml"/>
<xi:include href="counters.xml"/>
<xi:include href="driver_entry.xml"/>
- <xi:include href="epmd.xml"/>
- <xi:include href="erl.xml"/>
+ <xi:include href="epmd_cmd.xml"/>
+ <xi:include href="erl_cmd.xml"/>
<xi:include href="erlang.xml"/>
- <xi:include href="erlc.xml"/>
+ <xi:include href="erlc_cmd.xml"/>
<xi:include href="erl_driver.xml"/>
<xi:include href="erl_nif.xml"/>
<xi:include href="erl_prim_loader.xml"/>
- <xi:include href="erlsrv.xml"/>
+ <xi:include href="erlsrv_cmd.xml"/>
<xi:include href="erl_tracer.xml"/>
<xi:include href="erts_alloc.xml"/>
- <xi:include href="escript.xml"/>
+ <xi:include href="escript_cmd.xml"/>
<xi:include href="init.xml"/>
<xi:include href="persistent_term.xml"/>
- <xi:include href="run_erl.xml"/>
+ <xi:include href="run_erl_cmd.xml"/>
%ESOCK_USE_SOCKET_XML%
- <xi:include href="start.xml"/>
- <xi:include href="start_erl.xml"/>
- <xi:include href="werl.xml"/>
+ <xi:include href="start_cmd.xml"/>
+ <xi:include href="start_erl_cmd.xml"/>
+ <xi:include href="werl_cmd.xml"/>
<xi:include href="zlib.xml"/>
</application>
diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl_cmd.xml
index fa36457489..9f1984f784 100644
--- a/erts/doc/src/run_erl.xml
+++ b/erts/doc/src/run_erl_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>run_erl</title>
@@ -213,4 +213,3 @@
<seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 2d16ff2074..980479bd99 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2018</year><year>2019</year>
+ <year>2018</year><year>2020</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -94,7 +94,7 @@
<datatype>
<name>socket()</name>
<desc><p>As returned by
- <seealso marker="#open/2"><c>open/2,3,4</c></seealso> and
+ <seealso marker="#open/1"><c>open/1,2,3,4</c></seealso> and
<seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p>
</desc>
</datatype>
@@ -513,7 +513,58 @@
</func>
<func>
- <name name="open" arity="2" since="OTP 22.0"/>
+ <name name="open" arity="1" since="OTP @OTP-16398@"/>
+ <name name="open" arity="2" clause_i="1" since="OTP @OTP-16398@"/>
+ <fsummary>Create an endpoint for communication.</fsummary>
+ <desc>
+ <p>Create an endpoint (socket) for communication based on an
+ already existing file descriptor.
+ The function attempts to retrieve domain, type and protocol from
+ the system. This is however not possible on all platforms, and
+ in those cases it expects it in <c>Opts</c>. </p>
+
+ <p>The <c>Opts</c> argument is intended for providing extra
+ information for the open call:</p>
+ <taglist>
+ <tag><c><![CDATA[dup: boolean()]]></c></tag>
+ <item>
+ <p>Shall the provided descriptor be duplicated (dup) or not.
+ <br/>Defaults to <c>true</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[debug: boolean()]]></c></tag>
+ <item>
+ <p>Enable or disable debug during the open call.
+ <br/>Defaults to <c>false</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[domain: socket:domain()]]></c></tag>
+ <item>
+ <p>Which domain is the descriptor of. </p>
+ </item>
+
+ <tag><c><![CDATA[type: socket:type()]]></c></tag>
+ <item>
+ <p>Which type is the descriptor of. </p>
+ </item>
+
+ <tag><c><![CDATA[protocol: socket:protocol()]]></c></tag>
+ <item>
+ <p>Which protocol is the descriptor of. </p>
+ </item>
+
+ </taglist>
+
+ <note>
+ <p>This function should be used with care! </p>
+ <p>On some platforms its <em>necessary</em> to provide the
+ <c>protocol</c> as its impossible to retrieve it. </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="open" arity="2" clause_i="2" since="OTP 22.0"/>
<name name="open" arity="3" since="OTP 22.0"/>
<name name="open" arity="4" since="OTP 22.0"/>
<fsummary>Create an endpoint for communication.</fsummary>
@@ -526,9 +577,10 @@
And for <c>Domain = local</c>, if a protocol <em>is</em> pecified,
it <em>must</em> be <c>default</c>. </p>
- <p>The <c>Extra</c> argument is intended for "obscure" options.
- Currently the only supported option is <c>netns</c>, which
- is only supported on the linux platform.</p>
+ <p>The <c>Opts</c> argument is intended for "other" options.
+ Currently the only supported option(s) are <c>netns</c>, which
+ is only supported on the linux platform and <c>debug</c> (controls debug
+ printouts during the open call).</p>
<note>
<p>It may not be possible to specify the default protocol (except
@@ -911,7 +963,8 @@
<name name="supports" arity="1" clause_i="4" since="OTP 22.0"/>
<name name="supports" arity="1" clause_i="5" since="OTP @OTP-16153@"/>
<name name="supports" arity="1" clause_i="6" since="OTP @OTP-16153@"/>
- <name name="supports" arity="1" clause_i="7" since="OTP 22.0"/>
+ <name name="supports" arity="1" clause_i="7" since="OTP @OTP-16432@"/>
+ <name name="supports" arity="1" clause_i="8" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="1" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="2" since="OTP 22.0"/>
<name name="supports" arity="2" clause_i="3" since="OTP 22.0"/>
@@ -1014,4 +1067,3 @@ server(Addr, Port) ->
</code>
</section>
</erlref>
-
diff --git a/erts/doc/src/start.xml b/erts/doc/src/start_cmd.xml
index 6eac47fe94..9f208d43bb 100644
--- a/erts/doc/src/start.xml
+++ b/erts/doc/src/start_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>start</title>
@@ -70,4 +70,3 @@
<seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl_cmd.xml
index 4887d4606e..e9cb248da4 100644
--- a/erts/doc/src/start_erl.xml
+++ b/erts/doc/src/start_erl_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>start_erl</title>
@@ -89,7 +89,7 @@
only option <c>-rootdir</c> is specified, the directory is
assumed to be &lt;Erlang root&gt;\\releases.</p>
</item>
- <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
+ <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
<item>
<p>Mandatory if <c>-reldir</c> is not specified and no
<c><![CDATA[RELDIR]]></c> exists in the environment. This
@@ -166,4 +166,3 @@
<c>release_handler(3)</c></seealso></p>
</section>
</comref>
-
diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl_cmd.xml
index 792fe204e8..4f25565c64 100644
--- a/erts/doc/src/werl.xml
+++ b/erts/doc/src/werl_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>werl</title>
@@ -62,7 +62,7 @@
<p>In cases where you want to redirect standard input and/or
standard output or use Erlang in a pipeline, <c>werl</c> is
not suitable, and the <c>erl</c> program is to be used instead.</p>
-
+
<p>The <c>werl</c> window is in many ways modeled after the <c>xterm</c>
window present on other platforms, as the <c>xterm</c> model
fits well with line-oriented command-based interaction. This
@@ -102,7 +102,7 @@
use <c>Ctrl-P</c>.</p>
</item>
</list>
-
+
<p>A drop-down box in the toolbar contains the command
history. Selecting a command in the drop-down box inserts the command
at the prompt, as if you used the keyboard to retrieve the
@@ -115,4 +115,3 @@
</list>
</description>
</comref>
-
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index fb56eadf39..e0fca8b082 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -269,15 +269,16 @@ STRIP = strip
PERL = @PERL@
MKDIR = @MKDIR@
-USING_MINGW=@MIXED_CYGWIN_MINGW@
-MIXED_MSYS=@MIXED_MSYS@
+USING_MINGW=@MIXED_MINGW@
ifeq ($(TARGET),win32)
LIB_PREFIX=
LIB_SUFFIX=.lib
+EXE_SUFFIX=.exe
else
LIB_PREFIX=lib
LIB_SUFFIX=.a
+EXE_SUFFIX=
endif
ifeq (@EMU_LOCK_CHECKING@,yes)
@@ -308,9 +309,6 @@ ifeq ($(TARGET), win32)
GEN_OPT_FLGS = $(OPT_LEVEL)
UNROLL_FLG =
RC=rc.sh
-ifeq ($(MIXED_MSYS), yes)
-MAKE_PRELOAD_EXTRA = -msys
-endif
ifeq ($(USING_MINGW), yes)
RES_EXT = @OBJEXT@
MAKE_PRELOAD_EXTRA += " -windres"
@@ -461,6 +459,8 @@ endif
include zlib/zlib.mk
include pcre/pcre.mk
+YCF_SOURCE_DIR = $(ERL_TOP)/erts/lib_src/yielding_c_fun
+include $(ERL_TOP)/erts/lib_src/yielding_c_fun/main_target.mk
$(ERTS_LIB):
$(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) $(TYPE)
@@ -474,6 +474,10 @@ clean:
$(RM) -r pcre/obj/$(TARGET) $(PCRE_GENINC)
$(RM) -r zlib/obj/$(TARGET)
$(RM) -r bin/$(TARGET)
+ $(RM) $(YCF_EXECUTABLE)
+ $(RM) $(YCF_SOURCE_DIR)/*.o
+ $(RM) $(YCF_SOURCE_DIR)/lib/tiny_regex_c/*.o
+ $(RM) $(YCF_SOURCE_DIR)/lib/simple_c_gc/*.o
cd $(ERTS_LIB_DIR) && $(MAKE) clean
.PHONY: docs
@@ -599,7 +603,6 @@ endif
$(TTF_DIR)/erl_bif_table.c \
$(TTF_DIR)/erl_bif_table.h \
-$(TTF_DIR)/erl_bif_wrap.c \
$(TTF_DIR)/erl_bif_list.h \
$(TTF_DIR)/erl_atom_table.c \
$(TTF_DIR)/erl_atom_table.h \
@@ -626,7 +629,24 @@ $(TTF_DIR)/driver_tab.c: Makefile.in utils/make_driver_tab
$(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ -nifs $(NIF_OBJS) $(STATIC_NIF_LIBS) -drivers $(DRV_OBJS) $(STATIC_DRIVER_LIBS)
GENERATE += $(TTF_DIR)/driver_tab.c
-
+beam/erl_db_insert_list.ycf.h: $(YCF_EXECUTABLE) beam/erl_db.c
+ $(gen_verbose)$(YCF_EXECUTABLE) \
+ -yield \
+ -static_aux_funs \
+ -only_yielding_funs \
+ -output_file_name beam/erl_db_insert_list.ycf.h \
+ -f ets_insert_2_list_check \
+ -f ets_insert_new_2_list_has_member \
+ -f ets_insert_2_list_from_p_heap \
+ -f ets_insert_2_list_destroy_copied_dbterms \
+ -f ets_insert_2_list_copy_term_list \
+ -f ets_insert_new_2_dbterm_list_has_member \
+ -f ets_insert_2_list_insert_db_term_list \
+ -f ets_insert_2_list \
+ -fnoauto ets_insert_2_list_lock_tbl \
+ beam/erl_db.c
+
+GENERATE += beam/erl_db_insert_list.ycf.h
# Preloaded code.
#
@@ -886,7 +906,6 @@ RUN_OBJS += \
$(OBJDIR)/erl_bif_persistent.o \
$(OBJDIR)/erl_bif_atomics.o $(OBJDIR)/erl_bif_counters.o \
$(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_unique.o \
- $(OBJDIR)/erl_bif_wrap.o $(OBJDIR)/erl_nfunc_sched.o \
$(OBJDIR)/erl_guard_bifs.o $(OBJDIR)/erl_dirty_bif_wrap.o \
$(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
@@ -922,7 +941,8 @@ RUN_OBJS += \
$(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \
$(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \
$(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \
- $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o
+ $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o \
+ $(OBJDIR)/erl_nfunc_sched.o
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
@@ -1218,7 +1238,7 @@ DEP_FLAGS=-MM $(subst -O2,,$(CFLAGS)) $(INCLUDES) -I../etc/win32 \
-Idrivers/common -Idrivers/$(ERLANG_OSTYPE) \
-Inifs/common -Inifs/$(ERLANG_OSTYPE)
-# ifeq (@MIXED_CYGWIN_VC@,yes)
+# ifeq (@MIXED_VC@,yes)
# VC++ used for compiling. If __GNUC__ is defined we will include
# other headers then when compiling which will result in faulty
# dependencies.
@@ -1285,6 +1305,7 @@ endif
$(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) depend
endif
+ifneq ($(ERTS_SKIP_DEPEND),true)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),generate)
ifndef VOID_EMULATOR
@@ -1292,3 +1313,6 @@ ifndef VOID_EMULATOR
endif
endif
endif
+endif
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab
index f14b376419..335b121ad8 100644
--- a/erts/emulator/beam/arith_instrs.tab
+++ b/erts/emulator/beam/arith_instrs.tab
@@ -217,6 +217,11 @@ i_int_div(Fail, Op1, Op2, Dst) {
$BIF_ERROR_ARITY_2($Fail, BIF_intdiv_2, op1, op2);
} else if (ERTS_LIKELY(is_both_small(op1, op2))) {
Sint ires = signed_val(op1) / signed_val(op2);
+
+ /* We could skip this check if it weren't for the fact that dividing
+ * MIN_SMALL by -1 causes an overflow, and we have nothing to gain from
+ * fixed-point optimizing this instruction since there's no
+ * __builtin_div_overflow. */
if (ERTS_LIKELY(IS_SSMALL(ires))) {
$Dst = make_small(ires);
$NEXT0();
@@ -241,7 +246,14 @@ rem.execute(Fail, Dst) {
c_p->freason = BADARITH;
$BIF_ERROR_ARITY_2($Fail, BIF_rem_2, RemOp1, RemOp2);
} else if (ERTS_LIKELY(is_both_small(RemOp1, RemOp2))) {
- $Dst = make_small(signed_val(RemOp1) % signed_val(RemOp2));
+ Sint lhs_untagged, rhs_untagged, untagged_result;
+
+ /* See plus.execute */
+ lhs_untagged = (RemOp1 & ~_TAG_IMMED1_MASK);
+ rhs_untagged = (RemOp2 & ~_TAG_IMMED1_MASK);
+ untagged_result = lhs_untagged % rhs_untagged;
+
+ $Dst = untagged_result | _TAG_IMMED1_SMALL;
$NEXT0();
} else {
$OUTLINED_ARITH_2($Fail, int_rem, BIF_rem_2, RemOp1, RemOp2, $Dst);
@@ -447,10 +459,10 @@ shift.execute(Fail, Dst) {
reg[1] = Op2;
SWAPOUT;
if (IsOpCode(I[0], i_bsl_ssjd)) {
- I = handle_error(c_p, I, reg, &bif_export[BIF_bsl_2]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsl_2].info.mfa);
} else {
ASSERT(IsOpCode(I[0], i_bsr_ssjd));
- I = handle_error(c_p, I, reg, &bif_export[BIF_bsr_2]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsr_2].info.mfa);
}
goto post_error_handling;
}
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 7046eb5e65..3ea7677b6d 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -68,6 +68,7 @@ atom ErtsSecretAtom='3RT$'
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abandoned
atom abort
atom abs_path
atom absoluteURI
@@ -116,6 +117,7 @@ atom awaiting_load
atom awaiting_unload
atom backtrace backtrace_depth
atom badarg badarith badarity badfile badfun badkey badmap badmatch badsig
+atom badopt
atom bag
atom band
atom big
@@ -195,6 +197,7 @@ atom current_location
atom current_stacktrace
atom data
atom debug_flags
+atom decentralized_counters
atom decimals
atom default
atom delay_trap
@@ -216,6 +219,7 @@ atom dist_cmd
atom dist_ctrl_put_data
atom dist_ctrlr
atom dist_data
+atom dist_spawn_init
atom Div='/'
atom div
atom dmonitor_node
@@ -246,6 +250,7 @@ atom erlang
atom erl_signal_server
atom error_handler
atom error_logger
+atom error_only
atom erts_code_purger
atom erts_debug
atom erts_dflags
@@ -293,6 +298,7 @@ atom gc_minor_start
atom Ge='>='
atom generational
atom get_all_trap
+atom get_iovec
atom get_internal_state_blocked
atom get_seq_token
atom get_size
@@ -345,6 +351,8 @@ atom is_constant
atom is_seq_trace
atom iterator
atom io
+atom iodata
+atom iovec
atom keypos
atom kill
atom killed
@@ -408,6 +416,7 @@ atom min_bin_vheap_size
atom minor
atom minor_version
atom Minus='-'
+atom MinusMinus='--'
atom module
atom module_info
atom monitored_by
@@ -453,6 +462,7 @@ atom noeol
atom noproc
atom normal
atom nosuspend
+atom no_fail
atom no_float
atom no_integer
atom no_network
@@ -498,6 +508,7 @@ atom packet
atom packet_size
atom parallelism
atom Plus='+'
+atom PlusPlus='++'
atom pause
atom pending
atom pending_driver
@@ -550,10 +561,13 @@ atom register
atom registered_name
atom reload
atom rem
+atom reply
+atom reply_tag
atom report_errors
atom reset
atom reset_seq_trace
atom restart
+atom resume
atom return_from
atom return_to
atom return_trace
@@ -612,6 +626,10 @@ atom silent
atom size
atom spawn_executable
atom spawn_driver
+atom spawn_init
+atom spawn_reply
+atom spawn_request
+atom spawn_request_yield
atom spawned
atom ssl_tls
atom stack_size
@@ -622,6 +640,7 @@ atom stop
atom stream
atom strict_monotonic
atom strict_monotonic_timestamp
+atom success_only
atom sunrm
atom suspend
atom suspended
@@ -689,4 +708,4 @@ atom xor
atom x86
atom yes
atom yield
-atom nifs
+atom nifs \ No newline at end of file
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index c530063fa1..e130baa1c0 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -144,7 +144,7 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
BIF_ERROR(BIF_P, BADARG);
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_code_make_stub_module_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
@@ -279,7 +279,7 @@ finish_loading_1(BIF_ALIST_1)
int do_commit = 0;
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
}
/*
@@ -641,7 +641,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
}
{
@@ -768,7 +768,7 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_finish_after_on_load_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
@@ -819,21 +819,25 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
*/
num_exps = export_list_size(code_ix);
for (i = 0; i < num_exps; i++) {
- Export *ep = export_list(i,code_ix);
- if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
- continue;
- }
- if (ep->beam[1] != 0) {
- ep->addressv[code_ix] = (void *) ep->beam[1];
- ep->beam[1] = 0;
- } else {
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- }
+ Export *ep = export_list(i, code_ix);
+
+ if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
+ continue;
+ }
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->trampoline.not_loaded.deferred != 0) {
+ ep->addressv[code_ix] = (void*)ep->trampoline.not_loaded.deferred;
+ ep->trampoline.not_loaded.deferred = 0;
+ } else {
+ if (ep->bif_number != -1) {
+ continue;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ }
}
modp->curr.code_hdr->on_load_function_ptr = NULL;
@@ -856,10 +860,11 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
continue;
}
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
+ if (ep->bif_number != -1) {
continue;
}
- ep->beam[1] = 0;
+
+ ep->trampoline.not_loaded.deferred = 0;
}
}
erts_release_code_write_permission();
@@ -1109,16 +1114,15 @@ check_process_code(Process* rp, Module* modp, int *redsp, int fcalls)
mod_size = modp->old.code_length;
/*
- * Check if current instruction or continuation pointer points into module.
+ * Check if the instruction pointer points into module.
*/
- if (ErtsInArea(rp->i, mod_start, mod_size)
- || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ if (ErtsInArea(rp->i, mod_start, mod_size)) {
return am_true;
}
-
+
*redsp += 1;
- if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ if (erts_check_nfunc_in_area(rp, mod_start, mod_size))
return am_true;
*redsp += (STACK_START(rp) - rp->stop) / 32;
@@ -1657,7 +1661,7 @@ BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
ASSERT(old_area);
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP0(bif_export[BIF_erts_internal_release_literal_area_switch_0],
+ BIF_TRAP0(&bif_trap_export[BIF_erts_internal_release_literal_area_switch_0],
BIF_P);
}
@@ -2019,7 +2023,7 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_internal_purge_module_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
@@ -2043,23 +2047,22 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
ERTS_BIF_PREP_RET(ret, am_false);
}
else {
- /*
- * Unload any NIF library
- */
- if (modp->old.nif != NULL
- || IF_HIPE(hipe_purge_need_blocking(modp))) {
- /* ToDo: Do unload nif without blocking */
+ if (IF_HIPE(hipe_purge_need_blocking(modp))) {
erts_rwunlock_old_code(code_ix);
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
is_blocking = 1;
erts_rwlock_old_code(code_ix);
- if (modp->old.nif) {
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
- }
}
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif) {
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
/*
* Remove the old code.
*/
@@ -2191,25 +2194,28 @@ delete_code(Module* modp)
for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->info.mfa.module == module)) {
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(modp->curr.num_traced_exports > 0);
DBG_TRACE_MFA_P(&ep->info.mfa,
"export trace cleared, code_ix=%d", code_ix);
- erts_clear_export_break(modp, &ep->info);
+ erts_clear_export_break(modp, ep);
}
else {
- ASSERT(BeamIsOpCode(ep->beam[0], op_call_error_handler) ||
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_call_error_handler) ||
!erts_initialized);
}
}
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- ep->beam[1] = 0;
+
+ if (ep->bif_number != -1 && ep->is_bif_traced) {
+ /* Code unloading kills both global and local call tracing. */
+ ep->is_bif_traced = 0;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ ep->trampoline.not_loaded.deferred = 0;
DBG_TRACE_MFA_P(&ep->info.mfa,
"export invalidation, code_ix=%d", code_ix);
}
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index da43f65780..4144b0e751 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -207,9 +207,6 @@ erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
if (erts_is_function_native(ci)) {
continue;
}
- if (is_nil(ci->mfa.module)) { /* Ignore BIF stub */
- continue;
- }
switch (specified) {
case 3:
if (ci->mfa.arity != mfa->arity)
@@ -244,8 +241,10 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
ne = 0;
for (i = 0; i < num_exps; i++) {
- Export* ep = export_list(i, code_ix);
- BeamInstr* pc;
+ BeamInstr *func;
+ Export* ep;
+
+ ep = export_list(i, code_ix);
switch (specified) {
case 3:
@@ -263,19 +262,20 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
ASSERT(0);
}
- pc = ep->beam;
- if (ep->addressv[code_ix] == pc) {
- if (BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_call_error_handler)) {
- continue;
- }
- ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
- } else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) {
- continue;
- }
+ func = ep->addressv[code_ix];
+
+ if (func == ep->trampoline.raw) {
+ if (BeamIsOpCode(*func, op_call_error_handler)) {
+ continue;
+ }
+ ASSERT(BeamIsOpCode(*func, op_i_generic_breakpoint));
+ } else if (erts_is_function_native(erts_code_to_codeinfo(func))) {
+ continue;
+ }
f->matching[ne].ci = &ep->info;
f->matching[ne].mod = erts_get_module(ep->info.mfa.module, code_ix);
+
ne++;
}
@@ -305,18 +305,6 @@ erts_consolidate_bp_data(BpFunctions* f, int local)
}
}
-void
-erts_consolidate_bif_bp_data(void)
-{
- int i;
-
- ERTS_LC_ASSERT(erts_has_code_write_permission());
- for (i = 0; i < BIF_SIZE; i++) {
- Export *ep = bif_export[i];
- consolidate_bp_data(0, &ep->info, 0);
- }
-}
-
static void
consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local)
{
@@ -495,7 +483,7 @@ erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer)
}
void
-erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
+erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local)
{
Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
@@ -503,25 +491,6 @@ erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
}
void
-erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, ErtsTracer tracer)
-{
- set_function_break(ci, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
-}
-
-void
-erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op count_op)
-{
- set_function_break(ci, NULL,
- ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
- count_op, erts_tracer_nil);
-}
-
-void
-erts_clear_time_trace_bif(ErtsCodeInfo *ci) {
- clear_function_break(ci, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
-}
-
-void
erts_set_debug_break(BpFunctions* f) {
set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil);
}
@@ -547,7 +516,7 @@ erts_clear_trace_break(BpFunctions* f)
}
void
-erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local)
+erts_clear_export_trace(ErtsCodeInfo *ci, int local)
{
GenericBp* g = ci->u.gen_bp;
@@ -566,12 +535,6 @@ erts_clear_mtrace_break(BpFunctions* f)
}
void
-erts_clear_mtrace_bif(ErtsCodeInfo *ci)
-{
- clear_function_break(ci, ERTS_BPF_META_TRACE);
-}
-
-void
erts_clear_debug_break(BpFunctions* f)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
@@ -630,58 +593,56 @@ erts_clear_module_break(Module *modp) {
}
void
-erts_clear_export_break(Module* modp, ErtsCodeInfo *ci)
+erts_clear_export_break(Module* modp, Export *ep)
{
+ ErtsCodeInfo *ci;
+
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
+ ci = &ep->info;
+
+ ASSERT(erts_codeinfo_to_code(ci) == ep->trampoline.raw);
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint));
+ ep->trampoline.op = 0;
+
clear_function_break(ci, ERTS_BPF_ALL);
erts_commit_staged_bp();
- *erts_codeinfo_to_code(ci) = (BeamInstr) 0;
+
consolidate_bp_data(modp, ci, 0);
ASSERT(ci->u.gen_bp == NULL);
}
/*
- * If c_p->cp is a trace return instruction, we set cp
- * to be the place where we again start to execute code.
+ * If the topmost continuation pointer on the stack is a trace return
+ * instruction, we modify it to be the place where we again start to
+ * execute code.
*
- * cp is used by match spec {caller} to get the calling
- * function, and if we don't do this fixup it will be
- * 'undefined'. This has the odd side effect of {caller}
- * not really being which function is the caller, but
- * rather which function we are about to return to.
+ * This continuation pointer is used by match spec {caller} to get the
+ * calling function, and if we don't do this fixup it will be
+ * 'undefined'. This has the odd side effect of {caller} not really
+ * being the function which is the caller, but rather the function
+ * which we are about to return to.
*/
static void fixup_cp_before_trace(Process *c_p, int *return_to_trace)
{
- Eterm *cpp, *E = c_p->stop;
- BeamInstr w = *c_p->cp;
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp = &E[2];
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp = &E[0];
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- for (;;) {
- BeamInstr w = *cp_val(*cpp);
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp += 3;
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp += 1;
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp += 2;
- } else {
- break;
- }
+ Eterm *cpp = c_p->stop;
+
+ for (;;) {
+ BeamInstr w = *cp_val(*cpp);
+ if (BeamIsOpCode(w, op_return_trace)) {
+ cpp += 3;
+ } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
+ *return_to_trace = 1;
+ cpp += 1;
+ } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
+ cpp += 2;
+ } else {
+ break;
}
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
}
+ c_p->stop[0] = (Eterm) cp_val(*cpp);
+ ASSERT(is_CP(*cpp));
}
BeamInstr
@@ -742,13 +703,14 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
- Eterm w;
+ BeamInstr w;
+ Eterm* E;
ErtsCodeInfo* prev_info = erts_trace_time_call(c_p, info, bp->time);
- w = (BeamInstr) *c_p->cp;
+ E = c_p->stop;
+ w = *(BeamInstr*) E[0];
if (! (BeamIsOpCode(w, op_i_return_time_trace) ||
BeamIsOpCode(w, op_return_trace) ||
BeamIsOpCode(w, op_i_return_to_trace)) ) {
- Eterm* E = c_p->stop;
ASSERT(c_p->htop <= E && E <= c_p->hend);
if (E - 2 < c_p->htop) {
(void) erts_garbage_collect(c_p, 2, reg, info->mfa.arity);
@@ -759,9 +721,8 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
ASSERT(c_p->htop <= E && E <= c_p->hend);
E -= 2;
- E[0] = prev_info ? make_cp(erts_codeinfo_to_code(prev_info)) : NIL;
- E[1] = make_cp(c_p->cp); /* original return address */
- c_p->cp = beam_return_time_trace;
+ E[1] = prev_info ? make_cp(erts_codeinfo_to_code(prev_info)) : NIL;
+ E[0] = (Eterm) beam_return_time_trace;
c_p->stop = E;
}
}
@@ -773,237 +734,24 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
}
-/*
- * Entry point called by the trace wrap functions in erl_bif_wrap.c
- *
- * The trace wrap functions are themselves called through the export
- * entries instead of the original BIF functions.
- */
-Eterm
-erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
-{
- Eterm result;
- Eterm (*func)(Process*, Eterm*, BeamInstr*);
- Export* ep = bif_export[bif_index];
- Uint32 flags = 0, flags_meta = 0;
- ErtsTracer meta_tracer = erts_tracer_nil;
- int applying = (I == ep->beam); /* Yup, the apply code for a bif
- * is actually in the
- * export entry */
- BeamInstr *cp = p->cp;
- GenericBp* g;
- GenericBpData* bp = NULL;
- Uint bp_flags = 0;
- int return_to_trace = 0;
-
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
-
- g = ep->info.u.gen_bp;
- if (g) {
- bp = &g->data[erts_active_bp_ix()];
- bp_flags = bp->flags;
- }
-
- /*
- * Make continuation pointer OK, it is not during direct BIF calls,
- * but it is correct during apply of bif.
- */
- if (!applying) {
- p->cp = I;
- } else {
- fixup_cp_before_trace(p, &return_to_trace);
- }
- if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
- flags = erts_call_trace(p, &ep->info, bp->local_ms, args,
- local, &ERTS_TRACER(p));
- }
- if (bp_flags & ERTS_BPF_META_TRACE) {
- ErtsTracer old_tracer;
-
- meta_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer);
- old_tracer = meta_tracer;
- flags_meta = erts_call_trace(p, &ep->info, bp->meta_ms, args,
- 0, &meta_tracer);
-
- if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) {
- ErtsTracer new_tracer = erts_tracer_nil;
- erts_tracer_update(&new_tracer, meta_tracer);
- if (old_tracer == erts_atomic_cmpxchg_acqb(
- &bp->meta_tracer->tracer,
- (erts_aint_t)new_tracer,
- (erts_aint_t)old_tracer)) {
- ERTS_TRACER_CLEAR(&old_tracer);
- } else {
- ERTS_TRACER_CLEAR(&new_tracer);
- }
- }
- }
- if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- erts_trace_time_call(p, &ep->info, bp->time);
- }
-
- /* Restore original continuation pointer (if changed). */
- p->cp = cp;
-
- func = bif_table[bif_index].f;
-
- result = func(p, args, I);
-
- if (erts_nif_export_check_save_trace(p, result,
- applying, ep,
- cp, flags,
- flags_meta, I,
- meta_tracer)) {
- /*
- * erts_bif_trace_epilogue() will be called
- * later when appropriate via the NIF export
- * scheduling functionality...
- */
- return result;
- }
-
- return erts_bif_trace_epilogue(p, result, applying, ep, cp,
- flags, flags_meta, I,
- meta_tracer);
-}
-
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
- BeamInstr i_return_time_trace = beam_return_time_trace[0];
- Eterm *cpp;
- /* Maybe advance cp to skip trace stack frames */
- for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
- if (*cp == i_return_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 2; /* Skip return_trace parameters */
- } else if (*cp == i_return_time_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 1; /* Skip return_time_trace parameters */
- } else if (*cp == i_return_to_trace) {
- /* A return_to trace message is going to be generated
- * by normal means, so we do not have to.
- */
- cp = NULL;
- break;
- } else break;
- }
- }
-
- /* Try to get these in the order
- * they usually appear in normal code... */
- if (is_non_value(result)) {
- Uint reason = p->freason;
- if (reason != TRAP) {
- Eterm class;
- Eterm value = p->fvalue;
- /* Expand error value like in handle_error() */
- if (reason & EXF_ARGLIST) {
- Eterm *tp;
- ASSERT(is_tuple(value));
- tp = tuple_val(value);
- value = tp[1];
- }
- if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- Eterm *hp = HAlloc(p, 3);
- value = TUPLE2(hp, am_nocatch, value);
- reason = EXC_ERROR;
- }
- /* Note: expand_error_value() could theoretically
- * allocate on the heap, but not for any error
- * returned by a BIF, and it would do no harm,
- * just be annoying.
- */
- value = expand_error_value(p, reason, value);
- class = exception_tag[GET_EXC_CLASS(reason)];
-
- if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &meta_tracer);
- }
- if (flags & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &ERTS_TRACER(p));
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
- /* can only happen if(local)*/
- Eterm *ptr = p->stop;
- ASSERT(is_CP(*ptr));
- ASSERT(ptr <= STACK_START(p));
- /* Search the nearest stack frame for a catch */
- while (++ptr < STACK_START(p)) {
- if (is_CP(*ptr)) break;
- if (is_catch(*ptr)) {
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into
- * calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- } else {
- if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &meta_tracer);
- }
- /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
- if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &ERTS_TRACER(p));
- }
- if (flags & MATCH_SET_RETURN_TO_TRACE &&
- IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
- /* can only happen if(local)*/
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- return result;
-}
-
static ErtsTracer
do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
int local, Binary* ms, ErtsTracer tracer)
{
int return_to_trace = 0;
- BeamInstr *cp_save = c_p->cp;
Uint32 flags;
Uint need = 0;
+ Eterm cp_save;
Eterm* E = c_p->stop;
- fixup_cp_before_trace(c_p, &return_to_trace);
+ cp_save = E[0];
+ fixup_cp_before_trace(c_p, &return_to_trace);
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
flags = erts_call_trace(c_p, info, ms, reg, local, &tracer);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
- /* restore cp after potential fixup */
- c_p->cp = cp_save;
+ E[0] = cp_save;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
@@ -1023,28 +771,23 @@ do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
E -= 1;
ASSERT(c_p->htop <= E && E <= c_p->hend);
- E[0] = make_cp(c_p->cp);
- c_p->cp = beam_return_to_trace;
+ E[0] = (Eterm) beam_return_to_trace;
+ c_p->stop = E;
}
- if (flags & MATCH_SET_RX_TRACE)
- {
+ if (flags & MATCH_SET_RX_TRACE) {
E -= 3;
c_p->stop = E;
ASSERT(c_p->htop <= E && E <= c_p->hend);
ASSERT(is_CP((Eterm) (UWord) (&info->mfa.module)));
ASSERT(IS_TRACER_VALID(tracer));
- E[2] = make_cp(c_p->cp);
- E[1] = copy_object(tracer, c_p);
- E[0] = make_cp(&info->mfa.module);
- /* We ARE at the beginning of an instruction,
- the funcinfo is above i. */
- c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
- beam_exception_trace : beam_return_trace;
+ E[2] = copy_object(tracer, c_p);
+ E[1] = make_cp(&info->mfa.module);
+ E[0] = (Eterm) ((flags & MATCH_SET_EXCEPTION_TRACE) ?
+ beam_exception_trace : beam_return_trace);
erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- } else
- c_p->stop = E;
+ }
return tracer;
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index a10965191e..827302b9b5 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -119,20 +119,16 @@ void erts_bp_free_matched_functions(BpFunctions* f);
void erts_install_breakpoints(BpFunctions* f);
void erts_uninstall_breakpoints(BpFunctions* f);
void erts_consolidate_bp_data(BpFunctions* f, int local);
-void erts_consolidate_bif_bp_data(void);
void erts_set_trace_break(BpFunctions *f, Binary *match_spec);
void erts_clear_trace_break(BpFunctions *f);
-void erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local);
-void erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local);
+void erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local);
+void erts_clear_export_trace(ErtsCodeInfo *ci, int local);
void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec,
ErtsTracer tracer);
void erts_clear_mtrace_break(BpFunctions *f);
-void erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec,
- ErtsTracer tracer);
-void erts_clear_mtrace_bif(ErtsCodeInfo *ci);
void erts_set_debug_break(BpFunctions *f);
void erts_clear_debug_break(BpFunctions *f);
@@ -142,7 +138,7 @@ void erts_clear_count_break(BpFunctions *f);
void erts_clear_all_breaks(BpFunctions* f);
int erts_clear_module_break(Module *modp);
-void erts_clear_export_break(Module *modp, ErtsCodeInfo* ci);
+void erts_clear_export_break(Module *modp, Export *ep);
BeamInstr erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *ci, Eterm* reg);
BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
@@ -151,8 +147,6 @@ BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
int erts_is_trace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, int local);
int erts_is_mtrace_break(ErtsCodeInfo *ci, Binary **match_spec_ret,
ErtsTracer *tracer_ret);
-int erts_is_mtrace_bif(ErtsCodeInfo *ci, Binary **match_spec_ret,
- ErtsTracer *tracer_ret);
int erts_is_native_break(ErtsCodeInfo *ci);
int erts_is_count_break(ErtsCodeInfo *ci, Uint *count_ret);
int erts_is_time_break(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
@@ -163,10 +157,6 @@ void erts_schedule_time_break(Process *p, Uint out);
void erts_set_time_break(BpFunctions *f, enum erts_break_op);
void erts_clear_time_break(BpFunctions *f);
-int erts_is_time_trace_bif(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
-void erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op);
-void erts_clear_time_trace_bif(ErtsCodeInfo *ci);
-
ErtsCodeInfo *erts_find_local_func(ErtsCodeMFA *mfa);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 4d52435139..eeb3a465d0 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -47,7 +47,6 @@
#else
# define HEXF "%08bpX"
#endif
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
void dbg_bt(Process* p, Eterm* sp);
void dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg);
@@ -158,7 +157,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_breakpoint_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
@@ -332,7 +331,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
"unknown " HEXF "\n", instr);
code_ptr++;
}
- if (i == op_call_nif) {
+ if (i == op_call_nif_WWW) {
/*
* The rest of the code will not be executed. Don't disassemble any
* more code in this function.
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 07c16e3415..1a250470cb 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -109,14 +109,9 @@ do { \
# define CHECK_ARGS(T)
#endif
-#define CHECK_ALIGNED(Dst) ASSERT((((Uint)&Dst) & (sizeof(Uint)-1)) == 0)
-
-#define GET_BIF_MODULE(p) (p->info.mfa.module)
-#define GET_BIF_FUNCTION(p) (p->info.mfa.function)
-#define GET_BIF_ARITY(p) (p->info.mfa.arity)
-#define GET_BIF_ADDRESS(p) ((BifFunction) (p->beam[1]))
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
-
+#define GET_EXPORT_MODULE(p) ((p)->info.mfa.module)
+#define GET_EXPORT_FUNCTION(p) ((p)->info.mfa.function)
+#define GET_EXPORT_ARITY(p) ((p)->info.mfa.arity)
/*
* We reuse some of fields in the save area in the process structure.
@@ -141,10 +136,6 @@ do { \
BeamCodeAddr(IP) < (BeamInstr)LabelAddr(end_emulator_loop))
#endif /* NO_JUMP_TABLE */
-#define SET_CP(p, ip) \
- ASSERT(VALID_INSTR(*(ip))); \
- (p)->cp = (ip)
-
#define SET_I(ip) \
ASSERT(VALID_INSTR(* (Eterm *)(ip))); \
I = (ip)
@@ -165,7 +156,7 @@ BeamInstr beam_continue_exit[1];
/* NOTE These should be the only variables containing trace instructions.
-** Sometimes tests are form the instruction value, and sometimes
+** Sometimes tests are for the instruction value, and sometimes
** for the referring variable (one of these), and rouge references
** will most likely cause chaos.
*/
@@ -254,72 +245,6 @@ void** beam_ops;
#define Q(N) (N*sizeof(Eterm *))
#define l(N) (freg[N].fd)
-/*
- * Check that we haven't used the reductions and jump to function pointed to by
- * the I register. If we are out of reductions, do a context switch.
- */
-
-#define DispatchMacro() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch; \
- } \
- } while (0) \
-
-#define DispatchMacroFun() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch_fun; \
- } \
- } while (0)
-
-#define DispatchMacrox() \
- do { \
- if (FCALLS > 0) { \
- BeamInstr dis_next; \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- dis_next = *I; \
- FCALLS--; \
- CHECK_ARGS(I); \
- Goto(dis_next); \
- } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) \
- && FCALLS > neg_o_reds) { \
- goto save_calls1; \
- } else { \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- CHECK_ARGS(I); \
- goto context_switch; \
- } \
- } while (0)
-
-#ifdef DEBUG
-/*
- * To simplify breakpoint setting, put the code in one place only and jump to it.
- */
-# define Dispatch() goto do_dispatch
-# define Dispatchx() goto do_dispatchx
-# define Dispatchfun() goto do_dispatchfun
-#else
-/*
- * Inline for speed.
- */
-# define Dispatch() DispatchMacro()
-# define Dispatchx() DispatchMacrox()
-# define Dispatchfun() DispatchMacroFun()
-#endif
-
#define Arg(N) I[(N)+1]
#define GetSource(raw, dst) \
@@ -352,19 +277,6 @@ do { \
} \
} while(0)
-#define DispatchReturn \
-do { \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(*I); \
- } \
- else { \
- c_p->current = NULL; \
- c_p->arity = 1; \
- goto context_switch3; \
- } \
-} while (0)
-
#ifdef DEBUG
/* Better static type testing by the C compiler */
# define BEAM_IS_TUPLE(Src) is_tuple(Src)
@@ -521,10 +433,10 @@ init_emulator(void)
} \
} while(0)
-#define DTRACE_RETURN_FROM_PC(p) \
+#define DTRACE_RETURN_FROM_PC(p, i) \
do { \
ErtsCodeMFA* cmfa; \
- if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc((p)->cp))) { \
+ if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc(i))) { \
DTRACE_RETURN((p), cmfa); \
} \
} while(0)
@@ -534,7 +446,7 @@ init_emulator(void)
#define DTRACE_GLOBAL_CALL(p, mfa) do {} while (0)
#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p, e) do {} while (0)
#define DTRACE_RETURN(p, mfa) do {} while (0)
-#define DTRACE_RETURN_FROM_PC(p) do {} while (0)
+#define DTRACE_RETURN_FROM_PC(p, i) do {} while (0)
#define DTRACE_BIF_ENTRY(p, mfa) do {} while (0)
#define DTRACE_BIF_RETURN(p, mfa) do {} while (0)
#define DTRACE_NIF_ENTRY(p, mfa) do {} while (0)
@@ -772,27 +684,9 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
#endif
#include "beam_hot.h"
-
-#ifdef DEBUG
- /*
- * Set a breakpoint here to get control just after a call instruction.
- * I points to the first instruction in the called function.
- *
- * In gdb, use 'call dis(I-5, 1)' to show the name of the function.
- */
- do_dispatch:
- DispatchMacro();
-
- do_dispatchx:
- DispatchMacrox();
-
- do_dispatchfun:
- DispatchMacroFun();
-
-#endif
-
/*
- * Jumped to from the Dispatch() macro when the reductions are used up.
+ * The labels are jumped to from the $DISPATCH() macros when the reductions
+ * are used up.
*
* Since the I register points just beyond the FuncBegin instruction, we
* can get the module, function, and arity for the function being
@@ -986,18 +880,46 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
}
#endif
return; /* Never executed */
+}
- save_calls1:
- {
- BeamInstr dis_next;
+/*
+ * Enter all BIFs into the export table.
+ *
+ * Note that they will all call the error_handler until their modules have been
+ * loaded, which may prevent the system from booting if BIFs from non-preloaded
+ * modules are apply/3'd while loading code. Ordinary BIF calls will work fine
+ * however since they won't go through export entries.
+ */
+static void install_bifs(void) {
+ int i;
- save_calls(c_p, (Export *) Arg(0));
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry;
+ Export *ep;
+ int j;
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
+ entry = &bif_table[i];
- dis_next = *I;
- FCALLS--;
- Goto(dis_next);
+ ep = erts_export_put(entry->module, entry->name, entry->arity);
+
+ ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
+ ep->info.mfa.module = entry->module;
+ ep->info.mfa.function = entry->name;
+ ep->info.mfa.arity = entry->arity;
+ ep->bif_number = i;
+
+ memset(&ep->trampoline, 0, sizeof(ep->trampoline));
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+
+ for (j = 0; j < ERTS_NUM_CODE_IX; j++) {
+ ep->addressv[j] = ep->trampoline.raw;
+ }
+
+ /* Set up a hidden export entry so we can trap to this BIF without
+ * it being seen when tracing. */
+ erts_init_trap_export(&bif_trap_export[i],
+ entry->module, entry->name, entry->arity,
+ entry->f);
}
}
@@ -1008,43 +930,30 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
static void
init_emulator_finish(void)
{
- int i;
- Export* ep;
-
#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
- for (i = 0; i < NUMBER_OF_OPCODES; i++) {
- BeamInstr instr = BeamOpCodeAddr(i);
- if (instr >= (1ull << 32)) {
- erts_exit(ERTS_ERROR_EXIT,
- "This run-time was supposed be compiled with all code below 2Gb,\n"
- "but the instruction '%s' is located at %016lx.\n",
- opc[i].name, instr);
- }
- }
+ int i;
+
+ for (i = 0; i < NUMBER_OF_OPCODES; i++) {
+ BeamInstr instr = BeamOpCodeAddr(i);
+ if (instr >= (1ull << 32)) {
+ erts_exit(ERTS_ERROR_EXIT,
+ "This run-time was supposed be compiled with all code below 2Gb,\n"
+ "but the instruction '%s' is located at %016lx.\n",
+ opc[i].name, instr);
+ }
+ }
#endif
- beam_apply[0] = BeamOpCodeAddr(op_i_apply);
- beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
- beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
- beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
- beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
- beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
- beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
- beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
+ beam_apply[0] = BeamOpCodeAddr(op_i_apply);
+ beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
+ beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
+ beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
+ beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
+ beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
+ beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
+ beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
- /*
- * Enter all BIFs into the export table.
- */
- for (i = 0; i < BIF_SIZE; i++) {
- ep = erts_export_put(bif_table[i].module,
- bif_table[i].name,
- bif_table[i].arity);
- bif_export[i] = ep;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- /* XXX: set func info for bifs */
- ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
- }
+ install_bifs();
}
/*
@@ -1257,7 +1166,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
* I[2]: Pointer to erl_module_nif
* I[3]: Function pointer to dirty NIF
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
@@ -1271,11 +1180,11 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- if (BeamIsOpCode(*I, op_apply_bif)) {
+ if (BeamIsOpCode(*I, op_call_bif_W)) {
exiting = erts_call_dirty_bif(esdp, c_p, I, reg);
}
else {
- ASSERT(BeamIsOpCode(*I, op_call_nif));
+ ASSERT(BeamIsOpCode(*I, op_call_nif_WWW));
exiting = erts_call_dirty_nif(esdp, c_p, I, reg);
}
@@ -1303,7 +1212,7 @@ ubif2mfa(void* uf)
int i;
for (i = 0; erts_u_bifs[i].bif; i++) {
if (erts_u_bifs[i].bif == uf)
- return &bif_export[erts_u_bifs[i].exp_ix]->info.mfa;
+ return &bif_trap_export[erts_u_bifs[i].exp_ix].info.mfa;
}
erts_exit(ERTS_ERROR_EXIT, "bad u bif: %p\n", uf);
return NULL;
@@ -1344,6 +1253,33 @@ Eterm error_atom[NUMBER_EXIT_CODES] = {
am_badkey, /* 19 */
};
+/* Returns the return address at E[0] in printable form, skipping tracing in
+ * the same manner as gather_stacktrace.
+ *
+ * This is needed to generate correct stacktraces when throwing errors from
+ * instructions that return like an ordinary function, such as call_nif. */
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) {
+ Eterm *ptr = E;
+
+ ASSERT(is_CP(*ptr));
+
+ while (ptr < STACK_START(p)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ return cp;
+ }
+ }
+
+ ERTS_ASSERT(!"No continuation pointer on stack");
+}
+
/*
* To fully understand the error handling, one must keep in mind that
* when an exception is thrown, the search for a handler can jump back
@@ -1373,14 +1309,14 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */
- if (c_p->freason & EXF_RESTORE_NIF)
- erts_nif_export_restore_error(c_p, &pc, reg, &bif_mfa);
+ if (c_p->freason & EXF_RESTORE_NFUNC)
+ erts_nfunc_restore_error(c_p, &pc, reg, &bif_mfa);
#ifdef DEBUG
if (bif_mfa) {
- /* Verify that bif_mfa does not point into our nif export */
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(NifExport)));
+ /* Verify that bif_mfa does not point into our native function wrapper */
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
+ ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(ErtsNativeFunc)));
}
#endif
@@ -1443,7 +1379,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
reg[2] = Value;
reg[3] = c_p->ftrace;
if ((new_pc = next_catch(c_p, reg))) {
- c_p->cp = 0; /* To avoid keeping stale references. */
+ c_p->stop[0] = NIL; /* To avoid keeping stale references. */
ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
return new_pc;
}
@@ -1481,35 +1417,6 @@ next_catch(Process* c_p, Eterm *reg) {
return NULL;
}
- /*
- * Better safe than sorry here. In debug builds, produce a core
- * dump if the top of the stack doesn't point to a continuation
- * pointer. In other builds, ignore a non-CP at the top of stack.
- */
- ASSERT(is_CP(*ptr));
- if ((is_not_CP(*ptr) || (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace &&
- *cp_val(*ptr) != i_return_time_trace ))
- && c_p->cp) {
- /* Can not follow cp here - code may be unloaded */
- BeamInstr *cpp = c_p->cp;
- if (cpp == beam_exception_trace) {
- ErtsCodeMFA *mfa = (ErtsCodeMFA*)cp_val(ptr[0]);
- erts_trace_exception(c_p, mfa,
- reg[1], reg[2],
- ERTS_TRACER_FROM_ETERM(ptr+1));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_time_trace) {
- /* Skip return_trace parameters */
- ptr += 1;
- } else if (cpp == beam_return_to_trace) {
- have_return_to_trace = !0; /* Record next cp */
- }
- }
while (ptr < STACK_START(c_p)) {
if (is_catch(*ptr)) {
if (active_catches) goto found_catch;
@@ -1583,6 +1490,8 @@ terminate_proc(Process* c_p, Eterm Value)
if (GET_EXC_CLASS(c_p->freason) == EXTAG_ERROR) {
Value = add_stacktrace(c_p, Value, c_p->ftrace);
}
+ c_p->ftrace = NIL;
+
/* EXF_LOG is a primary exception flag */
if (c_p->freason & EXF_LOG) {
int alive = erts_is_alive;
@@ -1664,6 +1573,54 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) {
return Value;
}
+
+static void
+gather_stacktrace(Process* p, struct StackTrace* s, int depth)
+{
+ BeamInstr *prev;
+ Eterm *ptr;
+
+ if (depth == 0) {
+ return;
+ }
+
+ prev = s->depth ? s->trace[s->depth - 1] : s->pc;
+ ptr = p->stop;
+
+ /*
+ * Traverse the stack backwards and add all unique continuation
+ * pointers to the buffer, up to the maximum stack trace size.
+ *
+ * Skip trace stack frames.
+ */
+
+ ASSERT(ptr >= STACK_TOP(p) && ptr <= STACK_START(p));
+
+ while (ptr < STACK_START(p) && depth > 0) {
+ if (is_CP(*ptr)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ if (cp != prev) {
+ /* Record non-duplicates only */
+ prev = cp;
+ s->trace[s->depth++] = cp - 1;
+ depth--;
+ }
+ ptr++;
+ }
+ } else {
+ ptr++;
+ }
+ }
+}
+
/*
* Quick-saving the stack trace in an internal form on the heap. Note
* that c_p->ftrace will point to a cons cell which holds the given args
@@ -1750,11 +1707,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
s->trace[s->depth++] = pc;
depth--;
}
- /* Save second stack entry if CP is valid and different from pc */
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL;
args = make_arglist(c_p, reg, bif_mfa->arity); /* Overwrite CAR(c_p->ftrace) */
} else {
@@ -1762,9 +1714,9 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
non_bif_stacktrace:
s->current = c_p->current;
- /*
+ /*
* For a function_clause error, the arguments are in the beam
- * registers, c_p->cp is valid, and c_p->current is set.
+ * registers and c_p->current is set.
*/
if ( (GET_EXC_INDEX(s->freason)) ==
(GET_EXC_INDEX(EXC_FUNCTION_CLAUSE)) ) {
@@ -1772,18 +1724,8 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
ASSERT(s->current);
a = s->current->arity;
args = make_arglist(c_p, reg, a); /* Overwrite CAR(c_p->ftrace) */
- /* Save first stack entry */
- ASSERT(c_p->cp);
- if (depth > 0) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL; /* Ignore pc */
} else {
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = pc;
}
}
@@ -1796,80 +1738,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
}
/* Save the actual stack trace */
- erts_save_stacktrace(c_p, s, depth);
+ gather_stacktrace(c_p, s, depth);
}
void
erts_save_stacktrace(Process* p, struct StackTrace* s, int depth)
{
- if (depth > 0) {
- Eterm *ptr;
- BeamInstr *prev = s->depth ? s->trace[s->depth-1] : NULL;
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
-
- /*
- * Traverse the stack backwards and add all unique continuation
- * pointers to the buffer, up to the maximum stack trace size.
- *
- * Skip trace stack frames.
- */
- ptr = p->stop;
- if (ptr < STACK_START(p) &&
- (is_not_CP(*ptr)|| (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace)) &&
- p->cp) {
- /* Cannot follow cp here - code may be unloaded */
- BeamInstr *cpp = p->cp;
- int trace_cp;
- if (cpp == beam_exception_trace || cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- trace_cp = 1;
- } else if (cpp == beam_return_to_trace) {
- /* Skip return_to_trace parameters */
- ptr += 1;
- trace_cp = 1;
- }
- else {
- trace_cp = 0;
- }
- if (trace_cp && s->pc == cpp) {
- /*
- * If process 'cp' points to a return/exception trace
- * instruction and 'cp' has been saved as 'pc' in
- * stacktrace, we need to update 'pc' in stacktrace
- * with the actual 'cp' located on the top of the
- * stack; otherwise, we will lose the top stackframe
- * when building the stack trace.
- */
- ASSERT(is_CP(p->stop[0]));
- s->pc = cp_val(p->stop[0]);
- }
- }
- while (ptr < STACK_START(p) && depth > 0) {
- if (is_CP(*ptr)) {
- if (*cp_val(*ptr) == i_return_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (*cp_val(*ptr) == i_return_to_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- } else {
- BeamInstr *cp = cp_val(*ptr);
- if (cp != prev) {
- /* Record non-duplicates only */
- prev = cp;
- s->trace[s->depth++] = cp - 1;
- depth--;
- }
- ptr++;
- }
- } else ptr++;
- }
- }
+ gather_stacktrace(p, s, depth);
}
/*
@@ -2128,95 +2003,66 @@ apply_bif_error_adjustment(Process *p, Export *ep,
Eterm *reg, Uint arity,
BeamInstr *I, Uint stack_offset)
{
+ int apply_only;
+ Uint need;
+
+ need = stack_offset /* bytes */ / sizeof(Eterm);
+ apply_only = stack_offset == 0;
+
/*
* I is only set when the apply is a tail call, i.e.,
* from the instructions i_apply_only, i_apply_last_P,
* and apply_last_IP.
*/
- if (I
- && BeamIsOpCode(ep->beam[0], op_apply_bif)
- && (ep == bif_export[BIF_error_1]
- || ep == bif_export[BIF_error_2]
- || ep == bif_export[BIF_exit_1]
- || ep == bif_export[BIF_throw_1])) {
- /*
- * We are about to tail apply one of the BIFs
- * erlang:error/1, erlang:error/2, erlang:exit/1,
- * or erlang:throw/1. Error handling of these BIFs is
- * special!
- *
- * We need 'p->cp' to point into the calling
- * function when handling the error after the BIF has
- * been applied. This in order to get the topmost
- * stackframe correct. Without the following adjustment,
- * 'p->cp' will point into the function that called
- * current function when handling the error. We add a
- * dummy stackframe in order to achieve this.
- *
- * Note that these BIFs unconditionally will cause
- * an exception to be raised. That is, our modifications
- * of 'p->cp' as well as the stack will be corrected by
- * the error handling code.
- *
- * If we find an exception/return-to trace continuation
- * pointer as the topmost continuation pointer, we do not
- * need to do anything since the information already will
- * be available for generation of the stacktrace.
- */
- int apply_only = stack_offset == 0;
- BeamInstr *cpp;
+ if (!(I && (ep->bif_number == BIF_error_1 ||
+ ep->bif_number == BIF_error_2 ||
+ ep->bif_number == BIF_exit_1 ||
+ ep->bif_number == BIF_throw_1))) {
+ return;
+ }
- if (apply_only) {
- ASSERT(p->cp != NULL);
- cpp = p->cp;
- }
- else {
- ASSERT(is_CP(p->stop[0]));
- cpp = cp_val(p->stop[0]);
- }
+ /*
+ * We are about to tail apply one of the BIFs erlang:error/1,
+ * erlang:error/2, erlang:exit/1, or erlang:throw/1. Error handling of
+ * these BIFs is special!
+ *
+ * We need the topmost continuation pointer to point into the calling
+ * function when handling the error after the BIF has been applied. This in
+ * order to get the topmost stackframe correct.
+ *
+ * Note that these BIFs will unconditionally cause an exception to be
+ * raised. That is, our modifications of the stack will be corrected by the
+ * error handling code.
+ */
+ if (need == 0) {
+ need = 1; /* i_apply_only */
+ }
- if (cpp != beam_exception_trace
- && cpp != beam_return_trace
- && cpp != beam_return_to_trace) {
- Uint need = stack_offset /* bytes */ / sizeof(Eterm);
- if (need == 0)
- need = 1; /* i_apply_only */
- if (p->stop - p->htop < need)
- erts_garbage_collect(p, (int) need, reg, arity+1);
- p->stop -= need;
-
- if (apply_only) {
- /*
- * Called from the i_apply_only instruction.
- *
- * 'p->cp' contains continuation pointer pointing
- * into the function that called current function.
- * We push that continuation pointer onto the stack,
- * and set 'p->cp' to point into current function.
- */
+ if (p->stop - p->htop < need) {
+ erts_garbage_collect(p, (int) need, reg, arity+1);
+ }
- p->stop[0] = make_cp(p->cp);
- p->cp = I;
- }
- else {
- /*
- * Called from an i_apply_last_p, or apply_last_IP,
- * instruction.
- *
- * Calling instruction will after we return read
- * a continuation pointer from the stack and write
- * it to 'p->cp', and then remove the topmost
- * stackframe of size 'stack_offset'.
- *
- * We have sized the dummy-stackframe so that it
- * will be removed by the instruction we currently
- * are executing, and leave the stackframe that
- * normally would have been removed intact.
- *
- */
- p->stop[0] = make_cp(I);
- }
- }
+ if (apply_only) {
+ /*
+ * Called from the i_apply_only instruction.
+ *
+ * Push the continuation pointer for the current function to the stack.
+ */
+ p->stop -= need;
+ p->stop[0] = make_cp(I);
+ } else {
+ /*
+ * Called from an i_apply_last_* instruction.
+ *
+ * The calling instruction will deallocate a stack frame of size
+ * 'stack_offset'.
+ *
+ * Push the continuation pointer for the current function to the stack,
+ * and then add a dummy stackframe for the i_apply_last* instruction
+ * to discard.
+ */
+ p->stop[0] = make_cp(I);
+ p->stop -= need;
}
}
@@ -2437,10 +2283,10 @@ erts_hibernate(Process* c_p, Eterm* reg)
c_p->arg_reg[0] = module;
c_p->arg_reg[1] = function;
c_p->arg_reg[2] = args;
- c_p->stop = STACK_START(c_p);
+ c_p->stop = c_p->hend - 1; /* Keep first continuation pointer */
+ ASSERT(c_p->stop[0] == make_cp(beam_apply+1));
c_p->catches = 0;
c_p->i = beam_apply;
- c_p->cp = (BeamInstr *) beam_apply+1;
/*
* If there are no waiting messages, garbage collect and
@@ -2460,7 +2306,7 @@ erts_hibernate(Process* c_p, Eterm* reg)
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- c_p->current = &bif_export[BIF_hibernate_3]->info.mfa;
+ c_p->current = &bif_trap_export[BIF_hibernate_3].info.mfa;
c_p->flags |= F_HIBERNATE_SCHED; /* Needed also when woken! */
return 1;
}
@@ -3268,10 +3114,10 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
e.info.mfa.arity = arity;
if ((ep = export_get(&e)) == NULL) {
- return 0;
+ return 0;
}
- return ep->addressv[erts_active_code_ix()] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif);
+
+ return ep->bif_number != -1;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 35f2ea6688..3fc3b8168e 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -141,7 +141,7 @@ typedef struct {
* eventually patch with a pointer into
* the export entry.
*/
- BifFunction bf; /* Pointer to BIF function if BIF;
+ Export *bif; /* Pointer to export entry if BIF;
* NULL otherwise.
*/
} ImportEntry;
@@ -315,6 +315,7 @@ typedef struct LoaderState {
* (or 0 if there is no on_load function)
*/
int otp_20_or_higher; /* Compiled with OTP 20 or higher */
+ unsigned max_opcode; /* Highest opcode used in module */
/*
* Atom table.
@@ -844,17 +845,23 @@ erts_finish_loading(Binary* magic, Process* c_p,
if (ep == NULL || ep->info.mfa.module != module) {
continue;
}
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- } else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(mod_tab_p->curr.num_traced_exports > 0);
- erts_clear_export_break(mod_tab_p, &ep->info);
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
- ep->beam[1] = 0;
+
+ erts_clear_export_break(mod_tab_p, ep);
+
+ ep->addressv[code_ix] =
+ (BeamInstr*)ep->trampoline.breakpoint.address;
+ ep->trampoline.breakpoint.address = 0;
+
+ ASSERT(ep->addressv[code_ix] != ep->trampoline.raw);
}
- ASSERT(ep->beam[1] == 0);
+ ASSERT(ep->trampoline.breakpoint.address == 0);
}
}
ASSERT(mod_tab_p->curr.num_breakpoints == 0);
@@ -1470,15 +1477,14 @@ load_import_table(LoaderState* stp)
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
- stp->import[i].bf = NULL;
+ stp->import[i].bif = NULL;
/*
- * If the export entry refers to a BIF, get the pointer to
- * the BIF function.
+ * If the export entry refers to a BIF, save a pointer to the BIF entry.
*/
if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
- if (BeamIsOpCode(e->beam[0], op_apply_bif)) {
- stp->import[i].bf = (BifFunction) e->beam[1];
+ if (e->bif_number != -1) {
+ stp->import[i].bif = e;
if (func == am_load_nif && mod == am_erlang && arity == 2) {
stp->may_load_nif = 1;
}
@@ -1529,33 +1535,6 @@ read_export_table(LoaderState* stp)
LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
-
- /*
- * Find out if there is a BIF with the same name.
- */
-
- if (!is_bif(stp->module, func, arity)) {
- continue;
- }
-
- /*
- * This is a stub for a BIF.
- *
- * It should not be exported, and the information in its
- * func_info instruction should be invalidated so that it
- * can be filtered out by module_info(functions) and by
- * any other functions that walk through all local functions.
- */
-
- if (stp->labels[n].num_patches > 0) {
- LoadError3(stp, "there are local calls to the stub for "
- "the BIF %T:%T/%d",
- stp->module, func, arity);
- }
- stp->export[i].address = NULL;
- address[-1] = 0;
- address[-2] = NIL;
- address[-3] = NIL;
}
return 1;
@@ -1563,31 +1542,33 @@ read_export_table(LoaderState* stp)
return 0;
}
-
static int
is_bif(Eterm mod, Eterm func, unsigned arity)
{
- Export* e = erts_active_export_entry(mod, func, arity);
- if (e == NULL) {
- return 0;
- }
- if (! BeamIsOpCode(e->beam[0], op_apply_bif)) {
- return 0;
- }
- if (mod == am_erlang && func == am_apply && arity == 3) {
- /*
- * erlang:apply/3 is a special case -- it is implemented
- * as an instruction and it is OK to redefine it.
- */
- return 0;
+ Export *e = erts_active_export_entry(mod, func, arity);
+
+ if (e != NULL) {
+ return e->bif_number != -1;
}
- return 1;
+
+ return 0;
}
static int
read_lambda_table(LoaderState* stp)
{
unsigned int i;
+ unsigned int otp_22_or_lower;
+
+ /*
+ * Determine whether this module was compiled with OTP 22 or lower
+ * by looking at the max opcode number. The compiler in OTP 23 will
+ * always set the max opcode to the opcode for `swap` (whether
+ * actually used or not) so that a module compiled for OTP 23
+ * cannot be loaded in earlier versions.
+ */
+
+ otp_22_or_lower = stp->max_opcode < genop_swap_2;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1619,6 +1600,29 @@ read_lambda_table(LoaderState* stp)
GetInt(stp, 4, Index);
GetInt(stp, 4, stp->lambdas[i].num_free);
GetInt(stp, 4, OldUniq);
+
+ /*
+ * Fun entries are now keyed by the explicit ("new") index in
+ * the fun entry. That allows multiple make_fun2 instructions
+ * to share the same fun entry (when the `fun F/A` syntax is
+ * used). Before OTP 23, fun entries were keyed by the old
+ * index, which is the order of the entries in the fun
+ * chunk. Each make_fun2 needed to refer to its own fun entry.
+ *
+ * Modules compiled before OTP 23 can safely be loaded if the
+ * old index and the new index are equal. That is true for all
+ * modules compiled with OTP R15 and later.
+ */
+ if (otp_22_or_lower && i != Index) {
+ /*
+ * Compiled with a compiler before OTP R15B. The new indices
+ * are not reliable, so it is not safe to load this module.
+ */
+ LoadError2(stp, "please re-compile this module with an "
+ ERLANG_OTP_RELEASE " compiler "
+ "(old-style fun with indices: %d/%d)",
+ i, Index);
+ }
fe = erts_put_fun_entry2(stp->module, OldUniq, i, stp->mod_md5,
Index, arity-stp->lambdas[i].num_free);
stp->lambdas[i].fe = fe;
@@ -1839,7 +1843,6 @@ read_code_header(LoaderState* stp)
{
unsigned head_size;
unsigned version;
- unsigned opcode_max;
int i;
/*
@@ -1871,8 +1874,8 @@ read_code_header(LoaderState* stp)
/*
* Verify the number of the highest opcode used.
*/
- GetInt(stp, 4, opcode_max);
- if (opcode_max > MAX_GENERIC_OPCODE) {
+ GetInt(stp, 4, stp->max_opcode);
+ if (stp->max_opcode > MAX_GENERIC_OPCODE) {
LoadError2(stp,
"This BEAM file was compiled for a later version"
" of the run-time system than " ERLANG_OTP_RELEASE ".\n"
@@ -1880,7 +1883,7 @@ read_code_header(LoaderState* stp)
ERLANG_OTP_RELEASE " compiler.\n"
" (Use of opcode %d; this emulator supports "
"only up to %d.)",
- opcode_max, MAX_GENERIC_OPCODE);
+ stp->max_opcode, MAX_GENERIC_OPCODE);
}
GetInt(stp, 4, stp->num_labels);
@@ -1919,8 +1922,6 @@ read_code_header(LoaderState* stp)
code = stp->codev = (BeamInstr*) &stp->hdr->functions; \
} \
} while (0)
-
-#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
static void init_label(Label* lp)
{
@@ -2500,10 +2501,14 @@ load_code(LoaderState* stp)
if (i >= stp->num_imports) {
LoadError1(stp, "invalid import table index %d", i);
}
- if (stp->import[i].bf == NULL) {
+ if (stp->import[i].bif == NULL) {
LoadError1(stp, "not a BIF: import table index %d", i);
}
- code[ci++] = (BeamInstr) stp->import[i].bf;
+ {
+ int bif_index = stp->import[i].bif->bif_number;
+ BifEntry *bif_entry = &bif_table[bif_index];
+ code[ci++] = (BeamInstr) bif_entry->f;
+ }
break;
case 'P': /* Byte offset into tuple or stack */
case 'Q': /* Like 'P', but packable */
@@ -2711,36 +2716,30 @@ load_code(LoaderState* stp)
num_trailing_f = 0;
}
#endif
+ CodeNeed(1);
switch (tmp_op->a[arg].type) {
case TAG_i:
- CodeNeed(1);
code[ci++] = make_small(tmp_op->a[arg].val);
break;
case TAG_u:
case TAG_a:
case TAG_v:
- CodeNeed(1);
code[ci++] = tmp_op->a[arg].val;
break;
case TAG_f:
- CodeNeed(1);
register_label_patch(stp, tmp_op->a[arg].val, ci, -last_instr_start);
ci++;
break;
case TAG_x:
- CodeNeed(1);
code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
- CodeNeed(1);
code[ci++] = make_loader_y_reg(tmp_op->a[arg].val);
break;
case TAG_n:
- CodeNeed(1);
code[ci++] = NIL;
break;
case TAG_q:
- CodeNeed(1);
new_literal_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
@@ -2811,18 +2810,43 @@ load_code(LoaderState* stp)
switch (stp->specific_op) {
case op_i_func_info_IaaI:
{
+ int padding_required;
Sint offset;
+
if (function_number >= stp->num_functions) {
LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
- if (stp->may_load_nif) {
+ /* Native function calls may be larger than their stubs, so
+ * we'll need to make sure any potentially-native function stub
+ * is padded with enough room.
+ *
+ * Note that the padding is applied for the previous function,
+ * not the current one, so we check whether the old F/A is
+ * a BIF. */
+ padding_required = last_func_start && (stp->may_load_nif ||
+ is_bif(stp->module, stp->function, stp->arity));
+
+ /*
+ * Save context for error messages.
+ */
+ stp->function = code[ci-2];
+ stp->arity = code[ci-1];
+
+ /*
+ * Save current offset of into the line instruction array.
+ */
+ if (stp->func_line) {
+ stp->func_line[function_number] = stp->current_li;
+ }
+
+ if (padding_required) {
const int finfo_ix = ci - FUNC_INFO_SZ;
- if (finfo_ix - last_func_start < BEAM_NIF_MIN_FUNC_SZ && last_func_start) {
+ if (finfo_ix - last_func_start < BEAM_NATIVE_MIN_FUNC_SZ) {
/* Must make room for call_nif op */
- int pad = BEAM_NIF_MIN_FUNC_SZ - (finfo_ix - last_func_start);
- ASSERT(pad > 0 && pad < BEAM_NIF_MIN_FUNC_SZ);
+ int pad = BEAM_NATIVE_MIN_FUNC_SZ - (finfo_ix - last_func_start);
+ ASSERT(pad > 0 && pad < BEAM_NATIVE_MIN_FUNC_SZ);
CodeNeed(pad);
sys_memmove(&code[finfo_ix+pad], &code[finfo_ix],
FUNC_INFO_SZ*sizeof(BeamInstr));
@@ -2833,20 +2857,6 @@ load_code(LoaderState* stp)
}
last_func_start = ci;
- /*
- * Save current offset of into the line instruction array.
- */
-
- if (stp->func_line) {
- stp->func_line[function_number] = stp->current_li;
- }
-
- /*
- * Save context for error messages.
- */
- stp->function = code[ci-2];
- stp->arity = code[ci-1];
-
/* When this assert is triggered, it is normally a sign that
the size of the ops.tab i_func_info instruction is not
the same as FUNC_INFO_SZ */
@@ -2876,7 +2886,6 @@ load_code(LoaderState* stp)
case op_i_bs_match_string_yfWW:
new_string_patch(stp, ci-1);
break;
-
case op_catch_yf:
/* code[ci-3] &&lb_catch_yf
* code[ci-2] y-register offset in E
@@ -2977,6 +2986,7 @@ load_code(LoaderState* stp)
#define succ3(St, X, Y) ((X).type == (Y).type && (X).val + 3 == (Y).val)
#define succ4(St, X, Y) ((X).type == (Y).type && (X).val + 4 == (Y).val)
+#define offset(St, X, Y, Offset) ((X).type == (Y).type && (X).val + Offset == (Y).val)
#ifdef NO_FPE_SIGNALS
#define no_fpe_signals(St) 1
@@ -3131,27 +3141,6 @@ mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest)
return 0;
}
-static int
-is_killed_apply(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val+2 <= Reg.val;
-}
-
-static int
-is_killed(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val <= Reg.val;
-}
-
-static int
-is_killed_by_call_fun(LoaderState* stp, GenOpArg Reg, GenOpArg Live)
-{
- return Reg.type == TAG_x && Live.type == TAG_u &&
- Live.val+1 <= Reg.val;
-}
-
/*
* Test whether register Reg is killed by make_fun instruction that
* creates the fun given by index idx.
@@ -3171,14 +3160,23 @@ is_killed_by_make_fun(LoaderState* stp, GenOpArg Reg, GenOpArg idx)
}
}
-/*
- * Test whether register Reg is killed by the send instruction that follows.
- */
-
+/* Test whether Bif is "heavy" and should always go through its export entry */
static int
-is_killed_by_send(LoaderState* stp, GenOpArg Reg)
+is_heavy_bif(LoaderState* stp, GenOpArg Bif)
{
- return Reg.type == TAG_x && 2 <= Reg.val;
+ Export *ep;
+
+ if (Bif.type != TAG_u || Bif.val >= stp->num_imports) {
+ return 0;
+ }
+
+ ep = stp->import[Bif.val].bif;
+
+ if (ep) {
+ return bif_table[ep->bif_number].kind == BIF_KIND_HEAVY;
+ }
+
+ return 0;
}
/*
@@ -3324,8 +3322,8 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
}
goto generic;
}
- } else {
- GENOP_NAME_ARITY(op, i_bs_get_integer, 6);
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
+ GENOP_NAME_ARITY(op, i_bs_get_integer, 6);
op->a[0] = Ms;
op->a[1] = Fail;
op->a[2] = Live;
@@ -3335,6 +3333,9 @@ gen_get_integer2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[5] = Dst;
op->next = NULL;
return op;
+ } else {
+ /* Invalid literal size. */
+ goto error;
}
op->next = NULL;
return op;
@@ -3391,7 +3392,7 @@ gen_get_binary2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[4] = Flags;
op->a[5] = Dst;
}
- } else {
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
GENOP_NAME_ARITY(op, i_bs_get_binary2, 6);
op->a[0] = Ms;
op->a[1] = Fail;
@@ -3400,6 +3401,9 @@ gen_get_binary2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, GenOpArg Live,
op->a[4].type = TAG_u;
op->a[4].val = (Unit.val << 3) | Flags.val;
op->a[5] = Dst;
+ } else {
+ /* Invalid literal size. */
+ goto error;
}
op->next = NULL;
return op;
@@ -3636,12 +3640,20 @@ gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms,
goto error;
}
}
- } else {
+ } else if (Size.type == TAG_x || Size.type == TAG_y) {
GENOP_NAME_ARITY(op, i_bs_skip_bits2, 4);
op->a[0] = Ms;
op->a[1] = Size;
op->a[2] = Fail;
op->a[3] = Unit;
+ } else {
+ /*
+ * Invalid literal size. Can only happen if compiler
+ * optimizations are selectively disabled. For example,
+ * at the time of writing, [no_copt, no_type_opt] will allow
+ * skip instructions with invalid sizes to slip through.
+ */
+ goto error;
}
op->next = NULL;
return op;
@@ -5208,27 +5220,52 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
*/
for (i = 0; i < stp->num_exps; i++) {
- Export* ep;
- BeamInstr* address = stp->export[i].address;
+ Export* ep;
+ BeamInstr* address = stp->export[i].address;
- if (address == NULL) {
- /* Skip stub for a BIF */
- continue;
- }
- ep = erts_export_put(stp->module, stp->export[i].function,
- stp->export[i].arity);
- if (on_load) {
- /*
- * on_load: Don't make any of the exported functions
- * callable yet. Keep any function in the current
- * code callable.
- */
- ep->beam[1] = (BeamInstr) address;
- }
- else
+ ep = erts_export_put(stp->module,
+ stp->export[i].function,
+ stp->export[i].arity);
+
+ /* Fill in BIF stubs with a proper call to said BIF. */
+ if (ep->bif_number != -1) {
+ erts_write_bif_wrapper(ep, address);
+ }
+
+ if (on_load) {
+ /*
+ * on_load: Don't make any of the exported functions
+ * callable yet. Keep any function in the current
+ * code callable.
+ */
+ ep->trampoline.not_loaded.deferred = (BeamInstr) address;
+ } else {
ep->addressv[erts_staging_code_ix()] = address;
+ }
}
+#ifdef DEBUG
+ /* Ensure that we've loaded stubs for all BIFs in this module. */
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry = &bif_table[i];
+
+ if (stp->module == entry->module) {
+ Export *ep = erts_export_put(entry->module,
+ entry->name,
+ entry->arity);
+ BeamInstr *addr = ep->addressv[erts_staging_code_ix()];
+
+ if (!ErtsInArea(addr, stp->codev, stp->ci * sizeof(BeamInstr))) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Module %T doesn't export BIF %T/%i\n",
+ entry->module,
+ entry->name,
+ entry->arity);
+ }
+ }
+ }
+#endif
+
/*
* Import functions and patch all callers.
*/
@@ -5333,6 +5370,16 @@ transform_engine(LoaderState* st)
if (((1 << instr->a[ap].type) & mask) == 0)
goto restart;
break;
+#if defined(TOP_is_type_next_arg)
+ case TOP_is_type_next_arg:
+ mask = *pc++;
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ ap++;
+ break;
+#endif
case TOP_pred:
i = *pc++;
switch (i) {
@@ -5362,6 +5409,18 @@ transform_engine(LoaderState* st)
if (*pc++ != instr->a[ap].val)
goto restart;
break;
+#if defined(TOP_is_type_eq_next_arg)
+ case TOP_is_type_eq_next_arg:
+ mask = *pc++;
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ if (*pc++ != instr->a[ap].val)
+ goto restart;
+ ap++;
+ break;
+#endif
case TOP_is_same_var:
ASSERT(ap < instr->arity);
i = *pc++;
@@ -5400,15 +5459,16 @@ transform_engine(LoaderState* st)
i = instr->a[ap].val;
ASSERT(i < st->num_imports);
- if (i >= st->num_imports || st->import[i].bf == NULL)
- goto restart;
- if (bif_number != -1 &&
- bif_export[bif_number]->beam[1] != (BeamInstr) st->import[i].bf) {
+ if (i >= st->num_imports || st->import[i].bif == NULL)
goto restart;
- }
+ if (bif_number != -1) {
+ Export *bif = st->import[i].bif;
+ if (bif->bif_number != bif_number) {
+ goto restart;
+ }
+ }
}
break;
-
#endif
#if defined(TOP_is_not_bif)
case TOP_is_not_bif:
@@ -5438,7 +5498,7 @@ transform_engine(LoaderState* st)
* they are special.
*/
if (i < st->num_imports) {
- if (st->import[i].bf != NULL ||
+ if (st->import[i].bif != NULL ||
(st->import[i].module == am_erlang &&
st->import[i].function == am_apply &&
(st->import[i].arity == 2 || st->import[i].arity == 3))) {
@@ -5478,7 +5538,42 @@ transform_engine(LoaderState* st)
var[i].val = instr->a[ap].val;
ap++;
break;
-
+#if defined(TOP_is_type_set_var_next_arg)
+ case TOP_is_type_set_var_next_arg:
+ mask = pc[0];
+ i = pc[1];
+ ASSERT(i < TE_MAX_VARS);
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ ASSERT(i < TE_MAX_VARS);
+ var[i] = instr->a[ap];
+ ap++;
+ pc += 2;
+ break;
+#endif
+#if defined(TOP_is_type_eq_set_var_next_arg)
+ case TOP_is_type_eq_set_var_next_arg:
+ {
+ Eterm val;
+ mask = pc[0];
+ val = pc[1];
+ i = pc[2];
+ ASSERT(i < TE_MAX_VARS);
+ ASSERT(ap < instr->arity);
+ ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
+ if (((1 << instr->a[ap].type) & mask) == 0)
+ goto restart;
+ if (val != instr->a[ap].val)
+ goto restart;
+ ASSERT(i < TE_MAX_VARS);
+ var[i] = instr->a[ap];
+ ap++;
+ pc += 3;
+ }
+ break;
+#endif
#if defined(TOP_rest_args)
case TOP_rest_args:
{
@@ -5494,19 +5589,27 @@ transform_engine(LoaderState* st)
case TOP_commit:
instr = instr->next; /* The next_instr was optimized away. */
keep = instr;
- st->genop = instr;
-#ifdef DEBUG
- instr = 0;
-#endif
break;
+#if defined(TOP_commit_new_instr)
+ case TOP_commit_new_instr:
+ /*
+ * Reuse the last instruction on the left side instead of
+ * allocating a new instruction. Note that this is not
+ * safe if TOP_rest_args has been executed; therefore,
+ * this combined instruction is never used when that is
+ * the case.
+ */
+ ASSERT(instr->a == instr->def_args);
+ keep = instr;
+ instr->op = op = *pc++;
+ instr->arity = gen_opc[op].arity;
+ ap = 0;
+ break;
+#endif
#if defined(TOP_keep)
case TOP_keep:
/* Keep the current instruction unchanged. */
keep = instr;
- st->genop = instr;
-#ifdef DEBUG
- instr = 0;
-#endif
break;
#endif
#if defined(TOP_call_end)
@@ -5535,11 +5638,12 @@ transform_engine(LoaderState* st)
keep = instr->next; /* The next_instr was optimized away. */
*lastp = keep;
- st->genop = new_instr;
+ instr = new_instr;
}
/* FALLTHROUGH */
#endif
case TOP_end:
+ st->genop = instr;
while (first != keep) {
GenOp* next = first->next;
FREE_GENOP(st, first);
@@ -5550,28 +5654,28 @@ transform_engine(LoaderState* st)
/*
* Note that the instructions are generated in reverse order.
*/
- NEW_GENOP(st, instr);
- instr->next = st->genop;
- st->genop = instr;
- instr->op = op = *pc++;
- instr->arity = gen_opc[op].arity;
- ap = 0;
- break;
+ {
+ GenOp* new_instr;
+ NEW_GENOP(st, new_instr);
+ new_instr->next = instr;
+ instr = new_instr;
+ instr->op = op = *pc++;
+ instr->arity = gen_opc[op].arity;
+ ap = 0;
+ }
+ break;
#ifdef TOP_rename
case TOP_rename:
instr->op = op = *pc++;
instr->arity = gen_opc[op].arity;
return TE_OK;
#endif
- case TOP_store_type:
- i = *pc++;
- instr->a[ap].type = i;
- instr->a[ap].val = 0;
- break;
- case TOP_store_val:
- i = *pc++;
- instr->a[ap].val = i;
- break;
+ case TOP_store_val_next_arg:
+ instr->a[ap].type = pc[0];
+ instr->a[ap].val = pc[1];
+ ap++;
+ pc += 2;
+ break;
case TOP_store_var_next_arg:
i = *pc++;
ASSERT(i < TE_MAX_VARS);
@@ -5599,6 +5703,23 @@ transform_engine(LoaderState* st)
break;
case TOP_fail:
return TE_FAIL;
+#if defined(TOP_skip_unless)
+ case TOP_skip_unless:
+ /*
+ * Note that the caller of transform_engine() guarantees that
+ * there is always a second instruction available.
+ */
+ ASSERT(instr);
+ if (instr->next->op != pc[0]) {
+ /* The second instruction is wrong. Skip ahead. */
+ pc += pc[1] + 2;
+ ASSERT(*pc < NUM_TOPS); /* Valid instruction? */
+ } else {
+ /* Correct second instruction. */
+ pc += 2;
+ }
+ break;
+#endif
default:
ASSERT(0);
}
@@ -6283,12 +6404,12 @@ exported_from_module(Process* p, /* Process whose heap to use. */
if (ep->info.mfa.module == mod) {
Eterm tuple;
-
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_call_error_handler)) {
- /* There is a call to the function, but it does not exist. */
- continue;
- }
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw &&
+ BeamIsOpCode(ep->trampoline.op, op_call_error_handler)) {
+ /* There is a call to the function, but it does not exist. */
+ continue;
+ }
if (hp == hend) {
int need = 10 * 5;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index 156c3c45e2..e7127c5b08 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -106,7 +106,7 @@ typedef struct beam_code_header {
}BeamCodeHeader;
-# define BEAM_NIF_MIN_FUNC_SZ 4
+# define BEAM_NATIVE_MIN_FUNC_SZ 4
void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
int erts_is_module_native(BeamCodeHeader* code);
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 04e9db1f8e..9fc6ba4ac6 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -77,6 +77,8 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3)
Eterm pid;
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
+ so.tag = am_spawn_reply;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -309,6 +311,15 @@ demonitor(Process *c_p, Eterm ref, Eterm *multip)
int deleted;
ErtsDSigSendContext ctx;
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * Not allowed to remove this until spawn
+ * operation has succeeded; restore monitor...
+ */
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(c_p), mon);
+ return am_false;
+ }
+
ASSERT(is_external_pid(to) || is_node_name_atom(to));
if (is_external_pid(to))
@@ -701,8 +712,11 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3)
{
ErlSpawnOpts so;
Eterm pid;
+ Eterm tmp_heap[2];
so.flags = erts_default_spo_flags|SPO_LINK;
+ so.opts = CONS(&tmp_heap[0], am_link, NIL);
+ so.tag = am_spawn_reply;
pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
@@ -716,132 +730,39 @@ BIF_RETTYPE spawn_link_3(BIF_ALIST_3)
/**********************************************************************/
-BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
+BIF_RETTYPE spawn_opt_4(BIF_ALIST_4)
{
ErlSpawnOpts so;
Eterm pid;
- Eterm* tp;
- Eterm ap;
- Eterm arg;
Eterm res;
+ int opts_error;
/*
- * Check that the first argument is a tuple of four elements.
+ * Fail order:
+ * - Bad types
+ * - Bad options
*/
- if (is_not_tuple(BIF_ARG_1)) {
- error:
- BIF_ERROR(BIF_P, BADARG);
- }
- tp = tuple_val(BIF_ARG_1);
- if (*tp != make_arityval(4))
- goto error;
-
- /*
- * Store default values for options.
- */
- so.flags = erts_default_spo_flags|SPO_USE_ARGS;
- so.min_heap_size = H_MIN_SIZE;
- so.min_vheap_size = BIN_VH_MIN_SIZE;
- so.max_heap_size = H_MAX_SIZE;
- so.max_heap_flags = H_MAX_FLAGS;
- so.priority = PRIORITY_NORMAL;
- so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
- so.scheduler = 0;
-
- /*
- * Walk through the option list.
- */
- ap = tp[4];
- while (is_list(ap)) {
- arg = CAR(list_val(ap));
- if (arg == am_link) {
- so.flags |= SPO_LINK;
- } else if (arg == am_monitor) {
- so.flags |= SPO_MONITOR;
- } else if (is_tuple(arg)) {
- Eterm* tp2 = tuple_val(arg);
- Eterm val;
- if (*tp2 != make_arityval(2))
- goto error;
- arg = tp2[1];
- val = tp2[2];
- if (arg == am_priority) {
- if (val == am_max)
- so.priority = PRIORITY_MAX;
- else if (val == am_high)
- so.priority = PRIORITY_HIGH;
- else if (val == am_normal)
- so.priority = PRIORITY_NORMAL;
- else if (val == am_low)
- so.priority = PRIORITY_LOW;
- else
- goto error;
- } else if (arg == am_message_queue_data) {
- switch (val) {
- case am_on_heap:
- so.flags &= ~SPO_OFF_HEAP_MSGQ;
- so.flags |= SPO_ON_HEAP_MSGQ;
- break;
- case am_off_heap:
- so.flags &= ~SPO_ON_HEAP_MSGQ;
- so.flags |= SPO_OFF_HEAP_MSGQ;
- break;
- default:
- goto error;
- }
- } else if (arg == am_min_heap_size && is_small(val)) {
- Sint min_heap_size = signed_val(val);
- if (min_heap_size < 0) {
- goto error;
- } else if (min_heap_size < H_MIN_SIZE) {
- so.min_heap_size = H_MIN_SIZE;
- } else {
- so.min_heap_size = erts_next_heap_size(min_heap_size, 0);
- }
- } else if (arg == am_max_heap_size) {
- if (!erts_max_heap_size(val, &so.max_heap_size, &so.max_heap_flags))
- goto error;
- } else if (arg == am_min_bin_vheap_size && is_small(val)) {
- Sint min_vheap_size = signed_val(val);
- if (min_vheap_size < 0) {
- goto error;
- } else if (min_vheap_size < BIN_VH_MIN_SIZE) {
- so.min_vheap_size = BIN_VH_MIN_SIZE;
- } else {
- so.min_vheap_size = erts_next_heap_size(min_vheap_size, 0);
- }
- } else if (arg == am_fullsweep_after && is_small(val)) {
- Sint max_gen_gcs = signed_val(val);
- if (max_gen_gcs < 0) {
- goto error;
- } else {
- so.max_gen_gcs = max_gen_gcs;
- }
- } else if (arg == am_scheduler && is_small(val)) {
- Sint scheduler = signed_val(val);
- if (scheduler < 0 || erts_no_schedulers < scheduler)
- goto error;
- so.scheduler = (int) scheduler;
- } else {
- goto error;
- }
- } else {
- goto error;
- }
- ap = CDR(list_val(ap));
- }
- if (is_not_nil(ap)) {
- goto error;
- }
-
- if (so.max_heap_size != 0 && so.max_heap_size < so.min_heap_size) {
- goto error;
+ opts_error = erts_parse_spawn_opts(&so, BIF_ARG_4, NULL, 0);
+ if (opts_error) {
+ Sint arity;
+ if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+ arity = erts_list_length(BIF_ARG_3);
+ if (arity < 0)
+ BIF_ERROR(BIF_P, BADARG);
+ if (arity > MAX_SMALL)
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ if (opts_error > 0)
+ BIF_ERROR(BIF_P, BADARG);
+ BIF_ERROR(BIF_P, BADARG);
}
-
+
/*
* Spawn the process.
*/
- pid = erl_create_process(BIF_P, tp[1], tp[2], tp[3], &so);
+ so.opts = BIF_ARG_4;
+ so.tag = am_spawn_reply;
+ pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);
if (is_non_value(pid)) {
BIF_ERROR(BIF_P, so.error_code);
} else if (so.flags & SPO_MONITOR) {
@@ -859,6 +780,130 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
}
}
+/**********************************************************************/
+
+BIF_RETTYPE erts_internal_spawn_request_4(BIF_ALIST_4)
+{
+ ErlSpawnOpts so;
+ Eterm tmp_heap_mfna[4];
+ Eterm tmp_heap_alist[4 + 2];
+ Sint arity;
+ int opts_error;
+ Eterm tag, tmp, error;
+
+ if (!is_atom(BIF_ARG_1))
+ goto badarg;
+ if (!is_atom(BIF_ARG_2))
+ goto badarg;
+ arity = erts_list_length(BIF_ARG_3);
+ if (arity < 0)
+ goto badarg;
+
+ /*
+ * Fail order:
+ * - Bad types
+ * - Bad options
+ */
+ opts_error = erts_parse_spawn_opts(&so, BIF_ARG_4, &tag, !0);
+ if (arity > MAX_SMALL)
+ goto system_limit;
+ if (opts_error) {
+ if (opts_error > 0)
+ goto badarg;
+ goto badopt;
+ }
+
+ /* Make argument list for erts_internal:spawn_init/1 */
+ tmp = TUPLE3(&tmp_heap_alist[0], BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ tmp = CONS(&tmp_heap_alist[4], tmp, NIL);
+
+ so.mfa = TUPLE3(&tmp_heap_mfna[0], BIF_ARG_1, BIF_ARG_2, make_small(arity));
+ so.flags |= SPO_ASYNC;
+ so.mref = THE_NON_VALUE;
+ so.tag = tag;
+ so.opts = BIF_ARG_4;
+
+ /*
+ * Spawn the process.
+ */
+ tmp = erl_create_process(BIF_P, am_erts_internal, am_spawn_init, tmp, &so);
+ if (is_non_value(tmp)) {
+ switch (so.error_code) {
+ case SYSTEM_LIMIT:
+ goto system_limit;
+ case BADARG:
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected error from erl_create_process()");
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+ }
+
+ ASSERT(is_internal_pid(tmp));
+
+ if (ERTS_USE_MODIFIED_TIMING()) {
+ BIF_TRAP2(erts_delay_trap, BIF_P, so.mref, ERTS_MODIFIED_TIMING_DELAY);
+ }
+ else {
+ BIF_RET(so.mref);
+ }
+
+badarg:
+ BIF_RET(am_badarg);
+system_limit:
+ error = am_system_limit;
+ goto send_error;
+badopt:
+ error = am_badopt;
+ /* fall through... */
+send_error: {
+ Eterm ref = erts_make_ref(BIF_P);
+ if (!(so.flags & SPO_NO_EMSG))
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ tag, ref, error, am_undefined);
+ BIF_RET(ref);
+ }
+
+}
+
+BIF_RETTYPE spawn_request_abandon_1(BIF_ALIST_1)
+{
+ ErtsMonitor *omon;
+
+ if (is_not_internal_ref(BIF_ARG_1)) {
+ if (is_not_ref(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+ /* Not an outstanding spawn_request of this process... */
+ BIF_RET(am_false);
+ }
+
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), BIF_ARG_1);
+ if (!omon
+ || ((omon->flags & (ERTS_ML_FLG_SPAWN_PENDING
+ | ERTS_ML_FLG_SPAWN_ABANDONED))
+ != ERTS_ML_FLG_SPAWN_PENDING)) {
+ /* Not an outstanding spawn_request of this process... */
+ BIF_RET(am_false);
+ }
+
+ ASSERT(erts_monitor_is_origin(omon));
+
+ if (omon->flags & ERTS_ML_FLG_SPAWN_LINK) {
+ /* Leave it for reply... */
+ omon->flags |= ERTS_ML_FLG_SPAWN_ABANDONED;
+ }
+ else {
+ /* We don't need it anymore; remove it... */
+ ErtsMonitorData *mdp;
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), omon);
+ mdp = erts_monitor_to_data(omon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ }
+ BIF_RET(am_true);
+}
+
/**********************************************************************/
/* remove a link from a process */
@@ -990,8 +1035,7 @@ BIF_RETTYPE hibernate_3(BIF_ALIST_3)
BIF_RETTYPE get_stacktrace_0(BIF_ALIST_0)
{
- Eterm t = build_stacktrace(BIF_P, BIF_P->ftrace);
- BIF_RET(t);
+ BIF_RET(NIL);
}
/**********************************************************************/
@@ -1816,7 +1860,7 @@ ebif_bang_2(BIF_ALIST_2)
#define SEND_INTERNAL_ERROR (-6)
#define SEND_AWAIT_RESULT (-7)
#define SEND_YIELD_CONTINUE (-8)
-#define SEND_SYSTEM_LIMIT (-9)
+#define SEND_SYSTEM_LIMIT (-9)
static Sint remote_send(Process *p, DistEntry *dep,
@@ -1915,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%d) of this node (%d)\n",
+ "incarnation (%u) of this node (%u)\n",
msg,
p->common.id,
to,
@@ -1959,7 +2003,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%d) of this node (%d)\n",
+ "incarnation (%u) of this node (%u)\n",
msg,
p->common.id,
to,
@@ -1987,7 +2031,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
trace_send(p, portid, msg);
if (have_seqtrace(SEQ_TRACE_TOKEN(p))) {
- seq_trace_update_send(p);
+ seq_trace_update_serial(p);
seq_trace_output(SEQ_TRACE_TOKEN(p), msg,
SEQ_TRACE_SEND, portid, p);
}
@@ -2160,7 +2204,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
break;
case SEND_YIELD:
if (suspend) {
- ERTS_BIF_PREP_YIELD3(retval, bif_export[BIF_send_3], p, to, msg, opts);
+ ERTS_BIF_PREP_YIELD3(retval, &bif_trap_export[BIF_send_3], p, to, msg, opts);
} else {
ERTS_BIF_PREP_RET(retval, am_nosuspend);
}
@@ -2277,7 +2321,7 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ERTS_BIF_PREP_RET(retval, msg);
break;
case SEND_YIELD:
- ERTS_BIF_PREP_YIELD2(retval, bif_export[BIF_send_2], p, to, msg);
+ ERTS_BIF_PREP_YIELD2(retval, &bif_trap_export[BIF_send_2], p, to, msg);
break;
case SEND_YIELD_RETURN:
yield_return:
@@ -2584,7 +2628,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1)
} else {
ERTS_BIF_ERROR_TRAPPED1(BIF_P,
BADARG,
- bif_export[BIF_iolist_size_1],
+ &bif_trap_export[BIF_iolist_size_1],
input_list);
}
@@ -2604,7 +2648,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1)
ESTACK_SAVE(s, &context->stack);
erts_set_gc_state(BIF_P, 0);
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP1(bif_export[BIF_iolist_size_1], BIF_P, state_mref);
+ BIF_TRAP1(&bif_trap_export[BIF_iolist_size_1], BIF_P, state_mref);
}
/**********************************************************************/
@@ -3942,7 +3986,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
if (flush) {
erts_halt(pos_int_code);
- ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
}
else {
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -4531,7 +4575,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(am_enabled);
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, am_enabled,
@@ -4556,7 +4600,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(make_small(old_no));
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no),
@@ -4720,7 +4764,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(make_small(old_no));
case ERTS_SCHDLR_SSPND_YIELD_RESTART:
ERTS_VBUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_system_flag_2],
+ BIF_TRAP2(&bif_trap_export[BIF_system_flag_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_SCHDLR_SSPND_YIELD_DONE:
ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no),
@@ -4870,9 +4914,13 @@ BIF_RETTYPE phash_2(BIF_ALIST_2)
BIF_RETTYPE phash2_1(BIF_ALIST_1)
{
Uint32 hash;
-
- hash = make_hash2(BIF_ARG_1);
- BIF_RET(make_small(hash & ((1L << 27) - 1)));
+ Eterm trap_state = THE_NON_VALUE;
+ hash = trapping_make_hash2(BIF_ARG_1, &trap_state, BIF_P);
+ if (trap_state == THE_NON_VALUE) {
+ BIF_RET(make_small(hash & ((1L << 27) - 1)));
+ } else {
+ BIF_TRAP1(&bif_trap_export[BIF_phash2_1], BIF_P, trap_state);
+ }
}
BIF_RETTYPE phash2_2(BIF_ALIST_2)
@@ -4880,6 +4928,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
Uint32 hash;
Uint32 final_hash;
Uint32 range;
+ Eterm trap_state = THE_NON_VALUE;
/* Check for special case 2^32 */
if (term_equals_2pow32(BIF_ARG_2)) {
@@ -4891,7 +4940,10 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2)
}
range = (Uint32) u;
}
- hash = make_hash2(BIF_ARG_1);
+ hash = trapping_make_hash2(BIF_ARG_1, &trap_state, BIF_P);
+ if (trap_state != THE_NON_VALUE) {
+ BIF_TRAP2(&bif_trap_export[BIF_phash2_2], BIF_P, trap_state, BIF_ARG_2);
+ }
if (range) {
final_hash = hash % range; /* [0..range-1] */
} else {
@@ -4967,15 +5019,32 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
Eterm (*bif)(BIF_ALIST))
{
int i;
+
sys_memset((void *) ep, 0, sizeof(Export));
+
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
- ep->addressv[i] = ep->beam;
+ ep->addressv[i] = ep->trampoline.raw;
}
+
+ ep->bif_number = -1;
+
+ ep->info.op = op_i_func_info_IaaI;
ep->info.mfa.module = m;
ep->info.mfa.function = f;
ep->info.mfa.arity = a;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif;
+
+ ep->trampoline.op = BeamOpCodeAddr(op_call_bif_W);
+ ep->trampoline.raw[1] = (BeamInstr)bif;
+}
+
+/*
+ * Writes a BIF call wrapper to the given address.
+ */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address) {
+ BifEntry *entry = &bif_table[export->bif_number];
+
+ address[0] = BeamOpCodeAddr(op_call_bif_W);
+ address[1] = (BeamInstr)entry->f;
}
void erts_init_bif(void)
@@ -5027,7 +5096,7 @@ void erts_init_bif(void)
}
/*
- * Scheduling of BIFs via NifExport...
+ * Scheduling of BIFs via ErtsNativeFunc...
*/
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
@@ -5042,8 +5111,8 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
int argc, Eterm *argv)
{
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- (void) erts_nif_export_schedule(c_p, dirty_shadow_proc,
- mfa, pc, BeamOpCodeAddr(op_apply_bif),
+ (void) erts_nfunc_schedule(c_p, dirty_shadow_proc,
+ mfa, pc, BeamOpCodeAddr(op_call_bif_W),
dfunc, ifunc,
module, function,
argc, argv);
@@ -5052,23 +5121,23 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
static BIF_RETTYPE dirty_bif_result(BIF_ALIST_1)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
- erts_nif_export_restore(BIF_P, nep, BIF_ARG_1);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
+ erts_nfunc_restore(BIF_P, nep, BIF_ARG_1);
BIF_RET(BIF_ARG_1);
}
static BIF_RETTYPE dirty_bif_trap(BIF_ALIST)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
/*
* Arity and argument registers already set
* correct by call to dirty_bif_trap()...
*/
- ASSERT(BIF_P->arity == nep->exp.info.mfa.arity);
+ ASSERT(BIF_P->arity == nep->trampoline.info.mfa.arity);
- erts_nif_export_restore(BIF_P, nep, THE_NON_VALUE);
+ erts_nfunc_restore(BIF_P, nep, THE_NON_VALUE);
BIF_P->i = (BeamInstr *) nep->func;
BIF_P->freason = TRAP;
@@ -5083,8 +5152,8 @@ static BIF_RETTYPE dirty_bif_exception(BIF_ALIST_2)
freason = signed_val(BIF_ARG_1);
- /* Restore orig info for error and clear nif export in handle_error() */
- freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear nif wrapper in handle_error() */
+ freason |= EXF_RESTORE_NFUNC;
BIF_P->fvalue = BIF_ARG_2;
@@ -5122,6 +5191,7 @@ erts_schedule_bif(Process *proc,
if (!ERTS_PROC_IS_EXITING(c_p)) {
Export *exp;
BifFunction dbif, ibif;
+ BeamInstr call_instr;
BeamInstr *pc;
/*
@@ -5156,29 +5226,41 @@ erts_schedule_bif(Process *proc,
if (i == NULL) {
ERTS_INTERNAL_ERROR("Missing instruction pointer");
}
+
+ if (BeamIsOpCode(*i, op_i_generic_breakpoint)) {
+ ErtsCodeInfo *ci;
+ GenericBp *bp;
+
+ ci = erts_code_to_codeinfo(i);
+ bp = ci->u.gen_bp;
+
+ call_instr = bp->orig_instr;
+ } else {
+ call_instr = *i;
+ }
+
#ifdef HIPE
- else if (proc->flags & F_HIPE_MODE) {
+ if (proc->flags & F_HIPE_MODE) {
/* Pointer to bif export in i */
exp = (Export *) i;
- pc = c_p->cp;
+ pc = cp_val(c_p->stop[0]);
mfa = &exp->info.mfa;
- }
+ } else /* !! This is part of the if clause below !! */
#endif
- else if (BeamIsOpCode(*i, op_call_bif_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ if (BeamIsOpCode(call_instr, op_call_light_bif_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_call_bif_only_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ else if (BeamIsOpCode(call_instr, op_call_light_bif_only_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_apply_bif)) {
- /* Pointer to bif in i+1, and mfa in i-3 */
- pc = c_p->cp;
+ else if (BeamIsOpCode(call_instr, op_call_bif_W)) {
+ pc = cp_val(c_p->stop[0]);
mfa = erts_code_to_codemfa(i);
}
else {
@@ -5206,7 +5288,7 @@ erts_schedule_bif(Process *proc,
static BIF_RETTYPE
call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
{
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsBifFunc bif = (ErtsBifFunc) nep->func;
BIF_RETTYPE ret;
@@ -5219,12 +5301,12 @@ call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
ret = (*bif)(c_p, reg, I);
if (is_value(ret))
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
else if (c_p->freason != TRAP)
- c_p->freason |= EXF_RESTORE_NIF; /* restore in handle_error() */
+ c_p->freason |= EXF_RESTORE_NFUNC; /* restore in handle_error() */
else if (nep->func == ERTS_SCHED_BIF_TRAP_MARKER) {
/* BIF did an ordinary trap... */
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
}
/* else:
* BIF rescheduled itself using erts_schedule_bif().
@@ -5241,7 +5323,7 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
int exiting;
Process *dirty_shadow_proc;
ErtsBifFunc bf;
- NifExport *nep;
+ ErtsNativeFunc *nep;
#ifdef DEBUG
Eterm *c_p_htop;
erts_aint32_t state;
@@ -5254,8 +5336,8 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#endif
- nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
nep->func = ERTS_SCHED_BIF_TRAP_MARKER;
@@ -5269,7 +5351,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
dirty_shadow_proc->freason = c_p->freason;
dirty_shadow_proc->fvalue = c_p->fvalue;
dirty_shadow_proc->ftrace = c_p->ftrace;
- dirty_shadow_proc->cp = c_p->cp;
dirty_shadow_proc->i = c_p->i;
#ifdef DEBUG
@@ -5316,7 +5397,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
c_p->freason = dirty_shadow_proc->freason;
c_p->fvalue = dirty_shadow_proc->fvalue;
c_p->ftrace = dirty_shadow_proc->ftrace;
- c_p->cp = dirty_shadow_proc->cp;
c_p->i = dirty_shadow_proc->i;
c_p->arity = dirty_shadow_proc->arity;
}
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index a5f46c4264..63c9fc23d5 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -25,13 +25,14 @@
#
# <bif-decl> ::= "bif" <bif> <C-name>* |
# "ubif" <bif> <C-name>* |
-# "gcbif" <bif> <C-name>*
+# "hbif" <bif> <C-name>*
# <bif> ::= <module> ":" <name> "/" <arity>
#
-# ubif: Use for operators and guard BIFs that never build anything
-# on the heap (such as tuple_size/1) and operators.
+# ubif: Use for operators and guard BIFs.
#
-# gcbif: Use for guard BIFs that may build on the heap (such as abs/1).
+# hbif: Use for BIFs that perform garbage collection or need up-to-date
+# information on where they were called from. These must be called
+# through the export entry.
#
# bif: Use for all other BIFs.
#
@@ -60,7 +61,7 @@ bif erlang:display_string/1
bif erlang:display_nl/0
ubif erlang:element/2
bif erlang:erase/0
-bif erlang:erase/1
+hbif erlang:erase/1
bif erlang:exit/1
bif erlang:exit/2
bif erlang:exit_signal/2
@@ -70,7 +71,7 @@ ubif erlang:float/1
bif erlang:float_to_list/1
bif erlang:float_to_list/2
bif erlang:fun_info/2
-bif erts_internal:garbage_collect/1
+hbif erts_internal:garbage_collect/1
bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
@@ -127,10 +128,10 @@ bif erlang:ports/0
bif erlang:pre_loaded/0
bif erlang:process_flag/2
bif erts_internal:process_flag/3
-bif erlang:process_info/1
-bif erlang:process_info/2
+hbif erlang:process_info/1
+hbif erlang:process_info/2
bif erlang:processes/0
-bif erlang:put/2
+hbif erlang:put/2
bif erlang:register/2
bif erlang:registered/0
ubif erlang:round/1
@@ -143,6 +144,8 @@ bif erlang:split_binary/2
bif erlang:statistics/1
bif erlang:term_to_binary/1
bif erlang:term_to_binary/2
+bif erlang:term_to_iovec/1
+bif erlang:term_to_iovec/2
bif erlang:throw/1
bif erlang:time/0
ubif erlang:tl/1
@@ -153,7 +156,7 @@ bif erlang:universaltime_to_localtime/1
bif erlang:unlink/1
bif erlang:unregister/1
bif erlang:whereis/1
-bif erlang:spawn_opt/1
+bif erlang:spawn_opt/4
bif erlang:setnode/2
bif erlang:dist_get_stat/1
bif erlang:dist_ctrl_input_handler/2
@@ -174,7 +177,7 @@ bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
bif erts_internal:request_system_task/4
-bif erts_internal:check_process_code/1
+hbif erts_internal:check_process_code/1
bif erts_internal:map_to_tuple_keys/1
bif erts_internal:term_type/1
@@ -193,10 +196,14 @@ bif erts_internal:scheduler_wall_time/1
bif erts_internal:dirty_process_handle_signals/1
-bif erts_internal:create_dist_channel/4
+bif erts_internal:create_dist_channel/3
bif erts_internal:ets_super_user/1
+bif erts_internal:spawn_request/4
+bif erts_internal:dist_spawn_request/4
+bif erlang:spawn_request_abandon/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -466,7 +473,7 @@ bif code:is_module_native/1
# New Bifs in R9C.
#
-bif erlang:hibernate/3
+hbif erlang:hibernate/3
bif error_logger:warning_map/0
#
@@ -758,3 +765,9 @@ bif erts_internal:ets_raw_next/2
bif erts_internal:abort_pending_connection/2
+
+#
+# New in 23
+#
+
+bif erts_internal:get_creation/0
diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab
index 8e0caa38a3..9c5f9e0c51 100644
--- a/erts/emulator/beam/bif_instrs.tab
+++ b/erts/emulator/beam/bif_instrs.tab
@@ -212,26 +212,32 @@ i_length.execute(Fail, Live, Dst) {
// Call a BIF, store the result in x(0) and transfer control to the
// next instruction.
//
-call_bif(Exp) {
+call_light_bif(Bif, Exp) {
+ Export *export;
ErtsBifFunc bf;
+
Eterm result;
ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -243,21 +249,26 @@ call_bif(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -280,10 +291,9 @@ call_bif(Exp) {
* erlang code or by nif_bif.epilogue() when the BIF
* is done).
*/
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -297,29 +307,38 @@ call_bif(Exp) {
//
// Call a BIF tail-recursively, storing the result in x(0) and doing
-// a return to the continuation poiner (c_p->cp).
+// a return to the continuation poiner.
//
-
-call_bif_only(Exp) {
+call_light_bif_only(Bif, Exp) {
+ ErlHeapFragment *live_hf_end;
ErtsBifFunc bf;
+ Export *export;
Eterm result;
- ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ /* Set up a dummy stack frame so we can perform a normal call. Loader
+ * transformations ensure that the next instruction after this is
+ * 'deallocate_return 0'. */
+ $AH(0, 0, GET_EXPORT_ARITY(export));
+
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -331,21 +350,26 @@ call_bif_only(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -367,11 +391,10 @@ call_bif_only(Exp) {
} else if (c_p->freason == TRAP) {
/*
* Dispatch to a trap. When the trap is done, a jump
- * to the continuation pointer (c_p->cp) will be done.
+ * to the continuation pointer on the stack will be done.
*/
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -413,17 +436,26 @@ send() {
r(0) = result;
CHECK_TERM(r(0));
} else if (c_p->freason == TRAP) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
SWAPIN;
- Dispatch();
+ $DISPATCH();
} else {
goto find_func_info;
}
}
+call_nif_early() {
+ HEAVY_SWAPOUT;
+ I = erts_call_nif_early(c_p, erts_code_to_codeinfo(I));
+ HEAVY_SWAPIN;
+ ASSERT(VALID_INSTR(*I));
+ Goto(*I);
+ //| -no_next
+}
+
call_nif := nif_bif.call_nif.epilogue;
-apply_bif := nif_bif.apply_bif.epilogue;
+call_bif := nif_bif.call_bif.epilogue;
nif_bif.head() {
Eterm nif_bif_result;
@@ -433,7 +465,7 @@ nif_bif.head() {
ErtsCodeMFA *codemfa;
}
-nif_bif.call_nif() {
+nif_bif.call_nif(Func, NifMod, DirtyFunc) {
/*
* call_nif is always first instruction in function:
*
@@ -443,11 +475,14 @@ nif_bif.call_nif() {
* I[0]: &&call_nif
* I[1]: Function pointer to NIF function
* I[2]: Pointer to erl_module_nif
- * I[3]: Function pointer to dirty NIF
+ * I[3]: Function pointer to dirty NIF. This is not used in this
+ * instruction, but dirty schedulers look at it.
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
+ (void)$DirtyFunc;
+
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
codemfa = erts_code_to_codemfa(I);
@@ -465,12 +500,12 @@ nif_bif.call_nif() {
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
{
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
- NifF* fp = vbf = (NifF*) I[1];
+ NifF* fp = vbf = (NifF*) $Func;
struct enif_environment_t env;
ASSERT(c_p->scheduler_data);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
- erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)$NifMod, NULL);
ASSERT((c_p->scheduler_data)->current_nif == NULL);
(c_p->scheduler_data)->current_nif = &env;
@@ -495,15 +530,15 @@ nif_bif.call_nif() {
DTRACE_NIF_RETURN(c_p, codemfa);
}
-nif_bif.apply_bif() {
+nif_bif.call_bif(Func) {
/*
- * At this point, I points to the code[0] in the export entry for
- * the BIF:
+ * At this point, I points to the code[0] in the native function wrapper
+ * for the BIF:
*
* code[-3]: Module
* code[-2]: Function
* code[-1]: Arity
- * code[0]: &&apply_bif
+ * code[0]: &&call_bif
* code[1]: Function pointer to BIF function
*/
@@ -515,21 +550,19 @@ nif_bif.apply_bif() {
codemfa = erts_code_to_codemfa(I);
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)Arg(0));
-
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)$Func);
/* In case we apply process_info/1,2 or load_nif/1 */
c_p->current = codemfa;
$SET_CP_I_ABS(I); /* In case we apply check_process_code/2. */
c_p->arity = 0; /* To allow garbage collection on ourselves
- * (check_process_code/2).
- */
+ * (check_process_code/2, put/2, etc). */
DTRACE_BIF_ENTRY(c_p, codemfa);
SWAPOUT;
ERTS_DBG_CHK_REDS(c_p, FCALLS - 1);
c_p->fcalls = FCALLS - 1;
- vbf = (BifFunction) Arg(0);
+ vbf = (BifFunction)$Func;
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = codemfa->arity;
ASSERT(bif_nif_arity <= 4);
@@ -557,6 +590,7 @@ nif_bif.apply_bif() {
}
nif_bif.epilogue() {
+ //| -no_next
ERTS_REQ_PROC_MAIN_LOCK(c_p);
ERTS_HOLE_CHECK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
@@ -570,8 +604,7 @@ nif_bif.epilogue() {
if (ERTS_LIKELY(is_value(nif_bif_result))) {
r(0) = nif_bif_result;
CHECK_TERM(r(0));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
} else if (c_p->freason == TRAP) {
SET_I(c_p->i);
@@ -579,8 +612,42 @@ nif_bif.epilogue() {
c_p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
}
- Dispatch();
+ $DISPATCH();
+ }
+ {
+ BeamInstr *cp = erts_printable_return_address(c_p, E);
+ ASSERT(VALID_INSTR(*cp));
+ I = handle_error(c_p, cp, reg, c_p->current);
}
- I = handle_error(c_p, c_p->cp, reg, c_p->current);
goto post_error_handling;
}
+
+i_load_nif() {
+ //| -no_next
+ if (erts_try_seize_code_write_permission(c_p)) {
+ Eterm result;
+
+ PRE_BIF_SWAPOUT(c_p);
+ result = erts_load_nif(c_p, I, r(0), r(1));
+ erts_release_code_write_permission();
+ ERTS_REQ_PROC_MAIN_LOCK(c_p);
+ SWAPIN;
+
+ if (ERTS_LIKELY(is_value(result))) {
+ r(0) = result;
+ $NEXT0();
+ } else {
+ static ErtsCodeMFA mfa = {am_erlang, am_load_nif, 2};
+ c_p->freason = BADARG;
+ I = handle_error(c_p, I, reg, &mfa);
+ goto post_error_handling;
+ }
+ } else {
+ /* Yield and try again */
+ $SET_CP_I_ABS(I);
+ SWAPOUT;
+ c_p->current = NULL;
+ c_p->arity = 2;
+ goto do_schedule;
+ }
+}
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 522f50287a..7666f23a4f 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2176,6 +2176,24 @@ term_to_Uint64(Eterm term, Uint64 *up)
#endif
}
+int
+term_to_Uint32(Eterm term, Uint32 *up)
+{
+#if ERTS_SIZEOF_ETERM == 4
+ return term_to_Uint(term,up);
+#else
+ if (is_small(term)) {
+ Sint i = signed_val(term);
+ if (i >= 0) {
+ *up = (Uint32) i;
+ return 1;
+ }
+ }
+ *up = BADARG;
+ return 0;
+#endif
+}
+
int term_to_Sint(Eterm term, Sint *sp)
{
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index ad19cce395..3fed076419 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -168,6 +168,8 @@ Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *);
int term_to_Uint64(Eterm, Uint64*);
int term_to_Sint64(Eterm, Sint64*);
#endif
+int term_to_Uint32(Eterm, Uint32*);
+
Uint32 big_to_uint32(Eterm b);
int term_equals_2pow32(Eterm);
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 4ddf59092a..0ae4bc1e60 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -567,7 +567,7 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD1(bif_export[BIF_binary_to_list_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_binary_to_list_1],
BIF_P, BIF_ARG_1);
}
/* Allow a bit more reductions... */
@@ -621,7 +621,7 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD3(bif_export[BIF_binary_to_list_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_binary_to_list_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
/* Allow a bit more reductions... */
@@ -668,7 +668,7 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) {
if (reds_left <= L2B_B2L_RESCHED_REDS) {
/* Yield and do it with full context reds... */
- ERTS_BIF_YIELD1(bif_export[BIF_bitstring_to_list_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_bitstring_to_list_1],
BIF_P, BIF_ARG_1);
}
/* Allow a bit more reductions... */
@@ -1041,7 +1041,7 @@ 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, bif_export[BIF_list_to_binary_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_list_to_binary_1]);
}
HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
@@ -1054,7 +1054,7 @@ BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
}
BIF_ERROR(BIF_P, BADARG);
}
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_iolist_to_binary_1]);
}
static int bitstr_list_len(ErtsIOListState *);
@@ -1081,7 +1081,7 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1)
else {
ErtsL2BState state = ERTS_L2B_STATE_INITER(BIF_P,
BIF_ARG_1,
- bif_export[BIF_list_to_bitstring_1],
+ &bif_trap_export[BIF_list_to_bitstring_1],
bitstr_list_len,
list_to_bitstr_buf_yielding);
int orig_reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index 50352b4084..eab9d51b5e 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -37,9 +37,12 @@
erts_atomic32_t the_active_code_index;
erts_atomic32_t the_staging_code_index;
+static int code_writing_seized = 0;
static Process* code_writing_process = NULL;
struct code_write_queue_item {
Process *p;
+ void (*aux_func)(void *);
+ void *aux_arg;
struct code_write_queue_item* next;
};
static struct code_write_queue_item* code_write_queue = NULL;
@@ -108,19 +111,37 @@ void erts_abort_staging_code_ix(void)
CIX_TRACE("abort");
}
+static int try_seize_cwp(Process* c_p,
+ void (*aux_func)(void *),
+ void *aux_arg);
/*
* Calller _must_ yield if we return 0
*/
int erts_try_seize_code_write_permission(Process* c_p)
{
+ ASSERT(c_p != NULL);
+ return try_seize_cwp(c_p, NULL, NULL);
+}
+
+int erts_try_seize_code_write_permission_aux(void (*aux_func)(void *),
+ void *aux_arg)
+{
+ ASSERT(aux_func != NULL);
+ return try_seize_cwp(NULL, aux_func, aux_arg);
+}
+
+static int try_seize_cwp(Process* c_p,
+ void (*aux_func)(void *),
+ void *aux_arg)
+{
int success;
ASSERT(!erts_thr_progress_is_blocking()); /* to avoid deadlock */
- ASSERT(c_p != NULL);
erts_mtx_lock(&code_write_permission_mtx);
- success = (code_writing_process == NULL);
+ success = !code_writing_seized;
if (success) {
+ code_writing_seized = 1;
code_writing_process = c_p;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 1);
@@ -128,13 +149,22 @@ int erts_try_seize_code_write_permission(Process* c_p)
}
else { /* Already locked */
struct code_write_queue_item* qitem;
- ASSERT(code_writing_process != c_p);
- qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
- qitem->p = c_p;
- erts_proc_inc_refc(c_p);
- qitem->next = code_write_queue;
- code_write_queue = qitem;
- erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
+ if (c_p) {
+ ASSERT(code_writing_process != c_p);
+ qitem->p = c_p;
+ qitem->aux_func = NULL;
+ qitem->aux_arg = NULL;
+ erts_proc_inc_refc(c_p);
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ }
+ else {
+ qitem->p = NULL;
+ qitem->aux_func = aux_func;
+ qitem->aux_arg = aux_arg;
+ }
+ qitem->next = code_write_queue;
+ code_write_queue = qitem;
}
erts_mtx_unlock(&code_write_permission_mtx);
return success;
@@ -146,15 +176,25 @@ void erts_release_code_write_permission(void)
ERTS_LC_ASSERT(erts_has_code_write_permission());
while (code_write_queue != NULL) { /* unleash the entire herd */
struct code_write_queue_item* qitem = code_write_queue;
- erts_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(qitem->p)) {
- erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
- }
- erts_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
- code_write_queue = qitem->next;
- erts_proc_dec_refc(qitem->p);
+ if (qitem->p) {
+ erts_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(qitem->p)) {
+ erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(qitem->p);
+ }
+ else { /* aux work*/
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL);
+ erts_schedule_misc_aux_work((int) esdp->no,
+ qitem->aux_func,
+ qitem->aux_arg);
+ }
+ code_write_queue = qitem->next;
erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
}
+ code_writing_seized = 0;
code_writing_process = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 0);
@@ -165,6 +205,6 @@ void erts_release_code_write_permission(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
int erts_has_code_write_permission(void)
{
- return (code_writing_process != NULL) && erts_tsd_get(has_code_write_permission);
+ return code_writing_seized && erts_tsd_get(has_code_write_permission);
}
#endif
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
index 42976d2301..64a9c95979 100644
--- a/erts/emulator/beam/code_ix.h
+++ b/erts/emulator/beam/code_ix.h
@@ -133,6 +133,15 @@ ErtsCodeIndex erts_staging_code_ix(void);
*/
int erts_try_seize_code_write_permission(struct process* c_p);
+/* Try seize exclusive code write permission for aux work.
+ * System thread progress must not be blocked.
+ * On success return true.
+ * On failure return false and aux work func(arg) will be scheduled when
+ * permission is released. .
+ */
+int erts_try_seize_code_write_permission_aux(void (*func)(void *),
+ void *arg);
+
/* Release code write permission.
* Will resume any suspended waiters.
*/
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 1293ad2d83..3091e322bc 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -149,6 +149,12 @@ static char *erts_dop_to_string(enum dop dop) {
return "PAYLOAD_EXIT2_TT";
if (dop == DOP_PAYLOAD_MONITOR_P_EXIT)
return "PAYLOAD_MONITOR_P_EXIT";
+ if (dop == DOP_SPAWN_REQUEST)
+ return "SPAWN_REQUEST";
+ if (dop == DOP_SPAWN_REQUEST_TT)
+ return "SPAWN_REQUEST_TT";
+ if (dop == DOP_SPAWN_REPLY)
+ return "SPAWN_REPLY";
ASSERT(0);
return "UNKNOWN";
}
@@ -168,6 +174,9 @@ static char *erts_dop_to_string(enum dop dop) {
int erts_is_alive; /* System must be blocked on change */
int erts_dist_buf_busy_limit;
+int erts_dflags_test_remove_hopefull_flags;
+
+Export spawn_request_yield_export;
/* distribution trap functions */
Export* dmonitor_node_trap = NULL;
@@ -262,6 +271,12 @@ static int monitor_connection_down(ErtsMonitor *mon, void *unused, Sint reds)
return ERTS_MON_LNK_FIRE_REDS;
}
+static int dist_pend_spawn_exit_connection_down(ErtsMonitor *mon, void *unused, Sint reds)
+{
+ erts_monitor_release(mon);
+ return 1;
+}
+
static int link_connection_down(ErtsLink *lnk, void *vdist, Sint reds)
{
erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk,
@@ -273,12 +288,14 @@ typedef enum {
ERTS_CML_CLEANUP_STATE_LINKS,
ERTS_CML_CLEANUP_STATE_MONITORS,
ERTS_CML_CLEANUP_STATE_ONAME_MONITORS,
+ ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS,
ERTS_CML_CLEANUP_STATE_SEQUENCES,
ERTS_CML_CLEANUP_STATE_NODE_MONITORS
} ErtsConMonLnkSeqCleanupState;
typedef struct {
ErtsConMonLnkSeqCleanupState state;
+ DistEntry* dep;
ErtsMonLnkDist *dist;
DistSeqNode *seq;
void *yield_state;
@@ -327,6 +344,16 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
if (reds <= 0)
break;
+ ASSERT(!cmlcp->yield_state);
+ cmlcp->state = ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS;
+ case ERTS_CML_CLEANUP_STATE_PEND_SPAWN_EXIT_MONITORS:
+ reds = erts_monitor_tree_foreach_delete_yielding(&dist->dist_pend_spawn_exit,
+ dist_pend_spawn_exit_connection_down,
+ NULL, &cmlcp->yield_state,
+ reds);
+ if (reds <= 0)
+ break;
+
cmlcp->dist = NULL;
erts_mon_link_dist_dec_refc(dist);
@@ -343,11 +370,30 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
case ERTS_CML_CLEANUP_STATE_NODE_MONITORS:
if (cmlcp->trigger_node_monitors) {
+ Process* waiter;
send_nodes_mon_msgs(NULL,
am_nodedown,
cmlcp->nodename,
cmlcp->visability,
cmlcp->reason);
+ erts_de_rwlock(cmlcp->dep);
+ ASSERT(cmlcp->dep->state == ERTS_DE_STATE_IDLE ||
+ cmlcp->dep->state == ERTS_DE_STATE_PENDING);
+ ASSERT(cmlcp->dep->pending_nodedown);
+ waiter = cmlcp->dep->suspended_nodeup;
+ cmlcp->dep->suspended_nodeup = NULL;
+ cmlcp->dep->pending_nodedown = 0;
+ erts_de_rwunlock(cmlcp->dep);
+ erts_deref_dist_entry(cmlcp->dep);
+
+ if (waiter) {
+ erts_proc_lock(waiter, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(waiter)) {
+ erts_resume(waiter, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_proc_unlock(waiter, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(waiter);
+ }
}
erts_cleanup_offheap(&cmlcp->oh);
erts_free(ERTS_ALC_T_CML_CLEANUP, vcmlcp);
@@ -364,7 +410,8 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
}
static void
-schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
+schedule_con_monitor_link_seq_cleanup(DistEntry* dep,
+ ErtsMonLnkDist *dist,
DistSeqNode *seq,
Eterm nodename,
Eterm visability,
@@ -404,7 +451,16 @@ schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
cmlcp->seq = seq;
- cmlcp->trigger_node_monitors = is_value(nodename);
+ if (is_value(nodename)) {
+ ASSERT(dep);
+ cmlcp->trigger_node_monitors = 1;
+ cmlcp->dep = dep;
+ erts_ref_dist_entry(dep);
+ }
+ else {
+ cmlcp->trigger_node_monitors = 0;
+ cmlcp->dep = NULL;
+ }
cmlcp->nodename = nodename;
cmlcp->visability = visability;
if (rsz == 0)
@@ -422,6 +478,255 @@ schedule_con_monitor_link_seq_cleanup(ErtsMonLnkDist *dist,
}
}
+static void
+dist_pend_spawn_exit_save_child_result(Eterm result, Eterm ref, ErtsMonLnkDist *dist)
+{
+ ErtsMonitorData *new_mdp = NULL;
+ Process *proc = NULL;
+ int done = 0;
+
+ while (1) {
+ erts_mtx_lock(&dist->mtx);
+
+ if (!dist->alive)
+ done = !0;
+ else {
+ ErtsMonitor *mon;
+ mon = erts_monitor_tree_lookup(dist->dist_pend_spawn_exit, ref);
+ if (!mon) {
+ if (new_mdp) {
+ /*
+ * Save info so parent can get child pid when handling
+ * links during termination
+ */
+ erts_monitor_tree_insert(&dist->dist_pend_spawn_exit,
+ &new_mdp->target);
+ done = !0;
+ new_mdp = NULL;
+ }
+ }
+ else {
+ ErtsMonitorDataExtended *mdep;
+ /*
+ * The terminating parent is waiting for this signal.
+ * Store childs pid and resume parent if it has
+ * suspended (i.e, mon->other.ptr != NULL)...
+ */
+ proc = mon->other.ptr;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ if (is_atom(result))
+ mdep->md.origin.other.item = result;
+ else {
+ Eterm *hp;
+ ErlOffHeap oh;
+#ifdef DEBUG
+ int i;
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+#endif
+ hp = &(mdep)->heap[0];
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ mdep->md.origin.other.item
+ = copy_struct(result,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ }
+ done = !0;
+ }
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ if (done)
+ break;
+
+ /*
+ * No monitor previously saved by parent; create one
+ * and store child pid...
+ */
+ new_mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ am_undefined, result, NIL);
+ ASSERT(new_mdp->target.other.item == am_undefined);
+ new_mdp->target.other.ptr = NULL;
+
+ ((ErtsMonitorDataExtended *) new_mdp)->dist = dist;
+ erts_mon_link_dist_inc_refc(dist);
+ erts_monitor_release(&new_mdp->origin);
+ }
+
+ if (proc)
+ erts_resume(proc, 0);
+
+ if (new_mdp)
+ erts_monitor_release(&new_mdp->target);
+
+}
+
+int
+erts_dist_pend_spawn_exit_delete(ErtsMonitor *mon)
+{
+ ErtsMonitorDataExtended *mdep;
+ int delete;
+ ErtsMonLnkDist *dist;
+ Uint16 flags;
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon);
+ dist = mdep->dist;
+
+ erts_mtx_lock(&dist->mtx);
+
+ flags = mon->flags;
+ delete = !!dist->alive & !!(flags & ERTS_ML_FLG_IN_TABLE);
+
+ if (delete)
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, mon);
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return delete;
+}
+
+int
+erts_dist_pend_spawn_exit_parent_setup(ErtsMonitor *mon)
+{
+ /*
+ * Return:
+ * 0 -> connection is closing
+ * !0 -> moved target part of monitor to dist_pend_spawn_exit
+ */
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ int res;
+
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdp = (ErtsMonitorData *) erts_monitor_to_data(mon);
+
+ if (!erts_monitor_dist_delete(&mdp->target))
+ return 0;
+
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ while (1) {
+ ErtsMonitor *tmp_mon;
+
+ erts_mtx_lock(&dist->mtx);
+ mdp->target.other.ptr = NULL;
+
+ if (!dist->alive) {
+ res = 0;
+ tmp_mon = NULL;
+ }
+ else {
+ res = !0;
+ tmp_mon = erts_monitor_tree_lookup(dist->dist_pend_spawn_exit,
+ mdp->ref);
+ if (!tmp_mon)
+ erts_monitor_tree_insert(&dist->dist_pend_spawn_exit,
+ &mdp->target);
+ else
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, tmp_mon);
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ if (!tmp_mon) {
+ if (!res)
+ erts_monitor_release(&mdp->target);
+ return res;
+ }
+ else {
+ /*
+ * Child had responded; copy its pid then store
+ * original target end in 'dist_pend_spawn_exit'
+ */
+ ErtsMonitorData *tmp_mdp = erts_monitor_to_data(tmp_mon);
+
+ if (is_atom(tmp_mdp->origin.other.item)) {
+ erts_monitor_release(tmp_mon);
+ erts_monitor_release(&mdp->target);
+ return 0; /* Spawn failed; drop it... */
+ }
+
+ /*
+ * If mdp->origin.other.item != am_pending, the other
+ * end is behaving badly by sending multiple
+ * responses...
+ */
+ if (mdp->origin.other.item == am_pending) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ ErlOffHeap oh;
+ Eterm *hp;
+#ifdef DEBUG
+ int i;
+
+ ASSERT(is_external_pid(tmp_mdp->origin.other.item));
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+#endif
+ hp = &(mdep)->heap[0];
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ mdep->md.origin.other.item
+ = copy_struct(tmp_mdp->origin.other.item,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ }
+ erts_monitor_release(tmp_mon);
+ }
+ }
+}
+
+int
+erts_dist_pend_spawn_exit_parent_wait(Process *c_p,
+ ErtsProcLocks locks,
+ ErtsMonitor *mon)
+{
+ /*
+ * return value of
+ * > 0 --> Child pid can now be found in monitor
+ * 0 --> Connection not alive; drop it
+ * < 0 --> Setup completed; later need to wait for child pid
+ */
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ int res;
+
+ ASSERT(erts_monitor_is_origin(mon));
+
+ mdp = (ErtsMonitorData *) erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ erts_mtx_lock(&dist->mtx);
+
+ if (!dist->alive) {
+ res = 0;
+ }
+ else {
+ ASSERT(&mdp->target ==
+ erts_monitor_tree_lookup(dist->dist_pend_spawn_exit, mdp->ref));
+ if (mdp->origin.other.item != am_pending) {
+ erts_monitor_tree_delete(&dist->dist_pend_spawn_exit, &mdp->target);
+ res = 1;
+ }
+ else {
+ /* We need to suspend wait and wait for the result... */
+ mdp->target.other.ptr = (void *) c_p;
+ erts_suspend(c_p, locks, NULL);
+ res = -1;
+ }
+ }
+
+ erts_mtx_unlock(&dist->mtx);
+
+ return res;
+}
+
/*
** A full node name consists of a "n@h"
**
@@ -644,7 +949,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
ErtsAtomCache *cache;
ErtsProcList *suspendees;
ErtsDistOutputBuf *obuf;
- Uint32 flags;
+ Uint64 flags;
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 1);
erts_de_rwlock(dep);
@@ -674,7 +979,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
dep->sequences = NULL;
nodename = dep->sysname;
- flags = dep->flags;
+ flags = dep->dflags;
erts_atomic_set_nob(&dep->input_handler, (erts_aint_t) NIL);
cache = dep->cache;
@@ -693,10 +998,11 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
dep->send = NULL;
erts_set_dist_entry_not_connected(dep);
-
+ dep->pending_nodedown = 1;
erts_de_rwunlock(dep);
- schedule_con_monitor_link_seq_cleanup(mld,
+ schedule_con_monitor_link_seq_cleanup(dep,
+ mld,
sequences,
nodename,
(flags & DFLAG_PUBLISHED
@@ -711,8 +1017,6 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
delete_cache(cache);
free_de_out_queues(dep, obuf);
- if (dep->transcode_ctx)
- transcode_free_ctx(dep);
}
dec_no_nodes();
@@ -734,6 +1038,8 @@ trap_function(Eterm func, int arity)
*/
static Eterm erts_dflags_record;
+static BIF_RETTYPE spawn_request_yield_3(BIF_ALIST_3);
+
void init_dist(void)
{
init_nodes_monitors();
@@ -759,61 +1065,114 @@ void init_dist(void)
dist_ctrl_put_data_trap = erts_export_put(am_erts_internal,
am_dist_ctrl_put_data,
2);
+ erts_init_trap_export(&spawn_request_yield_export,
+ am_erts_internal, am_spawn_request_yield,
+ 3, spawn_request_yield_3);
{
- Eterm* hp = erts_alloc(ERTS_ALC_T_LITERAL, (1+6)*sizeof(Eterm));
- erts_dflags_record = TUPLE6(hp, am_erts_dflags,
- make_small(DFLAG_DIST_DEFAULT),
- make_small(DFLAG_DIST_MANDATORY),
- make_small(DFLAG_DIST_ADDABLE),
- make_small(DFLAG_DIST_REJECTABLE),
- make_small(DFLAG_DIST_STRICT_ORDER));
- erts_set_literal_tag(&erts_dflags_record, hp, (1+6));
+ Eterm *hp_start, *hp, **hpp = NULL;
+ Uint sz = 0, *szp = &sz;
+ while (1) {
+ erts_dflags_record =
+ erts_bld_tuple(hpp, szp, 6,
+ am_erts_dflags,
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_DEFAULT),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_MANDATORY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_ADDABLE),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_REJECTABLE),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_STRICT_ORDER));
+ if (hpp) {
+ ASSERT(is_value(erts_dflags_record));
+ ASSERT(hp == hp_start + sz);
+ erts_set_literal_tag(&erts_dflags_record, hp_start, sz);
+ break;
+ }
+ hp = hp_start = erts_alloc(ERTS_ALC_T_LITERAL, sz*sizeof(Eterm));
+ hpp = &hp;
+ szp = NULL;
+ }
}
}
-#define ErtsDistOutputBuf2Binary(OB) OB->bin
-
static ERTS_INLINE ErtsDistOutputBuf *
-alloc_dist_obuf(Uint size, Uint headers)
+alloc_dist_obufs(byte **extp, TTBEncodeContext *ctx,
+ Uint data_size, Uint fragments, Uint vlen)
{
- Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers);
+ int ix;
ErtsDistOutputBuf *obuf;
+ char *ptr;
+ Uint iov_sz, obsz;
Binary *bin;
- byte *extp;
- int i;
+ ErlIOVec **feiov;
+ Uint fragment_size;
- bin = erts_bin_drv_alloc(obuf_size + size);
- erts_refc_add(&bin->intern.refc, headers - 1, 1);
+ obsz = sizeof(ErtsDistOutputBuf)*fragments;
+ if (obsz % sizeof(void *) != 0)
+ obsz += sizeof(void *) - (obsz % sizeof(void *));
- obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0];
- extp = (byte *)&bin->orig_bytes[obuf_size];
+ iov_sz = erts_ttb_iov_size(0, vlen, fragments);
+
+ bin = erts_bin_drv_alloc(obsz + iov_sz + data_size);
+ ctx->result_bin = bin;
+ ptr = (char *) &bin->orig_bytes[0];
+
+ obuf = (ErtsDistOutputBuf *) ptr;
+ ptr += obsz;
+
+ if (fragments > 1)
+ fragment_size = ERTS_DIST_FRAGMENT_SIZE;
+ else
+ fragment_size = ~((Uint) 0);
+
+ feiov = erts_ttb_iov_init(ctx, 0, ptr, vlen,
+ fragments, fragment_size);
+ ptr += iov_sz;
- for (i = 0; i < headers; i++) {
- obuf[i].bin = bin;
- obuf[i].extp = extp;
+ erts_refc_add(&bin->intern.refc, fragments - 1, 1);
+
+ for (ix = 0; ix < fragments; ix++) {
+ obuf[ix].bin = bin;
+ obuf[ix].eiov = feiov[ix];
#ifdef DEBUG
- obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
- obuf[i].ext_startp = extp;
- obuf[i].alloc_endp = &extp[size];
- ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
+ obuf[ix].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
#endif
}
+ *extp = (byte *) ptr;
return obuf;
}
static ERTS_INLINE void
-free_dist_obuf(ErtsDistOutputBuf *obuf)
+free_dist_obuf(ErtsDistOutputBuf *obuf, int free_binv)
{
- Binary *bin = ErtsDistOutputBuf2Binary(obuf);
ASSERT(obuf->dbg_pattern == ERTS_DIST_OUTPUT_BUF_DBG_PATTERN);
- erts_bin_release(bin);
+
+ if (free_binv) {
+ int i;
+ int vlen = obuf->eiov->vsize;
+ ErlDrvBinary **binv = obuf->eiov->binv;
+ for (i = 0; i < vlen; i++) {
+ if (binv[i])
+ driver_free_binary(binv[i]);
+ }
+ }
+ erts_bin_release(obuf->bin);
}
static ERTS_INLINE Sint
size_obuf(ErtsDistOutputBuf *obuf)
{
- return sizeof(ErtsDistOutputBuf) + (obuf->ext_endp - obuf->ext_start)
- + (obuf->hdr_endp - obuf->hdrp);
+ Sint vlen = obuf->eiov->vsize;
+ Sint sz;
+#ifdef DEBUG
+ Sint i;
+ for (i = 0, sz = 0; i < vlen; i++)
+ sz += obuf->eiov->iov[i].iov_len;
+ ASSERT(sz == obuf->eiov->size);
+#endif
+ sz = sizeof(ErtsDistOutputBuf) + sizeof(ErlIOVec);
+ sz += obuf->eiov->size;
+ sz += sizeof(SysIOVec)*vlen;
+ sz += sizeof(ErlDrvBinary*)*vlen;
+ return sz;
}
static ErtsDistOutputBuf* clear_de_out_queues(DistEntry* dep)
@@ -853,7 +1212,7 @@ static void free_de_out_queues(DistEntry* dep, ErtsDistOutputBuf *obuf)
fobuf = obuf;
obuf = obuf->next;
obufsize += size_obuf(fobuf);
- free_dist_obuf(fobuf);
+ free_dist_obuf(fobuf, !0);
}
if (obufsize) {
@@ -880,7 +1239,7 @@ int erts_dsend_context_dtor(Binary* ctx_bin)
if (ctx->phase >= ERTS_DSIG_SEND_PHASE_ALLOC && ctx->obuf) {
int i;
for (i = 0; i < ctx->fragments; i++)
- free_dist_obuf(&ctx->obuf[i]);
+ free_dist_obuf(&ctx->obuf[i], !0);
}
if (ctx->deref_dep)
erts_deref_dist_entry(ctx->dep);
@@ -947,14 +1306,14 @@ erts_dsig_send_m_exit(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
{
Eterm ctl, msg;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_MONITOR_P_EXIT (see dsig_send_monitor)
*/
return ERTS_DSIG_SEND_OK;
}
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_MONITOR_P_EXIT),
watched, watcher, ref);
msg = reason;
@@ -976,7 +1335,7 @@ erts_dsig_send_monitor(ErtsDSigSendContext *ctx, Eterm watcher, Eterm watched,
{
Eterm ctl;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_MONITOR_P.
* Just avoid sending it and by doing that reduce this monitor
@@ -1002,7 +1361,7 @@ erts_dsig_send_demonitor(ErtsDSigSendContext *ctx, Eterm watcher,
{
Eterm ctl;
- if (~ctx->flags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
+ if (~ctx->dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)) {
/*
* Receiver does not support DOP_DEMONITOR_P (see dsig_send_monitor)
*/
@@ -1019,7 +1378,7 @@ erts_dsig_send_demonitor(ErtsDSigSendContext *ctx, Eterm watcher,
static int can_send_seqtrace_token(ErtsDSigSendContext* ctx, Eterm token) {
Eterm label;
- if (ctx->flags & DFLAG_BIG_SEQTRACE_LABELS) {
+ if (ctx->dflags & DFLAG_BIG_SEQTRACE_LABELS) {
/* The other end is capable of handling arbitrary seq_trace labels. */
return 1;
}
@@ -1052,7 +1411,7 @@ erts_dsig_send_msg(ErtsDSigSendContext* ctx, Eterm remote, Eterm message)
#endif
if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender);
}
@@ -1080,7 +1439,7 @@ erts_dsig_send_msg(ErtsDSigSendContext* ctx, Eterm remote, Eterm message)
send_token = (token != NIL && can_send_seqtrace_token(ctx, token));
- if (ctx->flags & DFLAG_SEND_SENDER) {
+ if (ctx->dflags & DFLAG_SEND_SENDER) {
dist_op = make_small(send_token ?
DOP_SEND_SENDER_TT :
DOP_SEND_SENDER);
@@ -1127,7 +1486,7 @@ erts_dsig_send_reg_msg(ErtsDSigSendContext* ctx, Eterm remote_name,
#endif
if (have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, full_to, sender);
}
@@ -1182,20 +1541,20 @@ erts_dsig_send_exit_tt(ErtsDSigSendContext *ctx, Eterm local, Eterm remote,
DTRACE_CHARBUF(reason_str, 128);
#endif
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD)
msg = reason;
if (have_seqtrace(token)) {
- seq_trace_update_send(ctx->c_p);
+ seq_trace_update_serial(ctx->c_p);
seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local);
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE4(&ctx->ctl_heap[0],
make_small(DOP_PAYLOAD_EXIT_TT), local, remote, token);
} else
ctl = TUPLE5(&ctx->ctl_heap[0],
make_small(DOP_EXIT_TT), local, remote, token, reason);
} else {
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD)
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD)
ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_EXIT), local, remote);
else
ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_EXIT), local, remote, reason);
@@ -1226,9 +1585,9 @@ erts_dsig_send_exit_tt(ErtsDSigSendContext *ctx, Eterm local, Eterm remote,
int
erts_dsig_send_exit(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm reason)
{
- Eterm ctl, msg = ctx->dep->flags & DFLAG_EXIT_PAYLOAD ? reason : THE_NON_VALUE;
+ Eterm ctl, msg = ctx->dep->dflags & DFLAG_EXIT_PAYLOAD ? reason : THE_NON_VALUE;
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE3(&ctx->ctl_heap[0], make_small(DOP_PAYLOAD_EXIT), local, remote);
msg = reason;
} else {
@@ -1243,7 +1602,7 @@ erts_dsig_send_exit2(ErtsDSigSendContext *ctx, Eterm local, Eterm remote, Eterm
{
Eterm ctl, msg;
- if (ctx->dep->flags & DFLAG_EXIT_PAYLOAD) {
+ if (ctx->dep->dflags & DFLAG_EXIT_PAYLOAD) {
ctl = TUPLE3(&ctx->ctl_heap[0],
make_small(DOP_PAYLOAD_EXIT2), local, remote);
msg = reason;
@@ -1268,6 +1627,65 @@ erts_dsig_send_group_leader(ErtsDSigSendContext *ctx, Eterm leader, Eterm remote
return dsig_send_ctl(ctx, ctl);
}
+static int
+dsig_send_spawn_request(ErtsDSigSendContext *ctx, Eterm ref, Eterm from,
+ Eterm gl, Eterm mfa, Eterm alist, Eterm opts)
+{
+ Process *sender = ctx->c_p;
+ if (!have_seqtrace(SEQ_TRACE_TOKEN(sender))) {
+ ctx->ctl = TUPLE6(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REQUEST),
+ ref, from, gl, mfa, opts);
+ }
+ else {
+ Eterm tmp_heap[8];
+ Eterm node = ctx->dep ? ctx->dep->sysname : ctx->node;
+ Eterm msg;
+ Eterm token;
+ /*
+ * Present this as two messages for the sequence tracing.
+ * All data except the argument list in the first message
+ * and then the argument list as second message (which
+ * willl become an actual message). For more info see
+ * handling of seq-trace token in erl_create_process().
+ */
+
+ seq_trace_update_serial(sender);
+ token = SEQ_TRACE_TOKEN(sender);
+ msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ ref, from, gl, mfa, opts);
+ seq_trace_output(token, msg, SEQ_TRACE_SEND, node, sender);
+
+ seq_trace_update_serial(sender);
+ token = SEQ_TRACE_TOKEN(sender);
+ seq_trace_output(token, alist, SEQ_TRACE_SEND, node, sender);
+
+ ctx->ctl = TUPLE7(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REQUEST_TT),
+ ref, from, gl, mfa, opts, token);
+ }
+ ctx->msg = alist;
+ return erts_dsig_send(ctx);
+}
+
+int
+erts_dsig_send_spawn_reply(ErtsDSigSendContext *ctx,
+ Eterm ref,
+ Eterm to,
+ Eterm flags,
+ Eterm result,
+ Eterm token)
+{
+ if (!have_seqtrace(token)) {
+ ctx->ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REPLY),
+ ref, to, flags, result);
+ }
+ else {
+ ctx->ctl = TUPLE6(&ctx->ctl_heap[0], make_small(DOP_SPAWN_REPLY_TT),
+ ref, to, flags, result, token);
+ }
+ ctx->msg = THE_NON_VALUE;
+ return erts_dsig_send(ctx);
+}
+
#define ERTS_RBT_PREFIX dist_seq
#define ERTS_RBT_T DistSeqNode
#define ERTS_RBT_KEY_T Uint
@@ -1832,7 +2250,7 @@ int erts_net_message(Port *prt,
* the atom '' (empty cookie).
*/
ASSERT((type == DOP_SEND_SENDER || type == DOP_SEND_SENDER_TT)
- ? (is_pid(tuple[2]) && (dep->flags & DFLAG_SEND_SENDER))
+ ? (is_pid(tuple[2]) && (dep->dflags & DFLAG_SEND_SENDER))
: tuple[2] == am_Empty);
#ifdef ERTS_DIST_MSG_DBG
@@ -2054,6 +2472,247 @@ int erts_net_message(Port *prt,
(void) erts_proc_sig_send_group_leader(NULL, to, from, NIL);
break;
+ case DOP_SPAWN_REQUEST_TT: {
+ Eterm tmp_heap[2];
+ ErlSpawnOpts so;
+ int code, opts_error;
+ Eterm pid, error, ref, from, gl, mfa, opts, token, args;
+ /* {DOP_SPAWN_REQUEST_TT, Ref, From, GL, MFA, Opts, Token} */
+
+ if (tuple_arity != 7)
+ goto invalid_message;
+
+ token = tuple[7];
+
+ if (0) {
+
+ case DOP_SPAWN_REQUEST:
+ /* {DOP_SPAWN_REQUEST, Ref, From, GL, MFA, Opts} */
+ if (tuple_arity != 6)
+ goto invalid_message;
+
+ token = NIL;
+ }
+
+ ref = tuple[2];
+ from = tuple[3];
+ gl = tuple[4];
+ mfa = tuple[5];
+ opts = tuple[6];
+
+ if (is_not_external_ref(ref))
+ goto invalid_message;
+ if (is_not_external_pid(from))
+ goto invalid_message;
+ if (is_not_pid(gl))
+ goto invalid_message;
+ if (is_not_tuple_arity(mfa, 3))
+ goto invalid_message;
+ else {
+ Eterm *tp = tuple_val(tuple[5]);
+ if (is_not_atom(tp[1]))
+ goto invalid_message;
+ if (is_not_atom(tp[2]))
+ goto invalid_message;
+ if (is_not_small(tp[3]))
+ goto invalid_message;
+ }
+
+ opts_error = erts_parse_spawn_opts(&so, opts, NULL, 0);
+ if (opts_error) {
+ ErtsDSigSendContext ctx;
+ if (opts_error > 1)
+ goto invalid_message;
+ error = am_badopt;
+ dist_spawn_error:
+ if (ede_hfrag) {
+ erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag));
+ free_message_buffer(ede_hfrag);
+ }
+ if (have_seqtrace(token)) {
+ /*
+ * See erl_create_process() for why we do this
+ * serial trickery...
+ */
+ Eterm tmp_heap[7];
+ Eterm seq_msg;
+ Eterm serial;
+ Uint serial_num;
+ /* Receiver spawn_request... */
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num--;
+ serial = make_small(serial_num);
+ SEQ_TRACE_T_SERIAL(token) = serial;
+ seq_msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ ref, from, gl, mfa, opts);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ erts_this_dist_entry->sysname, NULL);
+ SEQ_TRACE_T_LASTCNT(token) = serial;
+ /* Send spawn_reply... */
+ erts_seq_trace_update_node_token(token);
+ seq_msg = TUPLE4(&tmp_heap[0],
+ am_spawn_reply, ref, am_error, error);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND, from, NULL);
+ }
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_spawn_reply(&ctx,
+ tuple[2],
+ tuple[3],
+ make_small(0),
+ error,
+ token);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ break;
+ }
+
+ so.mref = ref;
+ so.tag = am_spawn_reply;
+ so.parent_id = from;
+ so.group_leader = gl;
+ so.mfa = mfa;
+ so.dist_entry = dep;
+ so.edep = edep;
+ so.ede_hfrag = ede_hfrag;
+ so.token = token;
+ so.opts = opts;
+
+ args = CONS(&tmp_heap[0], mfa, NIL);
+ pid = erl_create_process(NULL,
+ am_erts_internal,
+ am_dist_spawn_init,
+ args,
+ &so);
+ if (is_non_value(pid)) {
+ if (so.error_code == SYSTEM_LIMIT)
+ error = am_system_limit;
+ else
+ goto invalid_message; /* should not happen */
+ goto dist_spawn_error;
+ }
+
+ break;
+ }
+
+ case DOP_SPAWN_REPLY_TT: {
+ ErtsLinkData *ldp;
+ ErtsLink *lnk;
+ int monitor;
+ Eterm ref, result, flags_term, parent, token;
+ Uint flags;
+
+ /* {DOP_SPAWN_REPLY_TT, Ref, To, Flags, From, Token} */
+ if (tuple_arity != 6)
+ goto invalid_message;
+
+ token = tuple[6];
+
+ if (0) {
+ case DOP_SPAWN_REPLY:
+
+ /* {DOP_SPAWN_REPLY, Ref, To, Flags, From} */
+ if (tuple_arity != 5)
+ goto invalid_message;
+
+ token = NIL;
+ }
+
+ ldp = NULL;
+ lnk = NULL;
+ monitor = 0;
+
+ ref = tuple[2];
+ parent = tuple[3];
+ flags_term = tuple[4];
+ result = tuple[5];
+
+ if (is_not_internal_pid(parent)) {
+ if (is_external_pid(parent)) {
+ DistEntry *dep = external_pid_dist_entry(parent);
+ if (dep == erts_this_dist_entry)
+ break; /* Old incarnation of this node... */
+ }
+ goto invalid_message;
+ }
+
+ if (is_not_internal_ref(ref)) {
+ if (is_external_ref(ref)) {
+ DistEntry *dep = external_ref_dist_entry(ref);
+ if (dep == erts_this_dist_entry)
+ break; /* Old incarnation of this node... */
+ }
+ goto invalid_message;
+ }
+
+ if (is_not_small(flags_term))
+ goto invalid_message;
+
+ flags = unsigned_val(flags_term);
+ if (flags >= (1 << 27))
+ goto invalid_message;
+
+ if (is_not_external_pid(result) && is_not_atom(result))
+ goto invalid_message;
+
+ if (is_external_pid(result)) {
+
+ monitor = !!(flags & ERTS_DIST_SPAWN_FLAG_MONITOR);
+
+ if (flags & ERTS_DIST_SPAWN_FLAG_LINK) {
+ /* Successful spawn-link... */
+ int code;
+
+ ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ result, parent);
+ ASSERT(ldp->a.other.item == parent);
+ ASSERT(eq(ldp->b.other.item, result));
+ code = erts_link_dist_insert(&ldp->a, dep->mld);
+ ASSERT(code); (void)code;
+
+ lnk = &ldp->b;
+ }
+ }
+
+ if (!erts_proc_sig_send_dist_spawn_reply(dep->sysname, ref,
+ parent, lnk, result,
+ token)) {
+ ErtsDSigSendContext ctx;
+ int code;
+
+ if (monitor) {
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ code = erts_dsig_send_demonitor(&ctx, parent,
+ result, ref);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ }
+
+ if (lnk) {
+
+ code = erts_link_dist_delete(&ldp->a);
+ ASSERT(code);
+ erts_link_release_both(ldp);
+ }
+
+ if (flags & ERTS_DIST_SPAWN_FLAG_LINK) {
+ /*
+ * Save info so the terminating parent can send us
+ * an exit signal with the correct exit reason...
+ */
+ dist_pend_spawn_exit_save_child_result(result,
+ ref,
+ dep->mld);
+ }
+ }
+
+ break;
+ }
+
default:
goto invalid_message;
}
@@ -2201,7 +2860,7 @@ retry:
ctx->connection_id = dep->connection_id;
ctx->no_suspend = no_suspend;
ctx->no_trap = no_trap;
- ctx->flags = dep->flags;
+ ctx->dflags = dep->dflags;
ctx->return_term = am_true;
ctx->phase = ERTS_DSIG_SEND_PHASE_INIT;
ctx->from = proc ? proc->common.id : am_undefined;
@@ -2255,8 +2914,6 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
while (1) {
switch (ctx->phase) {
case ERTS_DSIG_SEND_PHASE_INIT:
- ctx->flags = ctx->flags;
- ctx->c_p = ctx->c_p;
if (!ctx->c_p) {
ctx->no_trap = 1;
@@ -2270,13 +2927,11 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
if (!erts_is_alive)
return ERTS_DSIG_SEND_OK;
- if (ctx->flags & DFLAG_DIST_HDR_ATOM_CACHE) {
+ if (ctx->dflags & DFLAG_DIST_HDR_ATOM_CACHE) {
ctx->acmp = erts_get_atom_cache_map(ctx->c_p);
- ctx->max_finalize_prepend = 0;
}
else {
ctx->acmp = NULL;
- ctx->max_finalize_prepend = 3;
}
#ifdef ERTS_DIST_MSG_DBG
@@ -2287,176 +2942,212 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
erts_fprintf(dbg_file, " MSG: %.160T\n", ctx->msg);
#endif
- ctx->data_size = ctx->max_finalize_prepend;
+ ctx->data_size = 0;
erts_reset_atom_cache_map(ctx->acmp);
- switch (erts_encode_dist_ext_size(ctx->ctl, ctx->flags,
- ctx->acmp, &ctx->data_size)) {
- case ERTS_EXT_SZ_OK:
- break;
- case ERTS_EXT_SZ_SYSTEM_LIMIT:
- retval = ERTS_DSIG_SEND_TOO_LRG;
- goto done;
- case ERTS_EXT_SZ_YIELD:
- ERTS_INTERNAL_ERROR("Unexpected yield result");
- break;
+ ERTS_INIT_TTBSizeContext(&ctx->u.sc, ctx->dflags);
+
+ while (1) {
+ ErtsExtSzRes sz_res;
+ Sint reds = CONTEXT_REDS;
+ sz_res = erts_encode_dist_ext_size(ctx->ctl,
+ ctx->acmp,
+ &ctx->u.sc,
+ &ctx->data_size,
+ &reds,
+ &ctx->vlen,
+ &ctx->fragments);
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (sz_res == ERTS_EXT_SZ_OK)
+ break;
+ if (sz_res == ERTS_EXT_SZ_SYSTEM_LIMIT) {
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ }
}
+
if (is_non_value(ctx->msg)) {
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
break;
}
- ctx->u.sc.wstack.wstart = NULL;
- ctx->u.sc.flags = ctx->flags;
- ctx->u.sc.level = 0;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE;
case ERTS_DSIG_SEND_PHASE_MSG_SIZE: {
- ErtsExtSzRes sz_res;
- sz_res = (!ctx->no_trap
- ? erts_encode_dist_ext_size_ctx(ctx->msg,
- ctx,
- &ctx->data_size)
- : erts_encode_dist_ext_size(ctx->msg,
- ctx->flags,
- ctx->acmp,
- &ctx->data_size));
- switch (sz_res) {
- case ERTS_EXT_SZ_OK:
- break;
- case ERTS_EXT_SZ_SYSTEM_LIMIT:
- retval = ERTS_DSIG_SEND_TOO_LRG;
- goto done;
- case ERTS_EXT_SZ_YIELD:
- if (ctx->no_trap)
- ERTS_INTERNAL_ERROR("Unexpected yield result");
+ Sint reds, *redsp;
+ if (!ctx->no_trap)
+ redsp = &ctx->reds;
+ else {
+ reds = CONTEXT_REDS;
+ redsp = &reds;
+ }
+ while (1) {
+ ErtsExtSzRes sz_res;
+ sz_res = erts_encode_dist_ext_size(ctx->msg,
+ ctx->acmp,
+ &ctx->u.sc,
+ &ctx->data_size,
+ redsp,
+ &ctx->vlen,
+ &ctx->fragments);
+ if (ctx->no_trap) {
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (sz_res == ERTS_EXT_SZ_YIELD) {
+ reds = CONTEXT_REDS;
+ continue;
+ }
+ }
+ if (sz_res == ERTS_EXT_SZ_OK)
+ break;
+ if (sz_res == ERTS_EXT_SZ_SYSTEM_LIMIT) {
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ }
+ ASSERT(sz_res == ERTS_EXT_SZ_YIELD);
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
}
- case ERTS_DSIG_SEND_PHASE_ALLOC:
- erts_finalize_atom_cache_map(ctx->acmp, ctx->flags);
-
- if (ctx->flags & DFLAG_FRAGMENTS && is_value(ctx->msg) && is_not_immed(ctx->msg)) {
- /* Calculate the max number of fragments that are needed */
- ASSERT(is_pid(ctx->from) &&
- "from has to be a pid because it is used as sequence id");
- ctx->fragments = ctx->data_size / ERTS_DIST_FRAGMENT_SIZE + 1;
- } else
- ctx->fragments = 1;
-
- ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(ctx->acmp, ctx->fragments);
-
- ctx->obuf = alloc_dist_obuf(
- ctx->dhdr_ext_size + ctx->data_size +
- (ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE,
- ctx->fragments);
- ctx->obuf->ext_start = &ctx->obuf->extp[0];
- ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend
- + ctx->dhdr_ext_size;
-
+ case ERTS_DSIG_SEND_PHASE_ALLOC: {
+
+ erts_finalize_atom_cache_map(ctx->acmp, ctx->dflags);
+
+ ERTS_INIT_TTBEncodeContext(&ctx->u.ec, ctx->dflags);
+ ctx->dhdr_ext_size = erts_encode_ext_dist_header_size(&ctx->u.ec,
+ ctx->acmp,
+ ctx->fragments);
+ ctx->obuf = alloc_dist_obufs(&ctx->extp,
+ &ctx->u.ec,
+ ctx->dhdr_ext_size + ctx->data_size
+ + ((ctx->fragments - 1)
+ * ERTS_DIST_FRAGMENT_HEADER_SIZE),
+ ctx->fragments,
+ ctx->vlen);
+ ctx->alloced_fragments = ctx->fragments;
/* Encode internal version of dist header */
- ctx->obuf->extp = erts_encode_ext_dist_header_setup(
- ctx->obuf->ext_endp, ctx->acmp, ctx->fragments, ctx->from);
- /* Encode control message */
- erts_encode_dist_ext(ctx->ctl, &ctx->obuf->ext_endp, ctx->flags, ctx->acmp, NULL, NULL);
-
- ctx->obuf->hdrp = NULL;
- ctx->obuf->hdr_endp = NULL;
+ ctx->dhdrp = ctx->extp;
+ ctx->extp += ctx->dhdr_ext_size;
+ ctx->dhdrp = erts_encode_ext_dist_header_setup(&ctx->u.ec,
+ ctx->extp,
+ ctx->acmp,
+ ctx->fragments,
+ ctx->from);
+ ctx->dhdr_ext_size = ctx->extp - ctx->dhdrp;
+ while (1) {
+ Sint reds = CONTEXT_REDS;
+ /* Encode control message */
+ int res = erts_encode_dist_ext(ctx->ctl, &ctx->extp,
+ ctx->dflags, ctx->acmp,
+ &ctx->u.ec, &ctx->fragments,
+ &reds);
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (res == 0)
+ break;
+ }
if (is_non_value(ctx->msg)) {
- ctx->obuf->msg_start = NULL;
ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
break;
}
- ctx->u.ec.flags = ctx->flags;
- ctx->u.ec.hopefull_flags = 0;
- ctx->u.ec.level = 0;
- ctx->u.ec.wstack.wstart = NULL;
- ctx->obuf->msg_start = ctx->obuf->ext_endp;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_ENCODE;
- case ERTS_DSIG_SEND_PHASE_MSG_ENCODE:
- if (!ctx->no_trap) {
- if (erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
- ctx->acmp, &ctx->u.ec, &ctx->reds)) {
+ }
+ case ERTS_DSIG_SEND_PHASE_MSG_ENCODE: {
+ Sint reds, *redsp;
+ if (!ctx->no_trap)
+ redsp = &ctx->reds;
+ else {
+ reds = CONTEXT_REDS;
+ redsp = &reds;
+ }
+ while (1) {
+ int res = erts_encode_dist_ext(ctx->msg, &ctx->extp,
+ ctx->dflags, ctx->acmp,
+ &ctx->u.ec,
+ &ctx->fragments,
+ redsp);
+ if (!ctx->no_trap) {
+ if (res == 0)
+ break;
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
- } else {
- erts_encode_dist_ext(ctx->msg, &ctx->obuf->ext_endp, ctx->flags,
- ctx->acmp, NULL, NULL);
+ else {
+ ctx->reds -= CONTEXT_REDS - reds;
+ if (res == 0)
+ break;
+ reds = CONTEXT_REDS;
+ }
}
ctx->phase = ERTS_DSIG_SEND_PHASE_FIN;
+ }
case ERTS_DSIG_SEND_PHASE_FIN: {
-
- ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp);
- ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend);
- ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size);
-
- ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
-
- ctx->obuf->hopefull_flags = ctx->u.ec.hopefull_flags;
-
- if (ctx->fragments > 1) {
- int fin_fragments;
- int i;
- byte *msg = ctx->obuf->msg_start,
- *msg_end = ctx->obuf->ext_endp,
- *hdrp = msg_end;
-
- ASSERT((ctx->obuf->hopefull_flags & ctx->flags) == ctx->obuf->hopefull_flags);
- ASSERT(get_int64(ctx->obuf->extp + 1 + 1 + 8) == ctx->fragments);
-
- /* Now that encoding is done we know how large the term will
- be so we adjust the number of fragments to send. Note that
- this can mean that only 1 fragment is sent. */
- fin_fragments = (ctx->obuf->ext_endp - ctx->obuf->msg_start + ERTS_DIST_FRAGMENT_SIZE-1) /
- ERTS_DIST_FRAGMENT_SIZE - 1;
-
+ Uint fid = ctx->fragments;
+ ErtsDistOutputBuf *obuf = ctx->obuf;
+ ErlIOVec *eiov;
+ Sint fix;
+
+ ASSERT(fid >= 1);
+ ASSERT(ctx->alloced_fragments >= ctx->fragments);
+
+ eiov = obuf[0].eiov;
+ ASSERT(eiov);
+ ASSERT(eiov->vsize >= 3);
+ ASSERT(!eiov->iov[0].iov_base);
+ ASSERT(!eiov->iov[0].iov_len);
+ ASSERT(!eiov->binv[0]);
+ ASSERT(!eiov->iov[1].iov_base);
+ ASSERT(!eiov->iov[1].iov_len);
+ ASSERT(!eiov->binv[1]);
+
+ if (ctx->alloced_fragments > 1) {
+ ASSERT(get_int64(ctx->dhdrp + 1 + 1 + 8) == ctx->alloced_fragments);
/* Update the frag_id in the DIST_FRAG_HEADER */
- put_int64(fin_fragments+1, ctx->obuf->extp + 1 + 1 + 8);
-
- if (fin_fragments > 0)
- msg += ERTS_DIST_FRAGMENT_SIZE;
- else
- msg = msg_end;
- ctx->obuf->next = &ctx->obuf[1];
- ctx->obuf->ext_endp = msg;
-
- /* Loop through all fragments, updating the output buffers
- to be correct and also writing the DIST_FRAG_CONT header. */
- for (i = 1; i < fin_fragments + 1; i++) {
- ctx->obuf[i].hopefull_flags = 0;
- ctx->obuf[i].extp = msg;
- ctx->obuf[i].ext_start = msg;
- if (msg + ERTS_DIST_FRAGMENT_SIZE > msg_end)
- ctx->obuf[i].ext_endp = msg_end;
- else {
- msg += ERTS_DIST_FRAGMENT_SIZE;
- ctx->obuf[i].ext_endp = msg;
- }
- ASSERT(ctx->obuf[i].ext_endp > ctx->obuf[i].extp);
- ctx->obuf[i].hdrp = erts_encode_ext_dist_header_fragment(
- &hdrp, fin_fragments - i + 1, ctx->from);
- ctx->obuf[i].hdr_endp = hdrp;
- ctx->obuf[i].next = &ctx->obuf[i+1];
- }
- /* If the initial fragment calculation was incorrect we free the
- remaining output buffers. */
- for (; i < ctx->fragments; i++) {
- free_dist_obuf(&ctx->obuf[i]);
- }
- if (!ctx->no_trap && !ctx->no_suspend)
- ctx->reds -= ctx->fragments;
- ctx->fragments = fin_fragments + 1;
+ put_int64(ctx->fragments, ctx->dhdrp + 1 + 1 + 8);
+ }
+
+ eiov->size += ctx->dhdr_ext_size;
+ eiov->iov[1].iov_base = ctx->dhdrp;
+ eiov->iov[1].iov_len = ctx->dhdr_ext_size;
+ erts_refc_inc(&obuf[0].bin->intern.refc, 2);
+ eiov->binv[1] = Binary2ErlDrvBinary(obuf[0].bin);
+ obuf[0].next = &obuf[1];
+
+ for (fix = 1; fix < ctx->fragments; fix++) {
+ byte *hdr = erts_encode_ext_dist_header_fragment(&ctx->extp,
+ --fid, ctx->from);
+ Uint sz = ctx->extp - hdr;
+
+ eiov = obuf[fix].eiov;
+ ASSERT(eiov);
+ ASSERT(eiov->vsize >= 3);
+ ASSERT(!eiov->iov[0].iov_base);
+ ASSERT(!eiov->iov[0].iov_len);
+ ASSERT(!eiov->binv[0]);
+ ASSERT(!eiov->iov[1].iov_base);
+ ASSERT(!eiov->iov[1].iov_len);
+ ASSERT(!eiov->binv[1]);
+
+ eiov->size += sz;
+ eiov->iov[1].iov_base = hdr;
+ eiov->iov[1].iov_len = sz;
+ erts_refc_inc(&obuf[fix].bin->intern.refc, 2);
+ eiov->binv[1] = Binary2ErlDrvBinary(obuf[fix].bin);
+ obuf[fix].next = &obuf[fix+1];
+ }
+ obuf[fix-1].next = NULL;
+ ASSERT(fid == 1);
+ /* If the initial fragment calculation was incorrect we free the
+ remaining output buffers. */
+ for (; fix < ctx->alloced_fragments; fix++) {
+ free_dist_obuf(&ctx->obuf[fix], 0);
}
ctx->phase = ERTS_DSIG_SEND_PHASE_SEND;
- if (ctx->reds <= 0) {
+ if (ctx->reds <= 0 && !ctx->no_trap) {
retval = ERTS_DSIG_SEND_CONTINUE;
goto done;
}
@@ -2478,7 +3169,7 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
/* Not the same connection as when we started; drop message... */
erts_de_runlock(dep);
for (i = 0; i < ctx->fragments; i++)
- free_dist_obuf(&ctx->obuf[i]);
+ free_dist_obuf(&ctx->obuf[i], !0);
ctx->fragments = 0;
}
else {
@@ -2538,13 +3229,9 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
erts_mtx_lock(&dep->qlock);
}
- if (fragments > 1) {
- if (!ctx->obuf->hdrp) {
- ASSERT(get_int64(ctx->obuf->extp + 10) == ctx->fragments);
- } else {
- ASSERT(get_int64(ctx->obuf->hdrp + 10) == ctx->fragments);
- }
- }
+ ASSERT(fragments < 2
+ || (get_int64(ctx->obuf->eiov->iov[1].iov_base + 10)
+ == ctx->fragments));
if (fragments) {
ctx->obuf[fragments-1].next = NULL;
@@ -2666,8 +3353,19 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
bufp = NULL;
}
else {
- size = obuf->ext_endp - obuf->extp;
- bufp = (char*) obuf->extp;
+ char *ptr;
+ Sint i, vlen = obuf->eiov->vsize;
+ SysIOVec *iov = obuf->eiov->iov;
+ size = obuf->eiov->size;
+ bufp = ptr = erts_alloc(ERTS_ALC_T_TMP, size);
+ for (i = 0; i < vlen; i++) {
+ Uint len = iov[i].iov_len;
+ if (len) {
+ sys_memcpy((void *) ptr, (void *) iov[i].iov_base, len);
+ ptr += len;
+ }
+ }
+ ASSERT(size == ptr - bufp);
}
#ifdef USE_VM_PROBES
@@ -2689,6 +3387,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
fpe_was_unmasked = erts_block_fpe();
(*prt->drv_ptr->output)((ErlDrvData) prt->drv_data, bufp, size);
erts_unblock_fpe(fpe_was_unmasked);
+ erts_free(ERTS_ALC_T_TMP, bufp);
return size;
}
@@ -2696,59 +3395,53 @@ static Uint
dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
{
int fpe_was_unmasked;
- ErlDrvSizeT size = 0;
- SysIOVec iov[3];
- ErlDrvBinary* bv[3];
- ErlIOVec eiov;
+ SysIOVec iov[1];
+ Uint size;
+ ErlDrvBinary* bv[1];
+ ErlIOVec eiov, *eiovp;
ERTS_CHK_NO_PROC_LOCKS;
ERTS_LC_ASSERT(erts_lc_is_port_locked(prt));
- iov[0].iov_base = NULL;
- iov[0].iov_len = 0;
- bv[0] = NULL;
-
- if (!obuf) {
- size = 0;
+ if (obuf)
+ eiovp = obuf->eiov;
+ else {
+ iov[0].iov_base = NULL;
+ iov[0].iov_len = 0;
+ bv[0] = NULL;
+ eiov.size = 0;
eiov.vsize = 1;
+ eiov.iov = iov;
+ eiov.binv = bv;
+ eiovp = &eiov;
}
- else {
- int i = 1;
- eiov.vsize = 2;
-
- if (obuf->hdrp) {
- eiov.vsize = 3;
- iov[i].iov_base = obuf->hdrp;
- iov[i].iov_len = obuf->hdr_endp - obuf->hdrp;
- size += iov[i].iov_len;
- bv[i] = Binary2ErlDrvBinary(ErtsDistOutputBuf2Binary(obuf));
-#ifdef ERTS_RAW_DIST_MSG_DBG
- erts_fprintf(dbg_file, "SEND: ");
- bw(iov[i].iov_base, iov[i].iov_len);
-#endif
- i++;
- }
+#ifdef DEBUG
+ {
+ Uint sz;
+ Sint i;
+ for (i = 0, sz = 0; i < eiovp->vsize; i++)
+ sz += eiovp->iov[i].iov_len;
+ ASSERT(sz == eiovp->size);
+ }
+#endif
- iov[i].iov_base = obuf->extp;
- iov[i].iov_len = obuf->ext_endp - obuf->extp;
#ifdef ERTS_RAW_DIST_MSG_DBG
- erts_fprintf(dbg_file, "SEND: ");
- bw(iov[i].iov_base, iov[i].iov_len);
-#endif
- size += iov[i].iov_len;
- bv[i] = Binary2ErlDrvBinary(ErtsDistOutputBuf2Binary(obuf));
+ {
+ Sint i;
+ erts_fprintf(dbg_file, "SEND: ");
+ for (i = 0; i < eiovp->vsize; i++) {
+ if (eiovp->iov[i].iov_len)
+ bw(eiovp->iov[i].iov_base, eiovp->iov[i].iov_len);
+ }
}
+#endif
- eiov.size = size;
- eiov.iov = iov;
- eiov.binv = bv;
-
- if (size > (Uint) INT_MAX)
+ if (eiovp->size > (Uint) INT_MAX)
erts_exit(ERTS_DUMP_EXIT,
"Absurdly large distribution output data buffer "
"(%beu bytes) passed.\n",
- size);
+ eiovp->size);
ASSERT(prt->drv_ptr->outputv);
@@ -2768,9 +3461,14 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
#endif
prt->caller = NIL;
fpe_was_unmasked = erts_block_fpe();
- (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, &eiov);
+ (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, eiovp);
erts_unblock_fpe(fpe_was_unmasked);
+ size = (Uint) eiovp->size;
+ /* Remove header used by driver... */
+ eiovp->size -= eiovp->iov[0].iov_len;
+ eiovp->iov[0].iov_base = NULL;
+ eiovp->iov[0].iov_len = 0;
return size;
}
@@ -2796,7 +3494,7 @@ erts_dist_command(Port *prt, int initial_reds)
{
Sint reds = initial_reds - ERTS_PORT_REDS_DIST_CMD_START;
enum dist_entry_state state;
- Uint32 flags;
+ Uint64 flags;
Sint qsize, obufsize = 0;
ErtsDistOutputQueue oq, foq;
DistEntry *dep = (DistEntry*) erts_prtsd_get(prt, ERTS_PRTSD_DIST_ENTRY);
@@ -2809,7 +3507,7 @@ erts_dist_command(Port *prt, int initial_reds)
erts_atomic_set_mb(&dep->dist_cmd_scheduled, 0);
erts_de_rlock(dep);
- flags = dep->flags;
+ flags = dep->dflags;
state = dep->state;
send = dep->send;
erts_de_runlock(dep);
@@ -2871,14 +3569,14 @@ erts_dist_command(Port *prt, int initial_reds)
do {
Uint size;
ErtsDistOutputBuf *fob;
+ obufsize += size_obuf(foq.first);
size = (*send)(prt, foq.first);
erts_atomic64_inc_nob(&dep->out);
esdp->io.out += (Uint64) size;
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = foq.first;
- obufsize += size_obuf(fob);
foq.first = foq.first->next;
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
preempt = reds < 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if (sched_flags & ERTS_PTS_FLG_BUSY_PORT)
@@ -2902,7 +3600,7 @@ erts_dist_command(Port *prt, int initial_reds)
reds = erts_encode_ext_dist_header_finalize(ob, dep, flags, reds);
obufsize -= size_obuf(ob);
if (reds < 0)
- break;
+ break; /* finalize needs to be restarted... */
last_finalized = ob;
ob = ob->next;
} while (ob);
@@ -2938,24 +3636,23 @@ erts_dist_command(Port *prt, int initial_reds)
int preempt = 0;
while (oq.first && !preempt) {
ErtsDistOutputBuf *fob;
- Uint size;
+ Uint size, obsz;
obufsize += size_obuf(oq.first);
reds = erts_encode_ext_dist_header_finalize(oq.first, dep, flags, reds);
- obufsize -= size_obuf(oq.first);
- if (reds < 0) {
+ obsz = size_obuf(oq.first);
+ obufsize -= obsz;
+ if (reds < 0) { /* finalize needs to be restarted... */
preempt = 1;
break;
}
- ASSERT(oq.first->bin->orig_bytes <= (char*)oq.first->extp
- && oq.first->extp <= oq.first->ext_endp);
size = (*send)(prt, oq.first);
erts_atomic64_inc_nob(&dep->out);
esdp->io.out += (Uint64) size;
reds -= ERTS_PORT_REDS_DIST_CMD_DATA(size);
fob = oq.first;
- obufsize += size_obuf(fob);
+ obufsize += obsz;
oq.first = oq.first->next;
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
sched_flags = erts_atomic32_read_nob(&prt->sched.flags);
preempt = reds <= 0 || (sched_flags & ERTS_PTS_FLG_EXIT);
if ((sched_flags & ERTS_PTS_FLG_BUSY_PORT) && oq.first && !preempt)
@@ -3008,7 +3705,6 @@ erts_dist_command(Port *prt, int initial_reds)
done:
if (obufsize != 0) {
- ASSERT(obufsize > 0);
erts_mtx_lock(&dep->qlock);
#ifdef DEBUG
qsize = (Sint) erts_atomic_add_read_nob(&dep->qsize,
@@ -3060,7 +3756,7 @@ erts_dist_command(Port *prt, int initial_reds)
ErtsDistOutputBuf *fob = oq.first;
oq.first = oq.first->next;
obufsize += size_obuf(fob);
- free_dist_obuf(fob);
+ free_dist_obuf(fob, !0);
}
foq.first = NULL;
@@ -3374,13 +4070,17 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(BIF_P);
const Sint initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
- Sint reds = initial_reds, obufsize = 0;
+ Sint reds = initial_reds, obufsize = 0, ix, vlen;
ErtsDistOutputBuf *obuf;
Eterm *hp, res;
- ProcBin *pb;
erts_aint_t qsize;
Uint32 conn_id, get_size;
- Uint hsz = 0, bin_sz;
+ Uint hsz = 0, data_sz;
+ SysIOVec *iov;
+ ErlDrvBinary **binv;
+#ifdef DEBUG
+ Eterm *hendp;
+#endif
if (!dep)
BIF_ERROR(BIF_P, EXC_NOTSUP);
@@ -3412,7 +4112,6 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
{
if (!dep->tmp_out_queue.first) {
ASSERT(!dep->tmp_out_queue.last);
- ASSERT(!dep->transcode_ctx);
qsize = erts_atomic_read_acqb(&dep->qsize);
if (qsize > 0) {
erts_mtx_lock(&dep->qlock);
@@ -3433,13 +4132,13 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
obuf = dep->tmp_out_queue.first;
obufsize += size_obuf(obuf);
- reds = erts_encode_ext_dist_header_finalize(obuf, dep, dep->flags, reds);
+ reds = erts_encode_ext_dist_header_finalize(obuf, dep, dep->dflags, reds);
obufsize -= size_obuf(obuf);
- if (reds < 0) {
+ if (reds < 0) { /* finalize needs to be restarted... */
erts_de_runlock(dep);
if (obufsize)
erts_atomic_add_nob(&dep->qsize, (erts_aint_t) -obufsize);
- ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_dist_ctrl_get_data_1],
BIF_P, BIF_ARG_1);
}
@@ -3452,53 +4151,70 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
erts_de_runlock(dep);
- bin_sz = obuf->ext_endp - obuf->extp + obuf->hdr_endp - obuf->hdrp;
+ vlen = obuf->eiov->vsize;
+ data_sz = obuf->eiov->size;
+ iov = obuf->eiov->iov;
+ binv = obuf->eiov->binv;
+
+#ifdef DEBUG
+ {
+ Uint dbg_sz;
+ for (ix = 0, dbg_sz = 0; ix < vlen; ix++)
+ dbg_sz += iov[ix].iov_len;
+ ASSERT(data_sz == dbg_sz);
+ }
+#endif
+
+ ASSERT(vlen >= 1);
+ ASSERT(iov[0].iov_len == 0);
+ ASSERT(!binv[0]);
+
+ hsz = 2 /* cons */ + PROC_BIN_SIZE;
+ hsz *= vlen - 1;
get_size = dep->opts & ERTS_DIST_CTRL_OPT_GET_SIZE;
if (get_size) {
hsz += 3; /* 2 tuple */
- if (!IS_USMALL(0, bin_sz))
+ if (!IS_USMALL(0, data_sz))
hsz += BIG_UINT_HEAP_SIZE;
}
- if (!obuf->hdrp) {
- hp = HAlloc(BIF_P, PROC_BIN_SIZE + hsz);
- pb = (ProcBin *) (char *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->ext_endp - obuf->extp;
- pb->next = MSO(BIF_P).first;
- MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- pb->bytes = (byte*) obuf->extp;
- pb->flags = 0;
- res = make_binary(pb);
- hp += PROC_BIN_SIZE;
- } else {
- hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz);
- pb = (ProcBin *) (char *) hp;
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->ext_endp - obuf->extp;
- pb->next = MSO(BIF_P).first;
- MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- pb->bytes = (byte*) obuf->extp;
- pb->flags = 0;
- hp += PROC_BIN_SIZE;
+ hp = HAlloc(BIF_P, hsz);
+#ifdef DEBUG
+ hendp = hp + hsz;
+#endif
- res = CONS(hp, make_binary(pb), NIL);
- hp += 2;
+ res = NIL;
+
+ for (ix = vlen - 1; ix > 0; ix--) {
+ Binary *bin;
+ ProcBin *pb;
+ Eterm bin_term;
+ ASSERT(binv[ix]);
+
+ /*
+ * We intentionally avoid using sub binaries
+ * since the GC might convert those to heap
+ * binaries and by this ruin the nice preparation
+ * for usage of this data as I/O vector in
+ * nifs/drivers.
+ */
+
+ bin = ErlDrvBinary2Binary(binv[ix]);
pb = (ProcBin *) (char *) hp;
+ hp += PROC_BIN_SIZE;
pb->thing_word = HEADER_PROC_BIN;
- pb->size = obuf->hdr_endp - obuf->hdrp;
+ pb->size = (Uint) iov[ix].iov_len;
pb->next = MSO(BIF_P).first;
MSO(BIF_P).first = (struct erl_off_heap_header*) pb;
- pb->val = ErtsDistOutputBuf2Binary(obuf);
- erts_refc_inc(&pb->val->intern.refc, 1);
- pb->bytes = (byte*) obuf->hdrp;
+ pb->val = bin;
+ pb->bytes = (byte*) iov[ix].iov_base;
pb->flags = 0;
- hp += PROC_BIN_SIZE;
- res = CONS(hp, make_binary(pb), res);
+ OH_OVERHEAD(&MSO(BIF_P), pb->size / sizeof(Eterm));
+ bin_term = make_binary(pb);
+
+ res = CONS(hp, bin_term, res);
hp += 2;
}
@@ -3522,15 +4238,20 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
if (get_size) {
Eterm sz_term;
- if (IS_USMALL(0, bin_sz))
- sz_term = make_small(bin_sz);
+ if (IS_USMALL(0, data_sz))
+ sz_term = make_small(data_sz);
else {
- sz_term = uint_to_big(bin_sz, hp);
+ sz_term = uint_to_big(data_sz, hp);
hp += BIG_UINT_HEAP_SIZE;
}
res = TUPLE2(hp, sz_term, res);
+ hp += 3;
}
+ ASSERT(hendp == hp);
+
+ free_dist_obuf(obuf, 0);
+
BIF_RET2(res, (initial_reds - reds));
}
@@ -3764,13 +4485,11 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
BIF_RETTYPE setnode_2(BIF_ALIST_2)
{
Process *net_kernel = NULL;
- Uint creation;
+ Uint32 creation;
int success;
/* valid creation ? */
- if(!term_to_Uint(BIF_ARG_2, &creation))
- goto error;
- if(creation > 3)
+ if(!term_to_Uint32(BIF_ARG_2, &creation))
goto error;
/* valid node name ? */
@@ -3810,6 +4529,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
if (success) {
inc_no_nodes();
erts_set_this_node(BIF_ARG_1, (Uint32) creation);
+ erts_this_dist_entry->creation = creation;
erts_is_alive = 1;
send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL);
erts_proc_lock(net_kernel, ERTS_PROC_LOCKS_ALL);
@@ -3850,7 +4570,9 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
typedef struct {
DistEntry *dep;
- Uint flags;
+ int de_locked;
+ Uint64 dflags;
+ Uint32 creation;
Uint version;
Eterm setup_pid;
Process *net_kernel;
@@ -3858,24 +4580,26 @@ typedef struct {
static int
setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
- Eterm ctrlr, Uint flags,
- Uint version, Eterm setup_pid,
+ Eterm ctrlr, Uint64 flags,
+ Uint32 creation, Eterm setup_pid,
Process *net_kernel);
static Eterm
setup_connection_distctrl(Process *c_p, void *arg,
int *redsp, ErlHeapFragment **bpp);
-BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
+BIF_RETTYPE erts_internal_create_dist_channel_3(BIF_ALIST_3)
{
BIF_RETTYPE ret;
- Uint flags;
+ Uint64 flags;
Uint version;
+ Uint32 creation;
Eterm *hp, res_tag = THE_NON_VALUE, res = THE_NON_VALUE;
DistEntry *dep = NULL;
int de_locked = 0;
Port *pp = NULL;
int true_nk;
+ Eterm *tpl;
Process *net_kernel = erts_whereis_process(BIF_P, ERTS_PROC_LOCK_MAIN,
am_net_kernel,
ERTS_PROC_LOCK_STATUS,
@@ -3901,19 +4625,27 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
if (!is_internal_port(BIF_ARG_2) && !is_internal_pid(BIF_ARG_2))
goto badarg;
+ if (!is_tuple_arity(BIF_ARG_3, 3))
+ goto badarg;
+
+ tpl = tuple_val(BIF_ARG_3);
+
/* Dist flags... */
- if (!is_small(BIF_ARG_3))
+ if (!term_to_Uint64(tpl[1], &flags))
goto badarg;
- flags = unsigned_val(BIF_ARG_3);
/* Version... */
- if (!is_small(BIF_ARG_4))
+ if (!is_small(tpl[2]))
goto badarg;
- version = unsigned_val(BIF_ARG_4);
+ version = unsigned_val(tpl[2]);
if (version == 0)
goto badarg;
+ /* Creation... */
+ if (!term_to_Uint32(tpl[3], &creation))
+ goto badarg;
+
if (~flags & DFLAG_DIST_MANDATORY) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp, "%T", BIF_P->common.id);
@@ -3944,11 +4676,18 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
goto system_limit; /* Should never happen!!! */
if (is_internal_pid(BIF_ARG_2)) {
+ erts_de_rwlock(dep);
+ de_locked = 1;
+ if (dep->pending_nodedown)
+ goto suspend;
+
if (BIF_P->common.id == BIF_ARG_2) {
ErtsSetupConnDistCtrl scdc;
scdc.dep = dep;
- scdc.flags = flags;
+ scdc.de_locked = 1;
+ scdc.dflags = flags;
+ scdc.creation = creation;
scdc.version = version;
scdc.setup_pid = BIF_P->common.id;
scdc.net_kernel = net_kernel;
@@ -3956,6 +4695,7 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
res = setup_connection_distctrl(BIF_P, &scdc, NULL, NULL);
/* Dec of refc on net_kernel by setup_connection_distctrl() */
net_kernel = NULL;
+ de_locked = 0;
BUMP_REDS(BIF_P, 5);
dep = NULL;
@@ -3968,11 +4708,16 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
else {
ErtsSetupConnDistCtrl *scdcp;
+ erts_de_rwunlock(dep);
+ de_locked = 0;
+
scdcp = erts_alloc(ERTS_ALC_T_SETUP_CONN_ARG,
sizeof(ErtsSetupConnDistCtrl));
scdcp->dep = dep;
- scdcp->flags = flags;
+ scdcp->de_locked = 0;
+ scdcp->dflags = flags;
+ scdcp->creation = creation;
scdcp->version = version;
scdcp->setup_pid = BIF_P->common.id;
scdcp->net_kernel = net_kernel;
@@ -4021,6 +4766,9 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
|| is_not_nil(dep->cid))
goto badarg;
+ if(dep->pending_nodedown)
+ goto suspend;
+
erts_atomic32_read_bor_nob(&pp->state, ERTS_PORT_SFLG_DISTRIBUTION);
erts_prtsd_set(pp, ERTS_PRTSD_DIST_ENTRY, dep);
@@ -4044,7 +4792,7 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
conn_id = dep->connection_id;
set_res = setup_connection_epiloge_rwunlock(BIF_P, dep, BIF_ARG_2, flags,
- version, BIF_P->common.id,
+ creation, BIF_P->common.id,
net_kernel);
/* Dec of refc on net_kernel by setup_connection_epiloge_rwunlock() */
net_kernel = NULL;
@@ -4084,6 +4832,17 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
return ret;
+ suspend:
+ ASSERT(de_locked);
+ ASSERT(!dep->suspended_nodeup);
+ dep->suspended_nodeup = BIF_P;
+ erts_proc_inc_refc(BIF_P);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_PREP_YIELD3(ret,
+ &bif_trap_export[BIF_erts_internal_create_dist_channel_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ goto done;
+
badarg:
ERTS_BIF_PREP_RET(ret, am_badarg);
goto done;
@@ -4095,8 +4854,8 @@ BIF_RETTYPE erts_internal_create_dist_channel_4(BIF_ALIST_4)
static int
setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
- Eterm ctrlr, Uint flags,
- Uint version, Eterm setup_pid,
+ Eterm ctrlr, Uint64 flags,
+ Uint32 creation, Eterm setup_pid,
Process *net_kernel)
{
Eterm notify_proc = NIL;
@@ -4125,8 +4884,7 @@ setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
if (!success)
return 0;
- dep->version = version;
- dep->creation = 0;
+ dep->creation = creation;
ASSERT(is_internal_port(ctrlr) || is_internal_pid(ctrlr));
ASSERT(dep->state == ERTS_DE_STATE_PENDING);
@@ -4175,7 +4933,6 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
{
ErtsSetupConnDistCtrl *scdcp = (ErtsSetupConnDistCtrl *) arg;
DistEntry *dep = scdcp->dep;
- int dep_locked = 0;
Eterm *hp;
Uint32 conn_id;
int dec_net_kernel_on_error = !0;
@@ -4186,8 +4943,10 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
if (ERTS_PROC_IS_EXITING(c_p))
goto badarg;
- erts_de_rwlock(dep);
- dep_locked = !0;
+ if (!scdcp->de_locked) {
+ erts_de_rwlock(dep);
+ scdcp->de_locked = !0;
+ }
if (dep->state != ERTS_DE_STATE_PENDING)
goto badarg;
@@ -4211,7 +4970,7 @@ setup_connection_distctrl(Process *c_p, void *arg, int *redsp, ErlHeapFragment *
*redsp = 5;
if (!setup_connection_epiloge_rwunlock(c_p, dep, c_p->common.id,
- scdcp->flags, scdcp->version,
+ scdcp->dflags, scdcp->creation,
scdcp->setup_pid,
scdcp->net_kernel)) {
erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -4241,7 +5000,7 @@ badarg:
if (bpp) /* not called directly */
erts_free(ERTS_ALC_T_SETUP_CONN_ARG, arg);
- if (dep_locked)
+ if (scdcp->de_locked)
erts_de_rwunlock(dep);
erts_deref_dist_entry(dep);
@@ -4252,7 +5011,40 @@ badarg:
BIF_RETTYPE erts_internal_get_dflags_0(BIF_ALIST_0)
{
+ if (erts_dflags_test_remove_hopefull_flags) {
+ /* For internal emulator tests only! */
+ Eterm *hp, **hpp = NULL;
+ Uint sz = 0, *szp = &sz;
+ Eterm res;
+ while (1) {
+ res = erts_bld_tuple(hpp, szp, 6,
+ am_erts_dflags,
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_DEFAULT & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_MANDATORY & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_ADDABLE & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_REJECTABLE & ~DFLAG_DIST_HOPEFULLY),
+ erts_bld_uint64(hpp, szp, DFLAG_DIST_STRICT_ORDER & ~DFLAG_DIST_HOPEFULLY));
+ if (hpp) {
+ ASSERT(is_value(res));
+ return res;
+ }
+ hp = HAlloc(BIF_P, sz);
+ hpp = &hp;
+ szp = NULL;
+ }
+ }
return erts_dflags_record;
+
+}
+
+BIF_RETTYPE erts_internal_get_creation_0(BIF_ALIST_0)
+{
+ Eterm *hp;
+ Uint hsz = 0;
+
+ erts_bld_uint(NULL, &hsz, erts_this_dist_entry->creation);
+ hp = HAlloc(BIF_P, hsz);
+ return erts_bld_uint(&hp, NULL, erts_this_dist_entry->creation);
}
BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1)
@@ -4331,7 +5123,7 @@ Sint erts_abort_pending_connection_rwunlock(DistEntry* dep,
erts_de_rwunlock(dep);
schedule_con_monitor_link_seq_cleanup(
- mld, NULL, THE_NON_VALUE,
+ NULL, mld, NULL, THE_NON_VALUE,
THE_NON_VALUE, THE_NON_VALUE);
if (resume_procs) {
@@ -4421,6 +5213,398 @@ int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks)
return 1;
}
+static BIF_RETTYPE spawn_request_yield_3(BIF_ALIST_3)
+{
+ Binary* bin = erts_magic_ref2bin(BIF_ARG_1);
+ ErtsDSigSendContext *ctx = (ErtsDSigSendContext*) ERTS_MAGIC_BIN_DATA(bin);
+ Sint initial_reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
+ int code;
+
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dsend_context_dtor);
+
+ ctx->reds = initial_reds;
+ code = erts_dsig_send(ctx);
+
+ switch (code) {
+ case ERTS_DSIG_SEND_OK:
+ erts_set_gc_state(BIF_P, 1);
+ BIF_RET(BIF_ARG_2);
+
+ case ERTS_DSIG_SEND_YIELD:
+ erts_set_gc_state(BIF_P, 1);
+ ERTS_BIF_YIELD_RETURN(BIF_P, BIF_ARG_2);
+
+ case ERTS_DSIG_SEND_CONTINUE: {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP3(&spawn_request_yield_export, BIF_P,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ }
+
+ case ERTS_DSIG_SEND_TOO_LRG: {
+ ErtsMonitor *mon;
+ ErtsMonitorData *mdp;
+ Eterm ref;
+
+ erts_set_gc_state(BIF_P, 1);
+
+ if (is_internal_ordinary_ref(BIF_ARG_2))
+ ref = BIF_ARG_2;
+ else {
+ Eterm *tp;
+ ASSERT(is_tuple_arity(BIF_ARG_2, 2));
+ tp = tuple_val(BIF_ARG_2);
+ ref = tp[1];
+ ASSERT(is_internal_ordinary_ref(ref));
+ }
+
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), ref);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon);
+ mdp = erts_monitor_to_data(mon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ BIF_ARG_3, ref, am_system_limit,
+ am_undefined);
+ BIF_RET(BIF_ARG_2);
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid spawn request result");
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+}
+
+BIF_RETTYPE erts_internal_dist_spawn_request_4(BIF_ALIST_4)
+{
+ BIF_RETTYPE ret_val;
+ Eterm node = BIF_ARG_1;
+ Eterm mfa = BIF_ARG_2;
+ Eterm opts = BIF_ARG_3;
+ Eterm tag = am_spawn_reply;
+ Eterm mod, func, alist, new_opts, error, ref,
+ ok_result;
+ Uint nargs, nopts, rm_opts, rebuild_opts;
+ DistEntry *dep = NULL;
+ Eterm list;
+ ErtsDSigSendContext ctx;
+ int code, link = 0, monitor = 0, success_message, error_message;
+
+ ok_result = THE_NON_VALUE;
+ success_message = error_message = !0;
+
+ if (!is_node_name_atom(node))
+ goto badarg;
+ dep = erts_find_or_insert_dist_entry(node);
+ if (dep == erts_this_dist_entry)
+ goto badarg;
+ if (!is_tuple_arity(mfa, 3))
+ goto badarg;
+ else {
+ Eterm *tp = tuple_val(mfa);
+ mod = tp[1];
+ func = tp[2];
+ alist = tp[3];
+ if (!is_atom(mod))
+ goto badarg;
+ if (!is_atom(func))
+ goto badarg;
+ nargs = 0;
+ list = alist;
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ list = CDR(cp);
+ nargs++;
+ }
+ if (!is_nil(list))
+ goto badarg;
+ }
+
+ new_opts = list = opts;
+ nopts = 0;
+ rm_opts = 0;
+ rebuild_opts = 0;
+
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ Eterm car = CAR(cp);
+ list = CDR(cp);
+ nopts++;
+ switch (car) {
+ case am_link:
+ link = !0;
+ break;
+ case am_monitor:
+ monitor = !0;
+ break;
+ default:
+ if (is_tuple_arity(car, 2)) {
+ Eterm *tp = tuple_val(car);
+ switch (tp[1]) {
+
+ case am_reply_tag:
+ tag = tp[2];
+
+ if (0) {
+ case am_reply:
+ switch (tp[2]) {
+ case am_error_only:
+ success_message = 0;
+ error_message = !0;
+ break;
+ case am_success_only:
+ success_message = !0;
+ error_message = 0;
+ break;
+ case am_no:
+ success_message = 0;
+ error_message = 0;
+ break;
+ case am_yes:
+ success_message = !0;
+ error_message = !0;
+ break;
+ default:
+ goto badarg;
+ }
+ }
+
+ if (BIF_ARG_4 != am_spawn_request)
+ goto badarg;
+
+ rm_opts++;
+ new_opts = list;
+ rebuild_opts = nopts - rm_opts;
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if (!is_nil(list))
+ goto badarg;
+
+ if (rm_opts) {
+ /*
+ * If no 'rebuild_opts', all options to drop were in
+ * the head of the 'opts' list. 'new_opts' now contain
+ * the tail of original option list without the dropped
+ * options.
+ */
+ if (rebuild_opts) {
+#ifdef DEBUG
+ Eterm reusable_tail, *hp_start;
+#endif
+ Uint rm_cnt;
+ Eterm *hp, *prev_cp;
+ /*
+ * Remove 'reply_tag' option in option list.
+ * This options are mixed with other options.
+ *
+ * We build the list backwards and reuse tail
+ * without options to remove, if such exist.
+ */
+
+ hp = HAlloc(BIF_P, 2*rebuild_opts);
+
+#ifdef DEBUG
+ hp_start = hp;
+ reusable_tail = new_opts;
+#endif
+
+ hp += 2*(rebuild_opts - 1);
+ new_opts = make_list(hp);
+ prev_cp = NULL;
+ list = opts;
+ rm_cnt = 0;
+
+ while (is_list(list)) {
+ Eterm *cp = list_val(list);
+ Eterm car = CAR(cp);
+ list = CDR(cp);
+ if (is_tuple_arity(car, 2)) {
+ Eterm *tp = tuple_val(car);
+ if (am_reply_tag == tp[1]
+ || am_reply == tp[1]) {
+ rm_cnt++;
+ /* skip option */
+ if (rm_cnt == rm_opts) {
+ ASSERT(prev_cp);
+ ASSERT(list == reusable_tail);
+ CDR(prev_cp) = list;
+ break; /* last option to skip */
+ }
+ continue;
+ }
+ }
+#ifdef DEBUG
+ rebuild_opts--;
+#endif
+ CAR(hp) = car;
+ prev_cp = hp;
+ hp -= 2;
+ CDR(prev_cp) = make_list(hp);
+ }
+ ASSERT(hp == hp_start - 2);
+ ASSERT(rebuild_opts == 0);
+ }
+
+ opts = new_opts;
+ }
+
+ /* Arguments checked; do it... */
+
+ ref = erts_make_ref(BIF_P);
+ if (BIF_ARG_4 == am_spawn_request)
+ ok_result = ref;
+ else {
+ Eterm *hp = HAlloc(BIF_P, 3);
+ ASSERT(BIF_ARG_4 == am_spawn_opt);
+ ok_result = TUPLE2(hp, ref, monitor ? am_true : am_false);
+ }
+
+ code = erts_dsig_prepare(&ctx, dep,
+ BIF_P, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_RLOCK, 0, 0, 1);
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ goto noconnection;
+
+ case ERTS_DSIG_PREP_CONNECTED:
+ if (!(dep->dflags & DFLAG_SPAWN)) {
+ erts_de_runlock(dep);
+ goto notsup;
+ }
+ /* Fall through... */
+ case ERTS_DSIG_PREP_PENDING: {
+ int inserted;
+ ErtsMonitorData *mdp;
+ Eterm nargs_term, mfna, *hp;
+
+ if (IS_USMALL(0, nargs)) {
+ hp = HAlloc(BIF_P, 4);
+ nargs_term = make_small(nargs);
+ }
+ else {
+ hp = HAlloc(BIF_P, 4+BIG_UINT_HEAP_SIZE);
+ nargs_term = uint_to_big(nargs, hp);
+ hp += BIG_UINT_HEAP_SIZE;
+ }
+
+ mfna = TUPLE3(hp, mod, func, nargs_term);
+
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref,
+ BIF_P->common.id, am_pending,
+ tag);
+ if (monitor)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_MONITOR;
+ if (link)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_LINK;
+ if (!success_message)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_SMSG;
+ if (!error_message)
+ mdp->origin.flags |= ERTS_ML_FLG_SPAWN_NO_EMSG;
+
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P),
+ &mdp->origin);
+ inserted = erts_monitor_dist_insert(&mdp->target, dep->mld);
+ ASSERT(inserted); (void)inserted;
+
+ erts_de_runlock(dep);
+
+ ctx.reds = (Sint) (ERTS_BIF_REDS_LEFT(BIF_P) * TERM_TO_BINARY_LOOP_FACTOR);
+
+ code = dsig_send_spawn_request(&ctx, ref, BIF_P->common.id,
+ BIF_P->group_leader, mfna,
+ alist, opts);
+ switch (code) {
+ case ERTS_DSIG_SEND_OK:
+ ERTS_BIF_PREP_RET(ret_val, ok_result);
+ break;
+
+ case ERTS_DSIG_SEND_YIELD:
+ ERTS_BIF_PREP_YIELD_RETURN(ret_val, BIF_P, ok_result);
+ break;
+
+ case ERTS_DSIG_SEND_CONTINUE: {
+ Eterm ctx_term;
+ /* Keep dist entry alive over trap... */
+ ctx.deref_dep = 1;
+ dep = NULL;
+
+ erts_set_gc_state(BIF_P, 0);
+ ctx_term = erts_dsend_export_trap_context(BIF_P, &ctx);
+ BUMP_ALL_REDS(BIF_P);
+ ERTS_BIF_PREP_TRAP3(ret_val, &spawn_request_yield_export,
+ BIF_P, ctx_term, ok_result, tag);
+ break;
+ }
+
+ case ERTS_DSIG_SEND_TOO_LRG: {
+ ErtsMonitor *mon;
+ ErtsMonitorData *mdp;
+
+ mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), ref);
+ ASSERT(mon);
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon);
+ mdp = erts_monitor_to_data(mon);
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ goto system_limit;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid spawn request result");
+ ERTS_BIF_PREP_RET(ret_val, am_internal_error);
+ }
+ break;
+ }
+ default:
+ ERTS_INTERNAL_ERROR("Invalid dsig prepare result");
+ ERTS_BIF_PREP_RET(ret_val, am_internal_error);
+ break;
+ }
+
+do_return:
+
+ if (dep)
+ erts_deref_dist_entry(dep);
+
+ return ret_val;
+
+badarg:
+ ERTS_BIF_PREP_RET(ret_val, am_badarg);
+ goto do_return;
+
+system_limit:
+ error = am_system_limit;
+ goto send_error;
+noconnection:
+ error = am_noconnection;
+ goto send_error;
+notsup:
+ error = am_notsup;
+ /* fall through... */
+send_error:
+ ASSERT(is_value(ok_result));
+ if (error_message)
+ erts_send_local_spawn_reply(BIF_P, ERTS_PROC_LOCK_MAIN, NULL,
+ tag, ref, error, am_undefined);
+ ERTS_BIF_PREP_RET(ret_val, ok_result);
+ goto do_return;
+}
+
+
/**********************************************************************/
/* node(Object) -> Node */
@@ -4756,7 +5940,7 @@ BIF_RETTYPE monitor_node_2(BIF_ALIST_2)
BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
{
DistEntry *de;
- Uint32 f;
+ Uint64 f;
if (is_not_pid(BIF_ARG_1)) {
BIF_ERROR(BIF_P,BADARG);
}
@@ -4766,7 +5950,7 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
BIF_RET(am_true);
}
erts_de_rlock(de);
- f = de->flags;
+ f = de->dflags;
erts_de_runlock(de);
BIF_RET(((f & DFLAG_UNICODE_IO) ? am_true : am_false));
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index f6cb79472f..65c29caeb3 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -25,46 +25,57 @@
#include "erl_node_tables.h"
#include "zlib.h"
-#define DFLAG_PUBLISHED 0x01
-#define DFLAG_ATOM_CACHE 0x02
-#define DFLAG_EXTENDED_REFERENCES 0x04
-#define DFLAG_DIST_MONITOR 0x08
-#define DFLAG_FUN_TAGS 0x10
-#define DFLAG_DIST_MONITOR_NAME 0x20
-#define DFLAG_HIDDEN_ATOM_CACHE 0x40
-#define DFLAG_NEW_FUN_TAGS 0x80
-#define DFLAG_EXTENDED_PIDS_PORTS 0x100
-#define DFLAG_EXPORT_PTR_TAG 0x200
-#define DFLAG_BIT_BINARIES 0x400
-#define DFLAG_NEW_FLOATS 0x800
-#define DFLAG_UNICODE_IO 0x1000
-#define DFLAG_DIST_HDR_ATOM_CACHE 0x2000
-#define DFLAG_SMALL_ATOM_TAGS 0x4000
-#define DFLAG_INTERNAL_TAGS 0x8000 /* used by ETS 'compressed' option */
-#define DFLAG_UTF8_ATOMS 0x10000
-#define DFLAG_MAP_TAG 0x20000
-#define DFLAG_BIG_CREATION 0x40000
-#define DFLAG_SEND_SENDER 0x80000
-#define DFLAG_BIG_SEQTRACE_LABELS 0x100000
-#define DFLAG_NO_MAGIC 0x200000 /* internal for pending connection */
-#define DFLAG_EXIT_PAYLOAD 0x400000
-#define DFLAG_FRAGMENTS 0x800000
+#define DFLAG_PUBLISHED ((Uint64)0x01)
+#define DFLAG_ATOM_CACHE ((Uint64)0x02)
+#define DFLAG_EXTENDED_REFERENCES ((Uint64)0x04)
+#define DFLAG_DIST_MONITOR ((Uint64)0x08)
+#define DFLAG_FUN_TAGS ((Uint64)0x10)
+#define DFLAG_DIST_MONITOR_NAME ((Uint64)0x20)
+#define DFLAG_HIDDEN_ATOM_CACHE ((Uint64)0x40)
+#define DFLAG_NEW_FUN_TAGS ((Uint64)0x80)
+#define DFLAG_EXTENDED_PIDS_PORTS ((Uint64)0x100)
+#define DFLAG_EXPORT_PTR_TAG ((Uint64)0x200)
+#define DFLAG_BIT_BINARIES ((Uint64)0x400)
+#define DFLAG_NEW_FLOATS ((Uint64)0x800)
+#define DFLAG_UNICODE_IO ((Uint64)0x1000)
+#define DFLAG_DIST_HDR_ATOM_CACHE ((Uint64)0x2000)
+#define DFLAG_SMALL_ATOM_TAGS ((Uint64)0x4000)
+#define DFLAG_ETS_COMPRESSED ((Uint64)0x8000) /* internal */
+#define DFLAG_UTF8_ATOMS ((Uint64)0x10000)
+#define DFLAG_MAP_TAG ((Uint64)0x20000)
+#define DFLAG_BIG_CREATION ((Uint64)0x40000)
+#define DFLAG_SEND_SENDER ((Uint64)0x80000)
+#define DFLAG_BIG_SEQTRACE_LABELS ((Uint64)0x100000)
+#define DFLAG_PENDING_CONNECT ((Uint64)0x200000) /* internal */
+#define DFLAG_EXIT_PAYLOAD ((Uint64)0x400000)
+#define DFLAG_FRAGMENTS ((Uint64)0x800000)
+#define DFLAG_HANDSHAKE_23 ((Uint64)0x1000000)
+#define DFLAG_RESERVED 0xfe000000
+/*
+ * As the old handshake only support 32 flag bits, we reserve the remainding
+ * bits in the lower 32 for changes in the handshake protocol or potentially
+ * new capabilities that we also want to backport to OTP-22 or older.
+ */
+#define DFLAG_SPAWN ((Uint64)0x100000000)
+
/* Mandatory flags for distribution */
#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_UTF8_ATOMS \
- | DFLAG_NEW_FUN_TAGS)
+ | DFLAG_NEW_FUN_TAGS \
+ | DFLAG_BIG_CREATION)
/*
* Additional optimistic flags when encoding toward pending connection.
- * If remote node (erl_interface) does not supporting these then we may need
+ * If remote node (erl_interface) does not support these then we may need
* to transcode messages enqueued before connection setup was finished.
*/
#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \
| DFLAG_BIT_BINARIES \
| DFLAG_DIST_MONITOR \
- | DFLAG_DIST_MONITOR_NAME)
+ | DFLAG_DIST_MONITOR_NAME \
+ | DFLAG_SPAWN)
/* Our preferred set of flags. Used for connection setup handshake */
#define DFLAG_DIST_DEFAULT (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY \
@@ -75,11 +86,12 @@
| DFLAG_SMALL_ATOM_TAGS \
| DFLAG_UTF8_ATOMS \
| DFLAG_MAP_TAG \
- | DFLAG_BIG_CREATION \
| DFLAG_SEND_SENDER \
| DFLAG_BIG_SEQTRACE_LABELS \
| DFLAG_EXIT_PAYLOAD \
- | DFLAG_FRAGMENTS)
+ | DFLAG_FRAGMENTS \
+ | DFLAG_HANDSHAKE_23 \
+ | DFLAG_SPAWN)
/* Flags addable by local distr implementations */
#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT
@@ -87,6 +99,7 @@
/* Flags rejectable by local distr implementation */
#define DFLAG_DIST_REJECTABLE (DFLAG_DIST_HDR_ATOM_CACHE \
| DFLAG_HIDDEN_ATOM_CACHE \
+ | DFLAG_FRAGMENTS \
| DFLAG_ATOM_CACHE)
/* Flags for all features needing strict order delivery */
@@ -130,9 +143,17 @@ enum dop {
DOP_PAYLOAD_EXIT_TT = 25,
DOP_PAYLOAD_EXIT2 = 26,
DOP_PAYLOAD_EXIT2_TT = 27,
- DOP_PAYLOAD_MONITOR_P_EXIT = 28
+ DOP_PAYLOAD_MONITOR_P_EXIT = 28,
+
+ DOP_SPAWN_REQUEST = 29,
+ DOP_SPAWN_REQUEST_TT = 30,
+ DOP_SPAWN_REPLY = 31,
+ DOP_SPAWN_REPLY_TT = 32
};
+#define ERTS_DIST_SPAWN_FLAG_LINK (1 << 0)
+#define ERTS_DIST_SPAWN_FLAG_MONITOR (1 << 1)
+
/* distribution trap functions */
extern Export* dmonitor_node_trap;
@@ -144,7 +165,7 @@ typedef enum {
/* Must be larger or equal to 16 */
#ifdef DEBUG
-#define ERTS_DIST_FRAGMENT_SIZE 16
+#define ERTS_DIST_FRAGMENT_SIZE 1024
#else
/* This should be made configurable */
#define ERTS_DIST_FRAGMENT_SIZE (64 * 1024)
@@ -175,6 +196,9 @@ extern int erts_is_alive;
/* dist_ctrl_{g,s}et_option/2 */
#define ERTS_DIST_CTRL_OPT_GET_SIZE ((Uint32) (1 << 0))
+/* for emulator internal testing... */
+extern int erts_dflags_test_remove_hopefull_flags;
+
#ifdef DEBUG
#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \
erts_dbg_chk_no_dist_proc_link((D), (R), (L))
@@ -193,23 +217,75 @@ extern int erts_is_alive;
typedef enum { TTBSize, TTBEncode, TTBCompress } TTBState;
typedef struct TTBSizeContext_ {
- Uint flags;
+ Uint64 dflags;
int level;
+ Sint vlen;
+ int iovec;
+ Uint fragment_size;
+ Uint last_result;
+ Uint extra_size;
Uint result;
Eterm obj;
ErtsWStack wstack;
} TTBSizeContext;
+#define ERTS_INIT_TTBSizeContext(Ctx, Flags) \
+ do { \
+ (Ctx)->wstack.wstart = NULL; \
+ (Ctx)->dflags = (Flags); \
+ (Ctx)->level = 0; \
+ (Ctx)->vlen = -1; \
+ (Ctx)->fragment_size = ~((Uint) 0); \
+ (Ctx)->extra_size = 0; \
+ (Ctx)->last_result = 0; \
+ } while (0)
+
typedef struct TTBEncodeContext_ {
- Uint flags;
- Uint hopefull_flags;
+ Uint64 dflags;
+ Uint64 hopefull_flags;
+ byte *hopefull_flagsp;
int level;
byte* ep;
Eterm obj;
ErtsWStack wstack;
Binary *result_bin;
+ byte *cptr;
+ Sint vlen;
+ Uint size;
+ byte *payload_ixp;
+ byte *hopefull_ixp;
+ SysIOVec* iov;
+ ErlDrvBinary** binv;
+ Eterm *termv;
+ int iovec;
+ Uint fragment_size;
+ Sint frag_ix;
+ ErlIOVec **fragment_eiovs;
+#ifdef DEBUG
+ int debug_fragments;
+ int debug_vlen;
+#endif
} TTBEncodeContext;
+#define ERTS_INIT_TTBEncodeContext(Ctx, Flags) \
+ do { \
+ (Ctx)->wstack.wstart = NULL; \
+ (Ctx)->dflags = (Flags); \
+ (Ctx)->level = 0; \
+ (Ctx)->vlen = 0; \
+ (Ctx)->size = 0; \
+ (Ctx)->termv = NULL; \
+ (Ctx)->iov = NULL; \
+ (Ctx)->binv = NULL; \
+ (Ctx)->fragment_size = ~((Uint) 0); \
+ if ((Flags) & DFLAG_PENDING_CONNECT) { \
+ (Ctx)->hopefull_flags = 0; \
+ (Ctx)->hopefull_flagsp = NULL; \
+ (Ctx)->hopefull_ixp = NULL; \
+ (Ctx)->payload_ixp = NULL; \
+ } \
+ } while (0)
+
typedef struct {
Uint real_size;
Uint dest_len;
@@ -246,7 +322,7 @@ typedef struct erts_dsig_send_context {
Eterm ctl;
Eterm msg;
Eterm from;
- Eterm ctl_heap[6];
+ Eterm ctl_heap[8]; /* 7-tuple (SPAWN_REQUEST_TT) */
Eterm return_term;
DistEntry *dep;
@@ -258,12 +334,13 @@ typedef struct erts_dsig_send_context {
enum erts_dsig_send_phase phase;
Sint reds;
- Uint32 max_finalize_prepend;
Uint data_size, dhdr_ext_size;
+ byte *dhdrp, *extp;
ErtsAtomCacheMap *acmp;
ErtsDistOutputBuf *obuf;
- Uint fragments;
- Uint32 flags;
+ Uint alloced_fragments, fragments;
+ Sint vlen;
+ Uint64 dflags;
Process *c_p;
union {
TTBSizeContext sc;
@@ -305,6 +382,7 @@ extern int erts_dsig_send_exit2(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
extern int erts_dsig_send_demonitor(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
extern int erts_dsig_send_monitor(ErtsDSigSendContext *, Eterm, Eterm, Eterm);
extern int erts_dsig_send_m_exit(ErtsDSigSendContext *, Eterm, Eterm, Eterm, Eterm);
+extern int erts_dsig_send_spawn_reply(ErtsDSigSendContext *, Eterm, Eterm, Eterm, Eterm, Eterm);
extern int erts_dsig_send(ErtsDSigSendContext *dsdp);
extern int erts_dsend_context_dtor(Binary*);
@@ -333,4 +411,8 @@ extern int erts_dsig_prepare(ErtsDSigSendContext *,
void erts_dist_print_procs_suspended_on_de(fmtfn_t to, void *to_arg);
int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks);
+
+Uint erts_ttb_iov_size(int use_termv, Sint vlen, Uint fragments);
+ErlIOVec **erts_ttb_iov_init(TTBEncodeContext *ctx, int use_termv, char *ptr,
+ Sint vlen, Uint fragments, Uint fragments_size);
#endif
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index b9f0334172..1bbc7d7f1e 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -653,8 +653,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
= erts_timer_type_size(ERTS_ALC_T_HL_PTIMER);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)]
= erts_timer_type_size(ERTS_ALC_T_BIF_TIMER);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_EXP_TRACE)]
- = sizeof(NifExportTrace);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MREF_NSCHED_ENT)]
= sizeof(ErtsNSchedMagicRefTableEntry);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MINDIRECTION)]
@@ -2392,10 +2390,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_BIF_TIMER);
- add_fix_values(&size.processes,
- &size.processes_used,
- fi,
- ERTS_ALC_T_NIF_EXP_TRACE);
}
if (want.atom || want.atom_used) {
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index b40f5d0622..3d14e86445 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -195,6 +195,8 @@ type DB_DMC_ERR_INFO ETS ETS db_dmc_error_info
type DB_TERM ETS ETS db_term
type DB_PROC_CLEANUP SHORT_LIVED ETS db_proc_cleanup_state
type ETS_ALL_REQ SHORT_LIVED ETS ets_all_request
+type ETS_CTRS ETS ETS ets_decentralized_ctrs
+type ETS_I_LST_TRAP SHORT_LIVED ETS ets_insert_list_bif_trap_state
type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf
type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf
type INFO_DSBUF SYSTEM SYSTEM info_dsbuf
@@ -279,6 +281,7 @@ type SETUP_CONN_ARG SHORT_LIVED PROCESSES setup_connection_argument
type LIST_TRAP SHORT_LIVED PROCESSES list_bif_trap_state
type CONT_EXIT_TRAP SHORT_LIVED PROCESSES continue_exit_trap_state
type SEQ_YIELD_STATE SHORT_LIVED SYSTEM dist_seq_yield_state
+type PHASH2_TRAP SHORT_LIVED PROCESSES phash2_trap_state
type ENVIRONMENT SYSTEM SYSTEM environment
@@ -286,6 +289,8 @@ type PERSISTENT_TERM LONG_LIVED CODE persisten_term
type PERSISTENT_LOCK_Q SHORT_LIVED SYSTEM persistent_lock_q
type PERSISTENT_TERM_TMP SHORT_LIVED SYSTEM persistent_term_tmp_table
+type T2B_VEC SHORT_LIVED PROCESSES term_to_binary_vector
+
#
# Types used for special emulators
#
@@ -331,8 +336,7 @@ type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
-type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
-type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
+type NFUNC_TRAP_WRAPPER STANDARD PROCESSES nfunc_trap_wrapper
type EXPORT LONG_LIVED CODE export_entry
type MONITOR FIXED_SIZE PROCESSES monitor
type MONITOR_SUSPEND STANDARD PROCESSES monitor_suspend
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index bfc2f5992c..8123f99375 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -481,244 +481,265 @@ static void check_blk_carrier(Allctr_t *, Block_t *);
/* Statistics updating ... */
#ifdef DEBUG
-#define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
- ASSERT(((AP)->sbcs.curr.norm.mseg.no \
- && (AP)->sbcs.curr.norm.mseg.size) \
- || (!(AP)->sbcs.curr.norm.mseg.no \
- && !(AP)->sbcs.curr.norm.mseg.size)); \
- ASSERT(((AP)->sbcs.curr.norm.sys_alloc.no \
- && (AP)->sbcs.curr.norm.sys_alloc.size) \
- || (!(AP)->sbcs.curr.norm.sys_alloc.no \
- && !(AP)->sbcs.curr.norm.sys_alloc.size)); \
- ASSERT(((AP)->mbcs.curr.norm.mseg.no \
- && (AP)->mbcs.curr.norm.mseg.size) \
- || (!(AP)->mbcs.curr.norm.mseg.no \
- && !(AP)->mbcs.curr.norm.mseg.size)); \
- ASSERT(((AP)->mbcs.curr.norm.sys_alloc.no \
- && (AP)->mbcs.curr.norm.sys_alloc.size) \
- || (!(AP)->mbcs.curr.norm.sys_alloc.no \
- && !(AP)->mbcs.curr.norm.sys_alloc.size));
+
+#define DEBUG_CHECK_CARRIER_NO_SZ_1(CSTATS) \
+ do { \
+ int ix__; \
+ for (ix__ = ERTS_CRR_ALLOC_MIN; ix__ <= ERTS_CRR_ALLOC_MAX; ix__++) { \
+ UWord no__ = (CSTATS)->carriers[ix__].no; \
+ UWord size__= (CSTATS)->carriers[ix__].size; \
+ ASSERT((no__ > 0 && size__ > 0) || (no__ == 0 && size__ == 0)); \
+ } \
+ } while (0)
+
+#define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
+ do { \
+ DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->sbcs); \
+ DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->mbcs); \
+ } while (0)
#else
#define DEBUG_CHECK_CARRIER_NO_SZ(AP)
#endif
-#define STAT_SBC_ALLOC(AP, BSZ) \
- (AP)->sbcs.blocks.curr.size += (BSZ); \
- if ((AP)->sbcs.blocks.max.size < (AP)->sbcs.blocks.curr.size) \
- (AP)->sbcs.blocks.max.size = (AP)->sbcs.blocks.curr.size; \
- if ((AP)->sbcs.max.no < ((AP)->sbcs.curr.norm.mseg.no \
- + (AP)->sbcs.curr.norm.sys_alloc.no)) \
- (AP)->sbcs.max.no = ((AP)->sbcs.curr.norm.mseg.no \
- + (AP)->sbcs.curr.norm.sys_alloc.no); \
- if ((AP)->sbcs.max.size < ((AP)->sbcs.curr.norm.mseg.size \
- + (AP)->sbcs.curr.norm.sys_alloc.size)) \
- (AP)->sbcs.max.size = ((AP)->sbcs.curr.norm.mseg.size \
- + (AP)->sbcs.curr.norm.sys_alloc.size)
-
-#define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
-do { \
- (AP)->sbcs.curr.norm.mseg.no++; \
- (AP)->sbcs.curr.norm.mseg.size += (CSZ); \
- STAT_SBC_ALLOC((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
-do { \
- (AP)->sbcs.curr.norm.sys_alloc.no++; \
- (AP)->sbcs.curr.norm.sys_alloc.size += (CSZ); \
- STAT_SBC_ALLOC((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-
-#define STAT_SBC_FREE(AP, BSZ) \
- ASSERT((AP)->sbcs.blocks.curr.size >= (BSZ)); \
- (AP)->sbcs.blocks.curr.size -= (BSZ)
-
-#define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
-do { \
- ASSERT((AP)->sbcs.curr.norm.mseg.no > 0); \
- (AP)->sbcs.curr.norm.mseg.no--; \
- ASSERT((AP)->sbcs.curr.norm.mseg.size >= (CSZ)); \
- (AP)->sbcs.curr.norm.mseg.size -= (CSZ); \
- STAT_SBC_FREE((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
-do { \
- ASSERT((AP)->sbcs.curr.norm.sys_alloc.no > 0); \
- (AP)->sbcs.curr.norm.sys_alloc.no--; \
- ASSERT((AP)->sbcs.curr.norm.sys_alloc.size >= (CSZ)); \
- (AP)->sbcs.curr.norm.sys_alloc.size -= (CSZ); \
- STAT_SBC_FREE((AP), (BSZ)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_ALLOC(AP) \
- if ((AP)->mbcs.max.no < ((AP)->mbcs.curr.norm.mseg.no \
- + (AP)->mbcs.curr.norm.sys_alloc.no)) \
- (AP)->mbcs.max.no = ((AP)->mbcs.curr.norm.mseg.no \
- + (AP)->mbcs.curr.norm.sys_alloc.no); \
- if ((AP)->mbcs.max.size < ((AP)->mbcs.curr.norm.mseg.size \
- + (AP)->mbcs.curr.norm.sys_alloc.size)) \
- (AP)->mbcs.max.size = ((AP)->mbcs.curr.norm.mseg.size \
- + (AP)->mbcs.curr.norm.sys_alloc.size)
-
-
-#define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
-do { \
- (AP)->mbcs.curr.norm.mseg.no++; \
- (AP)->mbcs.curr.norm.mseg.size += (CSZ); \
- STAT_MBC_ALLOC((AP)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
-do { \
- (AP)->mbcs.curr.norm.sys_alloc.no++; \
- (AP)->mbcs.curr.norm.sys_alloc.size += (CSZ); \
- STAT_MBC_ALLOC((AP)); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_CPOOL_FETCH(AP, CRR) \
-do { \
- UWord csz__ = CARRIER_SZ((CRR)); \
- if (IS_MSEG_CARRIER((CRR))) \
- STAT_MSEG_MBC_ALLOC((AP), csz__); \
- else \
- STAT_SYS_ALLOC_MBC_ALLOC((AP), csz__); \
- set_new_allctr_abandon_limit(AP); \
- (AP)->mbcs.blocks.curr.no += (CRR)->cpool.blocks[(AP)->alloc_no]; \
- if ((AP)->mbcs.blocks.max.no < (AP)->mbcs.blocks.curr.no) \
- (AP)->mbcs.blocks.max.no = (AP)->mbcs.blocks.curr.no; \
- (AP)->mbcs.blocks.curr.size += \
- (CRR)->cpool.blocks_size[(AP)->alloc_no]; \
- if ((AP)->mbcs.blocks.max.size < (AP)->mbcs.blocks.curr.size) \
- (AP)->mbcs.blocks.max.size = (AP)->mbcs.blocks.curr.size; \
-} while (0)
-
-#define STAT_MSEG_MBC_FREE(AP, CSZ) \
-do { \
- ASSERT((AP)->mbcs.curr.norm.mseg.no > 0); \
- (AP)->mbcs.curr.norm.mseg.no--; \
- ASSERT((AP)->mbcs.curr.norm.mseg.size >= (CSZ)); \
- (AP)->mbcs.curr.norm.mseg.size -= (CSZ); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
-do { \
- ASSERT((AP)->mbcs.curr.norm.sys_alloc.no > 0); \
- (AP)->mbcs.curr.norm.sys_alloc.no--; \
- ASSERT((AP)->mbcs.curr.norm.sys_alloc.size >= (CSZ)); \
- (AP)->mbcs.curr.norm.sys_alloc.size -= (CSZ); \
- DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
-} while (0)
-
-#define STAT_MBC_FREE(AP, CRR) \
-do { \
- UWord csz__ = CARRIER_SZ((CRR)); \
- if (IS_MSEG_CARRIER((CRR))) { \
- STAT_MSEG_MBC_FREE((AP), csz__); \
- } else { \
- STAT_SYS_ALLOC_MBC_FREE((AP), csz__); \
- } \
- set_new_allctr_abandon_limit(AP); \
-} while (0)
-
-#define STAT_MBC_ABANDON(AP, CRR) \
-do { \
- STAT_MBC_FREE(AP, CRR); \
- ERTS_ALC_CPOOL_ASSERT((AP)->mbcs.blocks.curr.no \
- >= (CRR)->cpool.blocks[(AP)->alloc_no]); \
- (AP)->mbcs.blocks.curr.no -= (CRR)->cpool.blocks[(AP)->alloc_no]; \
- ERTS_ALC_CPOOL_ASSERT((AP)->mbcs.blocks.curr.size \
- >= (CRR)->cpool.blocks_size[(AP)->alloc_no]); \
- (AP)->mbcs.blocks.curr.size -= (CRR)->cpool.blocks_size[(AP)->alloc_no]; \
-} while (0)
-
-#define STAT_MBC_BLK_ALLOC_CRR(AP, CRR, BSZ) \
-do { \
- (CRR)->cpool.blocks[(AP)->alloc_no]++; \
- (CRR)->cpool.blocks_size[(AP)->alloc_no] += (BSZ); \
- (CRR)->cpool.total_blocks_size += (BSZ); \
-} while (0)
-
-#define STAT_MBC_BLK_ALLOC(AP, CRR, BSZ, FLGS) \
-do { \
- CarriersStats_t *cstats__ = &(AP)->mbcs; \
- cstats__->blocks.curr.no++; \
- if (cstats__->blocks.max.no < cstats__->blocks.curr.no) \
- cstats__->blocks.max.no = cstats__->blocks.curr.no; \
- cstats__->blocks.curr.size += (BSZ); \
- if (cstats__->blocks.max.size < cstats__->blocks.curr.size) \
- cstats__->blocks.max.size = cstats__->blocks.curr.size; \
- STAT_MBC_BLK_ALLOC_CRR((AP), (CRR), (BSZ)); \
-} while (0)
+/* Carrier statistics */
+
+#define STAT_CRR_UPDATED_1(CSTATS) \
+ do { \
+ UWord no_sum__, size_sum__; \
+ int i__; \
+ no_sum__ = size_sum__ = 0; \
+ for (i__ = ERTS_CRR_ALLOC_MIN; i__ <= ERTS_CRR_ALLOC_MAX; i__++) { \
+ ASSERT(ERTS_UWORD_MAX - no_sum__ > (CSTATS)->carriers[i__].no); \
+ ASSERT(ERTS_UWORD_MAX - size_sum__ > (CSTATS)->carriers[i__].size); \
+ no_sum__ += (CSTATS)->carriers[i__].no; \
+ size_sum__ += (CSTATS)->carriers[i__].size; \
+ } \
+ if ((CSTATS)->max.no < no_sum__) { \
+ (CSTATS)->max.no = no_sum__; \
+ } \
+ if ((CSTATS)->max.size < size_sum__) { \
+ (CSTATS)->max.size = size_sum__; \
+ } \
+ } while (0)
+
+#define STAT_CRR_ALLOC_1(TYPE, CSTATS, SIZE) \
+ do { \
+ ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].no > 1); \
+ ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].size > (SIZE)); \
+ (CSTATS)->carriers[(TYPE)].no += 1; \
+ (CSTATS)->carriers[(TYPE)].size += (SIZE); \
+ STAT_CRR_UPDATED_1(CSTATS); \
+ } while (0)
+
+#define STAT_CRR_FREE_1(TYPE, CSTATS, SIZE) \
+ do { \
+ ASSERT((CSTATS)->carriers[(TYPE)].no >= 1); \
+ ASSERT((CSTATS)->carriers[(TYPE)].size >= (SIZE)); \
+ (CSTATS)->carriers[(TYPE)].no -= 1; \
+ (CSTATS)->carriers[(TYPE)].size -= (SIZE); \
+ STAT_CRR_UPDATED_1(CSTATS); \
+ } while (0)
+
+#define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->sbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MSEG_MBC_FREE(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+ } while (0)
+
+#define STAT_MBC_FREE(AP, CRR) \
+ do { \
+ UWord csz__ = CARRIER_SZ((CRR)); \
+ if (IS_MSEG_CARRIER((CRR))) { \
+ STAT_MSEG_MBC_FREE((AP), csz__); \
+ } else { \
+ STAT_SYS_ALLOC_MBC_FREE((AP), csz__); \
+ } \
+ set_new_allctr_abandon_limit(AP); \
+ } while (0)
+
+/* Block statistics */
+
+#define STAT_BLK_UPDATED_1(BSTATS) \
+ do { \
+ if ((BSTATS)->max.no < (BSTATS)->curr.no) { \
+ (BSTATS)->max.no = (BSTATS)->curr.no; \
+ } \
+ if ((BSTATS)->max.size < (BSTATS)->curr.size) { \
+ (BSTATS)->max.size = (BSTATS)->curr.size; \
+ } \
+ } while (0)
+
+#define STAT_BLK_ALLOC_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
+ do { \
+ BlockStats_t *bstats__ = \
+ &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
+ ASSERT(ERTS_UWORD_MAX - bstats__->curr.no > (COUNT)); \
+ ASSERT(ERTS_UWORD_MAX - bstats__->curr.size > (SIZE)); \
+ bstats__->curr.no += (COUNT); \
+ bstats__->curr.size += (SIZE); \
+ STAT_BLK_UPDATED_1(bstats__); \
+ } while (0)
+
+#define STAT_BLK_FREE_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
+ do { \
+ BlockStats_t *bstats__ = \
+ &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
+ ASSERT(bstats__->curr.no >= (COUNT)); \
+ ASSERT(bstats__->curr.size >= (SIZE)); \
+ bstats__->curr.no -= (COUNT); \
+ bstats__->curr.size -= (SIZE); \
+ STAT_BLK_UPDATED_1(bstats__); \
+ } while (0)
+
+#define STAT_MBC_CPOOL_FETCH(AP, CRR) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ UWord csz__ = CARRIER_SZ((CRR)); \
+ int alloc_no__; \
+ if (IS_MSEG_CARRIER((CRR))) { \
+ STAT_MSEG_MBC_ALLOC((AP), csz__); \
+ } else { \
+ STAT_SYS_ALLOC_MBC_ALLOC((AP), csz__); \
+ } \
+ set_new_allctr_abandon_limit(AP); \
+ for (alloc_no__ = ERTS_ALC_A_MIN; \
+ alloc_no__ <= ERTS_ALC_A_MAX; \
+ alloc_no__++) { \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ UWord count = (CRR)->cpool.blocks[ix__]; \
+ UWord size = (CRR)->cpool.blocks_size[ix__]; \
+ STAT_BLK_ALLOC_1(cstats__, alloc_no__, count, size); \
+ } \
+ } while (0)
+
+#define STAT_MBC_CPOOL_ABANDON(AP, CRR) \
+ do { \
+ CarriersStats_t *cstats__ = &(AP)->mbcs; \
+ int alloc_no__; \
+ STAT_MBC_FREE(AP, CRR); \
+ for (alloc_no__ = ERTS_ALC_A_MIN; \
+ alloc_no__ <= ERTS_ALC_A_MAX; \
+ alloc_no__++) { \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ UWord count = (CRR)->cpool.blocks[ix__]; \
+ UWord size = (CRR)->cpool.blocks_size[ix__]; \
+ STAT_BLK_FREE_1(cstats__, alloc_no__, count, size); \
+ } \
+ } while (0)
-static ERTS_INLINE int
+static ERTS_INLINE void
stat_cpool_mbc_blk_free(Allctr_t *allctr,
- ErtsAlcType_t type,
- Carrier_t *crr,
- Carrier_t **busy_pcrr_pp,
- UWord blksz)
+ int alloc_no,
+ Carrier_t *crr,
+ Carrier_t **busy_pcrr_pp,
+ UWord blksz)
{
- Allctr_t *orig_allctr;
- int alloc_no;
-
- alloc_no = ERTS_ALC_T2A(type);
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks[alloc_no] > 0);
- crr->cpool.blocks[alloc_no]--;
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks_size[alloc_no] >= blksz);
- crr->cpool.blocks_size[alloc_no] -= blksz;
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.total_blocks_size >= blksz);
- crr->cpool.total_blocks_size -= blksz;
-
- if (allctr->alloc_no == alloc_no && (!busy_pcrr_pp || !*busy_pcrr_pp)) {
+ if (!busy_pcrr_pp || !*busy_pcrr_pp) {
/* This is a local block, so we should not update the pool
* statistics. */
- return 0;
- }
-
- /* This is either a foreign block that's been fetched from the pool, or any
- * block that's in the pool. The carrier's owner keeps the statistics for
- * both pooled and foreign blocks. */
-
- orig_allctr = crr->cpool.orig_allctr;
+ CarriersStats_t *cstats__ = &allctr->mbcs;
+ STAT_BLK_FREE_1(cstats__, alloc_no, 1, blksz);
+ } else {
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix = alloc_no - ERTS_ALC_A_MIN;
- ERTS_ALC_CPOOL_ASSERT(alloc_no != allctr->alloc_no ||
- (crr == *busy_pcrr_pp && allctr == orig_allctr));
+ ERTS_ALC_CPOOL_ASSERT(crr == *busy_pcrr_pp && allctr == orig_allctr);
#ifdef ERTS_ALC_CPOOL_DEBUG
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_dec_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]) >= 0);
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) blksz)) >= 0);
+ ERTS_ALC_CPOOL_ASSERT(
+ erts_atomic_dec_read_nob(
+ &orig_allctr->cpool.stat.no_blocks[ix]) >= 0);
+ ERTS_ALC_CPOOL_ASSERT(
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) blksz)) >= 0);
#else
- erts_atomic_dec_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]);
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) blksz));
+ erts_atomic_dec_nob(&orig_allctr->cpool.stat.no_blocks[ix]);
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) blksz));
#endif
-
- return 1;
+ }
}
-#define STAT_MBC_BLK_FREE(AP, TYPE, CRR, BPCRRPP, BSZ, FLGS) \
-do { \
- if (!stat_cpool_mbc_blk_free((AP), (TYPE), (CRR), (BPCRRPP), (BSZ))) { \
- CarriersStats_t *cstats__ = &(AP)->mbcs; \
- ASSERT(cstats__->blocks.curr.no > 0); \
- cstats__->blocks.curr.no--; \
- ASSERT(cstats__->blocks.curr.size >= (BSZ)); \
- cstats__->blocks.curr.size -= (BSZ); \
- } \
-} while (0)
+#define STAT_MBC_BLK_ALLOC(AP, CRR, BSZ) \
+ do { \
+ int alloc_no__ = (AP)->alloc_no; \
+ int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks[ix__] > 1); \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks_size[ix__] > (BSZ)); \
+ ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.total_blocks_size > (BSZ)); \
+ (CRR)->cpool.blocks[ix__] += 1; \
+ (CRR)->cpool.blocks_size[ix__] += (BSZ); \
+ (CRR)->cpool.total_blocks_size += (BSZ); \
+ STAT_BLK_ALLOC_1(&(AP)->mbcs, alloc_no__, 1, (BSZ)); \
+ } while (0)
+
+#define STAT_MBC_BLK_FREE(AP, TYPE, CRR, BPCRRPP, BSZ) \
+ do { \
+ int alloc_no__ = ERTS_ALC_T2A(TYPE); \
+ int ix__ = (alloc_no__) - ERTS_ALC_A_MIN; \
+ ASSERT((CRR)->cpool.blocks[ix__] >= 1); \
+ ASSERT((CRR)->cpool.blocks_size[ix__] >= (BSZ)); \
+ ASSERT((CRR)->cpool.total_blocks_size >= (BSZ)); \
+ (CRR)->cpool.blocks[ix__] -= 1; \
+ (CRR)->cpool.blocks_size[ix__] -= (BSZ); \
+ (CRR)->cpool.total_blocks_size -= (BSZ); \
+ stat_cpool_mbc_blk_free((AP), alloc_no__, (CRR), (BPCRRPP), (BSZ)); \
+ } while (0)
/* Debug stuff... */
#ifdef DEBUG
@@ -1211,9 +1232,14 @@ static Uint
get_next_mbc_size(Allctr_t *allctr)
{
Uint size;
- int cs = (allctr->mbcs.curr.norm.mseg.no
- + allctr->mbcs.curr.norm.sys_alloc.no
- - (allctr->main_carrier ? 1 : 0));
+ int cs;
+ int i;
+
+ cs = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ cs += allctr->mbcs.carriers[i].no;
+ }
+ cs -= (allctr->main_carrier ? 1 : 0);
ASSERT(cs >= 0);
ASSERT(allctr->largest_mbc_size >= allctr->smallest_mbc_size);
@@ -2285,8 +2311,14 @@ check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
if (allctr->cpool.disable_abandon)
return;
- if (allctr->mbcs.blocks.curr.size > allctr->cpool.abandon_limit)
- return;
+ {
+ int ix = allctr->alloc_no - ERTS_ALC_A_MIN;
+
+ /* We only consider the current allocation type; . */
+ if (allctr->mbcs.blocks[ix].curr.size > allctr->cpool.abandon_limit) {
+ return;
+ }
+ }
ncrr_in_pool = erts_atomic_read_nob(&allctr->cpool.stat.no_carriers);
if (ncrr_in_pool >= allctr->cpool.in_pool_limit)
@@ -2515,7 +2547,7 @@ mbc_alloc_finalize(Allctr_t *allctr,
}
ERTS_ALC_CPOOL_ALLOC_OP(allctr);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(blk_sz == MBC_BLK_SZ(blk));
@@ -2734,7 +2766,7 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp
ERTS_ALC_CPOOL_FREE_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, busy_pcrr_pp, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, busy_pcrr_pp, blk_sz);
is_first_blk = IS_MBC_FIRST_ABLK(allctr, blk);
is_last_blk = IS_LAST_BLK(blk);
@@ -2934,8 +2966,8 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
crr = ABLK_TO_MBC(blk);
ERTS_ALC_CPOOL_REALLOC_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(MBC_BLK_SZ(blk) >= allctr->min_block_size);
@@ -3038,8 +3070,8 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
}
ERTS_ALC_CPOOL_REALLOC_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
- STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
+ STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(blk_sz == MBC_BLK_SZ(blk));
@@ -3186,7 +3218,7 @@ mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
0);
ERTS_ALC_CPOOL_FREE_OP(allctr);
- STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz, alcu_flgs);
+ STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
return new_p;
}
@@ -3357,27 +3389,17 @@ cpool_insert(Allctr_t *allctr, Carrier_t *crr)
erts_aint_t val;
ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
|| erts_thr_progress_is_managed_thread());
- {
- int alloc_no = allctr->alloc_no;
-
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no]) >= 0 &&
- crr->cpool.blocks_size[alloc_no] >= 0);
-
- ERTS_ALC_CPOOL_ASSERT(
- erts_atomic_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]) >= 0 &&
- crr->cpool.blocks[alloc_no] >= 0);
-
- /* We only modify the counter for our current type since the others are
- * conceptually still in the pool. */
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- ((erts_aint_t) crr->cpool.blocks_size[alloc_no]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no],
- ((erts_aint_t) crr->cpool.blocks[alloc_no]));
+ /* Add the carrier's block statistics to the pool. */
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ ((erts_aint_t) crr->cpool.blocks_size[ix]));
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ ((erts_aint_t) crr->cpool.blocks[ix]));
}
erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
@@ -3454,11 +3476,15 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
#ifdef ERTS_ALC_CPOOL_DEBUG
ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
#endif
+ Allctr_t *orig_allctr = crr->cpool.orig_allctr;
+ int ix;
ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
|| erts_thr_progress_is_managed_thread());
ERTS_ALC_CPOOL_ASSERT(sentinel != &crr->cpool);
+ ERTS_ALC_CPOOL_ASSERT(orig_allctr == prev_allctr);
+
/* Set mod marker on next ptr of our predecessor */
val = (erts_aint_t) &crr->cpool;
@@ -3531,30 +3557,31 @@ cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
crr->cpool.thr_prgr = erts_thr_progress_later(NULL);
- {
- Allctr_t *orig_allctr = crr->cpool.orig_allctr;
- int alloc_no = allctr->alloc_no;
-
- ERTS_ALC_CPOOL_ASSERT(orig_allctr == prev_allctr);
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks_size[alloc_no] <=
- erts_atomic_read_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no]));
-
- ERTS_ALC_CPOOL_ASSERT(crr->cpool.blocks[alloc_no] <=
- erts_atomic_read_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no]));
+ /* Subtract the carrier's block statistics from the pool. */
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+#ifdef ERTS_ALC_CPOOL_DEBUG
+ SWord new_blk_sz, new_blk_no;
- /* We only modify the counters for our current type since the others
- * were, conceptually, never taken out of the pool. */
- erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[alloc_no],
- -((erts_aint_t) crr->cpool.blocks_size[alloc_no]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[alloc_no],
- -((erts_aint_t) crr->cpool.blocks[alloc_no]));
+ new_blk_sz =
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t)crr->cpool.blocks_size[ix]));
+ new_blk_no =
+ erts_atomic_add_read_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ -((erts_aint_t)crr->cpool.blocks[ix]));
- erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
- -((erts_aint_t) CARRIER_SZ(crr)));
- erts_atomic_dec_wb(&orig_allctr->cpool.stat.no_carriers);
+ ERTS_ALC_CPOOL_ASSERT(new_blk_sz >= 0);
+ ERTS_ALC_CPOOL_ASSERT(new_blk_no >= 0);
+#else
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
+ -((erts_aint_t) crr->cpool.blocks_size[ix]));
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
+ -((erts_aint_t) crr->cpool.blocks[ix]));
+#endif
}
+ erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
+ -((erts_aint_t) CARRIER_SZ(crr)));
+ erts_atomic_dec_wb(&orig_allctr->cpool.stat.no_carriers);
}
static Carrier_t *
@@ -3937,9 +3964,12 @@ allctr_abandon_limit(Allctr_t *allctr)
{
UWord limit;
UWord csz;
+ int i;
- csz = allctr->mbcs.curr.norm.mseg.size;
- csz += allctr->mbcs.curr.norm.sys_alloc.size;
+ csz = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ csz += allctr->mbcs.carriers[i].size;
+ }
limit = csz*allctr->cpool.util_limit;
if (limit > csz)
@@ -3961,7 +3991,7 @@ abandon_carrier(Allctr_t *allctr, Carrier_t *crr)
{
erts_aint_t iallctr;
- STAT_MBC_ABANDON(allctr, crr);
+ STAT_MBC_CPOOL_ABANDON(allctr, crr);
unlink_carrier(&allctr->mbc_list, crr);
allctr->remove_mbc(allctr, crr);
@@ -4023,9 +4053,12 @@ static void
cpool_read_stat(Allctr_t *allctr, int alloc_no,
UWord *nocp, UWord *cszp, UWord *nobp, UWord *bszp)
{
+ int block_ix;
int i;
UWord noc = 0, csz = 0, nob = 0, bsz = 0;
+ block_ix = alloc_no - ERTS_ALC_A_MIN;
+
/*
* We try to get consistent values, but after
* ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS failed
@@ -4041,10 +4074,10 @@ cpool_read_stat(Allctr_t *allctr, int alloc_no,
? erts_atomic_read_nob(&allctr->cpool.stat.carriers_size)
: 0);
tnob = (UWord) (nobp
- ? erts_atomic_read_nob(&allctr->cpool.stat.no_blocks[alloc_no])
+ ? erts_atomic_read_nob(&allctr->cpool.stat.no_blocks[block_ix])
: 0);
tbsz = (UWord) (bszp
- ? erts_atomic_read_nob(&allctr->cpool.stat.blocks_size[alloc_no])
+ ? erts_atomic_read_nob(&allctr->cpool.stat.blocks_size[block_ix])
: 0);
if (tnoc == noc && tcsz == csz && tnob == nob && tbsz == bsz)
break;
@@ -4192,12 +4225,12 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (erts_mseg_no(&allctr->mseg_opt) >= max_mseg_carriers)
goto try_sys_alloc;
if (flags & CFLG_SBC) {
- if (allctr->sbcs.curr.norm.mseg.no >= allctr->max_mseg_sbcs)
+ if (allctr->sbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_sbcs)
goto try_sys_alloc;
}
#if !ERTS_SUPER_ALIGNED_MSEG_ONLY
else {
- if (allctr->mbcs.curr.norm.mseg.no >= allctr->max_mseg_mbcs)
+ if (allctr->mbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_mbcs)
goto try_sys_alloc;
}
#endif
@@ -4638,10 +4671,10 @@ static struct {
Eterm mseg_alloc_carriers;
#endif
Eterm carriers;
- Eterm blocks_size;
- Eterm blocks;
- Eterm foreign_blocks;
+ Eterm blocks;
+ Eterm count;
+ Eterm size;
Eterm calls;
Eterm sys_alloc;
@@ -4658,6 +4691,7 @@ static struct {
} am;
static Eterm alloc_type_atoms[ERTS_ALC_N_MAX + 1];
+static Eterm alloc_num_atoms[ERTS_ALC_A_MAX + 1];
static ERTS_INLINE void atom_init(Eterm *atom, char *name)
{
@@ -4741,9 +4775,9 @@ init_atoms(Allctr_t *allctr)
AM_INIT(mseg_alloc_carriers);
#endif
AM_INIT(carriers);
- AM_INIT(blocks_size);
AM_INIT(blocks);
- AM_INIT(foreign_blocks);
+ AM_INIT(count);
+ AM_INIT(size);
AM_INIT(calls);
AM_INIT(sys_alloc);
@@ -4767,6 +4801,13 @@ init_atoms(Allctr_t *allctr)
alloc_type_atoms[ix] = am_atom_put(name, len);
}
+
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ const char *name = ERTS_ALC_A2AD(ix);
+ size_t len = sys_strlen(name);
+
+ alloc_num_atoms[ix] = am_atom_put(name, len);
+ }
}
if (allctr && !allctr->atoms_initialized) {
@@ -4939,39 +4980,81 @@ sz_info_carriers(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- UWord curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ UWord curr_size;
+ int i;
+
+ curr_size = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ curr_size += cs->carriers[i].size;
+ }
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- erts_print(to,
- arg,
- "%sblocks size: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.size,
- cs->blocks.max.size,
- cs->blocks.max_ever.size);
- erts_print(to,
- arg,
- "%scarriers size: %bpu %bpu %bpu\n",
- prefix,
- curr_size,
- cs->max.size,
- cs->max_ever.size);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+
+ erts_print(to,
+ arg,
+ "%sblocks[%s] size: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.size,
+ cs->blocks[ix].max.size,
+ cs->blocks[ix].max_ever.size);
+ }
+
+ erts_print(to,
+ arg,
+ "%scarriers size: %bpu %bpu %bpu\n",
+ prefix,
+ curr_size,
+ cs->max.size,
+ cs->max_ever.size);
}
if (hpp || szp) {
- res = NIL;
- add_4tup(hpp, szp, &res,
- am.carriers_size,
- bld_unstable_uint(hpp, szp, curr_size),
- bld_unstable_uint(hpp, szp, cs->max.size),
- bld_unstable_uint(hpp, szp, cs->max_ever.size));
- add_4tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.size));
+ Eterm blocks;
+ int alloc_no;
+
+ res = NIL;
+
+ add_4tup(hpp, szp, &res,
+ am.carriers_size,
+ bld_unstable_uint(hpp, szp, curr_size),
+ bld_unstable_uint(hpp, szp, cs->max.size),
+ bld_unstable_uint(hpp, szp, cs->max_ever.size));
+
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ UWord curr, max, max_ever;
+ Eterm info = NIL;
+
+ curr = cs->blocks[ix].curr.size;
+ max = cs->blocks[ix].max.size;
+ max_ever = cs->blocks[ix].max_ever.size;
+
+ if (curr == 0 && max == 0 && max_ever == 0) {
+ continue;
+ }
+
+ add_4tup(hpp, szp, &info,
+ am.size,
+ bld_unstable_uint(hpp, szp, curr),
+ bld_unstable_uint(hpp, szp, max),
+ bld_unstable_uint(hpp, szp, max_ever));
+
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
+ }
+
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -4988,33 +5071,43 @@ info_cpool(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- UWord noc, csz, nob, bsz;
+ UWord noc, csz;
- noc = csz = nob = bsz = ~0;
+ noc = csz = ~0;
if (print_to_p || hpp) {
- if (sz_only)
- cpool_read_stat(allctr, allctr->alloc_no, NULL, &csz, NULL, &bsz);
- else
- cpool_read_stat(allctr, allctr->alloc_no, &noc, &csz, &nob, &bsz);
+ cpool_read_stat(allctr, allctr->alloc_no, &noc, &csz, NULL, NULL);
}
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- if (!sz_only)
- erts_print(to, arg, "%sblocks: %bpu\n", prefix, nob);
- erts_print(to, arg, "%sblocks size: %bpu\n", prefix, bsz);
- if (!sz_only)
- erts_print(to, arg, "%scarriers: %bpu\n", prefix, noc);
- erts_print(to, arg, "%scarriers size: %bpu\n", prefix, csz);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ UWord nob, bsz;
+
+ nob = bsz = ~0;
+ cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
+
+ if (!sz_only)
+ erts_print(to, arg, "%sblocks[%s] count: %bpu\n",
+ prefix, erts_alc_a2ad[alloc_no], nob);
+ erts_print(to, arg, "%sblocks[%s] size: %bpu\n",
+ prefix, erts_alc_a2ad[alloc_no], bsz);
+ }
+
+ if (!sz_only)
+ erts_print(to, arg, "%scarriers: %bpu\n", prefix, noc);
+ erts_print(to, arg, "%scarriers size: %bpu\n", prefix, csz);
}
if (hpp || szp) {
- Eterm foreign_blocks;
- int i;
+ Eterm blocks;
+ int alloc_no;
- foreign_blocks = NIL;
- res = NIL;
+ res = NIL;
if (!sz_only) {
add_3tup(hpp, szp, &res, am.fail_pooled,
@@ -5072,49 +5165,36 @@ info_cpool(Allctr_t *allctr,
bld_unstable_uint(hpp, szp, noc));
}
- add_2tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, bsz));
-
- if (!sz_only) {
- add_2tup(hpp, szp, &res,
- am.blocks,
- bld_unstable_uint(hpp, szp, nob));
- }
-
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
- const char *name_str;
- Eterm name, info;
-
- if (i == allctr->alloc_no) {
- continue;
- }
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ UWord nob, bsz;
+ Eterm info;
- cpool_read_stat(allctr, i, NULL, NULL, &nob, &bsz);
+ nob = bsz = ~0;
+ cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
if (bsz == 0 && (nob == 0 || sz_only)) {
continue;
}
- name_str = ERTS_ALC_A2AD(i);
info = NIL;
add_2tup(hpp, szp, &info,
- am.blocks_size,
+ am.size,
bld_unstable_uint(hpp, szp, bsz));
if (!sz_only) {
add_2tup(hpp, szp, &info,
- am.blocks,
+ am.count,
bld_unstable_uint(hpp, szp, nob));
}
- name = am_atom_put(name_str, sys_strlen(name_str));
-
- add_2tup(hpp, szp, &foreign_blocks, name, info);
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
}
- add_2tup(hpp, szp, &res, am.foreign_blocks, foreign_blocks);
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -5132,27 +5212,41 @@ info_carriers(Allctr_t *allctr,
{
Eterm res = THE_NON_VALUE;
UWord curr_no, curr_size;
-
- curr_no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
- curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ int i;
+
+ curr_no = curr_size = 0;
+ for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
+ curr_no += cs->carriers[i].no;
+ curr_size += cs->carriers[i].size;
+ }
if (print_to_p) {
- fmtfn_t to = *print_to_p;
- void *arg = print_to_arg;
- erts_print(to,
- arg,
- "%sblocks: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.no,
- cs->blocks.max.no,
- cs->blocks.max_ever.no);
- erts_print(to,
- arg,
- "%sblocks size: %bpu %bpu %bpu\n",
- prefix,
- cs->blocks.curr.size,
- cs->blocks.max.size,
- cs->blocks.max_ever.size);
+ fmtfn_t to = *print_to_p;
+ void *arg = print_to_arg;
+ int alloc_no;
+
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ erts_print(to,
+ arg,
+ "%sblocks[%s] count: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.no,
+ cs->blocks[ix].max.no,
+ cs->blocks[ix].max_ever.no);
+ erts_print(to,
+ arg,
+ "%sblocks[%s] size: %bpu %bpu %bpu\n",
+ prefix,
+ erts_alc_a2ad[alloc_no],
+ cs->blocks[ix].curr.size,
+ cs->blocks[ix].max.size,
+ cs->blocks[ix].max_ever.size);
+ }
+
erts_print(to,
arg,
"%scarriers: %bpu %bpu %bpu\n",
@@ -5165,13 +5259,13 @@ info_carriers(Allctr_t *allctr,
arg,
"%smseg carriers: %bpu\n",
prefix,
- cs->curr.norm.mseg.no);
+ cs->carriers[ERTS_CRR_ALLOC_MSEG].no);
#endif
erts_print(to,
arg,
"%ssys_alloc carriers: %bpu\n",
prefix,
- cs->curr.norm.sys_alloc.no);
+ cs->carriers[ERTS_CRR_ALLOC_SYS].no);
erts_print(to,
arg,
"%scarriers size: %bpu %bpu %bpu\n",
@@ -5184,24 +5278,27 @@ info_carriers(Allctr_t *allctr,
arg,
"%smseg carriers size: %bpu\n",
prefix,
- cs->curr.norm.mseg.size);
+ cs->carriers[ERTS_CRR_ALLOC_MSEG].size);
#endif
erts_print(to,
arg,
"%ssys_alloc carriers size: %bpu\n",
prefix,
- cs->curr.norm.sys_alloc.size);
+ cs->carriers[ERTS_CRR_ALLOC_SYS].size);
}
if (hpp || szp) {
+ Eterm blocks;
+ int alloc_no;
+
res = NIL;
add_2tup(hpp, szp, &res,
am.sys_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.size));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].size));
#if HAVE_ERTS_MSEG
add_2tup(hpp, szp, &res,
am.mseg_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.size));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].size));
#endif
add_4tup(hpp, szp, &res,
am.carriers_size,
@@ -5210,27 +5307,57 @@ info_carriers(Allctr_t *allctr,
bld_unstable_uint(hpp, szp, cs->max_ever.size));
add_2tup(hpp, szp, &res,
am.sys_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.no));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].no));
#if HAVE_ERTS_MSEG
add_2tup(hpp, szp, &res,
am.mseg_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.no));
+ bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].no));
#endif
add_4tup(hpp, szp, &res,
am.carriers,
bld_unstable_uint(hpp, szp, curr_no),
bld_unstable_uint(hpp, szp, cs->max.no),
bld_unstable_uint(hpp, szp, cs->max_ever.no));
- add_4tup(hpp, szp, &res,
- am.blocks_size,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max.size),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.size));
- add_4tup(hpp, szp, &res,
- am.blocks,
- bld_unstable_uint(hpp, szp, cs->blocks.curr.no),
- bld_unstable_uint(hpp, szp, cs->blocks.max.no),
- bld_unstable_uint(hpp, szp, cs->blocks.max_ever.no));
+
+ blocks = NIL;
+ for (alloc_no = ERTS_ALC_A_MIN;
+ alloc_no <= ERTS_ALC_A_MAX;
+ alloc_no++) {
+ int ix = alloc_no - ERTS_ALC_A_MIN;
+ UWord curr_size, max_size, max_ever_size;
+ UWord curr_no, max_no, max_ever_no;
+ Eterm info;
+
+ curr_size = cs->blocks[ix].curr.size;
+ max_size = cs->blocks[ix].max.size;
+ max_ever_size = cs->blocks[ix].max_ever.size;
+
+ curr_no = cs->blocks[ix].curr.no;
+ max_no = cs->blocks[ix].max.no;
+ max_ever_no = cs->blocks[ix].max_ever.no;
+
+ if (max_ever_no == 0) {
+ continue;
+ }
+
+ info = NIL;
+
+ add_4tup(hpp, szp, &info,
+ am.size,
+ bld_unstable_uint(hpp, szp, curr_size),
+ bld_unstable_uint(hpp, szp, max_size),
+ bld_unstable_uint(hpp, szp, max_ever_size));
+
+ add_4tup(hpp, szp, &info,
+ am.count,
+ bld_unstable_uint(hpp, szp, curr_no),
+ bld_unstable_uint(hpp, szp, max_no),
+ bld_unstable_uint(hpp, szp, max_ever_no));
+
+ add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
+ }
+
+ add_2tup(hpp, szp, &res, am.blocks, blocks);
}
return res;
@@ -5490,23 +5617,50 @@ info_options(Allctr_t *allctr,
static ERTS_INLINE void
update_max_ever_values(CarriersStats_t *cs)
{
- if (cs->max_ever.no < cs->max.no)
- cs->max_ever.no = cs->max.no;
- if (cs->max_ever.size < cs->max.size)
- cs->max_ever.size = cs->max.size;
- if (cs->blocks.max_ever.no < cs->blocks.max.no)
- cs->blocks.max_ever.no = cs->blocks.max.no;
- if (cs->blocks.max_ever.size < cs->blocks.max.size)
- cs->blocks.max_ever.size = cs->blocks.max.size;
+ int ix;
+
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ BlockStats_t *bstats = &cs->blocks[ix];
+
+ if (bstats->max_ever.no < bstats->max.no) {
+ bstats->max_ever.no = bstats->max.no;
+ }
+
+ if (bstats->max_ever.size < bstats->max.size) {
+ bstats->max_ever.size = bstats->max.size;
+ }
+ }
+
+ if (cs->max_ever.no < cs->max.no) {
+ cs->max_ever.no = cs->max.no;
+ }
+
+ if (cs->max_ever.size < cs->max.size) {
+ cs->max_ever.size = cs->max.size;
+ }
}
static ERTS_INLINE void
reset_max_values(CarriersStats_t *cs)
{
- cs->max.no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
- cs->max.size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
- cs->blocks.max.no = cs->blocks.curr.no;
- cs->blocks.max.size = cs->blocks.curr.size;
+ UWord curr_no, curr_size;
+ int ix;
+
+ for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
+ BlockStats_t *bstats = &cs->blocks[ix];
+
+ bstats->max.no = bstats->curr.no;
+ bstats->max.size = bstats->curr.size;
+ }
+
+ curr_no = curr_size = 0;
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ curr_no += cs->carriers[ix].no;
+ curr_size += cs->carriers[ix].size;
+ }
+
+ cs->max.no = curr_no;
+ cs->max.size = curr_size;
}
@@ -5614,11 +5768,6 @@ erts_alcu_sz_info(Allctr_t *allctr,
ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
- /* Update sbc values not continuously updated */
- allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
- allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
-
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
@@ -5690,11 +5839,6 @@ erts_alcu_info(Allctr_t *allctr,
ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
- /* Update sbc values not continuously updated */
- allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
- allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
-
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
@@ -5753,61 +5897,66 @@ erts_alcu_info(Allctr_t *allctr,
void
erts_alcu_foreign_size(Allctr_t *allctr, ErtsAlcType_t alloc_no, AllctrSize_t *size)
{
+ int ix;
+
+ sys_memset(size, 0, sizeof(*size));
+
+ if (allctr->thread_safe)
+ erts_mtx_lock(&allctr->mutex);
+
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ size->carriers += allctr->mbcs.carriers[ix].size;
+ size->carriers += allctr->sbcs.carriers[ix].size;
+ }
+
+ ix = alloc_no - ERTS_ALC_A_MIN;
+ size->blocks += allctr->mbcs.blocks[ix].curr.size;
+ size->blocks += allctr->sbcs.blocks[ix].curr.size;
+
if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
UWord csz, bsz;
+
+ csz = bsz = 0;
cpool_read_stat(allctr, alloc_no, NULL, &csz, NULL, &bsz);
- size->carriers = csz;
- size->blocks = bsz;
- } else {
- size->carriers = 0;
- size->blocks = 0;
+
+ size->carriers += csz;
+ size->blocks += bsz;
}
+
+ if (allctr->thread_safe)
+ erts_mtx_unlock(&allctr->mutex);
}
void
erts_alcu_current_size(Allctr_t *allctr, AllctrSize_t *size, ErtsAlcUFixInfo_t *fi, int fisz)
{
-
- if (allctr->thread_safe)
- erts_mtx_lock(&allctr->mutex);
-
- size->carriers = allctr->mbcs.curr.norm.mseg.size;
- size->carriers += allctr->mbcs.curr.norm.sys_alloc.size;
- size->carriers += allctr->sbcs.curr.norm.mseg.size;
- size->carriers += allctr->sbcs.curr.norm.sys_alloc.size;
-
- size->blocks = allctr->mbcs.blocks.curr.size;
- size->blocks += allctr->sbcs.blocks.curr.size;
-
- if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
- UWord csz, bsz;
- cpool_read_stat(allctr, allctr->alloc_no, NULL, &csz, NULL, &bsz);
- size->blocks += bsz;
- size->carriers += csz;
- }
+ erts_alcu_foreign_size(allctr, allctr->alloc_no, size);
if (fi) {
- int ix;
- for (ix = 0; ix < fisz; ix++) {
- if (allctr->fix) {
- if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
- fi[ix].allocated += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.cpool.allocated);
- fi[ix].used += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.cpool.used);
- }
- else {
- fi[ix].allocated += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.nocpool.allocated);
- fi[ix].used += (allctr->fix[ix].type_size
- * allctr->fix[ix].u.nocpool.used);
- }
- }
- }
- }
+ int ix;
+
+ if (allctr->thread_safe)
+ erts_mtx_lock(&allctr->mutex);
+
+ for (ix = 0; ix < fisz; ix++) {
+ if (allctr->fix) {
+ if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
+ fi[ix].allocated += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.cpool.allocated);
+ fi[ix].used += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.cpool.used);
+ } else {
+ fi[ix].allocated += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.nocpool.allocated);
+ fi[ix].used += (allctr->fix[ix].type_size
+ * allctr->fix[ix].u.nocpool.used);
+ }
+ }
+ }
- if (allctr->thread_safe)
- erts_mtx_unlock(&allctr->mutex);
+ if (allctr->thread_safe)
+ erts_mtx_unlock(&allctr->mutex);
+ }
}
/* ----------------------------------------------------------------------- */
@@ -6660,10 +6809,12 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->cpool.dc_list.last = NULL;
allctr->cpool.abandon_limit = 0;
allctr->cpool.disable_abandon = 0;
- for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+
+ for (i = 0; i < ERTS_ALC_A_COUNT; i++) {
erts_atomic_init_nob(&allctr->cpool.stat.blocks_size[i], 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_blocks[i], 0);
}
+
erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0);
erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0);
if (!init->ts && init->acul && init->acnl) {
@@ -7696,14 +7847,15 @@ typedef struct chist_node__ {
UWord carrier_size;
UWord unscanned_size;
- UWord allocated_size;
- /* BLOCKSCAN_BAILOUT_THRESHOLD guarantees we won't overflow this or the
- * counters in the free block histogram. */
- int allocated_count;
int flags;
- int histogram[1];
+ /* A mirror of the block counters in the carrier's ErtsAlcCPoolData_t. */
+ UWord alloc_count[ERTS_ALC_A_COUNT];
+ UWord alloc_size[ERTS_ALC_A_COUNT];
+
+ /* BLOCKSCAN_BAILOUT_THRESHOLD guarantees we won't overflow. */
+ int free_histogram[1];
} chist_node_t;
typedef struct {
@@ -7712,7 +7864,7 @@ typedef struct {
ErtsIRefStorage iref;
Process *process;
- Eterm allocator_desc;
+ int allocator_number;
chist_node_t *info_list;
UWord info_count;
@@ -7737,7 +7889,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
state = (gather_cinfo_t*)user_data;
node = calloc(1, sizeof(chist_node_t) +
(state->hist_slot_count - 1) *
- sizeof(node->histogram[0]));
+ sizeof(node->free_histogram[0]));
blocks_scanned = 1;
/* ERTS_CRR_ALCTR_FLG_BUSY is ignored since we've set it ourselves and it
@@ -7747,16 +7899,20 @@ static int gather_cinfo_scan(Allctr_t *allocator,
node->carrier_size = CARRIER_SZ(carrier);
if (IS_SB_CARRIER(carrier)) {
- UWord block_size;
-
- block = SBC2BLK(allocator, carrier);
- block_size = SBC_BLK_SZ(block);
+ int ix = allocator->alloc_no - ERTS_ALC_A_MIN;
- node->allocated_size = block_size;
- node->allocated_count = 1;
+ node->alloc_count[ix] = 1;
+ node->alloc_size[ix] = node->carrier_size;
} else {
UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
+ sys_memcpy(&node->alloc_count[0],
+ &carrier->cpool.blocks[0],
+ sizeof(UWord) * ERTS_ALC_A_COUNT);
+ sys_memcpy(&node->alloc_size[0],
+ &carrier->cpool.blocks_size[0],
+ sizeof(UWord) * ERTS_ALC_A_COUNT);
+
block = MBC_TO_FIRST_BLK(allocator, carrier);
while (1) {
@@ -7764,10 +7920,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
scanned_bytes += block_size;
- if (IS_ALLOCED_BLK(block)) {
- node->allocated_size += block_size;
- node->allocated_count++;
- } else {
+ if (IS_FREE_BLK(block)) {
UWord size_interval;
int hist_slot;
@@ -7776,7 +7929,7 @@ static int gather_cinfo_scan(Allctr_t *allocator,
hist_slot = MIN(size_interval, state->hist_slot_count - 1);
- node->histogram[hist_slot]++;
+ node->free_histogram[hist_slot]++;
}
if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
@@ -7801,46 +7954,89 @@ static int gather_cinfo_scan(Allctr_t *allocator,
static void gather_cinfo_append_result(gather_cinfo_t *state,
chist_node_t *info)
{
- Eterm carrier_size, unscanned_size, allocated_size;
- Eterm histogram_tuple, carrier_tuple;
+ Eterm carrier_tuple, block_list, histogram_tuple;
+ Eterm carrier_size, unscanned_size;
Uint term_size;
Eterm *hp;
int ix;
ASSERT(state->building_result);
+ term_size = 0;
+
+ /* Free block histogram. */
+ term_size += 1 + state->hist_slot_count;
+
+ /* Per-type block list. */
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
+ UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
+
+ if (count > 0) {
+ /* We have at least one block of this type, so we'll need a cons
+ * cell and a 3-tuple; {Type, Count, Size}. */
+ term_size += 2 + 4;
+ term_size += IS_USMALL(0, count) ? 0 : BIG_UINT_HEAP_SIZE;
+ term_size += IS_USMALL(0, size) ? 0 : BIG_UINT_HEAP_SIZE;
+ }
+ }
- term_size = 11 + state->hist_slot_count;
+ /* Carrier tuple and its fields. */
+ term_size += 7;
term_size += IS_USMALL(0, info->carrier_size) ? 0 : BIG_UINT_HEAP_SIZE;
term_size += IS_USMALL(0, info->unscanned_size) ? 0 : BIG_UINT_HEAP_SIZE;
- term_size += IS_USMALL(0, info->allocated_size) ? 0 : BIG_UINT_HEAP_SIZE;
+ /* ... and a finally a cons cell to keep the result in. */
+ term_size += 2;
+
+ /* * * */
hp = erts_produce_heap(&state->msg_factory, term_size, 0);
- hp[0] = make_arityval(state->hist_slot_count);
+ block_list = NIL;
+ for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
+ UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
+ UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
- for (ix = 0; ix < state->hist_slot_count; ix++) {
- hp[1 + ix] = make_small(info->histogram[ix]);
+ if (count > 0) {
+ Eterm block_count, block_size;
+ Eterm alloc_tuple;
+
+ block_count = bld_unstable_uint(&hp, NULL, count);
+ block_size = bld_unstable_uint(&hp, NULL, size);
+
+ hp[0] = make_arityval(3);
+ hp[1] = alloc_num_atoms[ix];
+ hp[2] = block_count;
+ hp[3] = block_size;
+
+ alloc_tuple = make_tuple(hp);
+ hp += 4;
+
+ block_list = CONS(hp, alloc_tuple, block_list);
+ hp += 2;
+ }
}
+ hp[0] = make_arityval(state->hist_slot_count);
+ for (ix = 0; ix < state->hist_slot_count; ix++) {
+ hp[1 + ix] = make_small(info->free_histogram[ix]);
+ }
histogram_tuple = make_tuple(hp);
hp += 1 + state->hist_slot_count;
carrier_size = bld_unstable_uint(&hp, NULL, info->carrier_size);
unscanned_size = bld_unstable_uint(&hp, NULL, info->unscanned_size);
- allocated_size = bld_unstable_uint(&hp, NULL, info->allocated_size);
- hp[0] = make_arityval(7);
- hp[1] = state->allocator_desc;
- hp[2] = carrier_size;
- hp[3] = unscanned_size;
- hp[4] = allocated_size;
- hp[5] = make_small(info->allocated_count);
- hp[6] = (info->flags & ERTS_CRR_ALCTR_FLG_IN_POOL) ? am_true : am_false;
- hp[7] = histogram_tuple;
+ hp[0] = make_arityval(6);
+ hp[1] = alloc_num_atoms[state->allocator_number];
+ hp[2] = (info->flags & ERTS_CRR_ALCTR_FLG_IN_POOL) ? am_true : am_false;
+ hp[3] = carrier_size;
+ hp[4] = unscanned_size;
+ hp[5] = block_list;
+ hp[6] = histogram_tuple;
carrier_tuple = make_tuple(hp);
- hp += 8;
+ hp += 7;
state->result_list = CONS(hp, carrier_tuple, state->result_list);
@@ -7936,8 +8132,6 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
{
gather_cinfo_t *gather_state;
blockscan_t *scanner;
-
- const char *allocator_desc;
Allctr_t *allocator;
ASSERT(is_internal_ref(ref));
@@ -7948,7 +8142,7 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
return 0;
}
- allocator_desc = ERTS_ALC_A2AD(allocator_num);
+ ensure_atoms_initialized(allocator);
/* Plain calloc is intentional. */
gather_state = (gather_cinfo_t*)calloc(1, sizeof(gather_cinfo_t));
@@ -7959,9 +8153,7 @@ int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
scanner->finish = gather_cinfo_finish;
scanner->user_data = gather_state;
- gather_state->allocator_desc = erts_atom_put((byte *)allocator_desc,
- sys_strlen(allocator_desc),
- ERTS_ATOM_ENC_LATIN1, 1);
+ gather_state->allocator_number = allocator_num;
erts_iref_storage_save(&gather_state->iref, ref);
gather_state->hist_slot_start = hist_start * 2;
gather_state->hist_slot_count = hist_width;
@@ -8069,14 +8261,22 @@ void
erts_alcu_verify_unused(Allctr_t *allctr)
{
UWord no;
+ int ix;
- no = allctr->sbcs.curr.norm.mseg.no;
- no += allctr->sbcs.curr.norm.sys_alloc.no;
- no += allctr->mbcs.blocks.curr.no;
+ no = 0;
+ for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
+ no += allctr->sbcs.carriers[ix].no;
+ }
+
+ ix = allctr->alloc_no - ERTS_ALC_A_MIN;
+ no += allctr->mbcs.blocks[ix].curr.no;
if (no) {
- UWord sz = allctr->sbcs.blocks.curr.size;
- sz += allctr->mbcs.blocks.curr.size;
+ UWord sz = 0;
+
+ sz += allctr->sbcs.blocks[ix].curr.size;
+ sz += allctr->mbcs.blocks[ix].curr.size;
+
erts_exit(ERTS_ABORT_EXIT,
"%salloc() used when expected to be unused!\n"
"Total amount of blocks allocated: %bpu\n"
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index b46b311c59..07cbd8470e 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -456,8 +456,8 @@ typedef struct {
ErtsThrPrgrVal thr_prgr;
erts_atomic_t max_size;
UWord abandon_limit;
- UWord blocks[ERTS_ALC_A_MAX + 1];
- UWord blocks_size[ERTS_ALC_A_MAX + 1];
+ UWord blocks[ERTS_ALC_A_COUNT];
+ UWord blocks_size[ERTS_ALC_A_COUNT];
UWord total_blocks_size;
enum {
ERTS_MBC_IS_HOME,
@@ -492,19 +492,28 @@ typedef struct {
} StatValues_t;
typedef struct {
- union {
- struct {
- StatValues_t mseg;
- StatValues_t sys_alloc;
- } norm;
- } curr;
- StatValues_t max;
- StatValues_t max_ever;
- struct {
- StatValues_t curr;
- StatValues_t max;
- StatValues_t max_ever;
- } blocks;
+ StatValues_t curr;
+ StatValues_t max;
+ StatValues_t max_ever;
+} BlockStats_t;
+
+enum {
+ ERTS_CRR_ALLOC_MIN = 0,
+
+ ERTS_CRR_ALLOC_MSEG = ERTS_CRR_ALLOC_MIN,
+ ERTS_CRR_ALLOC_SYS = 1,
+
+ ERTS_CRR_ALLOC_MAX,
+ ERTS_CRR_ALLOC_COUNT = ERTS_CRR_ALLOC_MAX + 1
+};
+
+typedef struct {
+ StatValues_t carriers[ERTS_CRR_ALLOC_COUNT];
+
+ StatValues_t max;
+ StatValues_t max_ever;
+
+ BlockStats_t blocks[ERTS_ALC_A_COUNT];
} CarriersStats_t;
#ifdef USE_LTTNG_VM_TRACEPOINTS
@@ -654,8 +663,8 @@ struct Allctr_t_ {
UWord in_pool_limit; /* acnl */
UWord fblk_min_limit; /* acmfl */
struct {
- erts_atomic_t blocks_size[ERTS_ALC_A_MAX + 1];
- erts_atomic_t no_blocks[ERTS_ALC_A_MAX + 1];
+ erts_atomic_t blocks_size[ERTS_ALC_A_COUNT];
+ erts_atomic_t no_blocks[ERTS_ALC_A_COUNT];
erts_atomic_t carriers_size;
erts_atomic_t no_carriers;
CallCounter_t fail_pooled;
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 44655ad5df..d160cda4df 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -254,25 +254,6 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
#endif
erts_thr_q_enqueue(&q->thr_q, a);
-#ifdef USE_LTTNG_VM_TRACEPOINTS
- if (LTTNG_ENABLED(aio_pool_put)) {
- lttng_decl_portbuf(port_str);
- lttng_portid_to_str(a->port, port_str);
- LTTNG2(aio_pool_put, port_str, -1);
- }
-#endif
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(aio_pool_add)) {
- DTRACE_CHARBUF(port_str, 16);
-
- 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);
- }
- gcc_optimizer_hack++;
-#endif
}
static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
@@ -293,25 +274,6 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
erts_thr_q_get_finalize_dequeue_data(q, &a->q.fin_deq);
if (saved_fin_deq)
erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq);
-#ifdef USE_LTTNG_VM_TRACEPOINTS
- if (LTTNG_ENABLED(aio_pool_get)) {
- lttng_decl_portbuf(port_str);
- int length = erts_thr_q_length_dirty(q);
- lttng_portid_to_str(a->port, port_str);
- LTTNG2(aio_pool_get, port_str, length);
- }
-#endif
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(aio_pool_get)) {
- DTRACE_CHARBUF(port_str, 16);
-
- 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);
- }
-#endif
return a;
}
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index b8e56390c1..03b8e0e632 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -2413,7 +2413,7 @@ 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, bif_export[BIF_binary_list_to_bin_1]);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_binary_list_to_bin_1]);
}
typedef struct {
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index cce8472ccb..4cab9bec1d 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -327,7 +327,7 @@ crc32_1(BIF_ALIST_1)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -354,7 +354,7 @@ crc32_2(BIF_ALIST_2)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -407,7 +407,7 @@ adler32_1(BIF_ALIST_1)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -434,7 +434,7 @@ adler32_2(BIF_ALIST_2)
res_sum = erts_make_integer(chksum,BIF_P);
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest);
}
BIF_RET(res_sum);
}
@@ -575,7 +575,7 @@ md5_update_2(BIF_ALIST_2)
bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX));
if (rest != NIL) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_md5_update_2], BIF_P, bin, rest);
+ BIF_TRAP2(&bif_trap_export[BIF_md5_update_2], BIF_P, bin, rest);
}
BUMP_REDS(BIF_P,res);
BIF_RET(bin);
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index 09757e473b..053797bc89 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -239,7 +239,7 @@ static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3)
* Signal an error. The original argument was tucked away in BIF_ARG_3.
*/
ERTS_BIF_ERROR_TRAPPED1(BIF_P, BIF_P->freason,
- bif_export[BIF_length_1], BIF_ARG_3);
+ &bif_trap_export[BIF_length_1], BIF_ARG_3);
}
}
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 1064c89d84..26020ef5b2 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -158,8 +158,9 @@ static Eterm os_version_tuple;
static Eterm
current_function(Process* p, ErtsHeapFactory *hfact, Process* rp,
int full_info, Uint reserve_size, int flags);
-static Eterm current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size);
+static Eterm
+current_stacktrace(Process* p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags);
Eterm
erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail)
@@ -548,6 +549,8 @@ static int collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp, Sint reds)
case ERTS_MON_TYPE_PORT:
case ERTS_MON_TYPE_DIST_PROC:
case ERTS_MON_TYPE_TIME_OFFSET:
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING)
+ break; /* Not an active monitor... */
if (!(mon->flags & ERTS_ML_FLG_NAME)) {
micp->mi[micp->mi_i].named = 0;
micp->mi[micp->mi_i].entity.term = mon->other.item;
@@ -1249,9 +1252,9 @@ exited:
yield:
if (pi2)
- ERTS_BIF_PREP_YIELD2(ret, bif_export[BIF_process_info_2], c_p, pid, opt);
+ ERTS_BIF_PREP_YIELD2(ret, &bif_trap_export[BIF_process_info_2], c_p, pid, opt);
else
- ERTS_BIF_PREP_YIELD1(ret, bif_export[BIF_process_info_1], c_p, pid);
+ ERTS_BIF_PREP_YIELD1(ret, &bif_trap_export[BIF_process_info_1], c_p, pid);
goto done;
send_signal: {
@@ -1384,7 +1387,7 @@ process_info_aux(Process *c_p,
break;
case ERTS_PI_IX_CURRENT_STACKTRACE:
- res = current_stacktrace(hfact, rp, reserve_size);
+ res = current_stacktrace(c_p, hfact, rp, reserve_size, flags);
break;
case ERTS_PI_IX_INITIAL_CALL:
@@ -2022,19 +2025,23 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
if (c_p == rp && !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) {
- FunctionInfo fi2;
+ BeamInstr* return_address;
+ FunctionInfo caller_fi;
- /*
- * The current function is erlang:process_info/{1,2},
- * which is not the answer that the application want.
- * We will use the function pointed into by rp->cp
- * instead if it can be looked up.
- */
- erts_lookup_function_info(&fi2, rp->cp, full_info);
- if (fi2.mfa) {
- fi = fi2;
- rp->current = fi2.mfa;
- }
+ /*
+ * The current function is erlang:process_info/{1,2}, and we've
+ * historically returned the *calling* function in that case. We
+ * therefore use the continuation pointer stored at the top of the
+ * stack instead, which is safe since process_info is a "heavy" BIF
+ * that is only called through its export entry.
+ */
+ return_address = erts_printable_return_address(rp, STACK_TOP(rp));
+
+ erts_lookup_function_info(&caller_fi, return_address, full_info);
+ if (caller_fi.mfa) {
+ fi = caller_fi;
+ rp->current = caller_fi.mfa;
+ }
}
/*
@@ -2055,8 +2062,8 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
static Eterm
-current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size)
+current_stacktrace(Process *p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags)
{
Uint sz;
struct StackTrace* s;
@@ -2073,13 +2080,14 @@ current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth;
s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz);
s->depth = 0;
- if (depth > 0 && rp->i) {
- s->trace[s->depth++] = rp->i;
- depth--;
- }
- if (depth > 0 && rp->cp != 0) {
- s->trace[s->depth++] = rp->cp - 1;
- depth--;
+ s->pc = NULL;
+
+ /* We skip current pc when requesting our own stack trace since it will
+ * inevitably point to process_info/1,2 */
+ if ((p != rp || (flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) &&
+ depth > 0 && rp->i) {
+ s->trace[s->depth++] = rp->i;
+ depth--;
}
erts_save_stacktrace(rp, s, depth);
@@ -2814,7 +2822,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (BIF_ARG_1 == am_threads) {
return am_true;
} else if (BIF_ARG_1 == am_creation) {
- return make_small(erts_this_node->creation);
+ Uint hsz = 0;
+ erts_bld_uint(NULL, &hsz, erts_this_node->creation);
+ hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
+ BIF_RET(erts_bld_uint(&hp, NULL, erts_this_node->creation));
} else if (BIF_ARG_1 == am_break_ignored) {
extern int ignore_break;
if (ignore_break)
@@ -2825,7 +2836,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
/* Arguments that are unusual follow ... */
else if (ERTS_IS_ATOM_STR("logical_processors", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(&no, NULL, NULL);
+ erts_get_logical_processors(&no, NULL, NULL, NULL);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -2835,7 +2846,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
}
else if (ERTS_IS_ATOM_STR("logical_processors_online", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(NULL, &no, NULL);
+ erts_get_logical_processors(NULL, &no, NULL, NULL);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -2845,7 +2856,17 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
}
else if (ERTS_IS_ATOM_STR("logical_processors_available", BIF_ARG_1)) {
int no;
- erts_get_logical_processors(NULL, NULL, &no);
+ erts_get_logical_processors(NULL, NULL, &no, NULL);
+ if (no > 0)
+ BIF_RET(make_small((Uint) no));
+ else {
+ DECL_AM(unknown);
+ BIF_RET(AM_unknown);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("cpu_quota", BIF_ARG_1)) {
+ int no;
+ erts_get_logical_processors(NULL, NULL, NULL, &no);
if (no > 0)
BIF_RET(make_small((Uint) no));
else {
@@ -4043,7 +4064,16 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(am_notsup);
#endif
}
-
+ else if (ERTS_IS_ATOM_STR("flxctr_memory_usage", BIF_ARG_1)) {
+ Sint mem = erts_flxctr_debug_memory_usage();
+ if (mem == -1) {
+ BIF_RET(am_notsup);
+ } else {
+ Uint hsz = BIG_UWORD_HEAP_SIZE((UWord)mem);
+ Eterm *hp = HAlloc(BIF_P, hsz);
+ BIF_RET(uword_to_big((UWord)mem, hp));
+ }
+ }
}
else if (is_tuple(BIF_ARG_1)) {
Eterm* tp = tuple_val(BIF_ARG_1);
@@ -4254,9 +4284,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
}
}
else if (ERTS_IS_ATOM_STR("term_to_binary_tuple_fallbacks", tp[1])) {
- Uint dflags = (TERM_TO_BINARY_DFLAGS
- & ~DFLAG_EXPORT_PTR_TAG
- & ~DFLAG_BIT_BINARIES);
+ Uint64 dflags = (TERM_TO_BINARY_DFLAGS
+ & ~DFLAG_EXPORT_PTR_TAG
+ & ~DFLAG_BIT_BINARIES);
Eterm res = erts_term_to_binary(BIF_P, tp[2], 0, dflags);
if (is_value(res))
BIF_RET(res);
@@ -4374,6 +4404,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
break;
BIF_RET(res);
}
+ else if (ERTS_IS_ATOM_STR("term_to_binary", tp[1])) {
+ return erts_debug_term_to_binary(BIF_P, tp[2], tp[3]);
+ }
break;
}
default:
@@ -4593,7 +4626,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
if (!flag && BIF_ARG_2 != am_false) {
erts_atomic_set_nob(&hipe_test_reschedule_flag, 1);
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
- ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_set_internal_state_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_set_internal_state_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
erts_atomic_set_nob(&hipe_test_reschedule_flag, !flag);
@@ -4639,9 +4672,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
flag = ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS;
else if (ERTS_IS_ATOM_STR("aux_work", BIF_ARG_2))
flag = ERTS_DEBUG_WAIT_COMPLETED_AUX_WORK;
+ else if (ERTS_IS_ATOM_STR("thread_progress", BIF_ARG_2))
+ flag = ERTS_DEBUG_WAIT_COMPLETED_THREAD_PROGRESS;
- if (flag && erts_debug_wait_completed(BIF_P, flag)) {
- ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
+ if (flag) {
+ if (erts_debug_wait_completed(BIF_P, flag))
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
+ else
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
}
else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) {
@@ -4720,7 +4758,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
else if (ERTS_IS_ATOM_STR("ets_debug_random_split_join", BIF_ARG_1)) {
if (is_tuple(BIF_ARG_2)) {
Eterm* tpl = tuple_val(BIF_ARG_2);
-
if (erts_ets_debug_random_split_join(tpl[1], tpl[2] == am_true))
BIF_RET(am_ok);
}
@@ -4735,11 +4772,225 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_P->mbuf_sz += sz;
BIF_RET(copy);
}
+ else if (ERTS_IS_ATOM_STR("remove_hopefull_dflags", BIF_ARG_1)) {
+ int old_val, new_val;
+
+ switch (BIF_ARG_2) {
+ case am_true: new_val = !0; break;
+ case am_false: new_val = 0; break;
+ default: BIF_ERROR(BIF_P, BADARG); break;
+ }
+
+ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+
+ old_val = erts_dflags_test_remove_hopefull_flags;
+ erts_dflags_test_remove_hopefull_flags = new_val;
+
+ erts_thr_progress_unblock();
+ erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(old_val ? am_true : am_false);
+ }
}
BIF_ERROR(BIF_P, BADARG);
}
+Eterm
+erts_get_ethread_info(Process *c_p)
+{
+ Uint sz, *szp;
+ Eterm res, *hp, **hpp, *end_hp = NULL;
+
+ sz = 0;
+ szp = &sz;
+ hpp = NULL;
+
+ while (1) {
+ Eterm tup, list, name;
+#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \
+ || defined(ETHR_NATIVE_ATOMIC64_IMPL) \
+ || defined(ETHR_NATIVE_DW_ATOMIC_IMPL)
+ char buf[1024];
+ int i;
+ char **str;
+#endif
+
+ res = NIL;
+
+#ifdef ETHR_X86_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "sse2"),
+#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
+ erts_bld_string(hpp, szp,
+ (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
+ ? "yes" : "no"))
+#else
+ erts_bld_string(hpp, szp, "yes")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp,
+ "x86"
+#ifdef ARCH_64
+ "_64"
+#endif
+ " OOO"),
+ erts_bld_string(hpp, szp,
+#ifdef ETHR_X86_OUT_OF_ORDER
+ "yes"
+#else
+ "no"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+#endif
+
+#ifdef ETHR_SPARC_V9_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Sparc V9"),
+ erts_bld_string(hpp, szp,
+#if defined(ETHR_SPARC_TSO)
+ "TSO"
+#elif defined(ETHR_SPARC_PSO)
+ "PSO"
+#elif defined(ETHR_SPARC_RMO)
+ "RMO"
+#else
+ "undefined"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+#endif
+
+#ifdef ETHR_PPC_MEMBAR_H__
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "lwsync"),
+ erts_bld_string(hpp, szp,
+#if defined(ETHR_PPC_HAVE_LWSYNC)
+ "yes"
+#elif defined(ETHR_PPC_HAVE_NO_LWSYNC)
+ "no"
+#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__)
+ ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no"
+#else
+ "undefined"
+#endif
+ ));
+
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+#endif
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Native rw-spinlocks"),
+#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL
+ erts_bld_string(hpp, szp, ETHR_NATIVE_RWSPINLOCK_IMPL)
+#else
+ erts_bld_string(hpp, szp, "no")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ tup = erts_bld_tuple(hpp, szp, 2,
+ erts_bld_string(hpp, szp, "Native spinlocks"),
+#ifdef ETHR_NATIVE_SPINLOCK_IMPL
+ erts_bld_string(hpp, szp, ETHR_NATIVE_SPINLOCK_IMPL)
+#else
+ erts_bld_string(hpp, szp, "no")
+#endif
+ );
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+
+ list = NIL;
+#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL
+ if (ethr_have_native_dw_atomic()) {
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_DW_ATOMIC_IMPL);
+ str = ethr_native_dw_atomic_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_dw_atomic_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+ str = ethr_native_su_dw_atomic_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_su_dw_atomic_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+ }
+ else
+#endif
+ name = erts_bld_string(hpp, szp, "no");
+
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "Double word native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ list = NIL;
+#ifdef ETHR_NATIVE_ATOMIC64_IMPL
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC64_IMPL);
+ str = ethr_native_atomic64_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_atomic64_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+#else
+ name = erts_bld_string(hpp, szp, "no");
+#endif
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "64-bit native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ list = NIL;
+#ifdef ETHR_NATIVE_ATOMIC32_IMPL
+ name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC32_IMPL);
+ str = ethr_native_atomic32_ops();
+ for (i = 0; str[i]; i++) {
+ erts_snprintf(buf, sizeof(buf), "ethr_native_atomic32_%s()", str[i]);
+ list = erts_bld_cons(hpp, szp,
+ erts_bld_string(hpp, szp, buf),
+ list);
+ }
+#else
+ name = erts_bld_string(hpp, szp, "no");
+#endif
+ tup = erts_bld_tuple(hpp, szp, 3,
+ erts_bld_string(hpp, szp, "32-bit native atomics"),
+ name,
+ list);
+ res = erts_bld_cons(hpp, szp, tup, res);
+
+ if (hpp) {
+ HRelease(c_p, end_hp, *hpp)
+ return res;
+ }
+
+ hp = HAlloc(c_p, sz);
+ end_hp = hp + sz;
+ hpp = &hp;
+ szp = NULL;
+ }
+}
+
static BIF_RETTYPE
gather_histograms_helper(Process * c_p, Eterm arg_tuple,
int gather(Process *, int, int, int, UWord, Eterm))
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index fa2edfef1e..40512a117d 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -32,8 +32,7 @@
#include "bif.h"
#include "erl_binary.h"
-
-static Eterm keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
+static Eterm keyfind(Export* Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
/* erlang:'++'/2
*
@@ -308,12 +307,12 @@ static Eterm append(Export *bif_entry, BIF_ALIST_2) {
Eterm
ebif_plusplus_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS);
+ return append(&bif_trap_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS);
}
BIF_RETTYPE append_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_append_2], BIF_CALL_ARGS);
+ return append(&bif_trap_export[BIF_append_2], BIF_CALL_ARGS);
}
/* erlang:'--'/2
@@ -1039,11 +1038,11 @@ static Eterm subtract(Export *bif_entry, BIF_ALIST_2) {
}
BIF_RETTYPE ebif_minusminus_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS);
+ return subtract(&bif_trap_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS);
}
BIF_RETTYPE subtract_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_subtract_2], BIF_CALL_ARGS);
+ return subtract(&bif_trap_export[BIF_subtract_2], BIF_CALL_ARGS);
}
@@ -1068,7 +1067,7 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2)
while (is_list(list)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_lists_member_2], BIF_P, term, list);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_member_2], BIF_P, term, list);
}
item = CAR(list_val(list));
if ((item == term) || (non_immed_key && eq(item, term))) {
@@ -1130,7 +1129,7 @@ static BIF_RETTYPE lists_reverse_alloc(Process *c_p,
}
ASSERT(is_list(tail) && cells_left == 0);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail);
}
static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
@@ -1179,7 +1178,7 @@ static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
}
BUMP_ALL_REDS(c_p);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail);
}
BIF_ERROR(c_p, BADARG);
@@ -1209,7 +1208,7 @@ lists_keymember_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keymember_3, BIF_P,
+ res = keyfind(&bif_trap_export[BIF_lists_keymember_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_value(res) && is_tuple(res)) {
return am_true;
@@ -1223,7 +1222,7 @@ lists_keysearch_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keysearch_3, BIF_P,
+ res = keyfind(&bif_trap_export[BIF_lists_keysearch_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_non_value(res) || is_not_tuple(res)) {
return res;
@@ -1236,12 +1235,12 @@ lists_keysearch_3(BIF_ALIST_3)
BIF_RETTYPE
lists_keyfind_3(BIF_ALIST_3)
{
- return keyfind(BIF_lists_keyfind_3, BIF_P,
+ return keyfind(&bif_trap_export[BIF_lists_keyfind_3], BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
static Eterm
-keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
+keyfind(Export *Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
{
int max_iter = 10 * CONTEXT_REDS;
Sint pos;
@@ -1257,7 +1256,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1282,7 +1281,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1300,7 +1299,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 9dc5c66a0a..2610833a33 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -45,23 +45,45 @@
#define MUST_SHRINK(t) (((Uint)200) * t->num_entries <= LOAD_FACTOR * t->allocated && \
t->allocated > INITIAL_SIZE)
+
+typedef struct delete_op {
+ enum { DELETE_OP_TUPLE, DELETE_OP_TABLE } type;
+ struct delete_op* next;
+ ErtsThrPrgrLaterOp thr_prog_op;
+ int is_scheduled;
+} DeleteOp;
+
typedef struct hash_table {
Uint allocated;
Uint num_entries;
Uint mask;
Uint first_to_delete;
Uint num_to_delete;
- erts_atomic_t refc;
- struct hash_table* delete_next;
- ErtsThrPrgrLaterOp thr_prog_op;
- Eterm term[1];
+ DeleteOp delete_op;
+ erts_atomic_t term[1];
} HashTable;
+static ERTS_INLINE Eterm get_bucket(HashTable* tab, Uint idx)
+{
+ return (Eterm) erts_atomic_read_nob(&tab->term[idx]);
+}
+
+static ERTS_INLINE void set_bucket(HashTable* tab, Uint idx, Eterm term)
+{
+ erts_atomic_set_nob(&tab->term[idx], (erts_aint_t)term);
+}
+
+static ERTS_INLINE Uint sizeof_HashTable(Uint sz)
+{
+ return offsetof(HashTable, term) + (sz * sizeof(erts_atomic_t));
+}
+
typedef struct trap_data {
HashTable* table;
Uint idx;
Uint remaining;
Uint memory; /* Used by info/0 to count used memory */
+ int got_update_permission;
} TrapData;
typedef enum {
@@ -89,8 +111,7 @@ typedef struct {
} ErtsPersistentTermCpyTableCtx;
typedef enum {
- PUT2_TRAP_LOCATION_NEW_KEY,
- PUT2_TRAP_LOCATION_REPLACE_VALUE
+ PUT2_TRAP_LOCATION_NEW_KEY
} ErtsPersistentTermPut2TrapLocation;
typedef struct {
@@ -117,6 +138,7 @@ typedef struct {
Uint entry_index;
Eterm old_term;
HashTable* tmp_table;
+ int must_shrink;
ErtsPersistentTermCpyTableCtx cpy_ctx;
} ErtsPersistentTermErase1Context;
@@ -126,20 +148,21 @@ typedef struct {
static HashTable* create_initial_table(void);
static Uint lookup(HashTable* hash_table, Eterm key);
+static int is_erasable(HashTable* hash_table, Uint idx);
static HashTable* copy_table(ErtsPersistentTermCpyTableCtx* ctx);
static int try_seize_update_permission(Process* c_p);
static void release_update_permission(int release_updater);
static void table_updater(void* table);
-static void table_deleter(void* hash_table);
-static void dec_table_refc(Process* c_p, HashTable* old_table);
-static void delete_table(Process* c_p, HashTable* table);
+static void scheduled_deleter(void* delete_op);
+static void delete_table(HashTable* table);
+static void delete_tuple(Eterm term);
static void mark_for_deletion(HashTable* hash_table, Uint entry_index);
static ErtsLiteralArea* term_to_area(Eterm tuple);
static void suspend_updater(Process* c_p);
static Eterm do_get_all(Process* c_p, TrapData* trap_data, Eterm res);
static Eterm do_info(Process* c_p, TrapData* trap_data);
-static void append_to_delete_queue(HashTable* table);
-static HashTable* next_to_delete(void);
+static void append_to_delete_queue(DeleteOp*);
+static DeleteOp* list_to_delete(DeleteOp*);
static Eterm alloc_trap_data(Process* c_p);
static int cleanup_trap_data(Binary *bp);
@@ -174,13 +197,16 @@ static Process* updater_process = NULL;
/* Protected by update_table_permission_mtx */
static ErtsThrPrgrLaterOp thr_prog_op;
+static Uint fast_update_index;
+static Eterm fast_update_term = THE_NON_VALUE;
+
/*
* Queue of hash tables to be deleted.
*/
static erts_mtx_t delete_queue_mtx;
-static HashTable* delete_queue_head = NULL;
-static HashTable** delete_queue_tail = &delete_queue_head;
+static DeleteOp* delete_queue_head = NULL;
+static DeleteOp** delete_queue_tail = &delete_queue_head;
/*
* The following variables are only used during crash dumping. They
@@ -284,7 +310,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
long iterations_until_trap;
long max_iterations;
#define PUT_TRAP_CODE \
- BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
+ BIF_TRAP2(&bif_trap_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
#define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \
TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE)
@@ -306,12 +332,8 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
ctx = ERTS_MAGIC_BIN_DATA(state_bin);
ASSERT(BIF_P->flags & F_DISABLE_GC);
erts_set_gc_state(BIF_P, 1);
- switch (ctx->trap_location) {
- case PUT2_TRAP_LOCATION_NEW_KEY:
- goto L_PUT2_TRAP_LOCATION_NEW_KEY;
- case PUT2_TRAP_LOCATION_REPLACE_VALUE:
- goto L_PUT2_TRAP_LOCATION_REPLACE_VALUE;
- }
+ ASSERT(ctx->trap_location == PUT2_TRAP_LOCATION_NEW_KEY);
+ goto L_PUT2_TRAP_LOCATION_NEW_KEY;
} else {
/* Save state in magic bin in case trapping is necessary */
Eterm* hp;
@@ -329,7 +351,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2],
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_persistent_term_put_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
@@ -344,20 +366,19 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
ctx->heap[2] = ctx->term;
ctx->tuple = make_tuple(ctx->heap);
- if (is_nil(ctx->hash_table->term[ctx->entry_index])) {
- Uint new_size = ctx->hash_table->allocated;
+ if (is_nil(get_bucket(ctx->hash_table, ctx->entry_index))) {
if (MUST_GROW(ctx->hash_table)) {
- new_size *= 2;
+ Uint new_size = ctx->hash_table->allocated * 2;
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_NEW_KEY);
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
}
- TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
- ctx->hash_table,
- new_size,
- ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
- PUT2_TRAP_LOCATION_NEW_KEY);
- ctx->entry_index = lookup(ctx->hash_table, ctx->key);
ctx->hash_table->num_entries++;
} else {
- Eterm tuple = ctx->hash_table->term[ctx->entry_index];
+ Eterm tuple = get_bucket(ctx->hash_table, ctx->entry_index);
Eterm old_term;
ASSERT(is_tuple_arity(tuple, 2));
@@ -366,14 +387,6 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
/* Same value. No need to update anything. */
release_update_permission(0);
BIF_RET(am_ok);
- } else {
- /* Mark the old term for deletion. */
- mark_for_deletion(ctx->hash_table, ctx->entry_index);
- TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
- ctx->hash_table,
- ctx->hash_table->allocated,
- ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
- PUT2_TRAP_LOCATION_REPLACE_VALUE);
}
}
@@ -402,8 +415,21 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
literal_area->off_heap = code_off_heap.first;
DESTROY_SHCOPY(info);
erts_set_literal_tag(&ctx->tuple, literal_area->start, term_size);
- ctx->hash_table->term[ctx->entry_index] = ctx->tuple;
+ if (ctx->hash_table == (HashTable *) erts_atomic_read_nob(&the_hash_table)) {
+ /* Schedule fast update in active hash table */
+ fast_update_index = ctx->entry_index;
+ fast_update_term = ctx->tuple;
+ }
+ else {
+ /* Do update in copied table */
+ set_bucket(ctx->hash_table, ctx->entry_index, ctx->tuple);
+ }
+
+ /*
+ * Now wait thread progress before making update visible to guarantee
+ * consistent view of table&term without memory barrier in every get/1.
+ */
erts_schedule_thr_prgr_later_op(table_updater, ctx->hash_table, &thr_prog_op);
suspend_updater(BIF_P);
}
@@ -419,23 +445,25 @@ BIF_RETTYPE persistent_term_get_0(BIF_ALIST_0)
Eterm magic_ref;
Binary* mbp;
+ /* Prevent concurrent updates to get a consistent view */
+ if (!try_seize_update_permission(BIF_P)) {
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_persistent_term_get_0], BIF_P);
+ }
+
magic_ref = alloc_trap_data(BIF_P);
mbp = erts_magic_ref2bin(magic_ref);
trap_data = ERTS_MAGIC_BIN_DATA(mbp);
trap_data->table = hash_table;
trap_data->idx = 0;
trap_data->remaining = hash_table->num_entries;
+ trap_data->got_update_permission = 1;
res = do_get_all(BIF_P, trap_data, res);
if (trap_data->remaining == 0) {
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, hash_table->num_entries);
- trap_data->table = NULL; /* Prevent refc decrement */
BIF_RET(res);
} else {
- /*
- * Increment the ref counter to prevent an update operation (by put/2
- * or erase/1) to delete this hash table.
- */
- erts_atomic_inc_nob(&hash_table->refc);
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_get_all_export, BIF_P, magic_ref, res);
}
@@ -449,7 +477,7 @@ BIF_RETTYPE persistent_term_get_1(BIF_ALIST_1)
Eterm term;
entry_index = lookup(hash_table, key);
- term = hash_table->term[entry_index];
+ term = get_bucket(hash_table, entry_index);
if (is_boxed(term)) {
ASSERT(is_tuple_arity(term, 2));
BIF_RET(tuple_val(term)[2]);
@@ -466,7 +494,7 @@ BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2)
Eterm term;
entry_index = lookup(hash_table, key);
- term = hash_table->term[entry_index];
+ term = get_bucket(hash_table, entry_index);
if (is_boxed(term)) {
ASSERT(is_tuple_arity(term, 2));
result = tuple_val(term)[2];
@@ -507,7 +535,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
#endif
#define ERASE_TRAP_CODE \
- BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
+ BIF_TRAP1(&bif_trap_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
#define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \
TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE)
if (is_internal_magic_ref(BIF_ARG_1) &&
@@ -542,58 +570,75 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
ctx->tmp_table = NULL;
}
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_persistent_term_erase_1],
BIF_P, BIF_ARG_1);
}
ctx->key = BIF_ARG_1;
ctx->old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
ctx->entry_index = lookup(ctx->old_table, ctx->key);
- ctx->old_term = ctx->old_table->term[ctx->entry_index];
+ ctx->old_term = get_bucket(ctx->old_table, ctx->entry_index);
if (is_boxed(ctx->old_term)) {
- Uint new_size;
- /*
- * Since we don't use any delete markers, we must rehash
- * the table when deleting terms to ensure that all terms
- * can still be reached if there are hash collisions.
- * We can't rehash in place and it would not be safe to modify
- * the old table yet, so we will first need a new
- * temporary table copy of the same size as the old one.
- */
+ ctx->must_shrink = MUST_SHRINK(ctx->old_table);
+ if (!ctx->must_shrink && is_erasable(ctx->old_table, ctx->entry_index)) {
+ /*
+ * Fast erase in active hash table.
+ * We schedule with thread progress even here (see put/2).
+ * It's not needed for read consistenty of the NIL word, BUT it's
+ * needed to guarantee sequential read consistenty of multiple
+ * updates. As we do thread progress between all updates, there is
+ * no risk seeing them out of order.
+ */
+ fast_update_index = ctx->entry_index;
+ fast_update_term = NIL;
+ ctx->old_table->num_entries--;
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->old_table, &thr_prog_op);
+ }
+ else {
+ Uint new_size;
+ /*
+ * Since we don't use any delete markers, we must rehash the table
+ * to ensure that all terms can still be reached if there are
+ * hash collisions.
+ * We can't rehash in place and it would not be safe to modify
+ * the old table yet, so we will first need a new
+ * temporary table copy of the same size as the old one.
+ */
- ASSERT(is_tuple_arity(ctx->old_term, 2));
- TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
- ctx->old_table,
- ctx->old_table->allocated,
- ERTS_PERSISTENT_TERM_CPY_TEMP,
- ERASE1_TRAP_LOCATION_TMP_COPY);
+ ASSERT(is_tuple_arity(ctx->old_term, 2));
+ TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
+ ctx->old_table,
+ ctx->old_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_TEMP,
+ ERASE1_TRAP_LOCATION_TMP_COPY);
- /*
- * Delete the term from the temporary table. Then copy the
- * temporary table to a new table, rehashing the entries
- * while copying.
- */
+ /*
+ * Delete the term from the temporary table. Then copy the
+ * temporary table to a new table, rehashing the entries
+ * while copying.
+ */
- ctx->tmp_table->term[ctx->entry_index] = NIL;
- ctx->tmp_table->num_entries--;
- new_size = ctx->tmp_table->allocated;
- if (MUST_SHRINK(ctx->tmp_table)) {
- new_size /= 2;
- }
- TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
- ctx->tmp_table,
- new_size,
- ERTS_PERSISTENT_TERM_CPY_REHASH,
- ERASE1_TRAP_LOCATION_FINAL_COPY);
- erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
- /*
- * IMPORTANT: Memory management depends on that ctx->tmp_table
- * is set to NULL on the line below
- */
- ctx->tmp_table = NULL;
+ set_bucket(ctx->tmp_table, ctx->entry_index, NIL);
+ ctx->tmp_table->num_entries--;
+ new_size = ctx->tmp_table->allocated;
+ if (ctx->must_shrink) {
+ new_size /= 2;
+ }
+ TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
+ ctx->tmp_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_REHASH,
+ ERASE1_TRAP_LOCATION_FINAL_COPY);
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ /*
+ * IMPORTANT: Memory management depends on that ctx->tmp_table
+ * is set to NULL on the line below
+ */
+ ctx->tmp_table = NULL;
- mark_for_deletion(ctx->old_table, ctx->entry_index);
- erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
+ mark_for_deletion(ctx->old_table, ctx->entry_index);
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
+ }
suspend_updater(BIF_P);
BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
@@ -614,7 +659,7 @@ BIF_RETTYPE erts_internal_erase_persistent_terms_0(BIF_ALIST_0)
HashTable* new_table;
if (!try_seize_update_permission(BIF_P)) {
- ERTS_BIF_YIELD0(bif_export[BIF_erts_internal_erase_persistent_terms_0],
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_erts_internal_erase_persistent_terms_0],
BIF_P);
}
old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
@@ -634,6 +679,11 @@ BIF_RETTYPE persistent_term_info_0(BIF_ALIST_0)
Eterm magic_ref;
Binary* mbp;
+ /* Prevent concurrent updates to get a consistent view */
+ if (!try_seize_update_permission(BIF_P)) {
+ ERTS_BIF_YIELD0(&bif_trap_export[BIF_persistent_term_info_0], BIF_P);
+ }
+
magic_ref = alloc_trap_data(BIF_P);
mbp = erts_magic_ref2bin(magic_ref);
trap_data = ERTS_MAGIC_BIN_DATA(mbp);
@@ -641,29 +691,19 @@ BIF_RETTYPE persistent_term_info_0(BIF_ALIST_0)
trap_data->idx = 0;
trap_data->remaining = hash_table->num_entries;
trap_data->memory = 0;
+ trap_data->got_update_permission = 0;
res = do_info(BIF_P, trap_data);
if (trap_data->remaining == 0) {
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, hash_table->num_entries);
- trap_data->table = NULL; /* Prevent refc decrement */
BIF_RET(res);
} else {
- /*
- * Increment the ref counter to prevent an update operation (by put/2
- * or erase/1) to delete this hash table.
- */
- erts_atomic_inc_nob(&hash_table->refc);
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_info_export, BIF_P, magic_ref, res);
}
}
-Uint
-erts_persistent_term_count(void)
-{
- HashTable* hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- return hash_table->num_entries;
-}
-
void
erts_init_persistent_dumping(void)
{
@@ -677,15 +717,15 @@ erts_init_persistent_dumping(void)
*/
erts_persistent_areas = (ErtsLiteralArea **) hash_table->term;
- erts_num_persistent_areas = hash_table->num_entries;
area_p = erts_persistent_areas;
for (i = 0; i < hash_table->allocated; i++) {
- Eterm term = hash_table->term[i];
+ Eterm term = get_bucket(hash_table, i);
if (is_boxed(term)) {
*area_p++ = term_to_area(term);
}
}
+ erts_num_persistent_areas = area_p - erts_persistent_areas;
}
/*
@@ -699,16 +739,14 @@ create_initial_table(void)
int i;
hash_table = (HashTable *) erts_alloc(ERTS_ALC_T_PERSISTENT_TERM,
- sizeof(HashTable)+sizeof(Eterm) *
- (INITIAL_SIZE-1));
+ sizeof_HashTable(INITIAL_SIZE));
hash_table->allocated = INITIAL_SIZE;
hash_table->num_entries = 0;
hash_table->mask = INITIAL_SIZE-1;
hash_table->first_to_delete = 0;
hash_table->num_to_delete = 0;
- erts_atomic_init_nob(&hash_table->refc, (erts_aint_t)1);
for (i = 0; i < INITIAL_SIZE; i++) {
- hash_table->term[i] = NIL;
+ erts_atomic_init_nob(&hash_table->term[i], NIL);
}
return hash_table;
}
@@ -731,12 +769,8 @@ persistent_term_get_all_trap(BIF_ALIST_2)
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(&persistent_term_get_all_export, BIF_P, BIF_ARG_1, res);
} else {
- /*
- * Decrement ref count (and possibly delete the hash table
- * and associated literal area).
- */
- dec_table_refc(BIF_P, trap_data->table);
- trap_data->table = NULL; /* Prevent refc decrement */
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, bump_reds);
BIF_RET(res);
}
@@ -774,7 +808,9 @@ do_get_all(Process* c_p, TrapData* trap_data, Eterm res)
i = 0;
heap_size = (2 + 3) * remaining;
while (remaining != 0) {
- Eterm term = hash_table->term[idx];
+ Eterm term;
+ ASSERT(idx < hash_table->allocated);
+ term = get_bucket(hash_table, idx);
if (is_tuple(term)) {
Uint key_size;
Eterm* tup_val;
@@ -829,12 +865,8 @@ persistent_term_info_trap(BIF_ALIST_1)
BUMP_ALL_REDS(BIF_P);
BIF_TRAP1(&persistent_term_info_export, BIF_P, BIF_ARG_1);
} else {
- /*
- * Decrement ref count (and possibly delete the hash table
- * and associated literal area).
- */
- dec_table_refc(BIF_P, trap_data->table);
- trap_data->table = NULL; /* Prevent refc decrement */
+ release_update_permission(0);
+ trap_data->got_update_permission = 0;
BUMP_REDS(BIF_P, bump_reds);
ASSERT(is_map(res));
BIF_RET(res);
@@ -861,9 +893,9 @@ do_info(Process* c_p, TrapData* trap_data)
remaining = trap_data->remaining < max_iter ? trap_data->remaining : max_iter;
trap_data->remaining -= remaining;
while (remaining != 0) {
- if (is_boxed(hash_table->term[idx])) {
+ if (is_boxed(get_bucket(hash_table, idx))) {
ErtsLiteralArea* area;
- area = term_to_area(hash_table->term[idx]);
+ area = term_to_area(get_bucket(hash_table, idx));
trap_data->memory += sizeof(ErtsLiteralArea) +
sizeof(Eterm) * (area->end - area->start - 1);
remaining--;
@@ -911,13 +943,8 @@ cleanup_trap_data(Binary *bp)
{
TrapData* trap_data = ERTS_MAGIC_BIN_DATA(bp);
- if (trap_data->table) {
- /*
- * The process has been killed and is now exiting.
- * Decrement the reference counter for the table.
- */
- dec_table_refc(NULL, trap_data->table);
- }
+ if (trap_data->got_update_permission)
+ release_update_permission(0);
return 1;
}
@@ -925,17 +952,26 @@ static Uint
lookup(HashTable* hash_table, Eterm key)
{
Uint mask = hash_table->mask;
- Eterm* table = hash_table->term;
Uint32 idx = make_internal_hash(key, 0);
Eterm term;
- do {
+ while (1) {
+ term = get_bucket(hash_table, idx & mask);
+ if (is_nil(term) || EQ(key, (tuple_val(term))[1]))
+ break;
idx++;
- term = table[idx & mask];
- } while (is_boxed(term) && !EQ(key, (tuple_val(term))[1]));
+ }
return idx & mask;
}
+static int
+is_erasable(HashTable* hash_table, Uint idx)
+{
+ /* It's ok to erase [idx] if it's not a stepping stone to [idx+1] */
+ return get_bucket(hash_table, (idx + 1) & hash_table->mask) == NIL;
+}
+
+
static HashTable*
copy_table(ErtsPersistentTermCpyTableCtx* ctx)
{
@@ -956,8 +992,7 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
alloc_type = ERTS_ALC_T_PERSISTENT_TERM;
}
ctx->new_table = (HashTable *) erts_alloc(alloc_type,
- sizeof(HashTable) +
- sizeof(Eterm) * (ctx->new_size-1));
+ sizeof_HashTable(ctx->new_size));
if (ctx->old_table->allocated == ctx->new_size &&
(ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_NO_REHASH ||
ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP)) {
@@ -970,7 +1005,8 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
ctx->new_size);
i++) {
- ctx->new_table->term[i] = ctx->old_table->term[i];
+ erts_atomic_init_nob(&ctx->new_table->term[i],
+ erts_atomic_read_nob(&ctx->old_table->term[i]));
}
ctx->total_iterations_done = (i - ctx->iterations_done);
if (i < ctx->new_size) {
@@ -993,7 +1029,7 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
ctx->new_size);
i++) {
- ctx->new_table->term[i] = NIL;
+ erts_atomic_init_nob(&ctx->new_table->term[i], (erts_aint_t)NIL);
}
ctx->total_iterations_done = (i - ctx->iterations_done);
ctx->max_iterations -= ctx->total_iterations_done;
@@ -1008,11 +1044,12 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
i < MIN(ctx->iterations_done + ctx->max_iterations,
old_size);
i++) {
- if (is_tuple(ctx->old_table->term[i])) {
- Eterm key = tuple_val(ctx->old_table->term[i])[1];
+ Eterm term = get_bucket(ctx->old_table, i);
+ if (is_tuple(term)) {
+ Eterm key = tuple_val(term)[1];
Uint entry_index = lookup(ctx->new_table, key);
- ASSERT(is_nil(ctx->new_table->term[entry_index]));
- ctx->new_table->term[entry_index] = ctx->old_table->term[i];
+ ASSERT(is_nil(get_bucket(ctx->new_table, entry_index)));
+ set_bucket(ctx->new_table, entry_index, term);
}
}
ctx->total_iterations_done += (i - ctx->iterations_done);
@@ -1025,7 +1062,6 @@ copy_table(ErtsPersistentTermCpyTableCtx* ctx)
}
ctx->new_table->first_to_delete = 0;
ctx->new_table->num_to_delete = 0;
- erts_atomic_init_nob(&ctx->new_table->refc, (erts_aint_t)1);
{
HashTable* new_table = ctx->new_table;
/*
@@ -1052,47 +1088,118 @@ term_to_area(Eterm tuple)
offsetof(ErtsLiteralArea, start));
}
+typedef struct {
+ Eterm term;
+ ErtsLiteralArea* area;
+ DeleteOp delete_op;
+} OldLiteral;
+
+static OldLiteral* alloc_old_literal(void)
+{
+ return erts_alloc(ERTS_ALC_T_RELEASE_LAREA, sizeof(OldLiteral));
+}
+
+static void free_old_literal(OldLiteral* olp)
+{
+ return erts_free(ERTS_ALC_T_RELEASE_LAREA, olp);
+}
+
static void
table_updater(void* data)
{
HashTable* old_table;
HashTable* new_table;
+ UWord cleanup_bytes;
old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
new_table = (HashTable *) data;
- ASSERT(new_table->num_to_delete == 0);
- erts_atomic_set_nob(&the_hash_table, (erts_aint_t)new_table);
- append_to_delete_queue(old_table);
- erts_schedule_thr_prgr_later_op(table_deleter,
- old_table,
- &old_table->thr_prog_op);
- release_update_permission(1);
-}
-
-static void
-table_deleter(void* data)
-{
- HashTable* old_table = (HashTable *) data;
+ if (new_table == old_table) {
+ Eterm old_term = get_bucket(old_table, fast_update_index);
+ ASSERT(is_value(fast_update_term));
+ ASSERT(fast_update_index < old_table->allocated);
+ set_bucket(old_table, fast_update_index, fast_update_term);
+#ifdef DEBUG
+ fast_update_term = THE_NON_VALUE;
+#endif
- dec_table_refc(NULL, old_table);
+ if (is_not_nil(old_term)) {
+ OldLiteral *olp = alloc_old_literal();
+ ASSERT(is_tuple_arity(old_term,2));
+ olp->term = old_term;
+ olp->area = term_to_area(old_term);
+ olp->delete_op.type = DELETE_OP_TUPLE;
+ olp->delete_op.is_scheduled = 1;
+ append_to_delete_queue(&olp->delete_op);
+ cleanup_bytes = (ERTS_LITERAL_AREA_SIZE(olp->area)
+ + sizeof(OldLiteral));
+ erts_schedule_thr_prgr_later_cleanup_op(scheduled_deleter,
+ &olp->delete_op,
+ &olp->delete_op.thr_prog_op,
+ cleanup_bytes);
+ }
+ }
+ else {
+ ASSERT(is_non_value(fast_update_term));
+ ASSERT(new_table->num_to_delete == 0);
+ erts_atomic_set_nob(&the_hash_table, (erts_aint_t)new_table);
+ old_table->delete_op.type = DELETE_OP_TABLE;
+ old_table->delete_op.is_scheduled = 1;
+ append_to_delete_queue(&old_table->delete_op);
+ cleanup_bytes = sizeof_HashTable(old_table->allocated);
+ if (old_table->num_to_delete <= 1) {
+ if (old_table->num_to_delete == 1) {
+ ErtsLiteralArea* area;
+ area = term_to_area(get_bucket(old_table,
+ old_table->first_to_delete));
+ cleanup_bytes += ERTS_LITERAL_AREA_SIZE(area);
+ }
+ erts_schedule_thr_prgr_later_cleanup_op(scheduled_deleter,
+ &old_table->delete_op,
+ &old_table->delete_op.thr_prog_op,
+ cleanup_bytes);
+ }
+ else {
+ /* Only at init:restart(). Don't bother with total cleanup size. */
+ ASSERT(old_table->num_to_delete == old_table->allocated);
+ erts_schedule_thr_prgr_later_op(scheduled_deleter,
+ &old_table->delete_op,
+ &old_table->delete_op.thr_prog_op);
+ }
+ }
+ release_update_permission(1);
}
static void
-dec_table_refc(Process* c_p, HashTable* old_table)
+scheduled_deleter(void* data)
{
- erts_aint_t refc = erts_atomic_dec_read_nob(&old_table->refc);
-
- if (refc == 0) {
- HashTable* to_delete;
-
- while ((to_delete = next_to_delete()) != NULL) {
- delete_table(c_p, to_delete);
+ DeleteOp* dop = (DeleteOp*)data;
+
+ dop = list_to_delete(dop);
+
+ while (dop) {
+ DeleteOp* next = dop->next;
+ ASSERT(!dop->is_scheduled);
+ switch (dop->type) {
+ case DELETE_OP_TUPLE: {
+ OldLiteral* olp = ErtsContainerStruct(dop, OldLiteral, delete_op);
+ delete_tuple(olp->term);
+ free_old_literal(olp);
+ break;
+ }
+ case DELETE_OP_TABLE: {
+ HashTable* table = ErtsContainerStruct(dop, HashTable, delete_op);
+ delete_table(table);
+ break;
+ }
+ default:
+ ASSERT(!!"Invalid DeleteOp");
}
+ dop = next;
}
}
static void
-delete_table(Process* c_p, HashTable* table)
+delete_table(HashTable* table)
{
Uint idx = table->first_to_delete;
Uint n = table->num_to_delete;
@@ -1106,25 +1213,32 @@ delete_table(Process* c_p, HashTable* table)
#ifdef DEBUG
if (n == 1) {
- ASSERT(is_tuple_arity(table->term[idx], 2));
+ ASSERT(is_tuple_arity(get_bucket(table, idx), 2));
}
#endif
while (n > 0) {
- Eterm term = table->term[idx];
-
- if (is_tuple_arity(term, 2)) {
- if (is_immed(tuple_val(term)[2])) {
- erts_release_literal_area(term_to_area(term));
- } else {
- erts_queue_release_literals(c_p, term_to_area(term));
- }
- }
+ delete_tuple(get_bucket(table, idx));
idx++, n--;
}
erts_free(ERTS_ALC_T_PERSISTENT_TERM, table);
}
+static void
+delete_tuple(Eterm term)
+{
+ if (is_tuple_arity(term, 2)) {
+ if (is_immed(tuple_val(term)[2])) {
+ erts_release_literal_area(term_to_area(term));
+ } else {
+ erts_queue_release_literals(NULL, term_to_area(term));
+ }
+ }
+ else {
+ ASSERT(is_nil(term));
+ }
+}
+
/*
* Caller *must* yield if this function returns 0.
*/
@@ -1199,42 +1313,46 @@ suspend_updater(Process* c_p)
}
static void
-append_to_delete_queue(HashTable* table)
+append_to_delete_queue(DeleteOp* dop)
{
erts_mtx_lock(&delete_queue_mtx);
- table->delete_next = NULL;
- *delete_queue_tail = table;
- delete_queue_tail = &table->delete_next;
+ dop->next = NULL;
+ *delete_queue_tail = dop;
+ delete_queue_tail = &dop->next;
erts_mtx_unlock(&delete_queue_mtx);
}
-static HashTable*
-next_to_delete(void)
+static DeleteOp*
+list_to_delete(DeleteOp* scheduled_dop)
{
- HashTable* table;
+ DeleteOp* dop;
+ DeleteOp* dop_list;
erts_mtx_lock(&delete_queue_mtx);
- table = delete_queue_head;
- if (table) {
- if (erts_atomic_read_nob(&table->refc)) {
- /*
- * This hash table is still referenced. Hash tables
- * must be deleted in order, so we return a NULL
- * pointer.
- */
- table = NULL;
- } else {
- /*
- * Remove the first hash table from the queue.
- */
- delete_queue_head = table->delete_next;
- if (delete_queue_head == NULL) {
- delete_queue_tail = &delete_queue_head;
- }
- }
+ ASSERT(delete_queue_head && delete_queue_head->is_scheduled);
+ ASSERT(scheduled_dop->is_scheduled);
+ scheduled_dop->is_scheduled = 0;
+
+ if (scheduled_dop == delete_queue_head) {
+ dop = delete_queue_head;
+ while (dop->next && !dop->next->is_scheduled)
+ dop = dop->next;
+
+ /*
+ * Remove list of ripe delete ops.
+ */
+ dop_list = delete_queue_head;
+ delete_queue_head = dop->next;
+ dop->next = NULL;
+ if (delete_queue_head == NULL)
+ delete_queue_tail = &delete_queue_head;
+ }
+ else {
+ dop_list = NULL;
}
erts_mtx_unlock(&delete_queue_mtx);
- return table;
+
+ return dop_list;
}
/*
@@ -1269,25 +1387,53 @@ erts_debug_save_accessed_literal_area(ErtsLiteralArea *lap)
accessed_literal_areas[accessed_no_literal_areas++] = lap;
}
-static void debug_foreach_off_heap(HashTable *tbl, void (*func)(ErlOffHeap *, void *), void *arg)
+static void debug_area_off_heap(ErtsLiteralArea* lap,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ ErlOffHeap oh;
+ if (!erts_debug_have_accessed_literal_area(lap)) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*func)(&oh, arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+}
+
+static void debug_table_foreach_off_heap(HashTable *tbl,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
{
int i;
for (i = 0; i < tbl->allocated; i++) {
- Eterm term = tbl->term[i];
+ Eterm term = get_bucket(tbl, i);
if (is_tuple_arity(term, 2)) {
- ErtsLiteralArea *lap = term_to_area(term);
- ErlOffHeap oh;
- if (!erts_debug_have_accessed_literal_area(lap)) {
- ERTS_INIT_OFF_HEAP(&oh);
- oh.first = lap->off_heap;
- (*func)(&oh, arg);
- erts_debug_save_accessed_literal_area(lap);
- }
+ debug_area_off_heap(term_to_area(term), func, arg);
}
}
}
+static void debug_delete_op_foreach_off_heap(DeleteOp *dop,
+ void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ switch (dop->type) {
+ case DELETE_OP_TABLE: {
+ HashTable* table = ErtsContainerStruct(dop, HashTable, delete_op);
+ debug_table_foreach_off_heap(table, func, arg);
+ break;
+ }
+ case DELETE_OP_TUPLE: {
+ OldLiteral* olp = ErtsContainerStruct(dop, OldLiteral, delete_op);
+ debug_area_off_heap(olp->area, func, arg);
+ break;
+ }
+ default:
+ ASSERT(!!"Invalid DeleteOp");
+ }
+}
+
struct debug_la_oh {
void (*func)(ErlOffHeap *, void *);
void *arg;
@@ -1299,13 +1445,16 @@ static void debug_handle_table(void *vfap,
{
struct debug_la_oh *fap = vfap;
HashTable *tbl = vtbl;
- debug_foreach_off_heap(tbl, fap->func, fap->arg);
+ debug_table_foreach_off_heap(tbl, fap->func, fap->arg);
}
+
void
-erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *), void *arg)
+erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *),
+ void *arg)
{
HashTable *tbl;
+ DeleteOp *dop;
struct debug_la_oh fa;
accessed_no_literal_areas = 0;
accessed_literal_areas_size = 10;
@@ -1314,19 +1463,16 @@ erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *),
* accessed_literal_areas_size));
tbl = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- debug_foreach_off_heap(tbl, func, arg);
+ debug_table_foreach_off_heap(tbl, func, arg);
erts_mtx_lock(&delete_queue_mtx);
- for (tbl = delete_queue_head; tbl; tbl = tbl->delete_next)
- debug_foreach_off_heap(tbl, func, arg);
+ for (dop = delete_queue_head; dop; dop = dop->next)
+ debug_delete_op_foreach_off_heap(dop, func, arg);
erts_mtx_unlock(&delete_queue_mtx);
fa.func = func;
fa.arg = arg;
erts_debug_later_op_foreach(table_updater,
debug_handle_table,
(void *) &fa);
- erts_debug_later_op_foreach(table_deleter,
- debug_handle_table,
- (void *) &fa);
erts_debug_foreach_release_literal_area_off_heap(func, arg);
erts_free(ERTS_ALC_T_TMP, accessed_literal_areas);
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 63bfaf8572..a989ba97ec 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -44,6 +44,7 @@
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
#include "erl_proc_sig_queue.h"
+#include "erl_osenv.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
@@ -210,7 +211,7 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
ERTS_BIF_PREP_RET(res, am_false);
else {
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt);
- ERTS_BIF_PREP_YIELD3(res, bif_export[BIF_erts_internal_port_command_3],
+ ERTS_BIF_PREP_YIELD3(res, &bif_trap_export[BIF_erts_internal_port_command_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
break;
@@ -1288,22 +1289,25 @@ static int http_request_erl(void* arg, const http_atom_t* meth,
}
static int
-http_header_erl(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len)
+http_header_erl(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len)
{
struct packet_callback_args* pca = (struct packet_callback_args*) arg;
- Eterm bit_term, name_term, val_term;
+ Eterm bit_term, name_term, oname_term, val_term;
Uint sz = 6;
Eterm* hp;
#ifdef DEBUG
Eterm* hend;
#endif
- /* {http_header,Bit,Name,IValue,Value} */
+ /* {http_header,Bit,Name,Oname,Value} */
if (name == NULL) {
http_bld_string(pca, NULL, &sz, name_ptr, name_len);
}
+ http_bld_string(pca, NULL, &sz, oname_ptr, oname_len);
http_bld_string(pca, NULL, &sz, value_ptr, value_len);
hp = HAlloc(pca->p, sz);
@@ -1320,8 +1324,9 @@ http_header_erl(void* arg, const http_atom_t* name, const char* name_ptr,
name_term = http_bld_string(pca, &hp,NULL,name_ptr,name_len);
}
+ oname_term = http_bld_string(pca, &hp, NULL, oname_ptr, oname_len);
val_term = http_bld_string(pca, &hp, NULL, value_ptr, value_len);
- pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, am_undefined, val_term);
+ pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, oname_term, val_term);
ASSERT(hp+6==hend);
return 1;
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 4162a6c591..e0777de298 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -80,9 +80,6 @@ static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
-
-static void reset_bif_trace(void);
-static void setup_bif_trace(void);
static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
@@ -133,7 +130,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
ErtsTracer meta_tracer = erts_tracer_nil;
if (!erts_try_seize_code_write_permission(p)) {
- ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist);
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist);
}
finish_bp.current = -1;
@@ -543,7 +540,7 @@ Eterm erts_internal_trace_3(BIF_ALIST_3)
}
if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_3],
+ ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_3],
BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
@@ -793,7 +790,7 @@ Eterm trace_info_2(BIF_ALIST_2)
Eterm res;
if (!erts_try_seize_code_write_permission(p)) {
- ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key);
+ ERTS_BIF_YIELD2(&bif_trap_export[BIF_trace_info_2], p, What, Key);
}
if (What == am_on_load) {
@@ -1047,14 +1044,13 @@ static int function_is_traced(Process *p,
e.info.mfa.function = mfa[1];
e.info.mfa.arity = mfa[2];
if ((ep = export_get(&e)) != NULL) {
- pc = ep->beam;
+ pc = ep->trampoline.raw;
if (ep->addressv[erts_active_code_ix()] == pc &&
! BeamIsOpCode(*pc, op_call_error_handler)) {
int r = 0;
- ASSERT(BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_i_generic_breakpoint));
+ ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
if (erts_is_trace_break(&ep->info, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
@@ -1426,18 +1422,21 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
int n;
BpFunction* fp;
- /*
- * First work on normal functions (not real BIFs).
- */
-
erts_bp_match_export(&finish_bp.e, mfa, specified);
fp = finish_bp.e.matching;
n = finish_bp.e.matched;
for (i = 0; i < n; i++) {
ErtsCodeInfo *ci = fp[i].ci;
- BeamInstr* pc = erts_codeinfo_to_code(ci);
- Export* ep = ErtsContainerStruct(ci, Export, info);
+ BeamInstr* pc;
+ Export* ep;
+
+ pc = erts_codeinfo_to_code(ci);
+ ep = ErtsContainerStruct(ci, Export, info);
+
+ if (ep->bif_number != -1) {
+ ep->is_bif_traced = !!on;
+ }
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1446,12 +1445,12 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
#ifdef DEBUG
ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
#endif
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
- ep->beam[1] = (BeamInstr) ep->addressv[code_ix];
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
+ ep->trampoline.trace.address = (BeamInstr) ep->addressv[code_ix];
}
- erts_set_call_trace_bif(ci, match_prog_set, 0);
+ erts_set_export_trace(ci, match_prog_set, 0);
if (ep->addressv[code_ix] != pc) {
- ep->beam[0] = BeamOpCodeAddr(op_i_generic_breakpoint);
+ ep->trampoline.op = BeamOpCodeAddr(op_i_generic_breakpoint);
}
} else if (!on && flags.breakpoint) {
/* Turn off breakpoint tracing -- nothing to do here. */
@@ -1460,91 +1459,14 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
* Turn off global tracing, either explicitly or implicitly
* before turning on breakpoint tracing.
*/
- erts_clear_call_trace_bif(ci, 0);
- if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
+ erts_clear_export_trace(ci, 0);
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
}
}
}
/*
- ** OK, now for the bif's
- */
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
-
- if (!ExportIsBuiltIn(ep)) {
- continue;
- }
-
- if (bif_table[i].f == bif_table[i].traced) {
- /* Trace wrapper same as regular function - untraceable */
- continue;
- }
-
- switch (specified) {
- case 3:
- if (mfa->arity != ep->info.mfa.arity)
- continue;
- case 2:
- if (mfa->function != ep->info.mfa.function)
- continue;
- case 1:
- if (mfa->module != ep->info.mfa.module)
- continue;
- case 0:
- break;
- default:
- ASSERT(0);
- }
-
- if (! flags.breakpoint) { /* Export entry call trace */
- if (on) {
- erts_clear_call_trace_bif(&ep->info, 1);
- erts_clear_mtrace_bif(&ep->info);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 0);
- } else { /* off */
- erts_clear_call_trace_bif(&ep->info, 0);
- }
- matches++;
- } else { /* Breakpoint call trace */
- int m = 0;
-
- if (on) {
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 0);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_set_mtrace_bif(&ep->info, meta_match_prog_set,
- meta_tracer);
- m = 1;
- }
- if (flags.call_time) {
- erts_set_time_trace_bif(&ep->info, on);
- /* I don't want to remove any other tracers */
- m = 1;
- }
- } else { /* off */
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_clear_mtrace_bif(&ep->info);
- m = 1;
- }
- if (flags.call_time) {
- erts_clear_time_trace_bif(&ep->info);
- m = 1;
- }
- }
- matches += m;
- }
- }
-
- /*
** So, now for breakpoint tracing
*/
erts_bp_match_functions(&finish_bp.f, mfa, specified);
@@ -1670,7 +1592,6 @@ erts_finish_breakpointing(void)
install_exp_breakpoints(&finish_bp.e);
}
}
- setup_bif_trace();
return 1;
case 1:
/*
@@ -1699,7 +1620,6 @@ erts_finish_breakpointing(void)
uninstall_exp_breakpoints(&finish_bp.e);
}
}
- reset_bif_trace();
return 1;
case 3:
/*
@@ -1710,7 +1630,6 @@ erts_finish_breakpointing(void)
* updated). If any breakpoints have been totally disabled,
* deallocate the GenericBp structs for them.
*/
- erts_consolidate_bif_bp_data();
clean_export_entries(&finish_bp.e);
erts_consolidate_bp_data(&finish_bp.e, 0);
erts_consolidate_bp_data(&finish_bp.f, 1);
@@ -1736,7 +1655,7 @@ install_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- ep->addressv[code_ix] = ep->beam;
+ ep->addressv[code_ix] = ep->trampoline.raw;
}
}
@@ -1751,11 +1670,12 @@ uninstall_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] != ep->beam) {
- continue;
- }
- ASSERT(BeamIsOpCode(ep->beam[0], op_trace_jump_W));
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
+ if (ep->addressv[code_ix] != ep->trampoline.raw) {
+ continue;
+ }
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_trace_jump_W));
+ ep->addressv[code_ix] = (BeamInstr *) ep->trampoline.trace.address;
}
}
@@ -1770,48 +1690,14 @@ clean_export_entries(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] == ep->beam) {
- continue;
- }
- if (BeamIsOpCode(ep->beam[0], op_trace_jump_W)) {
- ep->beam[0] = (BeamInstr) 0;
- ep->beam[1] = (BeamInstr) 0;
- }
- }
-}
-
-static void
-setup_bif_trace(void)
-{
- int i;
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].traced;
- }
- }
- }
-}
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ continue;
+ }
-static void
-reset_bif_trace(void)
-{
- int i;
- ErtsBpIndex active = erts_active_bp_ix();
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g && g->data[active].flags == 0) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- }
- }
+ if (BeamIsOpCode(ep->trampoline.op, op_trace_jump_W)) {
+ ep->trampoline.op = (BeamInstr) 0;
+ ep->trampoline.trace.address = (BeamInstr) 0;
+ }
}
}
@@ -1976,8 +1862,9 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item)
}
if (have_no_seqtrace(SEQ_TRACE_TOKEN(p))) {
- if ((item == am_send) || (item == am_receive) ||
- (item == am_print) || (item == am_timestamp)
+ if ((item == am_send) || (item == am_spawn) ||
+ (item == am_receive) || (item == am_print)
+ || (item == am_timestamp)
|| (item == am_monotonic_timestamp)
|| (item == am_strict_monotonic_timestamp)) {
hp = HAlloc(p,3);
@@ -2041,7 +1928,7 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) {
BIF_RET(am_false);
}
- seq_trace_update_send(BIF_P);
+ seq_trace_update_serial(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_1,
SEQ_TRACE_PRINT, NIL, BIF_P);
BIF_RET(am_true);
@@ -2062,7 +1949,7 @@ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2)
}
if (!EQ(BIF_ARG_1, SEQ_TRACE_TOKEN_LABEL(BIF_P)))
BIF_RET(am_false);
- seq_trace_update_send(BIF_P);
+ seq_trace_update_serial(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_2,
SEQ_TRACE_PRINT, NIL, BIF_P);
BIF_RET(am_true);
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 6a4f43297e..67eebfe8f6 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -1632,7 +1632,7 @@ erts_get_cpu_topology_term(Process *c_p, Eterm which)
}
static void
-get_logical_processors(int *conf, int *onln, int *avail)
+get_logical_processors(int *conf, int *onln, int *avail, int *quota)
{
if (conf)
*conf = erts_get_cpu_configured(cpuinfo);
@@ -1640,13 +1640,15 @@ get_logical_processors(int *conf, int *onln, int *avail)
*onln = erts_get_cpu_online(cpuinfo);
if (avail)
*avail = erts_get_cpu_available(cpuinfo);
+ if (quota)
+ *quota = erts_get_cpu_quota(cpuinfo);
}
void
-erts_get_logical_processors(int *conf, int *onln, int *avail)
+erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota)
{
erts_rwmtx_rlock(&cpuinfo_rwmtx);
- get_logical_processors(conf, onln, avail);
+ get_logical_processors(conf, onln, avail, quota);
erts_rwmtx_runlock(&cpuinfo_rwmtx);
}
@@ -1655,14 +1657,15 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p,
int *max_rg_p,
int *conf_p,
int *onln_p,
- int *avail_p)
+ int *avail_p,
+ int *quota_p)
{
cpu_groups_maps = NULL;
no_cpu_groups_callbacks = 0;
*max_rg_p = ERTS_MAX_READER_GROUPS;
*max_dcg_p = ERTS_MAX_FLXCTR_GROUPS;
cpuinfo = erts_cpu_info_create();
- get_logical_processors(conf_p, onln_p, avail_p);
+ get_logical_processors(conf_p, onln_p, avail_p, quota_p);
}
void
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index 4a428d7972..91e1322504 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -32,7 +32,8 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p,
int *max_rg_p,
int *conf_p,
int *onln_p,
- int *avail_p);
+ int *avail_p,
+ int *quota_p);
void
erts_early_init_cpu_topology(int no_schedulers,
int *max_main_threads_p,
@@ -81,7 +82,7 @@ Eterm erts_set_cpu_topology(Process *c_p, Eterm term);
Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which);
int erts_update_cpu_info(void);
-void erts_get_logical_processors(int *conf, int *onln, int *avail);
+void erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota);
int erts_sched_bind_atthrcreate_prepare(void);
int erts_sched_bind_atthrcreate_child(int unbind);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index b4a97b42c8..91625dd516 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -51,6 +51,15 @@ erts_atomic_t erts_ets_misc_mem_size;
** Utility macros
*/
+#if defined(DEBUG)
+# define DBG_RANDOM_REDS(REDS, SEED) \
+ ((REDS) * 0.1 * erts_sched_local_random_float(SEED))
+#else
+# define DBG_RANDOM_REDS(REDS, SEED) (REDS)
+#endif
+
+
+
#define DB_BIF_GET_TABLE(TB, WHAT, KIND, BIF_IX) \
DB_GET_TABLE(TB, BIF_ARG_1, WHAT, KIND, BIF_IX, NULL, BIF_P)
@@ -75,7 +84,7 @@ static BIF_RETTYPE db_bif_fail(Process* p, Uint freason,
{
if (freason == TRAP) {
if (!bif_exp)
- bif_exp = bif_export[bif_ix];
+ bif_exp = &bif_trap_export[bif_ix];
p->arity = bif_exp->info.mfa.arity;
p->i = (BeamInstr*) bif_exp->addressv[erts_active_code_ix()];
}
@@ -353,7 +362,9 @@ struct meta_name_tab_entry* meta_name_tab_bucket(Eterm name,
typedef enum {
LCK_READ=1, /* read only access */
LCK_WRITE=2, /* exclusive table write access */
- LCK_WRITE_REC=3 /* record write access */
+ LCK_WRITE_REC=3, /* record write access */
+ NOLCK_ACCESS=4 /* Used to access the table structure
+ without acquiring the table lock */
} db_lock_kind_t;
extern DbTableMethod db_hash;
@@ -409,9 +420,11 @@ static void
free_dbtable(void *vtb)
{
DbTable *tb = (DbTable *) vtb;
+ erts_flxctr_add(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID,
+ -((Sint)erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)));
ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
- sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
- ERTS_DB_TABLE_MEM_COUNTER_ID));
+ sizeof(DbTable) == DB_GET_APPROX_MEM_CONSUMED(tb));
ASSERT(is_immed(tb->common.heir_data));
@@ -423,7 +436,7 @@ free_dbtable(void *vtb)
if (tb->common.btid)
erts_bin_release(tb->common.btid);
- erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_DB_TABLE);
+ erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_ETS_CTRS);
erts_free(ERTS_ALC_T_DB_TABLE, tb);
}
@@ -619,7 +632,7 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
if (kind == LCK_WRITE) {
erts_rwmtx_rwlock(&tb->common.rwlock);
tb->common.is_thread_safe = 1;
- } else {
+ } else if (kind != NOLCK_ACCESS) {
erts_rwmtx_rlock(&tb->common.rwlock);
ASSERT(!tb->common.is_thread_safe);
}
@@ -631,6 +644,8 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
case LCK_WRITE_REC:
erts_rwmtx_rwlock(&tb->common.rwlock);
break;
+ case NOLCK_ACCESS:
+ return;
default:
erts_rwmtx_rlock(&tb->common.rwlock);
}
@@ -640,7 +655,7 @@ static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind)
{
- if (DB_LOCK_FREE(tb))
+ if (DB_LOCK_FREE(tb) || kind == NOLCK_ACCESS)
return;
if (tb->common.type & DB_FINE_LOCKED) {
if (kind == LCK_WRITE) {
@@ -671,7 +686,10 @@ static ERTS_INLINE int db_is_exclusive(DbTable* tb, db_lock_kind_t kind)
if (DB_LOCK_FREE(tb))
return 1;
- return kind != LCK_READ && tb->common.is_thread_safe;
+ return
+ kind != LCK_READ &&
+ kind != NOLCK_ACCESS &&
+ tb->common.is_thread_safe;
}
static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
@@ -679,11 +697,27 @@ static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
Uint* freason_p)
{
if (tb->common.status & DB_BUSY) {
+ void* continuation_state;
if (!db_is_exclusive(tb, kind)) {
db_unlock(tb, kind);
db_lock(tb, LCK_WRITE);
}
- delete_all_objects_continue(p, tb);
+ continuation_state = (void*)erts_atomic_read_nob(&tb->common.continuation_state);
+ if (continuation_state != NULL) {
+ const long iterations_per_red = 10;
+ const long reds = iterations_per_red * ERTS_BIF_REDS_LEFT(p);
+ long nr_of_reductions = DBG_RANDOM_REDS(reds, (Uint)freason_p);
+ const long init_reds = nr_of_reductions;
+ tb->common.continuation(&nr_of_reductions,
+ &continuation_state,
+ NULL);
+ if (continuation_state == NULL) {
+ erts_atomic_set_relb(&tb->common.continuation_state, (Sint)NULL);
+ }
+ BUMP_REDS(p, (init_reds - nr_of_reductions) / iterations_per_red);
+ } else {
+ delete_all_objects_continue(p, tb);
+ }
db_unlock(tb, LCK_WRITE);
tb = NULL;
*freason_p = TRAP;
@@ -722,9 +756,9 @@ DbTable* db_get_table_aux(Process *p,
if (!meta_already_locked)
erts_rwmtx_rlock(mtl);
else {
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(mtl)
- || erts_lc_rwmtx_is_rwlocked(mtl)
- || META_DB_LOCK_FREE());
+ ERTS_LC_ASSERT(META_DB_LOCK_FREE()
+ || erts_lc_rwmtx_is_rlocked(mtl)
+ || erts_lc_rwmtx_is_rwlocked(mtl));
}
tb = NULL;
if (bucket->pu.tb != NULL) {
@@ -752,15 +786,28 @@ DbTable* db_get_table_aux(Process *p,
if (tb) {
db_lock(tb, kind);
#ifdef ETS_DBG_FORCE_TRAP
- if (erts_atomic_read_nob(&tb->common.dbg_force_trap) &&
- erts_atomic_add_read_nob(&tb->common.dbg_force_trap, 2) & 2) {
- db_unlock(tb, kind);
- tb = NULL;
- *freason_p = TRAP;
+ if (erts_atomic_read_nob(&tb->common.dbg_force_trap)) {
+ Uint32 rand = erts_sched_local_random((Uint)&p);
+ if ( !(rand & 7) ) {
+ /* About 7 of 8 threads that are on the line above
+ will get here */
+ if (erts_atomic_add_read_nob(&tb->common.dbg_force_trap, 2) & 2) {
+ db_unlock(tb, kind);
+ tb = NULL;
+ *freason_p = TRAP;
+ return tb;
+ }
+ }
+
+
}
- else
#endif
- if (ERTS_UNLIKELY(!(tb->common.status & what)))
+ if (ERTS_UNLIKELY(what != DB_READ_TBL_STRUCT
+ /* IMPORTANT: the above check is
+ necessary as the status field might
+ be in an intermediate state when
+ kind==NOLCK_ACCESS */ &&
+ !(tb->common.status & what)))
tb = handle_lacking_permission(p, tb, kind, freason_p);
}
else
@@ -779,6 +826,17 @@ DbTable* db_get_table(Process *p,
return db_get_table_aux(p, id, what, kind, 0, freason_p);
}
+static BIF_RETTYPE db_get_table_or_fail_return(DbTable **tb, /* out */
+ Eterm table_id,
+ Uint32 what,
+ db_lock_kind_t kind,
+ Uint bif_ix,
+ Process* p)
+{
+ DB_GET_TABLE(*tb, table_id, what, kind, bif_ix, NULL, p);
+ return THE_NON_VALUE;
+}
+
static int insert_named_tab(Eterm name_atom, DbTable* tb, int have_lock)
{
int ret = 0;
@@ -861,7 +919,7 @@ static int remove_named_tab(DbTable *tb, int have_lock)
db_lock(tb, LCK_WRITE);
}
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(rwlock) || META_DB_LOCK_FREE());
+ ERTS_LC_ASSERT(META_DB_LOCK_FREE() || erts_lc_rwmtx_is_rwlocked(rwlock));
if (bucket->pu.tb == NULL) {
goto done;
@@ -1382,6 +1440,506 @@ BIF_RETTYPE ets_update_counter_4(BIF_ALIST_4)
return do_update_counter(BIF_P, tb, BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
+typedef enum {
+ ETS_INSERT_2_LIST_PROCESS_LOCAL,
+ ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK,
+ ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY,
+ ETS_INSERT_2_LIST_GLOBAL
+} ets_insert_2_list_status;
+
+typedef struct {
+ ets_insert_2_list_status status;
+ BIF_RETTYPE destroy_return_value;
+ DbTable* tb;
+ void* continuation_state;
+ Binary* continuation_res_bin;
+} ets_insert_2_list_info;
+
+
+static ERTS_INLINE BIF_RETTYPE
+ets_cret_to_return_value(Process* p, int cret)
+{
+ switch (cret) {
+ case DB_ERROR_NONE_FALSE:
+ BIF_RET(am_false);
+ case DB_ERROR_NONE:
+ BIF_RET(am_true);
+ case DB_ERROR_SYSRES:
+ BIF_ERROR(p, SYSTEM_LIMIT);
+ default:
+ BIF_ERROR(p, BADARG);
+ }
+}
+
+/*
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ *
+ * Start of code section that Yielding C Fun (YCF) transforms
+ *
+ * The functions within #idef YCF_FUNCTIONS below are not called directly.
+ * YCF generates yieldable versions of these functions before "erl_db.c" is
+ * compiled. These generated functions are placed in the file
+ * "erl_db_insert_list.ycf.h" which is included below. The generation of
+ * "erl_db_insert_list.ycf.h" is defined in
+ * "$ERL_TOP/erts/emulator/Makefile.in". See
+ * "$ERL_TOP/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md"
+ * for more information about YCF.
+ *
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ * > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
+ */
+
+/*
+ * The LOCAL_VARIABLE macro is a trick to create a local variable that does not
+ * get renamed by YCF.
+ * Such variables will not retain their values over yields. Beware!
+ *
+ * I use this as a workaround for a limitation/bug in YCF. It does not do
+ * proper variable name substitution in expressions passed as argument to
+ * YCF_CONSUME_REDS(Expr).
+ */
+#define LOCAL_VARIABLE(TYPE, NAME) TYPE NAME
+
+#ifdef YCF_FUNCTIONS
+static long ets_insert_2_list_check(int keypos, Eterm list)
+{
+ Eterm lst = THE_NON_VALUE;
+ long i = 0;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ i++;
+ if (is_not_tuple(CAR(list_val(lst))) ||
+ (arityval(*tuple_val(CAR(list_val(lst)))) < keypos)) {
+ return -1;
+ }
+ }
+ if (lst != NIL) {
+ return -1;
+ }
+ return i;
+}
+
+static int ets_insert_new_2_list_has_member(DbTable* tb, Eterm list)
+{
+ Eterm lst;
+ Eterm lookup_ret;
+ DbTableMethod* meth = tb->common.meth;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ meth->db_member(tb,
+ TERM_GETKEY(tb,CAR(list_val(lst))),
+ &lookup_ret);
+ if (lookup_ret != am_false) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ets_insert_2_list_from_p_heap(DbTable* tb, Eterm list)
+{
+ Eterm lst;
+ DbTableMethod* meth = tb->common.meth;
+ int cret = DB_ERROR_NONE;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ LOCAL_VARIABLE(SWord, consumed_reds);
+ consumed_reds = 1;
+ cret = meth->db_put(tb, CAR(list_val(lst)), 0, &consumed_reds);
+ if (cret != DB_ERROR_NONE)
+ return cret;
+ YCF_CONSUME_REDS(consumed_reds);
+ }
+ return DB_ERROR_NONE;
+}
+#endif /* YCF_FUNCTIONS */
+
+/* This function is called both as is, and as YCF transformed. */
+static void ets_insert_2_list_destroy_copied_dbterms(DbTableMethod* meth,
+ int compressed,
+ void* db_term_list)
+{
+ void* lst = db_term_list;
+ void* term = NULL;
+ while (lst != NULL) {
+ term = meth->db_dbterm_list_remove_first(&lst);
+ meth->db_free_dbterm(compressed, term);
+ }
+}
+
+#ifdef YCF_FUNCTIONS
+static void* ets_insert_2_list_copy_term_list(DbTableMethod* meth,
+ int compress,
+ int keypos,
+ Eterm list)
+{
+ void* db_term_list = NULL;
+ void *term;
+ Eterm lst;
+ for (lst = list; is_list(lst); lst = CDR(list_val(lst))) {
+ term = meth->db_eterm_to_dbterm(compress,
+ keypos,
+ CAR(list_val(lst)));
+ if (db_term_list != NULL) {
+ db_term_list =
+ meth->db_dbterm_list_prepend(db_term_list,
+ term);
+ } else {
+ db_term_list = term;
+ }
+ }
+
+ return db_term_list;
+
+ /* The following code will be executed if the calling process is
+ killed in the middle of the for loop above*/
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE); {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compress,
+ db_term_list);
+ } YCF_SPECIAL_CODE_END();
+}
+
+static int ets_insert_new_2_dbterm_list_has_member(DbTable* tb, void* db_term_list)
+{
+ Eterm lookup_ret;
+ DbTableMethod* meth = tb->common.meth;
+ void* lst = db_term_list;
+ void* term = NULL;
+ Eterm key;
+ while (lst != NULL) {
+ term = meth->db_dbterm_list_remove_first(&lst);
+ key = meth->db_get_dbterm_key(tb, term);
+ meth->db_member(tb, key, &lookup_ret);
+ if (lookup_ret != am_false) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void ets_insert_2_list_insert_db_term_list(DbTable* tb,
+ void* list)
+{
+ void* lst = list;
+ void* term = NULL;
+ DbTableMethod* meth = tb->common.meth;
+ do {
+ LOCAL_VARIABLE(SWord, consumed_reds);
+ consumed_reds = 1;
+ term = meth->db_dbterm_list_remove_first(&lst);
+ meth->db_put_dbterm(tb, term, 0, &consumed_reds);
+ YCF_CONSUME_REDS(consumed_reds);
+ } while (lst != NULL);
+ return;
+}
+
+static void ets_insert_2_list_lock_tbl(Eterm table_id,
+ Process* p,
+ Uint bif_ix,
+ ets_insert_2_list_status on_success_status)
+{
+ BIF_RETTYPE fail_ret;
+ DbTable* tb;
+ ets_insert_2_list_info *ctx;
+ do {
+ fail_ret = db_get_table_or_fail_return(&tb,
+ table_id,
+ DB_WRITE,
+ LCK_WRITE,
+ bif_ix,
+ p);
+ ctx = YCF_GET_EXTRA_CONTEXT();
+ if (tb == NULL) {
+ if (p->freason == TRAP) {
+ ctx->status = ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK;
+ } else {
+ ctx->status = ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY;
+ ctx->destroy_return_value = fail_ret;
+ }
+ YCF_YIELD();
+ } else {
+ ctx->status = on_success_status;
+ ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
+ ASSERT(!(tb->common.status & DB_DELETE));
+ }
+ } while (tb == NULL);
+}
+#endif /* YCF_FUNCTIONS */
+
+static ERTS_INLINE int can_insert_without_yield(Uint32 tb_type,
+ long list_len,
+ long reds_left)
+{
+ if (tb_type & DB_BAG) {
+ /* Bag inserts can be really bad and we don't know how much searching
+ * for duplicates we will do */
+ return 0;
+ }
+ else {
+ return list_len <= reds_left;
+ }
+}
+
+#ifdef YCF_FUNCTIONS
+static BIF_RETTYPE ets_insert_2_list(Process* p,
+ Eterm table_id,
+ DbTable *tb,
+ Eterm list,
+ int is_insert_new)
+{
+ int cret = DB_ERROR_NONE;
+ void* db_term_list = NULL; /* OBS: memory managements depends on that
+ db_term_list is initialized to NULL */
+ DbTableMethod* meth = tb->common.meth;
+ int compressed = tb->common.compress;
+ int keypos = tb->common.keypos;
+ Uint32 tb_type = tb->common.type;
+ Uint bif_ix = (is_insert_new ? BIF_ets_insert_new_2 : BIF_ets_insert_2);
+ long list_len;
+ /* tb should not be accessed after this point unless the table
+ lock is held as the table can get deleted while the function is
+ yielding */
+ list_len = ets_insert_2_list_check(keypos, list);
+ if (list_len < 0) {
+ return ets_cret_to_return_value(p, DB_ERROR_BADITEM);
+ }
+ if (can_insert_without_yield(tb_type, list_len, YCF_NR_OF_REDS_LEFT())) {
+ long reds_boost;
+ /* There is enough reductions left to do the inserts directly
+ from the heap without yielding */
+ ets_insert_2_list_lock_tbl(table_id, p, bif_ix, ETS_INSERT_2_LIST_PROCESS_LOCAL);
+ /* Ensure that we will not yield while inserting from heap */
+ reds_boost = YCF_MAX_NR_OF_REDS - YCF_NR_OF_REDS_LEFT();
+ YCF_SET_NR_OF_REDS_LEFT(YCF_MAX_NR_OF_REDS);
+ if (is_insert_new) {
+ if (ets_insert_new_2_list_has_member(tb, list)) {
+ cret = DB_ERROR_NONE_FALSE;
+ } else {
+ cret = ets_insert_2_list_from_p_heap(tb, list);
+ }
+ } else {
+ cret = ets_insert_2_list_from_p_heap(tb, list);
+ }
+ db_unlock(tb, LCK_WRITE);
+ YCF_SET_NR_OF_REDS_LEFT(YCF_NR_OF_REDS_LEFT() - reds_boost);
+ return ets_cret_to_return_value(p, cret);
+ }
+ /* Copy term list from heap so that other processes can help */
+ db_term_list =
+ ets_insert_2_list_copy_term_list(meth, compressed, keypos, list);
+ /* Lock table */
+ ets_insert_2_list_lock_tbl(table_id, p, bif_ix, ETS_INSERT_2_LIST_GLOBAL);
+ /* The operation must complete after this point */
+ if (is_insert_new) {
+ if (ets_insert_new_2_dbterm_list_has_member(tb, db_term_list)) {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compressed,
+ db_term_list);
+ cret = DB_ERROR_NONE_FALSE;
+ } else {
+ ets_insert_2_list_insert_db_term_list(tb, db_term_list);
+ }
+ } else {
+ ets_insert_2_list_insert_db_term_list(tb, db_term_list);
+ }
+ if (tb->common.continuation != NULL) {
+ /* Uninstall the continuation from the table struct */
+ tb->common.continuation = NULL;
+ if (is_insert_new) {
+ int* result_ptr =
+ ERTS_MAGIC_BIN_DATA(tb->common.continuation_res_bin);
+ *result_ptr = cret;
+ erts_bin_release(tb->common.continuation_res_bin);
+ }
+ tb->common.status |= tb->common.type & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC);
+ tb->common.status &= ~DB_BUSY;
+ erts_atomic_set_relb(&tb->common.continuation_state, (Sint)NULL);
+ }
+
+ return ets_cret_to_return_value(NULL, cret);
+
+ /* The following code will be executed if the initiating process
+ is killed before an ets_insert_2_list_lock_tbl call has
+ succeeded */
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE); {
+ ets_insert_2_list_destroy_copied_dbterms(meth,
+ compressed,
+ db_term_list);
+ } YCF_SPECIAL_CODE_END();
+}
+#endif /* YCF_FUNCTIONS */
+
+/*
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ *
+ * End of code section that Yielding C Fun (YCF) transforms
+ *
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ * < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <
+ */
+#include "erl_db_insert_list.ycf.h"
+
+static void* ets_insert_2_yield_alloc(size_t size, void* ctx)
+{
+ (void)ctx;
+ return erts_alloc(ERTS_ALC_T_ETS_I_LST_TRAP, size);
+}
+
+static void ets_insert_2_yield_free(void* data, void* ctx)
+{
+ (void)ctx;
+ erts_free(ERTS_ALC_T_ETS_I_LST_TRAP, data);
+}
+
+static int ets_insert_2_list_yield_dtor(Binary* bin)
+{
+ ets_insert_2_list_info* ctx = ERTS_MAGIC_BIN_DATA(bin);
+ if (ctx->status != ETS_INSERT_2_LIST_GLOBAL &&
+ ctx->continuation_state != NULL) {
+ /* The operation has not been committed to the table and has
+ not completed*/
+ ets_insert_2_list_ycf_gen_destroy(ctx->continuation_state);
+ }
+ return 1;
+}
+
+static void ets_insert_2_list_continuation(long *reds_ptr,
+ void** state,
+ void* extra_context)
+{
+ ets_insert_2_list_ycf_gen_continue(reds_ptr, state, extra_context);
+}
+
+static int db_insert_new_2_res_bin_dtor(Binary *context_bin)
+{
+ (void)context_bin;
+ return 1;
+}
+
+#define ITERATIONS_PER_RED 8
+
+static BIF_RETTYPE ets_insert_2_list_driver(Process* p,
+ Eterm tid,
+ Eterm list,
+ int is_insert_new) {
+ const long reds = ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(p);
+ long nr_of_reductions = DBG_RANDOM_REDS(reds, (Uint)&p);
+ const long init_reds = nr_of_reductions;
+ ets_insert_2_list_info* ctx = NULL;
+ ets_insert_2_list_info ictx;
+ BIF_RETTYPE ret = THE_NON_VALUE;
+ Eterm state_mref = list;
+ Uint bix = (is_insert_new ? BIF_ets_insert_new_2 : BIF_ets_insert_2);
+ if (is_internal_magic_ref(state_mref)) {
+ Binary* state_bin = erts_magic_ref2bin(state_mref);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(state_bin) != ets_insert_2_list_yield_dtor) {
+ BIF_ERROR(p, BADARG);
+ }
+ /* Continue a trapped call */
+ erts_set_gc_state(p, 1);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL) {
+ /* An operation that can be helped by other operations is
+ handled here */
+ Uint freason__;
+ int cret = DB_ERROR_NONE;
+ DbTable* tb;
+ /* First check if another process has completed the
+ operation without acquiring the lock */
+ if (NULL == (tb = db_get_table(p, tid, DB_READ_TBL_STRUCT, NOLCK_ACCESS, &freason__))) {
+ if (freason__ == TRAP){
+ erts_set_gc_state(p, 0);
+ return db_bif_fail(p, freason__, bix, NULL);
+ }
+ }
+ if (tb != NULL &&
+ (void*)erts_atomic_read_acqb(&tb->common.continuation_state) ==
+ ctx->continuation_state) {
+ /* The lock has to be taken to complete the operation */
+ if (NULL == (tb = db_get_table(p, tid, DB_WRITE, LCK_WRITE, &freason__))) {
+ if (freason__ == TRAP){
+ erts_set_gc_state(p, 0);
+ return db_bif_fail(p, freason__, bix, NULL);
+ }
+ }
+ /* Must be done since the db_get_table call did not trap */
+ if (tb != NULL) {
+ db_unlock(tb, LCK_WRITE);
+ }
+ }
+ if (is_insert_new) {
+ int* res = ERTS_MAGIC_BIN_DATA(ctx->continuation_res_bin);
+ cret = *res;
+ }
+ return ets_cret_to_return_value(NULL, cret);
+ } else {
+ ret = ets_insert_2_list_ycf_gen_continue(&nr_of_reductions,
+ &ctx->continuation_state,
+ ctx);
+ }
+ } else {
+ /* Start call */
+ ictx.continuation_state = NULL;
+ ictx.status = ETS_INSERT_2_LIST_PROCESS_LOCAL;
+ ictx.tb = NULL;
+ ctx = &ictx;
+ DB_GET_TABLE(ctx->tb, tid, DB_READ_TBL_STRUCT, NOLCK_ACCESS, bix, NULL, p);
+ ret = ets_insert_2_list_ycf_gen_yielding(&nr_of_reductions,
+ &ctx->continuation_state,
+ ctx,
+ ets_insert_2_yield_alloc,
+ ets_insert_2_yield_free,
+ NULL,
+ 0,
+ NULL,
+ p,
+ tid,
+ ctx->tb,
+ list,
+ is_insert_new);
+ if (ctx->continuation_state != NULL) {
+ Binary* state_bin = erts_create_magic_binary(sizeof(ets_insert_2_list_info),
+ ets_insert_2_list_yield_dtor);
+ Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ *ctx = ictx;
+ }
+ }
+ BUMP_REDS(p, (init_reds - nr_of_reductions) / ITERATIONS_PER_RED);
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL &&
+ ctx->continuation_state != NULL &&
+ ctx->tb->common.continuation == NULL) {
+ /* Install the continuation in the table structure so other
+ threads can help */
+ if (is_insert_new) {
+ Binary* bin =
+ erts_create_magic_binary(sizeof(int),
+ db_insert_new_2_res_bin_dtor);
+ Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ erts_mk_magic_ref(&hp, &MSO(p), bin);
+ erts_refc_inctest(&bin->intern.refc, 2);
+ ctx->tb->common.continuation_res_bin = bin;
+ ctx->continuation_res_bin = bin;
+ }
+ ctx->tb->common.continuation = ets_insert_2_list_continuation;
+ ctx->tb->common.status &= ~(DB_PRIVATE|DB_PROTECTED|DB_PUBLIC);
+ ctx->tb->common.status |= DB_BUSY;
+ erts_atomic_set_relb(&ctx->tb->common.continuation_state,
+ (Sint)ctx->continuation_state);
+ }
+ if (ctx->status == ETS_INSERT_2_LIST_FAILED_TO_GET_LOCK_DESTROY) {
+ return ctx->destroy_return_value;
+ }
+ if (ctx->status == ETS_INSERT_2_LIST_GLOBAL) {
+ db_unlock(ctx->tb, LCK_WRITE);
+ }
+ if (ctx->continuation_state != NULL) {
+ erts_set_gc_state(p, 0);
+ BIF_TRAP2(&bif_trap_export[bix], p, tid, state_mref);
+ }
+ return ret;
+}
/*
** The put BIF
@@ -1390,59 +1948,42 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2)
{
DbTable* tb;
int cret = DB_ERROR_NONE;
- Eterm lst;
+ Eterm insert_term;
DbTableMethod* meth;
- db_lock_kind_t kind;
-
+ SWord consumed_reds = 0;
CHECK_TABLES();
-
- /* Write lock table if more than one object to keep atomicity */
- kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL)
- ? LCK_WRITE : LCK_WRITE_REC);
-
- DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_2);
-
if (BIF_ARG_2 == NIL) {
- db_unlock(tb, kind);
+ /* Check that the table exists */
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+ db_unlock(tb, LCK_WRITE_REC);
BIF_RET(am_true);
- }
- meth = tb->common.meth;
- if (is_list(BIF_ARG_2)) {
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- if (is_not_tuple(CAR(list_val(lst))) ||
- (arityval(*tuple_val(CAR(list_val(lst)))) < tb->common.keypos)) {
- goto badarg;
- }
- }
- if (lst != NIL) {
- goto badarg;
- }
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_put(tb, CAR(list_val(lst)), 0);
- if (cret != DB_ERROR_NONE)
- break;
- }
+ } if ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL) ||
+ is_internal_magic_ref(BIF_ARG_2)) {
+ /* Handle list case */
+ return ets_insert_2_list_driver(BIF_P,
+ BIF_ARG_1,
+ BIF_ARG_2,
+ 0);
+ } else if (is_list(BIF_ARG_2)) {
+ insert_term = CAR(list_val(BIF_ARG_2));
} else {
- if (is_not_tuple(BIF_ARG_2) ||
- (arityval(*tuple_val(BIF_ARG_2)) < tb->common.keypos)) {
- goto badarg;
- }
- cret = meth->db_put(tb, BIF_ARG_2, 0);
+ insert_term = BIF_ARG_2;
}
- db_unlock(tb, kind);
-
- switch (cret) {
- case DB_ERROR_NONE:
- BIF_RET(am_true);
- case DB_ERROR_SYSRES:
- BIF_ERROR(BIF_P, SYSTEM_LIMIT);
- default:
- BIF_ERROR(BIF_P, BADARG);
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+
+ meth = tb->common.meth;
+ if (is_not_tuple(insert_term) ||
+ (arityval(*tuple_val(insert_term)) < tb->common.keypos)) {
+ db_unlock(tb, LCK_WRITE_REC);
+ BIF_ERROR(BIF_P, BADARG);
}
- badarg:
- db_unlock(tb, kind);
- BIF_ERROR(BIF_P, BADARG);
+ cret = meth->db_put(tb, insert_term, 0, &consumed_reds);
+
+ db_unlock(tb, LCK_WRITE_REC);
+
+ BUMP_REDS(BIF_P, consumed_reds / ITERATIONS_PER_RED);
+ return ets_cret_to_return_value(BIF_P, cret);
}
@@ -1456,69 +1997,40 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2)
Eterm ret = am_true;
Eterm obj;
db_lock_kind_t kind;
-
+ SWord consumed_reds = 0;
CHECK_TABLES();
- if (is_list(BIF_ARG_2)) {
- if (CDR(list_val(BIF_ARG_2)) != NIL) {
- Eterm lst;
- Eterm lookup_ret;
- DbTableMethod* meth;
-
- /* More than one object, use LCK_WRITE to keep atomicity */
- kind = LCK_WRITE;
- DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_new_2);
-
- meth = tb->common.meth;
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- if (is_not_tuple(CAR(list_val(lst)))
- || (arityval(*tuple_val(CAR(list_val(lst))))
- < tb->common.keypos)) {
- goto badarg;
- }
- }
- if (lst != NIL) {
- goto badarg;
- }
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_member(tb, TERM_GETKEY(tb,CAR(list_val(lst))),
- &lookup_ret);
- if ((cret != DB_ERROR_NONE) || (lookup_ret != am_false)) {
- ret = am_false;
- goto done;
- }
- }
-
- for (lst = BIF_ARG_2; is_list(lst); lst = CDR(list_val(lst))) {
- cret = meth->db_put(tb,CAR(list_val(lst)), 0);
- if (cret != DB_ERROR_NONE)
- break;
- }
- goto done;
- }
- obj = CAR(list_val(BIF_ARG_2));
- }
- else {
- obj = BIF_ARG_2;
+ if (BIF_ARG_2 == NIL) {
+ /* Check that the table exists */
+ DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_insert_2);
+ db_unlock(tb, LCK_WRITE_REC);
+ BIF_RET(am_true);
+ } if ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL) ||
+ is_internal_magic_ref(BIF_ARG_2)) {
+ /* Handle list case */
+ return ets_insert_2_list_driver(BIF_P, BIF_ARG_1, BIF_ARG_2, 1);
+ } else if (is_list(BIF_ARG_2)) {
+ obj = CAR(list_val(BIF_ARG_2));
+ } else {
+ obj = BIF_ARG_2;
}
- /* Only one object (or NIL)
- */
+
+ /* Only one object */
kind = LCK_WRITE_REC;
DB_BIF_GET_TABLE(tb, DB_WRITE, kind, BIF_ets_insert_new_2);
- if (BIF_ARG_2 == NIL) {
- db_unlock(tb, kind);
- BIF_RET(am_true);
- }
if (is_not_tuple(obj)
|| (arityval(*tuple_val(obj)) < tb->common.keypos)) {
- goto badarg;
+ db_unlock(tb, kind);
+ BIF_ERROR(BIF_P, BADARG);
}
cret = tb->common.meth->db_put(tb, obj,
- 1); /* key_clash_fail */
+ 1, /* key_clash_fail */
+ &consumed_reds);
-done:
db_unlock(tb, kind);
+
+ BUMP_REDS(BIF_P, consumed_reds / ITERATIONS_PER_RED);
switch (cret) {
case DB_ERROR_NONE:
BIF_RET(ret);
@@ -1529,9 +2041,6 @@ done:
default:
BIF_ERROR(BIF_P, BADARG);
}
- badarg:
- db_unlock(tb, kind);
- BIF_ERROR(BIF_P, BADARG);
}
/*
@@ -1643,6 +2152,8 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
Sint keypos;
int is_named, is_compressed;
int is_fine_locked, frequent_read;
+ int is_decentralized_counters;
+ int is_decentralized_counters_option;
int cret;
DbTableMethod* meth;
@@ -1658,6 +2169,8 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
is_named = 0;
is_fine_locked = 0;
frequent_read = 0;
+ is_decentralized_counters = 0;
+ is_decentralized_counters_option = -1;
heir = am_none;
heir_data = (UWord) am_undefined;
is_compressed = erts_ets_always_compress;
@@ -1674,6 +2187,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
status &= ~(DB_SET | DB_BAG | DB_ORDERED_SET | DB_CA_ORDERED_SET);
}
else if (val == am_ordered_set) {
+ is_decentralized_counters = 1;
status |= DB_ORDERED_SET;
status &= ~(DB_SET | DB_BAG | DB_DUPLICATE_BAG | DB_CA_ORDERED_SET);
}
@@ -1699,12 +2213,18 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
} else if (tp[2] == am_false) {
frequent_read = 0;
} else break;
-
}
else if (tp[1] == am_heir && tp[2] == am_none) {
heir = am_none;
heir_data = am_undefined;
}
+ else if (tp[1] == am_decentralized_counters) {
+ if (tp[2] == am_true) {
+ is_decentralized_counters_option = 1;
+ } else if (tp[2] == am_false) {
+ is_decentralized_counters_option = 0;
+ } else break;
+ }
else break;
}
else if (arityval(tp[0]) == 3 && tp[1] == am_heir
@@ -1738,6 +2258,9 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
if (is_not_nil(list)) { /* bad opt or not a well formed list */
BIF_ERROR(BIF_P, BADARG);
}
+ if (-1 != is_decentralized_counters_option) {
+ is_decentralized_counters = is_decentralized_counters_option;
+ }
if (IS_TREE_TABLE(status) && is_fine_locked && !(status & DB_PRIVATE)) {
meth = &db_catree;
status |= DB_CA_ORDERED_SET;
@@ -1762,27 +2285,30 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
/* we create table outside any table lock
* and take the unusal cost of destroy table if it
- * fails to find a slot
+ * fails to find a slot
*/
{
DbTable init_tb;
- erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_DB_TABLE);
+ erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_ETS_CTRS);
tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb, sizeof(DbTable));
erts_flxctr_init(&tb->common.counters,
- status & DB_CA_ORDERED_SET,
+ (status & DB_FINE_LOCKED) && is_decentralized_counters,
2,
- ERTS_ALC_T_DB_TABLE);
+ ERTS_ALC_T_ETS_CTRS);
erts_flxctr_add(&tb->common.counters,
ERTS_DB_TABLE_MEM_COUNTER_ID,
- DB_GET_APPROX_MEM_CONSUMED(&init_tb));
+ DB_GET_APPROX_MEM_CONSUMED(&init_tb) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters));
}
tb->common.meth = meth;
tb->common.the_name = BIF_ARG_1;
- tb->common.status = status;
+ tb->common.status = status;
tb->common.type = status;
/* Note, 'type' is *read only* from now on... */
+ tb->common.continuation = NULL;
+ erts_atomic_set_nob(&tb->common.continuation_state, (Sint)NULL);
erts_refc_init(&tb->common.fix_count, 0);
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ));
tb->common.keypos = keypos;
@@ -1819,7 +2345,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
table_dec_refc(tb, 0);
BIF_ERROR(BIF_P, BADARG);
}
-
+
BIF_P->flags |= F_USING_DB; /* So we can remove tb if p dies */
#ifdef HARDDEBUG
@@ -2194,7 +2720,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
tb->common.status |= DB_BUSY;
db_unlock(tb, LCK_WRITE);
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P,
+ BIF_TRAP2(&bif_trap_export[BIF_ets_internal_delete_all_2], BIF_P,
BIF_ARG_1, nitems_holder);
}
else {
@@ -2225,7 +2751,7 @@ static void delete_all_objects_continue(Process* p, DbTable* tb)
SWord initial_reds = ERTS_BIF_REDS_LEFT(p);
SWord reds = initial_reds;
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock) || DB_LOCK_FREE(tb));
+ ERTS_LC_ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
if ((tb->common.status & (DB_DELETE|DB_BUSY)) != DB_BUSY)
return;
@@ -3310,6 +3836,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed,
am_write_concurrency,
am_read_concurrency,
+ am_decentralized_counters,
am_id};
Eterm results[sizeof(fields)/sizeof(Eterm)];
DbTable* tb;
@@ -3358,7 +3885,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
BIF_RET(am_undefined);
}
if (rp == ERTS_PROC_LOCK_BUSY) {
- ERTS_BIF_YIELD1(bif_export[BIF_ets_info_1], BIF_P, BIF_ARG_1);
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_ets_info_1], BIF_P, BIF_ARG_1);
}
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL
|| tb->common.owner != owner) {
@@ -3373,16 +3900,16 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
if (!is_ctrs_read_result_set) {
ErtsFlxCtrSnapshotResult res =
- erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_ETS_CTRS, BIF_P);
if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
Eterm tuple;
db_unlock(tb, LCK_READ);
hp = HAlloc(BIF_P, 3);
tuple = TUPLE2(hp, res.trap_resume_state, table);
- BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple);
+ BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, tuple);
} else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
db_unlock(tb, LCK_READ);
- BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table);
+ BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, table);
} else {
size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID];
memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID];
@@ -3450,13 +3977,13 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
}
if (BIF_ARG_2 == am_size || BIF_ARG_2 == am_memory) {
ErtsFlxCtrSnapshotResult res =
- erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_ETS_CTRS, BIF_P);
if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
db_unlock(tb, LCK_READ);
- BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2);
+ BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2);
} else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
db_unlock(tb, LCK_READ);
- BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2);
+ BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2);
} else if (BIF_ARG_2 == am_size) {
ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P);
} else { /* BIF_ARG_2 == am_memory */
@@ -3522,7 +4049,7 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
for (lst = BIF_ARG_1; is_list(lst); lst = CDR(list_val(lst))) {
if (++i > CONTEXT_REDS) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3],
+ BIF_TRAP3(&bif_trap_export[BIF_ets_match_spec_run_r_3],
BIF_P,lst,BIF_ARG_2,ret);
}
res = db_prog_match(BIF_P, BIF_P,
@@ -4189,7 +4716,7 @@ static SWord free_fixations_locked(Process* p, DbTable *tb)
{
struct free_fixations_ctx ctx;
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock) || DB_LOCK_FREE(tb));
+ ERTS_LC_ASSERT(DB_LOCK_FREE(tb) || erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
ctx.p = p;
ctx.tb = tb;
@@ -4360,7 +4887,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
} else if (What == am_heir) {
ret = tb->common.heir;
} else if (What == am_protection) {
- if (tb->common.status & DB_PRIVATE)
+ if (tb->common.status & DB_PRIVATE)
ret = am_private;
else if (tb->common.status & DB_PROTECTED)
ret = am_protected;
@@ -4382,6 +4909,8 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = tb->common.compress ? am_true : am_false;
} else if (What == am_id) {
ret = make_tid(p, tb);
+ } else if (What == am_decentralized_counters) {
+ ret = tb->common.counters.is_decentralized ? am_true : am_false;
}
/*
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index c604744687..6327c56625 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -159,13 +159,15 @@ extern erts_aint_t erts_ets_dbg_force_trap;
*/
#define ERTS_DB_ALC_MEM_UPDATE_(TAB, FREE_SZ, ALLOC_SZ) \
-do { \
- erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \
- - ((erts_aint_t) (FREE_SZ))); \
- ASSERT((TAB)); \
- erts_flxctr_add(&(TAB)->common.counters, \
- ERTS_DB_TABLE_MEM_COUNTER_ID, \
- sz__); \
+do { \
+ if ((TAB) != NULL) { \
+ erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \
+ - ((erts_aint_t) (FREE_SZ))); \
+ ASSERT((TAB)); \
+ erts_flxctr_add(&(TAB)->common.counters, \
+ ERTS_DB_TABLE_MEM_COUNTER_ID, \
+ sz__); \
+ } \
} while (0)
#define ERTS_ETS_MISC_MEM_ADD(SZ) \
@@ -310,7 +312,8 @@ erts_db_free(ErtsAlcType_t type, DbTable *tab, void *ptr, Uint size)
ASSERT(ptr != 0);
ASSERT(size == ERTS_ALC_DBG_BLK_SZ(ptr));
ERTS_DB_ALC_MEM_UPDATE_(tab, size, 0);
- ASSERT(((void *) tab) != ptr ||
+ ASSERT(tab == NULL ||
+ ((void *) tab) != ptr ||
tab->common.counters.is_decentralized ||
0 == erts_flxctr_read_centralized(&tab->common.counters,
ERTS_DB_TABLE_MEM_COUNTER_ID));
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 4e08f89692..ccf570d3de 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -104,7 +104,8 @@ static int db_last_catree(Process *p, DbTable *tbl,
static int db_prev_catree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
-static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail);
+static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p);
static int db_get_catree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
static int db_member_catree(DbTable *tbl, Eterm key, Eterm *ret);
@@ -160,6 +161,10 @@ db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *);
static int db_get_binary_info_catree(Process*, DbTable*, Eterm key, Eterm *ret);
+static int db_put_dbterm_catree(DbTable* tbl,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
static void split_catree(DbTableCATree *tb,
DbTableCATreeNode* ERTS_RESTRICT base,
@@ -213,6 +218,12 @@ DbTableMethod db_catree =
db_foreach_offheap_catree,
db_lookup_dbterm_catree,
db_finalize_dbterm_catree,
+ db_eterm_to_dbterm_tree_common,
+ db_dbterm_list_prepend_tree_common,
+ db_dbterm_list_remove_first_tree_common,
+ db_put_dbterm_catree,
+ db_free_dbterm_tree_common,
+ db_get_dbterm_key_tree_common,
db_get_binary_info_catree,
db_first_catree, /* raw_first same as first */
db_next_catree /* raw_next same as next */
@@ -1367,7 +1378,7 @@ static void split_catree(DbTableCATree *tb,
}
}
-/* @brief Free the entire catree and its sub-trees.
+/** @brief Free the entire catree and its sub-trees.
*
* @param reds Reductions to spend.
* @return Reductions left. Negative value if not done.
@@ -1464,7 +1475,7 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
return reds;
}
-/* @brief Free all objects of a base node, but keep the base node.
+/** @brief Free all objects of a base node, but keep the base node.
*
* @param reds Reductions to spend.
* @return Reductions left. Negative value if not done.
@@ -1632,7 +1643,27 @@ static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return result;
}
-static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_put_dbterm_catree(DbTable* tbl,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p)
+{
+ TreeDbTerm *value_to_insert = obj;
+ DbTableCATree *tb = &tbl->catree;
+ Eterm key = GETKEY(tb, value_to_insert->dbterm.tpl);
+ FindBaseNode fbn;
+ DbTableCATreeNode* node = find_wlock_valid_base_node(tb, key, &fbn);
+ int result = db_put_dbterm_tree_common(&tb->common,
+ &node->u.base.root,
+ value_to_insert,
+ key_clash_fail,
+ NULL);
+ wunlock_adapt_base_node(tb, node, fbn.parent, fbn.current_level);
+ return result;
+}
+
+static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableCATree *tb = &tbl->catree;
Eterm key = GETKEY(&tb->common, tuple_val(obj));
@@ -1776,7 +1807,7 @@ TreeDbTerm** catree_find_prev_root(CATreeRootIterator *iter, Eterm* keyp)
return catree_find_nextprev_root(iter, 0, keyp);
}
-/* @brief Find root of tree where object with smallest key of all larger than
+/** @brief Find root of tree where object with smallest key of all larger than
* partially bound key may reside. Can be used as a starting point for
* a reverse iteration with pb_key.
*
@@ -1829,7 +1860,7 @@ TreeDbTerm** catree_find_next_from_pb_key_root(Eterm pb_key,
}
}
-/* @brief Find root of tree where object with largest key of all smaller than
+/** @brief Find root of tree where object with largest key of all smaller than
* partially bound key may reside. Can be used as a starting point for
* a forward iteration with pb_key.
*
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 5508f5c34e..8076cf33ac 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -85,19 +85,58 @@
#include "erl_db_hash.h"
-#define ADD_NITEMS(DB, TO_ADD) \
- erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD)
-#define INC_NITEMS(DB) \
- erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
-#define DEC_NITEMS(DB) \
- erts_flxctr_dec_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define IS_DECENTRALIZED_CTRS(DB) ((DB)->common.counters.is_decentralized)
+
+#define NITEMS_ESTIMATE_FROM_LCK_CTR(LCK_CTR_P) \
+ (LCK_CTR_P->nitems <= 0 ? 1: LCK_CTR_P->nitems)
+
+#define NITEMS_ESTIMATE(DB, LCK_CTR, HASH) \
+ (IS_DECENTRALIZED_CTRS(DB) ? \
+ (DB_HASH_LOCK_CNT * \
+ (LCK_CTR != NULL ? \
+ NITEMS_ESTIMATE_FROM_LCK_CTR(LCK_CTR) : \
+ NITEMS_ESTIMATE_FROM_LCK_CTR(GET_LOCK_AND_CTR(DB, HASH)))) : \
+ erts_flxctr_read_centralized(&(DB)->common.counters, \
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID))
+
+#define ADD_NITEMS(DB, LCK_CTR, HASH, TO_ADD) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems += TO_ADD; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems += TO_ADD; \
+ } \
+ } \
+ erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD); \
+ } while(0)
+#define INC_NITEMS(DB, LCK_CTR, HASH) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems++; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems++; \
+ } \
+ } \
+ erts_flxctr_inc(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID); \
+ } while(0)
+#define DEC_NITEMS(DB, LCK_CTR, HASH) \
+ do { \
+ if (IS_DECENTRALIZED_CTRS(DB)) { \
+ if (LCK_CTR != NULL) { \
+ LCK_CTR->nitems--; \
+ } else { \
+ GET_LOCK_AND_CTR(DB,HASH)->nitems--; \
+ } \
+ } \
+ erts_flxctr_dec(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID); \
+ } while(0)
#define RESET_NITEMS(DB) \
erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
-/*
- * The following symbols can be manipulated to "tune" the linear hash array
- */
+
#define GROW_LIMIT(NACTIVE) ((NACTIVE)*1)
-#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2)
+#define SHRINK_LIMIT(TB) erts_atomic_read_nob(&(TB)->shrink_limit)
/*
** We want the first mandatory segment to be small (to reduce minimal footprint)
@@ -129,14 +168,16 @@
: ((struct segment**) erts_atomic_read_nob(&(tb)->segtab)))
#endif
#define NACTIVE(tb) ((int)erts_atomic_read_nob(&(tb)->nactive))
-#define NITEMS(tb) \
- ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \
- ERTS_DB_TABLE_NITEMS_COUNTER_ID))
#define SLOT_IX_TO_SEG_IX(i) (((i)+(EXT_SEGSZ-FIRST_SEGSZ)) >> EXT_SEGSZ_EXP)
#define BUCKET(tb, i) SEGTAB(tb)[SLOT_IX_TO_SEG_IX(i)]->buckets[(i) & EXT_SEGSZ_MASK]
+#ifdef DEBUG
+# define DBG_BUCKET_INACTIVE ((HashDbTerm*)0xdead5107)
+#endif
+
+
/*
* When deleting a table, the number of records to delete.
* Approximate number, because we must delete entire buckets.
@@ -224,7 +265,8 @@ static ERTS_INLINE int is_pseudo_deleted(HashDbTerm* p)
make_internal_hash(term, 0)) & MAX_HASH_MASK)
# define DB_HASH_LOCK_MASK (DB_HASH_LOCK_CNT-1)
-# define GET_LOCK(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck)
+# define GET_LOCK(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck_ctr.lck)
+# define GET_LOCK_AND_CTR(tb,hval) (&(tb)->locks->lck_vec[(hval) & DB_HASH_LOCK_MASK].lck_ctr)
# define GET_LOCK_MAYBE(tb,hval) ((tb)->common.is_thread_safe ? NULL : GET_LOCK(tb,hval))
/* Fine grained read lock */
@@ -252,6 +294,20 @@ static ERTS_INLINE erts_rwmtx_t* WLOCK_HASH(DbTableHash* tb, HashValue hval)
}
}
+/* Fine grained write lock */
+static ERTS_INLINE
+DbTableHashLockAndCounter* WLOCK_HASH_GET_LCK_AND_CTR(DbTableHash* tb, HashValue hval)
+{
+ if (tb->common.is_thread_safe) {
+ return NULL;
+ } else {
+ DbTableHashLockAndCounter* lck_ctr = GET_LOCK_AND_CTR(tb,hval);
+ ASSERT(tb->common.type & DB_FINE_LOCKED);
+ erts_rwmtx_rwlock(&lck_ctr->lck);
+ return lck_ctr;
+ }
+}
+
static ERTS_INLINE void RUNLOCK_HASH(erts_rwmtx_t* lck)
{
if (lck != NULL) {
@@ -266,6 +322,13 @@ static ERTS_INLINE void WUNLOCK_HASH(erts_rwmtx_t* lck)
}
}
+static ERTS_INLINE void WUNLOCK_HASH_LCK_CTR(DbTableHashLockAndCounter* lck_ctr)
+{
+ if (lck_ctr != NULL) {
+ erts_rwmtx_rwunlock(&lck_ctr->lck);
+ }
+}
+
#ifdef ERTS_ENABLE_LOCK_CHECK
# define IFN_EXCL(tb,cmd) (((tb)->common.is_thread_safe) || (cmd))
@@ -377,7 +440,7 @@ typedef int (*extra_match_validator_t)(int keypos, Eterm match, Eterm guard, Ete
*/
static struct ext_segtab* alloc_ext_segtab(DbTableHash* tb, unsigned seg_ix);
static void alloc_seg(DbTableHash *tb);
-static int free_seg(DbTableHash *tb, int free_records);
+static int free_seg(DbTableHash *tb);
static HashDbTerm* next_live(DbTableHash *tb, Uint *iptr, erts_rwmtx_t** lck_ptr,
HashDbTerm *list);
static HashDbTerm* search_list(DbTableHash* tb, Eterm key,
@@ -468,18 +531,24 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
DbUpdateHandle* handle);
static void
db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
+static void* db_eterm_to_dbterm_hash(int compress, int keypos, Eterm obj);
+static void* db_dbterm_list_prepend_hash(void* list, void* db_term);
+static void* db_dbterm_list_remove_first_hash(void** list);
+static int db_put_dbterm_hash(DbTable* tb,
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
+static void db_free_dbterm_hash(int compressed, void* obj);
+static Eterm db_get_dbterm_key_hash(DbTable* tb, void* db_term);
static int
db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
static int db_raw_first_hash(Process* p, DbTable *tbl, Eterm *ret);
static int db_raw_next_hash(Process* p, DbTable *tbl, Eterm key, Eterm *ret);
-static ERTS_INLINE void try_shrink(DbTableHash* tb)
+static ERTS_INLINE void try_shrink(DbTableHash* tb, Sint nitems)
{
- int nactive = NACTIVE(tb);
- int nitems = NITEMS(tb);
- if (nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive)
- && !IS_FIXED(tb)) {
+ if (nitems < SHRINK_LIMIT(tb) && !IS_FIXED(tb)) {
shrink(tb, nitems);
}
}
@@ -512,28 +581,55 @@ static ERTS_INLINE int has_key(DbTableHash* tb, HashDbTerm* b,
}
}
-static ERTS_INLINE HashDbTerm* new_dbterm(DbTableHash* tb, Eterm obj)
+static ERTS_INLINE HashDbTerm* new_dbterm_hash(DbTableCommon* tb, Eterm obj)
{
HashDbTerm* p;
- if (tb->common.compress) {
- p = db_store_term_comp(&tb->common, NULL, offsetof(HashDbTerm,dbterm), obj);
+ if (tb->compress) {
+ p = db_store_term_comp(tb, tb->keypos, NULL, offsetof(HashDbTerm,dbterm), obj);
}
else {
- p = db_store_term(&tb->common, NULL, offsetof(HashDbTerm,dbterm), obj);
+ p = db_store_term(tb, NULL, offsetof(HashDbTerm,dbterm), obj);
+ }
+ return p;
+}
+
+/*
+ * This function only differ from new_dbterm_hash in that it does not
+ * adjust the memory size of a given table.
+ */
+static ERTS_INLINE HashDbTerm* new_dbterm_hash_no_tab(int compress, int keypos, Eterm obj)
+{
+ HashDbTerm* p;
+ if (compress) {
+ p = db_store_term_comp(NULL, keypos, NULL, offsetof(HashDbTerm,dbterm), obj);
+ } else {
+ p = db_store_term(NULL, NULL, offsetof(HashDbTerm,dbterm), obj);
}
return p;
}
+static ERTS_INLINE HashDbTerm* new_dbterm(DbTableHash* tb, Eterm obj)
+{
+ return new_dbterm_hash(&tb->common, obj);
+}
+
static ERTS_INLINE HashDbTerm* replace_dbterm(DbTableHash* tb, HashDbTerm* old,
Eterm obj)
{
HashDbTerm* ret;
ASSERT(old != NULL);
if (tb->common.compress) {
- ret = db_store_term_comp(&tb->common, &(old->dbterm), offsetof(HashDbTerm,dbterm), obj);
+ ret = db_store_term_comp(&tb->common,
+ tb->common.keypos,
+ &(old->dbterm),
+ offsetof(HashDbTerm,dbterm),
+ obj);
}
else {
- ret = db_store_term(&tb->common, &(old->dbterm), offsetof(HashDbTerm,dbterm), obj);
+ ret = db_store_term(&tb->common,
+ &(old->dbterm),
+ offsetof(HashDbTerm,dbterm),
+ obj);
}
return ret;
}
@@ -575,6 +671,12 @@ DbTableMethod db_hash =
db_foreach_offheap_hash,
db_lookup_dbterm_hash,
db_finalize_dbterm_hash,
+ db_eterm_to_dbterm_hash,
+ db_dbterm_list_prepend_hash,
+ db_dbterm_list_remove_first_hash,
+ db_put_dbterm_hash,
+ db_free_dbterm_hash,
+ db_get_dbterm_key_hash,
db_get_binary_info_hash,
db_raw_first_hash,
db_raw_next_hash
@@ -693,6 +795,7 @@ int db_create_hash(Process *p, DbTable *tbl)
erts_atomic_init_nob(&tb->szm, FIRST_SEGSZ_MASK);
erts_atomic_init_nob(&tb->nactive, FIRST_SEGSZ);
+ erts_atomic_init_nob(&tb->shrink_limit, 0);
erts_atomic_init_nob(&tb->fixdel, (erts_aint_t)NULL);
erts_atomic_init_nob(&tb->segtab, (erts_aint_t)NULL);
SET_SEGTAB(tb, tb->first_segtab);
@@ -715,8 +818,9 @@ int db_create_hash(Process *p, DbTable *tbl)
(DbTable *) tb,
sizeof(DbTableHashFineLocks));
for (i=0; i<DB_HASH_LOCK_CNT; ++i) {
- erts_rwmtx_init_opt(&tb->locks->lck_vec[i].lck, &rwmtx_opt,
+ erts_rwmtx_init_opt(&tb->locks->lck_vec[i].lck_ctr.lck, &rwmtx_opt,
"db_hash_slot", tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
+ tb->locks->lck_vec[i].lck_ctr.nitems = 0;
}
/* This important property is needed to guarantee the two buckets
* involved in a grow/shrink operation it protected by the same lock:
@@ -779,7 +883,7 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
b = next_live(tb, &ix, &lck, b->next);
if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) {
while (b != 0) {
- if (!has_key(tb, b, key, hval) && !is_pseudo_deleted(b)) {
+ if (!has_key(tb, b, key, hval)) {
break;
}
b = next_live(tb, &ix, &lck, b->next);
@@ -789,13 +893,56 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
*ret = am_EOT;
}
else {
+ ASSERT(!is_pseudo_deleted(b));
*ret = db_copy_key(p, tbl, &b->dbterm);
RUNLOCK_HASH(lck);
}
return DB_ERROR_NONE;
}
-int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_eq_terms_comp(DbTableCommon* tb, DbTerm* a, DbTerm* b)
+{
+ ErlOffHeap tmp_offheap_a;
+ Eterm* allocp_a;
+ Eterm* hp_a;
+ Eterm tmp_a;
+ ErlOffHeap tmp_offheap_b;
+ Eterm* allocp_b;
+ Eterm* hp_b;
+ Eterm tmp_b;
+ int is_eq;
+
+ ASSERT(tb->compress);
+ hp_a = allocp_a = erts_alloc(ERTS_ALC_T_TMP, b->size*sizeof(Eterm));
+ tmp_offheap_a.first = NULL;
+ tmp_a = db_copy_from_comp(tb, a, &hp_a, &tmp_offheap_a);
+
+ hp_b = allocp_b = erts_alloc(ERTS_ALC_T_TMP, b->size*sizeof(Eterm));
+ tmp_offheap_b.first = NULL;
+ tmp_b = db_copy_from_comp(tb, b, &hp_b, &tmp_offheap_b);
+
+ is_eq = eq(tmp_a,tmp_b);
+ erts_cleanup_offheap(&tmp_offheap_a);
+ erts_free(ERTS_ALC_T_TMP, allocp_a);
+ erts_cleanup_offheap(&tmp_offheap_b);
+ erts_free(ERTS_ALC_T_TMP, allocp_b);
+ return is_eq;
+}
+
+static ERTS_INLINE int db_terms_eq(DbTableCommon* tb, DbTerm* a, DbTerm* b)
+{
+ if (!tb->compress) {
+ return EQ(make_tuple(a->tpl), make_tuple(b->tpl));
+ }
+ else {
+ return db_eq_terms_comp(tb, a, b);
+ }
+}
+
+static int db_put_dbterm_hash(DbTable* tbl,
+ void* ob,
+ int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableHash *tb = &tbl->hash;
HashValue hval;
@@ -804,13 +951,126 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* q;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems;
int ret = DB_ERROR_NONE;
+ HashDbTerm *value_to_insert = ob;
+ Uint size_to_insert = db_term_size(tbl, value_to_insert, offsetof(HashDbTerm, dbterm));
+ ERTS_DB_ALC_MEM_UPDATE_(tbl, 0, size_to_insert);
+ key = GETKEY(tb, value_to_insert->dbterm.tpl);
+ hval = MAKE_HASH(key);
+ value_to_insert->hvalue = hval;
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
+ ix = hash_to_ix(tb, hval);
+ bp = &BUCKET(tb, ix);
+ b = *bp;
+
+ for (;;) {
+ if (b == NULL) {
+ goto Lnew;
+ }
+ if (has_key(tb,b,key,hval)) {
+ break;
+ }
+ bp = &b->next;
+ b = b->next;
+ }
+ /* Key found
+ */
+ if (tb->common.status & DB_SET) {
+ HashDbTerm* bnext = b->next;
+ if (is_pseudo_deleted(b)) {
+ INC_NITEMS(tb, lck_ctr, hval);
+ b->pseudo_deleted = 0;
+ }
+ else if (key_clash_fail) {
+ ret = DB_ERROR_BADKEY;
+ goto Ldone;
+ }
+ value_to_insert->pseudo_deleted = b->pseudo_deleted;
+ free_term(tb, b);
+ q = value_to_insert;
+ q->next = bnext;
+ ASSERT(q->hvalue == hval);
+ *bp = q;
+ goto Ldone;
+ }
+ else if (key_clash_fail) { /* && (DB_BAG || DB_DUPLICATE_BAG) */
+ q = b;
+ do {
+ if (!is_pseudo_deleted(q)) {
+ ret = DB_ERROR_BADKEY;
+ goto Ldone;
+ }
+ q = q->next;
+ }while (q != NULL && has_key(tb,q,key,hval));
+ }
+ else if (tb->common.status & DB_BAG) {
+ HashDbTerm** qp = bp;
+ q = b;
+ do {
+ if (db_terms_eq(&tb->common,
+ &value_to_insert->dbterm,
+ &q->dbterm)) {
+ if (is_pseudo_deleted(q)) {
+ INC_NITEMS(tb, lck_ctr, hval);
+ q->pseudo_deleted = 0;
+ ASSERT(q->hvalue == hval);
+ if (q != b) { /* must move to preserve key insertion order */
+ *qp = q->next;
+ q->next = b;
+ *bp = q;
+ }
+ }
+ free_term(tb, value_to_insert);
+ goto Ldone;
+ }
+ qp = &q->next;
+ q = *qp;
+ (*consumed_reds_p)++;
+ }while (q != NULL && has_key(tb,q,key,hval));
+ }
+ /*else DB_DUPLICATE_BAG */
+
+Lnew:
+ q = value_to_insert;
+ q->hvalue = hval;
+ q->pseudo_deleted = 0;
+ q->next = b;
+ *bp = q;
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ {
+ int nactive = NACTIVE(tb);
+ if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
+ grow(tb, nitems);
+ }
+ }
+ return DB_ERROR_NONE;
+
+Ldone:
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ return ret;
+}
+
+int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ int ix;
+ Eterm key;
+ HashDbTerm** bp;
+ HashDbTerm* b;
+ HashDbTerm* q;
+ DbTableHashLockAndCounter* lck_ctr;
+ Sint nitems;
+ int ret = DB_ERROR_NONE;
key = GETKEY(tb, tuple_val(obj));
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb, hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -830,7 +1090,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
if (tb->common.status & DB_SET) {
HashDbTerm* bnext = b->next;
if (is_pseudo_deleted(b)) {
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
b->pseudo_deleted = 0;
}
else if (key_clash_fail) {
@@ -859,7 +1119,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
do {
if (db_eq(&tb->common,obj,&q->dbterm)) {
if (is_pseudo_deleted(q)) {
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
q->pseudo_deleted = 0;
ASSERT(q->hvalue == hval);
if (q != b) { /* must move to preserve key insertion order */
@@ -871,8 +1131,10 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
goto Ldone;
}
qp = &q->next;
- q = *qp;
- }while (q != NULL && has_key(tb,q,key,hval));
+ q = *qp;
+ (*consumed_reds_p)++;
+ }while (q != NULL && has_key(tb,q,key,hval));
+
}
/*else DB_DUPLICATE_BAG */
@@ -882,10 +1144,11 @@ Lnew:
q->pseudo_deleted = 0;
q->next = b;
*bp = q;
- nitems = INC_NITEMS(tb);
- WUNLOCK_HASH(lck);
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
{
- int nactive = NACTIVE(tb);
+ int nactive = NACTIVE(tb);
if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
grow(tb, nitems);
}
@@ -893,7 +1156,7 @@ Lnew:
return DB_ERROR_NONE;
Ldone:
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
return ret;
}
@@ -1047,11 +1310,11 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* free_us = NULL;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems_diff = 0;
-
+ Sint nitems;
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb,hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb,hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -1078,10 +1341,13 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
bp = &b->next;
b = b->next;
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
*ret = am_true;
@@ -1099,14 +1365,15 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret)
HashDbTerm** bp;
HashDbTerm* b;
HashDbTerm* free_us = NULL;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int nitems_diff = 0;
+ Sint nitems;
int nkeys = 0;
Eterm key;
key = GETKEY(tb, tuple_val(object));
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb,hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb,hval);
ix = hash_to_ix(tb, hval);
bp = &BUCKET(tb, ix);
b = *bp;
@@ -1139,10 +1406,13 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret)
bp = &b->next;
b = b->next;
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
*ret = am_true;
@@ -2029,9 +2299,11 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix
HashDbTerm** current_ptr = *current_ptr_ptr;
select_delete_context_t* ctx = (select_delete_context_t*) ctx_base;
HashDbTerm* del;
+ DbTableHashLockAndCounter* lck_ctr;
+ Uint32 hval;
if (match_res != am_true)
return 0;
-
+ hval = (*current_ptr)->hvalue;
if (NFIXED(ctx->base.tb) > ctx->fixated_by_me) { /* fixated by others? */
if (slot_ix != ctx->last_pseudo_delete) {
if (!add_fixed_deletion(ctx->base.tb, slot_ix, ctx->fixated_by_me))
@@ -2047,23 +2319,58 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix
del->next = ctx->free_us;
ctx->free_us = del;
}
- DEC_NITEMS(ctx->base.tb);
+ lck_ctr = GET_LOCK_AND_CTR(ctx->base.tb,slot_ix);
+ DEC_NITEMS(ctx->base.tb, lck_ctr, hval);
return 1;
}
+/* This function is only safe to call while the table lock is held in
+ write mode */
+static Sint get_nitems_from_locks_or_counter(DbTableHash* tb)
+{
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ int i;
+ Sint total = 0;
+ for (i=0; i < DB_HASH_LOCK_CNT; ++i) {
+ total += tb->locks->lck_vec[i].lck_ctr.nitems;
+ }
+ return total;
+ } else {
+ return erts_flxctr_read_centralized(&tb->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ }
+}
+
static int select_delete_on_loop_ended(traverse_context_t* ctx_base,
Sint slot_ix, Sint got,
Sint iterations_left, Binary** mpp,
Eterm* ret)
{
select_delete_context_t* ctx = (select_delete_context_t*) ctx_base;
- free_term_list(ctx->base.tb, ctx->free_us);
+ DbTableHash* tb = ctx->base.tb;
+ free_term_list(tb, ctx->free_us);
ctx->free_us = NULL;
ASSERT(iterations_left <= MAX_SELECT_DELETE_ITERATIONS);
BUMP_REDS(ctx->base.p, MAX_SELECT_DELETE_ITERATIONS - iterations_left);
if (got) {
- try_shrink(ctx->base.tb);
+ Sint nitems;
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ /* Get a random hash value so we can get an nitems
+ estimate from a random lock */
+ HashValue hval =
+ (HashValue)&ctx +
+ (HashValue)iterations_left +
+ (HashValue)erts_get_scheduler_data()->reductions;
+ erts_rwmtx_t* lck = RLOCK_HASH(tb, hval);
+ DbTableHashLockAndCounter* lck_ctr = GET_LOCK_AND_CTR(tb, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ RUNLOCK_HASH(lck);
+ } else {
+ nitems = erts_flxctr_read_centralized(&tb->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ }
+ try_shrink(tb, nitems);
}
*ret = erts_make_integer(got, ctx->base.p);
return DB_ERROR_NONE;
@@ -2294,9 +2601,10 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
HashDbTerm **bp, *b;
HashDbTerm *free_us = NULL;
HashValue hval = MAKE_HASH(key);
- erts_rwmtx_t *lck = WLOCK_HASH(tb, hval);
+ DbTableHashLockAndCounter *lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
int ix = hash_to_ix(tb, hval);
int nitems_diff = 0;
+ Sint nitems;
*ret = NIL;
for (bp = &BUCKET(tb, ix), b = *bp; b; bp = &b->next, b = b->next) {
@@ -2322,10 +2630,13 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
break;
}
}
- WUNLOCK_HASH(lck);
if (nitems_diff) {
- ADD_NITEMS(tb, nitems_diff);
- try_shrink(tb);
+ ADD_NITEMS(tb, lck_ctr, hval, nitems_diff);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ }
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ if (nitems_diff) {
+ try_shrink(tb, nitems);
}
free_term_list(tb, free_us);
return DB_ERROR_NONE;
@@ -2449,7 +2760,7 @@ static void db_print_hash(fmtfn_t to, void *to_arg, int show, DbTable *tbl)
static int db_free_empty_table_hash(DbTable *tbl)
{
- ASSERT(NITEMS(tbl) == 0);
+ ASSERT(get_nitems_from_locks_or_counter(&tbl->hash) == 0);
while (db_free_table_continue_hash(tbl, ERTS_SWORD_MAX) < 0)
;
return 0;
@@ -2474,7 +2785,7 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
erts_atomic_set_relb(&tb->fixdel, (erts_aint_t)NULL);
while(tb->nslots != 0) {
- reds -= EXT_SEGSZ/64 + free_seg(tb, 1);
+ reds -= EXT_SEGSZ/64 + free_seg(tb);
/*
* If we have done enough work, get out here.
@@ -2492,8 +2803,11 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
(void*)tb->locks, sizeof(DbTableHashFineLocks));
tb->locks = NULL;
}
- ASSERT(sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
- ERTS_DB_TABLE_MEM_COUNTER_ID));
+ ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
+ ((sizeof(DbTable) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)) ==
+ erts_flxctr_read_approx(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID)));
return reds; /* Done */
}
@@ -2672,6 +2986,63 @@ static struct ext_segtab* alloc_ext_segtab(DbTableHash* tb, unsigned seg_ix)
return est;
}
+static void calc_shrink_limit(DbTableHash* tb)
+{
+ erts_aint_t shrink_limit;
+ int sample_size_is_enough = 1;
+
+ if (IS_DECENTRALIZED_CTRS(tb)) {
+ /*
+ Cochran’s Sample Size Formula indicates that we will get
+ good estimates if we have 100 buckets or more per lock (see
+ calculations below)
+ */
+ /* square of z-score 95% confidence */
+ /* const double z2 = 1.96*1.96; */
+ /* Estimated propotion used buckets */
+ /* const double p = 0.5; */
+ /* margin of error */
+ /* const double moe = 0.1; */
+ /* const double moe2 = moe*moe; */
+ /* Cochran’s Sample Size Formula x=96.040 */
+ /* const double x = (z2 * p * (1-p)) / moe2; */
+ /* Modification for smaller populations */
+ /* for(int n = 10; n < 1000; n = n + 100){ */
+ /* const double d = n*x / (x + n - 1) + 1; */
+ /* printf("Cochran_formula=%f size=%d mod_with_size=%f\n", x, n, d); */
+ /* } */
+ const int needed_slots = 100 * DB_HASH_LOCK_CNT;
+ if (tb->nslots < needed_slots) {
+ sample_size_is_enough = 0;
+ }
+ }
+
+ if (sample_size_is_enough && tb->nslots >= (FIRST_SEGSZ + 2*EXT_SEGSZ)) {
+ /*
+ * Start shrink when the sample size is big enough for
+ * decentralized counters if decentralized counters are used
+ * and when we can remove one extra segment and still remain
+ * below 50% load.
+ */
+ shrink_limit = (tb->nslots - EXT_SEGSZ) / 2;
+ }
+ else {
+ /*
+ * But don't shrink below two segments.
+ * Why? In order to have chance of getting rid of the last extra segment,
+ * and rehash it into the first small segment, we either have to start
+ * early and do speculative joining of buckets or we have to join a lot
+ * of buckets during each delete-op.
+ *
+ * Instead keep segment #2 once allocated. I also think it's a good bet
+ * a shrinking large table will grow large again.
+ */
+ shrink_limit = 0;
+ }
+ erts_atomic_set_nob(&tb->shrink_limit, shrink_limit);
+}
+
+
/* Extend table with one new segment
*/
static void alloc_seg(DbTableHash *tb)
@@ -2690,8 +3061,17 @@ static void alloc_seg(DbTableHash *tb)
segtab[seg_ix] = (struct segment*) erts_db_alloc(ERTS_ALC_T_DB_SEG,
(DbTable *) tb,
SIZEOF_SEGMENT(EXT_SEGSZ));
- sys_memset(segtab[seg_ix], 0, SIZEOF_SEGMENT(EXT_SEGSZ));
+#ifdef DEBUG
+ {
+ int i;
+ for (i = 0; i < EXT_SEGSZ; i++) {
+ segtab[seg_ix]->buckets[i] = DBG_BUCKET_INACTIVE;
+ }
+ }
+#endif
tb->nslots += EXT_SEGSZ;
+
+ calc_shrink_limit(tb);
}
static void dealloc_ext_segtab(void* lop_data)
@@ -2701,10 +3081,19 @@ static void dealloc_ext_segtab(void* lop_data)
erts_free(ERTS_ALC_T_DB_SEG, est);
}
-/* Shrink table by freeing the top segment
+struct dealloc_seg_ops {
+ struct segment* segp;
+ Uint seg_sz;
+
+ struct ext_segtab* est;
+};
+
+/* Shrink table by removing the top segment
** free_records: 1=free any records in segment, 0=assume segment is empty
+** ds_ops: (out) Instructions for dealloc_seg().
*/
-static int free_seg(DbTableHash *tb, int free_records)
+static int remove_seg(DbTableHash *tb, int free_records,
+ struct dealloc_seg_ops *ds_ops)
{
const int seg_ix = SLOT_IX_TO_SEG_IX(tb->nslots) - 1;
struct segment** const segtab = SEGTAB(tb);
@@ -2712,24 +3101,47 @@ static int free_seg(DbTableHash *tb, int free_records)
Uint seg_sz;
int nrecords = 0;
+ ERTS_LC_ASSERT(IS_TAB_WLOCKED(tb) || tb->common.status & DB_DELETE
+ || erts_atomic_read_nob(&tb->is_resizing));
+
ASSERT(segp != NULL);
-#ifndef DEBUG
- if (free_records)
-#endif
- {
- int i = (seg_ix == 0) ? FIRST_SEGSZ : EXT_SEGSZ;
- while (i--) {
- HashDbTerm* p = segp->buckets[i];
+ if (free_records) {
+ int ix, n;
+ if (seg_ix == 0) {
+ /* First segment (always fully active) */
+ n = FIRST_SEGSZ;
+ ix = FIRST_SEGSZ-1;
+ }
+ else if (NACTIVE(tb) < tb->nslots) {
+ /* Last extended segment partially active */
+ n = (NACTIVE(tb) - FIRST_SEGSZ) & EXT_SEGSZ_MASK;
+ ix = (NACTIVE(tb)-1) & EXT_SEGSZ_MASK;
+ }
+ else {
+ /* Full extended segment */
+ n = EXT_SEGSZ;
+ ix = EXT_SEGSZ - 1;
+ }
+ for ( ; n > 0; n--, ix--) {
+ HashDbTerm* p = segp->buckets[ix & EXT_SEGSZ_MASK];
while(p != 0) {
HashDbTerm* nxt = p->next;
- ASSERT(free_records); /* segment not empty as assumed? */
free_term(tb, p);
p = nxt;
++nrecords;
}
}
}
-
+#ifdef DEBUG
+ else {
+ int ix = (seg_ix == 0) ? FIRST_SEGSZ-1 : EXT_SEGSZ-1;
+ for ( ; ix >= 0; ix--) {
+ ASSERT(segp->buckets[ix] == DBG_BUCKET_INACTIVE);
+ }
+ }
+#endif
+
+ ds_ops->est = NULL;
if (seg_ix >= NSEG_1) {
struct ext_segtab* est = ErtsContainerStruct_(segtab,struct ext_segtab,segtab);
@@ -2738,35 +3150,64 @@ static int free_seg(DbTableHash *tb, int free_records)
SET_SEGTAB(tb, est->prev_segtab);
tb->nsegs = est->prev_nsegs;
- if (!tb->common.is_thread_safe) {
- /*
- * Table is doing a graceful shrink operation and we must avoid
- * deallocating this segtab while it may still be read by other
- * threads. Schedule deallocation with thread progress to make
- * sure no lingering threads are still hanging in BUCKET macro
- * with an old segtab pointer.
- */
- erts_schedule_db_free(&tb->common, dealloc_ext_segtab,
- est, &est->lop,
- SIZEOF_EXT_SEGTAB(est->nsegs));
- }
- else
- erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable*)tb, est,
- SIZEOF_EXT_SEGTAB(est->nsegs));
+ ds_ops->est = est;
}
}
+
seg_sz = (seg_ix == 0) ? FIRST_SEGSZ : EXT_SEGSZ;
- erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable *)tb, segp, SIZEOF_SEGMENT(seg_sz));
+ tb->nslots -= seg_sz;
+ ASSERT(tb->nslots >= 0);
+
+ ds_ops->segp = segp;
+ ds_ops->seg_sz = seg_sz;
#ifdef DEBUG
if (seg_ix < tb->nsegs)
SEGTAB(tb)[seg_ix] = NULL;
#endif
- tb->nslots -= seg_sz;
- ASSERT(tb->nslots >= 0);
+ calc_shrink_limit(tb);
return nrecords;
}
+/*
+ * Deallocate segment removed by remove_seg()
+ */
+static void dealloc_seg(DbTableHash *tb, struct dealloc_seg_ops* ds_ops)
+{
+ struct ext_segtab* est = ds_ops->est;
+
+ if (est) {
+ if (!tb->common.is_thread_safe) {
+ /*
+ * Table is doing a graceful shrink operation and we must avoid
+ * deallocating this segtab while it may still be read by other
+ * threads. Schedule deallocation with thread progress to make
+ * sure no lingering threads are still hanging in BUCKET macro
+ * with an old segtab pointer.
+ */
+ erts_schedule_db_free(&tb->common, dealloc_ext_segtab,
+ est, &est->lop,
+ SIZEOF_EXT_SEGTAB(est->nsegs));
+ }
+ else
+ erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable*)tb, est,
+ SIZEOF_EXT_SEGTAB(est->nsegs));
+ }
+
+ erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable *)tb,
+ ds_ops->segp, SIZEOF_SEGMENT(ds_ops->seg_sz));
+}
+
+/* Remove and deallocate top segment and all its contained objects */
+static int free_seg(DbTableHash *tb)
+{
+ struct dealloc_seg_ops ds_ops;
+ int reds;
+
+ reds = remove_seg(tb, 1, &ds_ops);
+ dealloc_seg(tb, &ds_ops);
+ return reds;
+}
/*
** Copy terms from ptr1 until ptr2
@@ -2811,9 +3252,11 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2,
static ERTS_INLINE int
begin_resizing(DbTableHash* tb)
{
- if (DB_USING_FINE_LOCKING(tb))
- return !erts_atomic_xchg_acqb(&tb->is_resizing, 1);
- else
+ if (DB_USING_FINE_LOCKING(tb)) {
+ return
+ !erts_atomic_read_acqb(&tb->is_resizing) &&
+ !erts_atomic_xchg_acqb(&tb->is_resizing, 1);
+ } else
ERTS_LC_ASSERT(IS_TAB_WLOCKED(tb));
return 1;
}
@@ -2888,6 +3331,7 @@ static void grow(DbTableHash* tb, int nitems)
pnext = &BUCKET(tb, from_ix);
p = *pnext;
to_pnext = &BUCKET(tb, to_ix);
+ ASSERT(*to_pnext == DBG_BUCKET_INACTIVE);
while (p != NULL) {
if (is_pseudo_deleted(p)) { /* rare but possible with fine locking */
*pnext = p->next;
@@ -2924,19 +3368,21 @@ abort:
*/
static void shrink(DbTableHash* tb, int nitems)
{
- HashDbTerm** src_bp;
- HashDbTerm** dst_bp;
+ struct dealloc_seg_ops ds_ops;
+ HashDbTerm* src;
+ HashDbTerm* tail;
HashDbTerm** bp;
erts_rwmtx_t* lck;
int src_ix, dst_ix, low_szm;
int nactive;
int loop_limit = 5;
+ ds_ops.segp = NULL;
do {
if (!begin_resizing(tb))
return; /* already in progress */
nactive = NACTIVE(tb);
- if (!(nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive))) {
+ if (!(nitems < SHRINK_LIMIT(tb))) {
goto abort; /* already done (race) */
}
src_ix = nactive - 1;
@@ -2953,41 +3399,49 @@ static void shrink(DbTableHash* tb, int nitems)
goto abort;
}
- src_bp = &BUCKET(tb, src_ix);
- dst_bp = &BUCKET(tb, dst_ix);
- bp = src_bp;
-
- /*
- * We join lists by appending "dst" at the end of "src"
- * as we must step through "src" anyway to purge pseudo deleted.
- */
- while(*bp != NULL) {
- if (is_pseudo_deleted(*bp)) {
- HashDbTerm* deleted = *bp;
- *bp = deleted->next;
- free_term(tb, deleted);
- } else {
- bp = &(*bp)->next;
- }
- }
- *bp = *dst_bp;
- *dst_bp = *src_bp;
- *src_bp = NULL;
-
+ src = BUCKET(tb, src_ix);
+#ifdef DEBUG
+ BUCKET(tb, src_ix) = DBG_BUCKET_INACTIVE;
+#endif
nactive = src_ix;
erts_atomic_set_nob(&tb->nactive, nactive);
if (dst_ix == 0) {
erts_atomic_set_relb(&tb->szm, low_szm);
}
- WUNLOCK_HASH(lck);
-
if (tb->nslots - src_ix >= EXT_SEGSZ) {
- free_seg(tb, 0);
+ remove_seg(tb, 0, &ds_ops);
}
done_resizing(tb);
- } while (--loop_limit
- && nactive > FIRST_SEGSZ && nitems < SHRINK_LIMIT(nactive));
+ if (src) {
+ /*
+ * We join buckets by appending "dst" list at the end of "src" list
+ * as we must step through "src" anyway to purge pseudo deleted.
+ */
+ bp = &BUCKET(tb, dst_ix);
+ tail = *bp;
+ *bp = src;
+
+ while(*bp != NULL) {
+ if (is_pseudo_deleted(*bp)) {
+ HashDbTerm* deleted = *bp;
+ *bp = deleted->next;
+ free_term(tb, deleted);
+ } else {
+ bp = &(*bp)->next;
+ }
+ }
+ *bp = tail;
+ }
+
+ WUNLOCK_HASH(lck);
+
+ if (ds_ops.segp) {
+ dealloc_seg(tb, &ds_ops);
+ ds_ops.segp = NULL;
+ }
+
+ } while (--loop_limit && nitems < SHRINK_LIMIT(tb));
return;
abort:
@@ -3047,13 +3501,13 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
DbTableHash *tb = &tbl->hash;
HashValue hval;
HashDbTerm **bp, *b;
- erts_rwmtx_t* lck;
+ DbTableHashLockAndCounter* lck_ctr;
int flags = 0;
ASSERT(tb->common.status & DB_SET);
hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb, hval);
+ lck_ctr = WLOCK_HASH_GET_LCK_AND_CTR(tb, hval);
bp = &BUCKET(tb, hash_to_ix(tb, hval));
b = *bp;
@@ -3072,7 +3526,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
}
if (obj == THE_NON_VALUE) {
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
return 0;
}
@@ -3105,7 +3559,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
ASSERT(q->hvalue == hval);
q->pseudo_deleted = 0;
*bp = b = q;
- INC_NITEMS(tb);
+ INC_NITEMS(tb, lck_ctr, hval);
}
HRelease(p, hend, htop);
@@ -3118,7 +3572,7 @@ Ldone:
handle->dbterm = &b->dbterm;
handle->flags = flags;
handle->new_size = b->dbterm.size;
- handle->u.hash.lck = lck;
+ handle->u.hash.lck_ctr = lck_ctr;
return 1;
}
@@ -3131,10 +3585,12 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
DbTableHash *tb = &tbl->hash;
HashDbTerm **bp = (HashDbTerm **) handle->bp;
HashDbTerm *b = *bp;
- erts_rwmtx_t* lck = handle->u.hash.lck;
+ Uint32 hval = b->hvalue;
+ DbTableHashLockAndCounter* lck_ctr = handle->u.hash.lck_ctr;
HashDbTerm* free_me = NULL;
+ Sint nitems;
- ERTS_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */
+ ERTS_LC_ASSERT(IS_HASH_WLOCKED(tb, &lck_ctr->lck)); /* locked by db_lookup_dbterm_hash */
ASSERT((&b->dbterm == handle->dbterm) == !(tb->common.compress && handle->flags & DB_MUST_RESIZE));
@@ -3146,11 +3602,11 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
*bp = b->next;
free_me = b;
}
-
- WUNLOCK_HASH(lck);
if (!(handle->flags & DB_INC_TRY_GROW))
- DEC_NITEMS(tb);
- try_shrink(tb);
+ DEC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
+ try_shrink(tb, nitems);
} else {
if (handle->flags & DB_MUST_RESIZE) {
ASSERT(cret == DB_ERROR_NONE);
@@ -3159,16 +3615,18 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
}
if (handle->flags & DB_INC_TRY_GROW) {
int nactive;
- int nitems = INC_NITEMS(tb);
+ int nitems;
ASSERT(cret == DB_ERROR_NONE);
- WUNLOCK_HASH(lck);
+ INC_NITEMS(tb, lck_ctr, hval);
+ nitems = NITEMS_ESTIMATE(tb, lck_ctr, hval);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
nactive = NACTIVE(tb);
if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
grow(tb, nitems);
}
} else {
- WUNLOCK_HASH(lck);
+ WUNLOCK_HASH_LCK_CTR(lck_ctr);
}
}
@@ -3187,9 +3645,7 @@ static SWord db_delete_all_objects_hash(Process* p,
Eterm* nitems_holder_wb)
{
if (nitems_holder_wb != NULL) {
- Uint nr_of_items =
- erts_flxctr_read_centralized(&tbl->common.counters,
- ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ Uint nr_of_items = get_nitems_from_locks_or_counter(&tbl->hash);
*nitems_holder_wb = erts_make_integer(nr_of_items, p);
}
if (IS_FIXED(tbl)) {
@@ -3397,6 +3853,49 @@ static int db_raw_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return DB_ERROR_NONE;
}
+static void* db_eterm_to_dbterm_hash(int compress, int keypos, Eterm obj)
+{
+ HashDbTerm* term = new_dbterm_hash_no_tab(compress, keypos, obj);
+ term->next = NULL;
+ return term;
+}
+
+static void* db_dbterm_list_prepend_hash(void* list, void* db_term)
+{
+ HashDbTerm* l = list;
+ HashDbTerm* t = db_term;
+ t->next = l;
+ return t;
+}
+
+static void* db_dbterm_list_remove_first_hash(void** list)
+{
+ if (*list == NULL) {
+ return NULL;
+ } else {
+ HashDbTerm* t = (*list);
+ HashDbTerm* l = t->next;
+ *list = l;
+ return t;
+ }
+}
+
+/*
+ * Frees a HashDbTerm without updating the memory footprint of the
+ * table.
+ */
+static void db_free_dbterm_hash(int compressed, void* obj)
+{
+ HashDbTerm* p = obj;
+ db_free_term_no_tab(compressed, p, offsetof(HashDbTerm, dbterm));
+}
+
+static Eterm db_get_dbterm_key_hash(DbTable* tb, void* db_term)
+{
+ HashDbTerm *value_to_insert = db_term;
+ return GETKEY(tb, value_to_insert->dbterm.tpl);
+}
+
/* For testing only */
Eterm erts_ets_hash_sizeof_ext_segtab(void)
{
@@ -3418,7 +3917,7 @@ void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable) {
}
for(i = 0; i < DB_HASH_LOCK_CNT; i++) {
- erts_lcnt_ref_t *ref = &tb->locks->lck_vec[i].lck.lcnt;
+ erts_lcnt_ref_t *ref = &tb->locks->lck_vec[i].lck_ctr.lck.lcnt;
if(enable) {
erts_lcnt_install_new_lock_info(ref, "db_hash_slot", tb->common.the_name,
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index 9759d8b466..b119129099 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -53,9 +53,14 @@ typedef struct hash_db_term {
#define DB_HASH_LOCK_CNT 64
#endif
+typedef struct DbTableHashLockAndCounter {
+ Sint nitems;
+ erts_rwmtx_t lck;
+} DbTableHashLockAndCounter;
+
typedef struct db_table_hash_fine_locks {
union {
- erts_rwmtx_t lck;
+ DbTableHashLockAndCounter lck_ctr;
byte _cache_line_alignment[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_rwmtx_t))];
}lck_vec[DB_HASH_LOCK_CNT];
} DbTableHashFineLocks;
@@ -63,9 +68,10 @@ typedef struct db_table_hash_fine_locks {
typedef struct db_table_hash {
DbTableCommon common;
- /* SMP: szm and nactive are write-protected by is_resizing or table write lock */
+ /* szm, nactive, shrink_limit are write-protected by is_resizing or table write lock */
erts_atomic_t szm; /* current size mask. */
erts_atomic_t nactive; /* Number of "active" slots */
+ erts_atomic_t shrink_limit; /* Shrink table when fewer objects than this */
erts_atomic_t segtab; /* The segment table (struct segment**) */
struct segment* first_segtab[1];
@@ -93,7 +99,7 @@ Uint db_kept_items_hash(DbTableHash *tb);
int db_create_hash(Process *p,
DbTable *tbl /* [in out] */);
-int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail);
+int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail, SWord* consumed_reds_p);
int db_get_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 49158108a2..f22a7ae1e9 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -142,20 +142,33 @@ static ERTS_INLINE TreeDbTerm* new_dbterm(DbTableCommon *tb, Eterm obj)
{
TreeDbTerm* p;
if (tb->compress) {
- p = db_store_term_comp(tb, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ p = db_store_term_comp(tb, tb->keypos, NULL, offsetof(TreeDbTerm,dbterm), obj);
}
else {
p = db_store_term(tb, NULL, offsetof(TreeDbTerm,dbterm), obj);
}
return p;
}
+
+static ERTS_INLINE TreeDbTerm* new_dbterm_no_tab(int compress, int keypos, Eterm obj)
+{
+ TreeDbTerm* p;
+ if (compress) {
+ p = db_store_term_comp(NULL, keypos, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ }
+ else {
+ p = db_store_term(NULL, NULL, offsetof(TreeDbTerm,dbterm), obj);
+ }
+ return p;
+}
+
static ERTS_INLINE TreeDbTerm* replace_dbterm(DbTableCommon *tb, TreeDbTerm* old,
Eterm obj)
{
TreeDbTerm* p;
ASSERT(old != NULL);
if (tb->compress) {
- p = db_store_term_comp(tb, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
+ p = db_store_term_comp(tb, tb->keypos, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
}
else {
p = db_store_term(tb, &(old->dbterm), offsetof(TreeDbTerm,dbterm), obj);
@@ -396,7 +409,7 @@ static int db_last_tree(Process *p, DbTable *tbl,
static int db_prev_tree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
-static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail);
+static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail, SWord *consumed_reds_p);
static int db_get_tree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
static int db_member_tree(DbTable *tbl, Eterm key, Eterm *ret);
@@ -458,9 +471,11 @@ db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void
db_finalize_dbterm_tree(int cret, DbUpdateHandle *);
-
static int db_get_binary_info_tree(Process*, DbTable*, Eterm key, Eterm *ret);
-
+static int db_put_dbterm_tree(DbTable* tbl, /* [in out] */
+ void* obj,
+ int key_clash_fail,
+ SWord *consumed_reds_p);
/*
** Static variables
@@ -503,6 +518,12 @@ DbTableMethod db_tree =
db_foreach_offheap_tree,
db_lookup_dbterm_tree,
db_finalize_dbterm_tree,
+ db_eterm_to_dbterm_tree_common,
+ db_dbterm_list_prepend_tree_common,
+ db_dbterm_list_remove_first_tree_common,
+ db_put_dbterm_tree,
+ db_free_dbterm_tree_common,
+ db_get_dbterm_key_tree_common,
db_get_binary_info_tree,
db_first_tree, /* raw_first same as first */
db_next_tree /* raw_next same as next */
@@ -660,6 +681,139 @@ static ERTS_INLINE int cmp_key_eq(DbTableCommon* tb, Eterm key, TreeDbTerm* obj)
return is_same(key, obj_key) || CMP(key, obj_key) == 0;
}
+/*
+ * This function differ to db_put_tree_common in that it inserts a TreeDbTerm
+ * instead of an Eterm.
+ */
+int db_put_dbterm_tree_common(DbTableCommon *tb,
+ TreeDbTerm **root,
+ TreeDbTerm *value_to_insert,
+ int key_clash_fail,
+ DbTableTree *stack_container)
+{
+ /* Non recursive insertion in AVL tree, building our own stack */
+ TreeDbTerm **tstack[STACK_NEED];
+ int tpos = 0;
+ int dstack[STACK_NEED+1];
+ int dpos = 0;
+ int state = 0;
+ TreeDbTerm **this = root;
+ Sint c;
+ Eterm key;
+ int dir;
+ TreeDbTerm *p1, *p2, *p;
+ Uint size_to_insert = db_term_size((DbTable*)tb, value_to_insert, offsetof(TreeDbTerm, dbterm));
+ ERTS_DB_ALC_MEM_UPDATE_((DbTable*)tb, 0, size_to_insert);
+ key = GETKEY(tb, value_to_insert->dbterm.tpl);
+
+ reset_static_stack(stack_container);
+
+ dstack[dpos++] = DIR_END;
+ for (;;)
+ if (!*this) { /* Found our place */
+ state = 1;
+ INC_NITEMS(((DbTable*)tb));
+ *this = value_to_insert;
+ (*this)->balance = 0;
+ (*this)->left = (*this)->right = NULL;
+ break;
+ } else if ((c = cmp_key(tb, key, *this)) < 0) {
+ /* go lefts */
+ dstack[dpos++] = DIR_LEFT;
+ tstack[tpos++] = this;
+ this = &((*this)->left);
+ } else if (c > 0) { /* go right */
+ dstack[dpos++] = DIR_RIGHT;
+ tstack[tpos++] = this;
+ this = &((*this)->right);
+ } else if (!key_clash_fail) { /* Equal key and this is a set, replace. */
+ value_to_insert->balance = (*this)->balance;
+ value_to_insert->left = (*this)->left;
+ value_to_insert->right = (*this)->right;
+ free_term((DbTable*)tb, *this);
+ *this = value_to_insert;
+ break;
+ } else {
+ return DB_ERROR_BADKEY; /* key already exists */
+ }
+
+ while (state && ( dir = dstack[--dpos] ) != DIR_END) {
+ this = tstack[--tpos];
+ p = *this;
+ if (dir == DIR_LEFT) {
+ switch (p->balance) {
+ case 1:
+ p->balance = 0;
+ state = 0;
+ break;
+ case 0:
+ p->balance = -1;
+ break;
+ case -1: /* The icky case */
+ p1 = p->left;
+ if (p1->balance == -1) { /* Single LL rotation */
+ p->left = p1->right;
+ p1->right = p;
+ p->balance = 0;
+ (*this) = p1;
+ } else { /* Double RR rotation */
+ p2 = p1->right;
+ p1->right = p2->left;
+ p2->left = p1;
+ p->left = p2->right;
+ p2->right = p;
+ p->balance = (p2->balance == -1) ? +1 : 0;
+ p1->balance = (p2->balance == 1) ? -1 : 0;
+ (*this) = p2;
+ }
+ (*this)->balance = 0;
+ state = 0;
+ break;
+ }
+ } else { /* dir == DIR_RIGHT */
+ switch (p->balance) {
+ case -1:
+ p->balance = 0;
+ state = 0;
+ break;
+ case 0:
+ p->balance = 1;
+ break;
+ case 1:
+ p1 = p->right;
+ if (p1->balance == 1) { /* Single RR rotation */
+ p->right = p1->left;
+ p1->left = p;
+ p->balance = 0;
+ (*this) = p1;
+ } else { /* Double RL rotation */
+ p2 = p1->left;
+ p1->left = p2->right;
+ p2->right = p1;
+ p->right = p2->left;
+ p2->left = p;
+ p->balance = (p2->balance == 1) ? -1 : 0;
+ p1->balance = (p2->balance == -1) ? 1 : 0;
+ (*this) = p2;
+ }
+ (*this)->balance = 0;
+ state = 0;
+ break;
+ }
+ }
+ }
+ return DB_ERROR_NONE;
+}
+
+static int db_put_dbterm_tree(DbTable* tbl, /* [in out] */
+ void* obj,
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p)
+{
+ DbTableTree *tb = &tbl->tree;
+ return db_put_dbterm_tree_common(&tb->common, &tb->root, obj, key_clash_fail, tb);
+}
+
int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
int key_clash_fail, DbTableTree *stack_container)
{
@@ -772,7 +926,8 @@ int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
return DB_ERROR_NONE;
}
-static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail)
+static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail,
+ SWord *consumed_reds_p)
{
DbTableTree *tb = &tbl->tree;
return db_put_tree_common(&tb->common, &tb->root, obj, key_clash_fail, tb);
@@ -1175,7 +1330,7 @@ int db_select_continue_tree_common(Process *p,
sc.accum,
tptr[7],
make_small(sc.got));
- RET_TO_BIF(bif_trap1(bif_export[BIF_ets_select_1], p, continuation),
+ RET_TO_BIF(bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation),
DB_ERROR_NONE);
#undef RET_TO_BIF
@@ -1320,7 +1475,7 @@ int db_select_tree_common(Process *p, DbTable *tb,
make_small(sc.got));
/* Don't free mpi.mp, so don't use macro */
- *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation);
+ *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation);
return DB_ERROR_NONE;
#undef RET_TO_BIF
@@ -1728,7 +1883,7 @@ int db_select_chunk_tree_common(Process *p, DbTable *tb,
make_small(reverse),
make_small(sc.got));
/* Don't let RET_TO_BIF macro free mpi.mp*/
- *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation);
+ *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation);
return DB_ERROR_NONE;
#undef RET_TO_BIF
@@ -2307,9 +2462,12 @@ static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds)
sizeof(TreeDbTerm *) * STACK_NEED);
ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
((APPROX_MEM_CONSUMED(tb)
- == sizeof(DbTable)) ||
+ == (sizeof(DbTable) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters))) ||
(APPROX_MEM_CONSUMED(tb)
- == (sizeof(DbTable) + sizeof(DbFixation)))));
+ == (sizeof(DbTable) +
+ sizeof(DbFixation) +
+ erts_flxctr_nr_of_allocated_bytes(&tb->common.counters)))));
}
return reds;
}
@@ -3028,8 +3186,8 @@ found_prev:
}
-/* @brief Find object with smallest key of all larger than partially bound key.
- * Can be used as a starting point for a reverse iteration with pb_key.
+/** @brief Find object with smallest key of all larger than partially bound
+ * key. Can be used as a starting point for a reverse iteration with pb_key.
*
* @param pb_key The partially bound key. Example {42, '$1'}
* @param *rootpp Will return pointer to root pointer of tree with found object.
@@ -3078,8 +3236,8 @@ static TreeDbTerm *find_next_from_pb_key(DbTable *tbl, TreeDbTerm*** rootpp,
}
}
-/* @brief Find object with largest key of all smaller than partially bound key.
- * Can be used as a starting point for a forward iteration with pb_key.
+/** @brief Find object with largest key of all smaller than partially bound
+ * key. Can be used as a starting point for a forward iteration with pb_key.
*
* @param pb_key The partially bound key. Example {42, '$1'}
* @param *rootpp Will return pointer to root pointer of found object.
@@ -3355,6 +3513,50 @@ Eterm db_binary_info_tree_common(Process* p, TreeDbTerm* this)
}
+void* db_eterm_to_dbterm_tree_common(int compress, int keypos, Eterm obj)
+{
+ TreeDbTerm* term = new_dbterm_no_tab(compress, keypos, obj);
+ term->left = NULL;
+ term->right = NULL;
+ return term;
+}
+
+void* db_dbterm_list_prepend_tree_common(void *list, void *db_term)
+{
+ TreeDbTerm* l = list;
+ TreeDbTerm* t = db_term;
+ t->left = l;
+ return t;
+}
+
+void* db_dbterm_list_remove_first_tree_common(void **list)
+{
+ if (*list == NULL) {
+ return NULL;
+ } else {
+ TreeDbTerm* t = (*list);
+ TreeDbTerm* l = t->left;
+ *list = l;
+ return t;
+ }
+}
+
+/*
+ * Frees a TreeDbTerm without updating the memory footprint of the
+ * table.
+ */
+void db_free_dbterm_tree_common(int compressed, void* obj)
+{
+ TreeDbTerm* p = obj;
+ db_free_term_no_tab(compressed, p, offsetof(TreeDbTerm, dbterm));
+}
+
+Eterm db_get_dbterm_key_tree_common(DbTable* tb, void* db_term)
+{
+ TreeDbTerm *term = db_term;
+ return GETKEY(tb, term->dbterm.tpl);
+}
+
/*
* Traverse the tree with a callback function, used by db_match_xxx
*/
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index 90ac8c7ba7..4285111810 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -171,6 +171,13 @@ void db_finalize_dbterm_tree_common(int cret,
DbUpdateHandle *handle,
TreeDbTerm **root,
DbTableTree *stack_container);
+void* db_eterm_to_dbterm_tree_common(int compress, int keypos, Eterm obj);
+void* db_dbterm_list_prepend_tree_common(void* list, void* db_term);
+void* db_dbterm_list_remove_first_tree_common(void **list);
+int db_put_dbterm_tree_common(DbTableCommon *tb, TreeDbTerm **root, TreeDbTerm *value_to_insert,
+ int key_clash_fail, DbTableTree *stack_container);
+void db_free_dbterm_tree_common(int compressed, void* obj);
+Eterm db_get_dbterm_key_tree_common(DbTable* tb, void* db_term);
Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key);
TreeDbTerm *db_find_tree_node_common(DbTableCommon*, TreeDbTerm *root,
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 2a0b28f232..5435868c8c 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2612,7 +2612,10 @@ restart:
break;
case matchCaller:
ASSERT(c_p == self);
- if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
+ t = c_p->stop[0];
+ if (is_not_CP(t)) {
+ *esp++ = am_undefined;
+ } else if (!(cp = find_function_from_pc(cp_val(t)))) {
*esp++ = am_undefined;
} else {
ehp = HAllocX(build_proc, 4, HEAP_XTRA);
@@ -2999,6 +3002,34 @@ void db_free_term(DbTable *tb, void* basep, Uint offset)
erts_db_free(ERTS_ALC_T_DB_TERM, tb, basep, size);
}
+Uint db_term_size(DbTable *tb, void* basep, Uint offset)
+{
+ DbTerm* db = (DbTerm*) ((byte*)basep + offset);
+ if (tb->common.compress) {
+ return db_alloced_size_comp(db);
+ }
+ else {
+ return offset + offsetof(DbTerm,tpl) + db->size*sizeof(Eterm);
+ }
+}
+
+void db_free_term_no_tab(int compress, void* basep, Uint offset)
+{
+ DbTerm* db = (DbTerm*) ((byte*)basep + offset);
+ Uint size;
+ if (compress) {
+ db_cleanup_offheap_comp(db);
+ size = db_alloced_size_comp(db);
+ }
+ else {
+ ErlOffHeap tmp_oh;
+ tmp_oh.first = db->first_oh;
+ erts_cleanup_offheap(&tmp_oh);
+ size = offset + offsetof(DbTerm,tpl) + db->size*sizeof(Eterm);
+ }
+ erts_db_free(ERTS_ALC_T_DB_TERM, NULL, basep, size);
+}
+
static ERTS_INLINE Uint align_up(Uint value, Uint pow2)
{
ASSERT((pow2 & (pow2-1)) == 0);
@@ -3007,7 +3038,7 @@ static ERTS_INLINE Uint align_up(Uint value, Uint pow2)
/* Compressed size of an uncompressed term
*/
-static Uint db_size_dbterm_comp(DbTableCommon* tb, Eterm obj)
+static Uint db_size_dbterm_comp(int keypos, Eterm obj)
{
Eterm* tpl = tuple_val(obj);
int i;
@@ -3016,11 +3047,11 @@ static Uint db_size_dbterm_comp(DbTableCommon* tb, Eterm obj)
+ sizeof(Uint); /* "alloc_size" */
for (i = arityval(*tpl); i>0; i--) {
- if (i != tb->keypos && is_not_immed(tpl[i])) {
+ if (i != keypos && is_not_immed(tpl[i])) {
size += erts_encode_ext_size_ets(tpl[i]);
}
}
- size += size_object(tpl[tb->keypos]) * sizeof(Eterm);
+ size += size_object(tpl[keypos]) * sizeof(Eterm);
return align_up(size, sizeof(Uint));
}
@@ -3036,13 +3067,13 @@ static ERTS_INLINE byte* elem2ext(Eterm* tpl, Uint ix)
return (byte*)tpl + (tpl[ix] >> _TAG_PRIMARY_SIZE);
}
-static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
+static void* copy_to_comp(int keypos, Eterm obj, DbTerm* dest,
Uint alloc_size)
{
ErlOffHeap tmp_offheap;
Eterm* src = tuple_val(obj);
Eterm* tpl = dest->tpl;
- Eterm key = src[tb->keypos];
+ Eterm key = src[keypos];
int arity = arityval(src[0]);
union {
Eterm* ep;
@@ -3056,10 +3087,10 @@ static void* copy_to_comp(DbTableCommon* tb, Eterm obj, DbTerm* dest,
tpl[arity + 1] = alloc_size;
tmp_offheap.first = NULL;
- tpl[tb->keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap);
+ tpl[keypos] = copy_struct(key, size_object(key), &top.ep, &tmp_offheap);
dest->first_oh = tmp_offheap.first;
for (i=1; i<=arity; i++) {
- if (i != tb->keypos) {
+ if (i != keypos) {
if (is_immed(src[i])) {
tpl[i] = src[i];
}
@@ -3131,14 +3162,17 @@ void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
}
-void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
+void* db_store_term_comp(DbTableCommon *tb, /* May be NULL */
+ int keypos,
+ DbTerm* old,
+ Uint offset,Eterm obj)
{
- Uint new_sz = offset + db_size_dbterm_comp(tb, obj);
+ Uint new_sz = offset + db_size_dbterm_comp(keypos, obj);
byte* basep;
DbTerm* newp;
byte* top;
- ASSERT(tb->compress);
+ ASSERT(tb == NULL || tb->compress);
if (old != 0) {
Uint old_sz = db_alloced_size_comp(old);
db_cleanup_offheap_comp(old);
@@ -3158,7 +3192,7 @@ void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj)
}
newp->size = size_object(obj);
- top = copy_to_comp(tb, obj, newp, new_sz);
+ top = copy_to_comp(keypos, obj, newp, new_sz);
ASSERT(top <= basep + new_sz); (void)top;
/* ToDo: Maybe realloc if ((basep+new_sz) - top) > WASTED_SPACE_LIMIT */
@@ -3173,7 +3207,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset)
DbTerm* newDbTerm;
Uint alloc_sz = offset +
(tbl->common.compress ?
- db_size_dbterm_comp(&tbl->common, make_tuple(handle->dbterm->tpl)) :
+ db_size_dbterm_comp(tbl->common.keypos, make_tuple(handle->dbterm->tpl)) :
sizeof(DbTerm)+sizeof(Eterm)*(handle->new_size-1));
byte* newp = erts_db_alloc(ERTS_ALC_T_DB_TERM, tbl, alloc_sz);
byte* oldp = *(handle->bp);
@@ -3189,7 +3223,7 @@ void db_finalize_resize(DbUpdateHandle* handle, Uint offset)
/* make a flat copy */
if (tbl->common.compress) {
- copy_to_comp(&tbl->common, make_tuple(handle->dbterm->tpl),
+ copy_to_comp(tbl->common.keypos, make_tuple(handle->dbterm->tpl),
newDbTerm, alloc_sz);
db_free_tmp_uncompressed(handle->dbterm);
}
@@ -5215,7 +5249,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
Eterm l;
Uint32 ret_flags;
Uint sz;
- BeamInstr *save_cp;
+ Eterm save_cp;
if (trace && !(is_list(against) || against == NIL)) {
return THE_NON_VALUE;
@@ -5259,13 +5293,13 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
++n;
l = CDR(list_val(l));
}
- save_cp = p->cp;
- p->cp = NULL;
+ save_cp = p->stop[0];
+ p->stop[0] = NIL;
res = erts_match_set_run_trace(p, p,
mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
- p->cp = save_cp;
+ p->stop[0] = save_cp;
} else {
n = 0;
arr = NULL;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 7846a5c98a..4e384606d1 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -43,18 +43,19 @@
* checks for negative returns and issues BIF_ERRORS based
* upon these values.
*/
-#define DB_ERROR_NONE 0 /* No error */
-#define DB_ERROR_BADITEM -1 /* The item was malformed ie no
- tuple or to small*/
-#define DB_ERROR_BADTABLE -2 /* The Table is inconsisitent */
-#define DB_ERROR_SYSRES -3 /* Out of system resources */
-#define DB_ERROR_BADKEY -4 /* Returned if a key that should
- exist does not. */
+#define DB_ERROR_NONE_FALSE 1 /* No error am_false reult */
+#define DB_ERROR_NONE 0 /* No error */
+#define DB_ERROR_BADITEM -1 /* The item was malformed ie no
+ tuple or to small*/
+#define DB_ERROR_BADTABLE -2 /* The Table is inconsisitent */
+#define DB_ERROR_SYSRES -3 /* Out of system resources */
+#define DB_ERROR_BADKEY -4 /* Returned if a key that should
+ exist does not. */
#define DB_ERROR_BADPARAM -5 /* Returned if a specified slot does
not exist (hash table only) or
the state parameter in db_match_object
is broken.*/
-#define DB_ERROR_UNSPEC -10 /* Unspecified error */
+#define DB_ERROR_UNSPEC -10 /* Unspecified error */
/*#define DEBUG_CLONE*/
@@ -92,7 +93,7 @@ typedef struct {
int flags;
union {
struct {
- erts_rwmtx_t* lck;
+ struct DbTableHashLockAndCounter* lck_ctr;
} hash;
struct {
struct DbTableCATreeNode* base_node;
@@ -130,7 +131,8 @@ typedef struct db_table_method
Eterm* ret);
int (*db_put)(DbTable* tb, /* [in out] */
Eterm obj,
- int key_clash_fail); /* DB_ERROR_BADKEY if key exists */
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p);
int (*db_get)(Process* p,
DbTable* tb, /* [in out] */
Eterm key,
@@ -234,14 +236,20 @@ typedef struct db_table_method
** dbterm was not updated. If the handle was of a new object and cret is
** not DB_ERROR_NONE, the object is removed from the table. */
void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
-
+ void* (*db_eterm_to_dbterm)(int compress, int keypos, Eterm obj);
+ void* (*db_dbterm_list_prepend)(void* list, void* db_term);
+ void* (*db_dbterm_list_remove_first)(void** list);
+ int (*db_put_dbterm)(DbTable* tb, /* [in out] */
+ void* obj,
+ int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
+ SWord *consumed_reds_p);
+ void (*db_free_dbterm)(int compressed, void* obj);
+ Eterm (*db_get_dbterm_key)(DbTable* tb, void* db_term);
int (*db_get_binary_info)(Process*, DbTable* tb, Eterm key, Eterm* ret);
-
/* Raw first/next same as first/next but also return pseudo deleted keys.
Only internal use by ets:info(_,binary) */
int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
-
} DbTableMethod;
typedef struct db_fixation {
@@ -312,6 +320,12 @@ typedef struct db_table_common {
int keypos; /* defaults to 1 */
int compress;
+ /* For unfinished operations that needs to be helped */
+ void (*continuation)(long *reds_ptr,
+ void** state,
+ void* extra_context); /* To help yielded process */
+ erts_atomic_t continuation_state;
+ Binary* continuation_res_bin;
#ifdef ETS_DBG_FORCE_TRAP
erts_atomic_t dbg_force_trap; /* &1 force enabled, &2 trap this call */
#endif
@@ -407,6 +421,7 @@ ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b)
#define DB_READ (DB_PROTECTED|DB_PUBLIC)
#define DB_WRITE DB_PUBLIC
#define DB_INFO (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE)
+#define DB_READ_TBL_STRUCT (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE|DB_BUSY)
#define ONLY_WRITER(P,T) (((T)->common.status & (DB_PRIVATE|DB_PROTECTED)) \
&& (T)->common.owner == (P)->common.id)
@@ -424,8 +439,13 @@ void db_initialize_util(void);
Eterm db_getkey(int keypos, Eterm obj);
void db_cleanup_offheap_comp(DbTerm* p);
void db_free_term(DbTable *tb, void* basep, Uint offset);
+void db_free_term_no_tab(int compress, void* basep, Uint offset);
+Uint db_term_size(DbTable *tb, void* basep, Uint offset);
void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
-void* db_store_term_comp(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
+void* db_store_term_comp(DbTableCommon *tb, /*May be NULL*/
+ int keypos,
+ DbTerm* old,
+ Uint offset,Eterm obj);
Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, DbTerm* obj,
Uint pos, Eterm** hpp, Uint extra);
int db_has_map(Eterm obj);
@@ -546,9 +566,9 @@ ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary(Eterm term);
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary_unchecked(Eterm term);
-/* @brief Ensure off-heap header is word aligned, make a temporary copy if not.
- * Needed when inspecting ETS off-heap lists that may contain unaligned
- * ProcBins if table is 'compressed'.
+/** @brief Ensure off-heap header is word aligned, make a temporary copy if
+ * not. Needed when inspecting ETS off-heap lists that may contain unaligned
+ * ProcBins if table is 'compressed'.
*/
struct erts_tmp_aligned_offheap
{
diff --git a/erts/emulator/beam/erl_flxctr.c b/erts/emulator/beam/erl_flxctr.c
index 35f4a21508..35c4de1a27 100644
--- a/erts/emulator/beam/erl_flxctr.c
+++ b/erts/emulator/beam/erl_flxctr.c
@@ -46,6 +46,29 @@ typedef enum {
ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE = 2
} erts_flxctr_snapshot_status;
+#define ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE \
+ (sizeof(ErtsFlxCtrDecentralizedCtrArray) + \
+ (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) * \
+ ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) + \
+ ERTS_CACHE_LINE_SIZE)
+
+#ifdef DEBUG
+#define FLXCTR_MEM_DEBUG 1
+#endif
+
+#ifdef FLXCTR_MEM_DEBUG
+static erts_atomic_t debug_mem_usage;
+#endif
+
+#ifdef FLXCTR_MEM_DEBUG
+#define FLXCTR_FREE(ALLOC_TYPE, ADDRESS) do { \
+ erts_free(ALLOC_TYPE, ADDRESS); \
+ erts_atomic_add_mb(&debug_mem_usage, -ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE); \
+ } while(0)
+#else
+#define FLXCTR_FREE(ALLOC_TYPE, ADDRESS) erts_free(ALLOC_TYPE, ADDRESS)
+#endif
+
static void
thr_prg_wake_up_and_count(void* bin_p)
{
@@ -72,13 +95,13 @@ thr_prg_wake_up_and_count(void* bin_p)
}
/* Announce that the snapshot is done */
{
- Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
- if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status,
- ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING,
- expected)) {
- /* The CAS failed which means that this thread need to free the next array. */
- erts_free(info->alloc_type, next->block_start);
- }
+ Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
+ if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status,
+ ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING,
+ expected)) {
+ /* The CAS failed which means that this thread need to free the next array. */
+ FLXCTR_FREE(info->alloc_type, next->block_start);
+ }
}
/* Resume the process that requested the snapshot */
erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
@@ -86,7 +109,7 @@ thr_prg_wake_up_and_count(void* bin_p)
erts_resume(p, ERTS_PROC_LOCK_STATUS);
}
/* Free the memory that is no longer needed */
- erts_free(info->alloc_type, array->block_start);
+ FLXCTR_FREE(info->alloc_type, array->block_start);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
erts_proc_dec_refc(p);
erts_bin_release(bin);
@@ -141,6 +164,14 @@ static void suspend_until_thr_prg(Process* p)
erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op);
}
+size_t erts_flxctr_nr_of_allocated_bytes(ErtsFlxCtr* c)
+{
+ if (c->is_decentralized) {
+ return ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE;
+ } else {
+ return 0;
+ }
+}
static ErtsFlxCtrDecentralizedCtrArray*
create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
@@ -148,14 +179,14 @@ create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
the array field is located at the start of a cache line */
char* bytes =
erts_alloc(alloc_type,
- sizeof(ErtsFlxCtrDecentralizedCtrArray) +
- (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) *
- ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) +
- ERTS_CACHE_LINE_SIZE);
+ ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE);
void* block_start = bytes;
int bytes_to_next_cacheline_border;
ErtsFlxCtrDecentralizedCtrArray* array;
int i, sched;
+#ifdef FLXCTR_MEM_DEBUG
+ erts_atomic_add_mb(&debug_mem_usage, ERTS_FLXCTR_DECENTRALIZED_COUNTER_ARRAY_SIZE);
+#endif
bytes = &bytes[offsetof(ErtsFlxCtrDecentralizedCtrArray, array)];
bytes_to_next_cacheline_border =
ERTS_CACHE_LINE_SIZE - (((Uint)bytes) % ERTS_CACHE_LINE_SIZE);
@@ -178,6 +209,9 @@ create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
void erts_flxctr_setup(int decentralized_counter_groups)
{
reader_groups_array_size = decentralized_counter_groups+1;
+#ifdef FLXCTR_MEM_DEBUG
+ erts_atomic_init_mb(&debug_mem_usage, 0);
+#endif
}
void erts_flxctr_init(ErtsFlxCtr* c,
@@ -203,7 +237,7 @@ void erts_flxctr_init(ErtsFlxCtr* c,
}
}
-void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type)
+void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t alloc_type)
{
if (c->is_decentralized) {
if (erts_flxctr_is_snapshot_ongoing(c)) {
@@ -220,10 +254,10 @@ void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type)
snapshot is ongoing anymore and the freeing needs
to be done here */
ERTS_ASSERT(!erts_flxctr_is_snapshot_ongoing(c));
- erts_free(type, array->block_start);
+ FLXCTR_FREE(alloc_type, array->block_start);
}
} else {
- erts_free(type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start);
+ FLXCTR_FREE(alloc_type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start);
}
}
}
@@ -257,7 +291,7 @@ erts_flxctr_snapshot(ErtsFlxCtr* c,
ErtsFlxCtrSnapshotResult res =
{.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP};
suspend_until_thr_prg(p);
- erts_free(alloc_type, new_array->block_start);
+ FLXCTR_FREE(alloc_type, new_array->block_start);
return res;
}
/* Create binary with info about the operation that can be
@@ -364,7 +398,19 @@ void erts_flxctr_reset(ErtsFlxCtr* c,
}
-void erts_flxctr_set_slot(int group) {
+void erts_flxctr_set_slot(int group)
+{
ErtsSchedulerData *esdp = erts_get_scheduler_data();
esdp->flxctr_slot_no = group;
}
+
+Sint erts_flxctr_debug_memory_usage(void)
+{
+#ifdef FLXCTR_MEM_DEBUG
+ return erts_atomic_read_mb(&debug_mem_usage);
+#else
+ return -1;
+#endif
+}
+
+
diff --git a/erts/emulator/beam/erl_flxctr.h b/erts/emulator/beam/erl_flxctr.h
index 5cab02b9eb..df60f3651e 100644
--- a/erts/emulator/beam/erl_flxctr.h
+++ b/erts/emulator/beam/erl_flxctr.h
@@ -288,6 +288,24 @@ int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c);
*/
int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p);
+/**
+ * @brief This function returns the number of bytes that are allocated
+ * for for the given FlxCtr.
+ *
+ * @return nr of bytes allocated for the FlxCtr
+ */
+size_t erts_flxctr_nr_of_allocated_bytes(ErtsFlxCtr* c);
+
+/**
+ * @brief This debug function returns the amount of memory allocated
+ * for decentralized counter arrays when compiled with the DEBUG
+ * macro. The function returns -1 if the DEBUG macro is undefined.
+ *
+ * @return number of bytes allocated for decentralized counter arrays
+ * if in debug mode and otherwise -1
+ */
+Sint erts_flxctr_debug_memory_usage(void);
+
/* End: Public Interface */
/* Internal Declarations */
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 9c866250bb..79a1fdb8b9 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -100,27 +100,6 @@ int erts_fun_table_sz(void)
}
ErlFunEntry*
-erts_put_fun_entry(Eterm mod, int uniq, int index)
-{
- ErlFunEntry template;
- ErlFunEntry* fe;
- erts_aint_t refc;
- ASSERT(is_atom(mod));
- template.old_uniq = uniq;
- template.old_index = index;
- template.module = mod;
- erts_fun_write_lock();
- fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
- sys_memset(fe->uniq, 0, sizeof(fe->uniq));
- fe->index = 0;
- refc = erts_refc_inctest(&fe->refc, 0);
- if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
- erts_fun_write_unlock();
- return fe;
-}
-
-ErlFunEntry*
erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
byte* uniq, int index, int arity)
{
@@ -130,12 +109,12 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
ASSERT(is_atom(mod));
template.old_uniq = old_uniq;
- template.old_index = old_index;
+ template.index = index;
template.module = mod;
erts_fun_write_lock();
fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
sys_memcpy(fe->uniq, uniq, sizeof(fe->uniq));
- fe->index = index;
+ fe->old_index = old_index;
fe->arity = arity;
refc = erts_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
@@ -144,13 +123,6 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
return fe;
}
-struct my_key {
- Eterm mod;
- byte* uniq;
- int index;
- ErlFunEntry* fe;
-};
-
ErlFunEntry*
erts_get_fun_entry(Eterm mod, int uniq, int index)
{
@@ -159,7 +131,7 @@ erts_get_fun_entry(Eterm mod, int uniq, int index)
ASSERT(is_atom(mod));
template.old_uniq = uniq;
- template.old_index = index;
+ template.index = index;
template.module = mod;
erts_fun_read_lock();
ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template);
@@ -199,36 +171,33 @@ erts_erase_fun_entry(ErlFunEntry* fe)
erts_fun_write_unlock();
}
+struct fun_purge_foreach_args {
+ BeamInstr *start;
+ BeamInstr *end;
+};
+
+static void fun_purge_foreach(ErlFunEntry *fe, struct fun_purge_foreach_args *arg)
+{
+ BeamInstr* addr = fe->address;
+ if (arg->start <= addr && addr < arg->end) {
+ fe->pend_purge_address = addr;
+ ERTS_THR_WRITE_MEMORY_BARRIER;
+ fe->address = unloaded_fun;
+#ifdef HIPE
+ fe->pend_purge_native_address = fe->native_address;
+ hipe_set_closure_stub(fe);
+#endif
+ erts_purge_state_add_fun(fe);
+ }
+}
+
void
erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct fun_purge_foreach_args args = {start, end};
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- BeamInstr* addr = fe->address;
-
- if (start <= addr && addr < end) {
- fe->pend_purge_address = addr;
- ERTS_THR_WRITE_MEMORY_BARRIER;
- fe->address = unloaded_fun;
-#ifdef HIPE
- fe->pend_purge_native_address = fe->native_address;
- hipe_set_closure_stub(fe);
-#endif
- erts_purge_state_add_fun(fe);
- }
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)fun_purge_foreach, &args);
erts_fun_read_unlock();
}
@@ -278,36 +247,34 @@ erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
ERTS_THR_WRITE_MEMORY_BARRIER;
}
+struct dump_fun_foreach_args {
+ fmtfn_t to;
+ void *to_arg;
+};
+
+static void
+dump_fun_foreach(ErlFunEntry *fe, struct dump_fun_foreach_args *args)
+{
+ erts_print(args->to, args->to_arg, "=fun\n");
+ erts_print(args->to, args->to_arg, "Module: %T\n", fe->module);
+ erts_print(args->to, args->to_arg, "Uniq: %d\n", fe->old_uniq);
+ erts_print(args->to, args->to_arg, "Index: %d\n",fe->old_index);
+ erts_print(args->to, args->to_arg, "Address: %p\n", fe->address);
+#ifdef HIPE
+ erts_print(args->to, args->to_arg, "Native_address: %p\n", fe->native_address);
+#endif
+ erts_print(args->to, args->to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
+}
+
void
erts_dump_fun_entries(fmtfn_t to, void *to_arg)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct dump_fun_foreach_args args = {to, to_arg};
int lock = !ERTS_IS_CRASH_DUMPING;
-
if (lock)
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- erts_print(to, to_arg, "=fun\n");
- erts_print(to, to_arg, "Module: %T\n", fe->module);
- erts_print(to, to_arg, "Uniq: %d\n", fe->old_uniq);
- erts_print(to, to_arg, "Index: %d\n",fe->old_index);
- erts_print(to, to_arg, "Address: %p\n", fe->address);
-#ifdef HIPE
- erts_print(to, to_arg, "Native_address: %p\n", fe->native_address);
-#endif
- erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)dump_fun_foreach, &args);
if (lock)
erts_fun_read_unlock();
}
@@ -315,15 +282,27 @@ erts_dump_fun_entries(fmtfn_t to, void *to_arg)
static HashValue
fun_hash(ErlFunEntry* obj)
{
- return (HashValue) (obj->old_uniq ^ obj->old_index ^ atom_val(obj->module));
+ return (HashValue) (obj->old_uniq ^ obj->index ^ atom_val(obj->module));
}
static int
fun_cmp(ErlFunEntry* obj1, ErlFunEntry* obj2)
{
- return !(obj1->module == obj2->module &&
+ /*
+ * OTP 23: Use 'index' (instead of 'old_index') when comparing fun
+ * entries. In OTP 23, multiple make_fun2 instructions may refer to the
+ * the same 'index' (for the wrapper function generated for the
+ * 'fun F/A' syntax).
+ *
+ * This is safe when loading code compiled with OTP R15 and later,
+ * because since R15 (2011), the 'index' has been reliably equal
+ * to 'old_index'. The loader refuses to load modules compiled before
+ * OTP R15.
+ */
+
+ return !(obj1->module == obj2->module &&
obj1->old_uniq == obj2->old_uniq &&
- obj1->old_index == obj2->old_index);
+ obj1->index == obj2->index);
}
static ErlFunEntry*
@@ -333,7 +312,7 @@ fun_alloc(ErlFunEntry* template)
sizeof(ErlFunEntry));
obj->old_uniq = template->old_uniq;
- obj->old_index = template->old_index;
+ obj->index = template->index;
obj->module = template->module;
erts_refc_init(&obj->refc, -1);
obj->address = unloaded_fun;
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index fb2901d866..eefc7a95bb 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -74,7 +74,6 @@ void erts_init_fun_table(void);
void erts_fun_info(fmtfn_t, void *);
int erts_fun_table_sz(void);
-ErlFunEntry* erts_put_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_get_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index f4d6ca8eb7..7f51718d1e 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -65,6 +65,8 @@
# define HARDDEBUG 1
#endif
+extern BeamInstr beam_apply[2];
+
/*
* Returns number of elements in an array.
*/
@@ -937,13 +939,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
*/
erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
- ASSERT(p->stop == p->hend); /* Stack must be empty. */
+ ASSERT(p->stop == p->hend - 1); /* Only allow one continuation pointer. */
+ ASSERT(p->stop[0] == make_cp(beam_apply+1));
/*
* Do it.
*/
heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
+ heap_size += 1; /* Reserve place for continuation pointer */
heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
sizeof(Eterm)*heap_size);
@@ -969,13 +973,11 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
p->high_water = htop;
p->htop = htop;
p->hend = p->heap + heap_size;
- p->stop = p->hend;
+ p->stop = p->hend - 1;
p->heap_sz = heap_size;
heap_size = actual_size = p->htop - p->heap;
- if (heap_size == 0) {
- heap_size = 1; /* We want a heap... */
- }
+ heap_size += 1; /* Reserve place for continuation pointer */
FLAGS(p) &= ~F_FORCE_GC;
p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
@@ -991,14 +993,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
* hibernated.
*/
- ASSERT(p->hend - p->stop == 0); /* Empty stack */
ASSERT(actual_size < p->heap_sz);
heap = ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*heap_size);
sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm));
ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm));
- p->stop = p->hend = heap + heap_size;
+ p->hend = heap + heap_size;
+ p->stop = p->hend - 1;
+ p->stop[0] = make_cp(beam_apply+1);
offs = heap - p->heap;
area = (char *) p->heap;
@@ -2572,13 +2575,17 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
/*
* The process may be garbage-collected while it is terminating.
- * (fvalue contains the EXIT reason and ftrace the saved stack trace.)
+ * fvalue contains the EXIT reason.
*/
if (is_not_immed(p->fvalue)) {
roots[n].v = &p->fvalue;
roots[n].sz = 1;
n++;
}
+
+ /*
+ * The raise/3 BIF will store the stacktrace in ftrace.
+ */
if (is_not_immed(p->ftrace)) {
roots[n].v = &p->ftrace;
roots[n].sz = 1;
@@ -2588,7 +2595,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
/*
* If a NIF or BIF has saved arguments, they need to be added
*/
- if (erts_setup_nif_export_rootset(p, &roots[n].v, &roots[n].sz))
+ if (erts_setup_nfunc_rootset(p, &roots[n].v, &roots[n].sz))
n++;
ASSERT(n <= rootset->size);
@@ -3236,7 +3243,7 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(objv, nobj, offs, area, area_size);
}
offset_off_heap(p, offs, area, area_size);
- if (erts_setup_nif_export_rootset(p, &v, &sz))
+ if (erts_setup_nfunc_rootset(p, &v, &sz))
offset_heap_ptr(v, sz, offs, area, area_size);
}
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 5f0352f5c0..d282eeb12c 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -429,6 +429,7 @@ erl_first_process_otp(char* mod_name, int argc, char** argv)
args = CONS(hp, boot_mod, args);
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
res = erl_spawn_system_process(&parent, am_erl_init, am_start, args, &so);
ASSERT(is_internal_pid(res));
@@ -462,6 +463,7 @@ erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int p
so.priority = prio;
so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
so.scheduler = 0;
+ so.opts = NIL;
res = erl_spawn_system_process(parent, mod, am_start, NIL, &so);
ASSERT(is_internal_pid(res));
@@ -484,6 +486,7 @@ Eterm erts_internal_spawn_system_process_3(BIF_ALIST_3) {
ASSERT(erts_list_length(args) >= 0);
so.flags = erts_default_spo_flags;
+ so.opts = NIL;
res = erl_spawn_system_process(BIF_P, mod, func, args, &so);
if (is_non_value(res)) {
@@ -583,7 +586,7 @@ void erts_usage(void)
ERTS_ASYNC_THREAD_MIN_STACK_SIZE,
ERTS_ASYNC_THREAD_MAX_STACK_SIZE);
erts_fprintf(stderr, "-A number set number of threads in async thread pool,\n");
- erts_fprintf(stderr, " valid range is [0-%d]\n",
+ erts_fprintf(stderr, " valid range is [1-%d]\n",
ERTS_MAX_NO_OF_ASYNC_THREADS);
erts_fprintf(stderr, "-B[c|d|i] c to have Ctrl-c interrupt the Erlang shell,\n");
erts_fprintf(stderr, " d (or no extra option) to disable the break\n");
@@ -774,6 +777,7 @@ early_init(int *argc, char **argv) /*
int ncpu;
int ncpuonln;
int ncpuavail;
+ int ncpuquota;
int schdlrs;
int schdlrs_onln;
int schdlrs_percentage = 100;
@@ -811,7 +815,8 @@ early_init(int *argc, char **argv) /*
&max_reader_groups,
&ncpu,
&ncpuonln,
- &ncpuavail);
+ &ncpuavail,
+ &ncpuquota);
ignore_break = 0;
replace_intr = 0;
@@ -838,9 +843,18 @@ early_init(int *argc, char **argv) /*
* can initialize the allocators.
*/
no_schedulers = (Uint) (ncpu > 0 ? ncpu : 1);
- no_schedulers_online = (ncpuavail > 0
- ? ncpuavail
- : (ncpuonln > 0 ? ncpuonln : no_schedulers));
+
+ if (ncpuavail > 0) {
+ if (ncpuquota > 0) {
+ no_schedulers_online = MIN(ncpuquota, ncpuavail);
+ } else {
+ no_schedulers_online = ncpuavail;
+ }
+ } else if (ncpuonln > 0) {
+ no_schedulers_online = ncpuonln;
+ } else {
+ no_schedulers_online = no_schedulers;
+ }
schdlrs = no_schedulers;
schdlrs_onln = no_schedulers_online;
@@ -1129,6 +1143,9 @@ early_init(int *argc, char **argv) /*
i++;
}
+ if (erts_async_max_threads < 1)
+ erts_async_max_threads = 1;
+
/* apply any scheduler percentages */
if (schdlrs_percentage != 100 || schdlrs_onln_percentage != 100) {
schdlrs = schdlrs * schdlrs_percentage / 100;
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index 2ae5b56b5c..c82d67f893 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -1078,7 +1078,7 @@ static BIF_RETTYPE iol2v_yield(iol2v_state_t *state) {
state = boxed_state;
}
- ERTS_BIF_YIELD1(bif_export[BIF_iolist_to_iovec_1],
+ ERTS_BIF_YIELD1(&bif_trap_export[BIF_iolist_to_iovec_1],
state->process, state->magic_reference);
}
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index b391c05643..507db504db 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -84,6 +84,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "reg_tab", NULL },
{ "proc_main", "pid" },
{ "old_code", "address" },
+ { "nif_call_tab", NULL },
#ifdef HIPE
{ "hipe_mfait_lock", NULL },
#endif
@@ -148,6 +149,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "instr_x", NULL },
{ "instr", NULL },
{ "dyn_lock_check", NULL },
+ { "nif_load", NULL },
{ "alcu_allocator", "index" },
{ "mseg", NULL },
{ "get_time", NULL },
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 0d47b16e0b..c061e7894d 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -78,7 +78,7 @@ typedef struct {
} erts_lcnt_time_t;
typedef struct {
- /* @brief log2 array of nano seconds occurences */
+ /** @brief log2 array of nano seconds occurences */
Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE];
} erts_lcnt_hist_t;
@@ -271,7 +271,7 @@ int erts_lcnt_check_ref_installed(erts_lcnt_ref_t *ref);
erts_lcnt_lock_info_carrier_t *erts_lcnt_create_lock_info_carrier(int count);
-/* @brief Initializes the lock info at the given index.
+/** @brief Initializes the lock info at the given index.
* @param id An immediate erlang term with whatever extra data you want to
* identify this lock with.
* @param flags The flags the lock itself was initialized with. Keep in mind
@@ -300,9 +300,10 @@ void erts_lcnt_pre_thr_init(void);
void erts_lcnt_post_thr_init(void);
void erts_lcnt_late_init(void);
-/* @brief Called after everything in the system has been initialized, including
- * the schedulers. This is mainly a backwards compatibility shim for matching
- * the old lcnt behavior where all lock counting was enabled by default. */
+/** @brief Called after everything in the system has been initialized,
+ * including the schedulers. This is mainly a backwards compatibility shim for
+ * matching the old lcnt behavior where all lock counting was enabled by
+ * default. */
void erts_lcnt_post_startup(void);
void erts_lcnt_thread_setup(void);
diff --git a/erts/emulator/beam/erl_lock_flags.h b/erts/emulator/beam/erl_lock_flags.h
index 2db133b598..9d2216eaf6 100644
--- a/erts/emulator/beam/erl_lock_flags.h
+++ b/erts/emulator/beam/erl_lock_flags.h
@@ -71,10 +71,10 @@
typedef unsigned short erts_lock_flags_t;
typedef unsigned short erts_lock_options_t;
-/* @brief Gets the type name of the lock, honoring the RW flag if supplied. */
+/** @brief Gets the type name of the lock, honoring the RW flag if supplied. */
const char *erts_lock_flags_get_type_name(erts_lock_flags_t flags);
-/* @brief Gets a short-form description of the given lock options. (rw/r/w) */
+/** @brief Gets a short-form description of the given lock options. (rw/r/w) */
const char *erts_lock_options_get_short_desc(erts_lock_options_t options);
#endif /* ERTS_LOCK_FLAGS_H__ */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 62dd85e425..9097a36e62 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -973,7 +973,13 @@ BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
HIPE_WRAPPER_BIF_DISABLE_GC(maps_merge, 2)
BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
- if (is_flatmap(BIF_ARG_1)) {
+ if (BIF_ARG_1 == BIF_ARG_2) {
+ /* Merging upon itself always returns itself */
+ if (is_map(BIF_ARG_1)) {
+ return BIF_ARG_1;
+ }
+ BIF_P->fvalue = BIF_ARG_1;
+ } else if (is_flatmap(BIF_ARG_1)) {
if (is_flatmap(BIF_ARG_2)) {
BIF_RET(flatmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2));
} else if (is_hashmap(BIF_ARG_2)) {
@@ -1008,6 +1014,9 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) {
n1 = flatmap_get_size(mp1);
n2 = flatmap_get_size(mp2);
+ if (n1 == 0) return nodeB;
+ if (n2 == 0) return nodeA;
+
need = MAP_HEADER_FLATMAP_SZ + 1 + 2 * (n1 + n2);
hp = HAlloc(p, need);
@@ -1127,6 +1136,7 @@ static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args)
mp = (flatmap_t*)flatmap_val(flat);
n = flatmap_get_size(mp);
+ if (n == 0) return tree;
ks = flatmap_get_keys(mp);
vs = flatmap_get_values(mp);
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 7901aa668c..3ac2530068 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -456,6 +456,24 @@ erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
}
/**
+ *
+ * @brief Send one message from *NOT* a local process.
+ *
+ * But with a token!
+ */
+void
+erts_queue_message_token(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from, Eterm token)
+{
+ ASSERT(is_not_internal_pid(from));
+ ERL_MESSAGE_TERM(mp) = msg;
+ ERL_MESSAGE_FROM(mp) = from;
+ ERL_MESSAGE_TOKEN(mp) = token;
+ queue_messages(receiver, receiver_locks, mp, &mp->next, 1);
+}
+
+
+/**
* @brief Send one message from a local process.
*
* It is up to the caller of this function to set the
@@ -674,7 +692,7 @@ erts_send_message(Process* sender,
* Make sure we don't use the heap between those instances.
*/
if (have_seqtrace(stoken)) {
- seq_trace_update_send(sender);
+ seq_trace_update_serial(sender);
seq_trace_output(stoken, message, SEQ_TRACE_SEND,
receiver->common.id, sender);
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 38a2d567d3..6d551a5108 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -447,6 +447,7 @@ void free_message_buffer(ErlHeapFragment *);
void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *,
ErlHeapFragment *, Eterm, Eterm);
void erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
+void erts_queue_message_token(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm, Eterm);
void erts_queue_proc_message(Process* from,Process* to, ErtsProcLocks,ErtsMessage*, Eterm);
void erts_queue_proc_messages(Process* from, Process* to, ErtsProcLocks,
ErtsMessage*, ErtsMessage**, Uint);
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
index 43028be39d..7b3010bab2 100644
--- a/erts/emulator/beam/erl_monitor_link.c
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -540,6 +540,7 @@ erts_mon_link_dist_create(Eterm nodename)
mld->links = NULL;
mld->monitors = NULL;
mld->orig_name_monitors = NULL;
+ mld->dist_pend_spawn_exit = NULL;
return mld;
}
@@ -551,6 +552,7 @@ erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld)
ERTS_ML_ASSERT(!mld->links);
ERTS_ML_ASSERT(!mld->monitors);
ERTS_ML_ASSERT(!mld->orig_name_monitors);
+ ERTS_ML_ASSERT(!mld->dist_pend_spawn_exit);
erts_mtx_destroy(&mld->mtx);
erts_free(ERTS_ALC_T_ML_DIST, mld);
@@ -834,10 +836,29 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
Uint rsz, osz, tsz;
Eterm *hp;
ErlOffHeap oh;
- Uint16 name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+ Uint16 name_flag;
+ Uint16 pending_flag;
rsz = is_immed(ref) ? 0 : size_object(ref);
- tsz = is_immed(trgt) ? 0 : size_object(trgt);
+ if (trgt != am_pending) {
+ if (is_not_immed(trgt))
+ tsz = size_object(trgt);
+ else
+ tsz = 0;
+ pending_flag = (Uint16) 0;
+ name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME;
+ }
+ else {
+ /* Pending spawn_request() */
+ pending_flag = ERTS_ML_FLG_SPAWN_PENDING;
+ /* Prepare for storage of exteral pid */
+ tsz = EXTERNAL_THING_HEAD_SIZE + 1;
+ /* name contains tag */
+
+ /* Not by name */
+ name_flag = (Uint16) 0;
+
+ }
if (type == ERTS_MON_TYPE_RESOURCE)
osz = 0;
else
@@ -851,6 +872,16 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
hp = &mdep->heap[0];
+ if (pending_flag) {
+ /* Make room for the future pid... */
+#ifdef DEBUG
+ int i;
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++)
+ hp[i] = THE_NON_VALUE;
+#endif
+ hp += EXTERNAL_THING_HEAD_SIZE + 1;
+ }
+
mdp = &mdep->md;
ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdep));
@@ -858,7 +889,7 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
mdp->origin.other.item = tsz ? copy_struct(trgt, tsz, &hp, &oh) : trgt;
mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin);
- mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag;
+ mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag|pending_flag;
mdp->origin.type = type;
if (type == ERTS_MON_TYPE_RESOURCE)
@@ -878,6 +909,25 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name)
}
else {
mdep->u.name = name;
+ if (pending_flag) {
+ /* spawn_request() tag is in 'name' */
+ if (is_not_immed(name)) {
+ /*
+ * Save the tag in its own heap fragment with a
+ * little trick:
+ *
+ * bp->mem[0] = The tag
+ * bp->mem[1] = Beginning of heap
+ * mdep->u.name = Countinuation pointer to
+ * heap fragment...
+ */
+ Uint hsz = size_object(name)+1;
+ ErlHeapFragment *bp = new_message_buffer(hsz);
+ Eterm *hp = &bp->mem[1];
+ bp->mem[0] = copy_struct(name, hsz-1, &hp, &bp->off_heap);
+ mdep->u.name = make_cp((void*)bp);
+ }
+ }
mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref);
ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset);
@@ -964,6 +1014,20 @@ erts_monitor_destroy__(ErtsMonitorData *mdp)
}
if (mdep->dist)
erts_mon_link_dist_dec_refc(mdep->dist);
+ if (mdp->origin.flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * We have the spawn_request() tag stored in
+ * mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ * If non-immediate value make sure to release
+ * this heap fragment as well.
+ */
+ if (is_not_immed(mdep->u.name)) {
+ ErlHeapFragment *bp;
+ bp = (ErlHeapFragment *) cp_val(mdep->u.name);
+ free_message_buffer(bp);
+ }
+ }
erts_free(ERTS_ALC_T_MONITOR_EXT, mdp);
}
}
@@ -1248,11 +1312,26 @@ erts_link_create(Uint16 type, Eterm a, Eterm b)
Eterm *hp;
ErlOffHeap oh;
- if (is_internal_pid(a))
- hsz = NC_HEAP_SIZE(b);
- else
- hsz = NC_HEAP_SIZE(a);
- ERTS_ML_ASSERT(hsz > 0);
+ hsz = EXTERNAL_THING_HEAD_SIZE + 1;
+ if (hsz < ERTS_REF_THING_SIZE
+ && (is_internal_ordinary_ref(a)
+ || is_internal_ordinary_ref(b))) {
+ hsz = ERTS_REF_THING_SIZE;
+ }
+
+#ifdef DEBUG
+ if (is_internal_pid(a)) {
+ ERTS_ML_ASSERT(is_external_pid(b)
+ || is_internal_ordinary_ref(b));
+ ERTS_ML_ASSERT(NC_HEAP_SIZE(b) <= hsz);
+ }
+ else {
+ ERTS_ML_ASSERT(is_internal_pid(b));
+ ERTS_ML_ASSERT(is_external_pid(a)
+ || is_internal_ordinary_ref(a));
+ ERTS_ML_ASSERT(NC_HEAP_SIZE(a) <= hsz);
+ }
+#endif
size = sizeof(ErtsLinkDataExtended) - sizeof(Eterm);
size += hsz*sizeof(Eterm);
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
index 86be400c09..e10bb27077 100644
--- a/erts/emulator/beam/erl_monitor_link.h
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -431,9 +431,22 @@
#define ERTS_ML_FLG_IN_SUBTABLE (((Uint16) 1) << 2)
#define ERTS_ML_FLG_NAME (((Uint16) 1) << 3)
#define ERTS_ML_FLG_EXTENDED (((Uint16) 1) << 4)
+#define ERTS_ML_FLG_SPAWN_PENDING (((Uint16) 1) << 5)
+#define ERTS_ML_FLG_SPAWN_MONITOR (((Uint16) 1) << 6)
+#define ERTS_ML_FLG_SPAWN_LINK (((Uint16) 1) << 7)
+#define ERTS_ML_FLG_SPAWN_ABANDONED (((Uint16) 1) << 8)
+#define ERTS_ML_FLG_SPAWN_NO_SMSG (((Uint16) 1) << 9)
+#define ERTS_ML_FLG_SPAWN_NO_EMSG (((Uint16) 1) << 10)
#define ERTS_ML_FLG_DBG_VISITED (((Uint16) 1) << 15)
+#define ERTS_ML_FLGS_SPAWN (ERTS_ML_FLG_SPAWN_PENDING \
+ | ERTS_ML_FLG_SPAWN_MONITOR \
+ | ERTS_ML_FLG_SPAWN_LINK \
+ | ERTS_ML_FLG_SPAWN_ABANDONED \
+ | ERTS_ML_FLG_SPAWN_NO_SMSG \
+ | ERTS_ML_FLG_SPAWN_NO_EMSG)
+
/* Flags that should be the same on both monitor/link halves */
#define ERTS_ML_FLGS_SAME \
(ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME)
@@ -478,6 +491,7 @@ typedef struct {
ErtsMonLnkNode *monitors; /* Monitor double linked circular list */
ErtsMonLnkNode *orig_name_monitors; /* Origin named monitors
read-black tree */
+ ErtsMonLnkNode *dist_pend_spawn_exit;
} ErtsMonLnkDist;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
@@ -1397,7 +1411,7 @@ erts_monitor_release(ErtsMonitor *mon)
ErtsMonitorData *mdp = erts_monitor_to_data(mon);
ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) > 0);
- if (erts_atomic32_dec_read_nob(&mdp->refc) == 0) {
+ if (erts_atomic32_dec_read_mb(&mdp->refc) == 0) {
ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
@@ -1412,7 +1426,7 @@ erts_monitor_release_both(ErtsMonitorData *mdp)
== (mdp->target.flags & ERTS_ML_FLGS_SAME));
ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) >= 2);
- if (erts_atomic32_add_read_nob(&mdp->refc, (erts_aint32_t) -2) == 0) {
+ if (erts_atomic32_add_read_mb(&mdp->refc, (erts_aint32_t) -2) == 0) {
ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE));
ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE));
diff --git a/erts/emulator/beam/erl_nfunc_sched.c b/erts/emulator/beam/erl_nfunc_sched.c
index b8cf2bee0e..8263a6e9b7 100644
--- a/erts/emulator/beam/erl_nfunc_sched.c
+++ b/erts/emulator/beam/erl_nfunc_sched.c
@@ -30,77 +30,37 @@
#include "erl_nfunc_sched.h"
#include "erl_trace.h"
-NifExport *
-erts_new_proc_nif_export(Process *c_p, int argc)
+ErtsNativeFunc *
+erts_new_proc_nfunc(Process *c_p, int argc)
{
+ ErtsNativeFunc *nep, *old_nep;
size_t size;
- int i;
- NifExport *nep, *old_nep;
-
- size = sizeof(NifExport) + (argc-1)*sizeof(Eterm);
- nep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, size);
- for (i = 0; i < ERTS_NUM_CODE_IX; i++)
- nep->exp.addressv[i] = &nep->exp.beam[0];
+ size = sizeof(ErtsNativeFunc) + (argc-1)*sizeof(Eterm);
+ nep = erts_alloc(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, size);
nep->argc = -1; /* unused marker */
nep->argv_size = argc;
- nep->trace = NULL;
- old_nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(c_p, nep);
+ old_nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(c_p, nep);
if (old_nep) {
- ASSERT(!nep->trace);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, old_nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, old_nep);
}
return nep;
}
void
-erts_destroy_nif_export(Process *p)
+erts_destroy_nfunc(Process *p)
{
- NifExport *nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL);
+ ErtsNativeFunc *nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(p, NULL);
if (nep) {
if (nep->m)
- erts_nif_export_cleanup_nif_mod(nep);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, nep);
+ erts_nfunc_cleanup_nif_mod(nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, nep);
}
}
-void
-erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- NifExportTrace *netp;
- ASSERT(nep && nep->argc >= 0);
- ASSERT(!nep->trace);
- netp = erts_alloc(ERTS_ALC_T_NIF_EXP_TRACE,
- sizeof(NifExportTrace));
- netp->applying = applying;
- netp->ep = ep;
- netp->cp = cp;
- netp->flags = flags;
- netp->flags_meta = flags_meta;
- netp->I = I;
- netp->meta_tracer = NIL;
- erts_tracer_update(&netp->meta_tracer, meta_tracer);
- nep->trace = netp;
-}
-
-void
-erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep)
-{
- NifExportTrace *netp = nep->trace;
- nep->trace = NULL;
- erts_bif_trace_epilogue(c_p, result, netp->applying, netp->ep,
- netp->cp, netp->flags, netp->flags_meta,
- netp->I, netp->meta_tracer);
- erts_tracer_update(&netp->meta_tracer, NIL);
- erts_free(ERTS_ALC_T_NIF_EXP_TRACE, netp);
-}
-
-NifExport *
-erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
+ErtsNativeFunc *
+erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
ErtsCodeMFA *mfa, BeamInstr *pc,
BeamInstr instr,
void *dfunc, void *ifunc,
@@ -110,7 +70,7 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
Process *used_proc;
ErtsSchedulerData *esdp;
Eterm* reg;
- NifExport* nep;
+ ErtsNativeFunc* nep;
int i;
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
@@ -133,10 +93,10 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg = esdp->x_reg_array;
if (mfa)
- nep = erts_get_proc_nif_export(c_p, (int) mfa->arity);
+ nep = erts_get_proc_nfunc(c_p, (int) mfa->arity);
else {
/* If no mfa, this is not the first schedule... */
- nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
ASSERT(nep && nep->argc >= 0);
}
@@ -148,16 +108,15 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
for (i = 0; i < (int) mfa->arity; i++)
nep->argv[i] = reg[i];
nep->pc = pc;
- nep->cp = c_p->cp;
nep->mfa = mfa;
nep->current = c_p->current;
ASSERT(argc >= 0);
nep->argc = (int) mfa->arity;
nep->m = NULL;
- ASSERT(!erts_check_nif_export_in_area(c_p,
+ ASSERT(!erts_check_nfunc_in_area(c_p,
(char *) nep,
- (sizeof(NifExport)
+ (sizeof(ErtsNativeFunc)
+ (sizeof(Eterm)
*(nep->argc-1)))));
}
@@ -167,14 +126,14 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg[i] = argv[i];
}
ASSERT(is_atom(mod) && is_atom(func));
- nep->exp.info.mfa.module = mod;
- nep->exp.info.mfa.function = func;
- nep->exp.info.mfa.arity = (Uint) argc;
- nep->exp.beam[0] = (BeamInstr) instr; /* call_nif || apply_bif */
- nep->exp.beam[1] = (BeamInstr) dfunc;
+ nep->trampoline.info.mfa.module = mod;
+ nep->trampoline.info.mfa.function = func;
+ nep->trampoline.info.mfa.arity = (Uint) argc;
+ nep->trampoline.call_op = (BeamInstr) instr; /* call_bif || call_nif */
+ nep->trampoline.dfunc = (BeamInstr) dfunc;
nep->func = ifunc;
used_proc->arity = argc;
used_proc->freason = TRAP;
- used_proc->i = (BeamInstr*) nep->exp.addressv[0];
+ used_proc->i = (BeamInstr*)&nep->trampoline.call_op;
return nep;
}
diff --git a/erts/emulator/beam/erl_nfunc_sched.h b/erts/emulator/beam/erl_nfunc_sched.h
index 1cb252eba5..4dae242d4f 100644
--- a/erts/emulator/beam/erl_nfunc_sched.h
+++ b/erts/emulator/beam/erl_nfunc_sched.h
@@ -25,92 +25,78 @@
#include "bif.h"
#include "error.h"
-typedef struct {
- int applying;
- Export* ep;
- BeamInstr *cp;
- Uint32 flags;
- Uint32 flags_meta;
- BeamInstr* I;
- ErtsTracer meta_tracer;
-} NifExportTrace;
-
/*
- * NIF exports need a few more items than the Export struct provides,
- * including the erl_module_nif* and a NIF function pointer, so the
- * NifExport below adds those. The Export member must be first in the
- * struct. A number of values are stored for error handling purposes
- * only.
+ * Native function wrappers are used to schedule native functions on both
+ * normal and dirty schedulers.
+ *
+ * A number of values are only stored for error handling, and the fields
+ * following `current` can be omitted when a wrapper is statically "scheduled"
+ * through placement in a function stub.
*
- * 'argc' is >= 0 when NifExport is in use, and < 0 when not.
+ * 'argc' is >= 0 when ErtsNativeFunc is in use, and < 0 when not.
*/
typedef struct {
- Export exp;
+ struct {
+ ErtsCodeInfo info;
+ BeamInstr call_op; /* call_bif || call_nif */
+ BeamInstr dfunc;
+ } trampoline;
+
struct erl_module_nif* m; /* NIF module, or NULL if BIF */
void *func; /* Indirect NIF or BIF to execute (may be unused) */
ErtsCodeMFA *current;/* Current as set when originally called */
- NifExportTrace *trace;
/* --- The following is only used on error --- */
BeamInstr *pc; /* Program counter */
- BeamInstr *cp; /* Continuation pointer */
ErtsCodeMFA *mfa; /* MFA of original call */
int argc; /* Number of arguments in original call */
int argv_size; /* Allocated size of argv */
Eterm argv[1]; /* Saved arguments from the original call */
-} NifExport;
-
-NifExport *erts_new_proc_nif_export(Process *c_p, int argc);
-void erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
-void erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep);
-void erts_destroy_nif_export(Process *p);
-NifExport *erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
- ErtsCodeMFA *mfa, BeamInstr *pc,
- BeamInstr instr,
- void *dfunc, void *ifunc,
- Eterm mod, Eterm func,
- int argc, const Eterm *argv);
-void erts_nif_export_cleanup_nif_mod(NifExport *ep); /* erl_nif.c */
-ERTS_GLB_INLINE NifExport *erts_get_proc_nif_export(Process *c_p, int extra);
-ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv,
- Uint* nobj);
-ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p,
- char *start, Uint size);
-ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep,
- Eterm result);
-ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
- Eterm *reg, ErtsCodeMFA **nif_mfa);
-ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
+} ErtsNativeFunc;
+
+ErtsNativeFunc *erts_new_proc_nfunc(Process *c_p, int argc);
+void erts_destroy_nfunc(Process *p);
+ErtsNativeFunc *erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
+ ErtsCodeMFA *mfa, BeamInstr *pc,
+ BeamInstr instr,
+ void *dfunc, void *ifunc,
+ Eterm mod, Eterm func,
+ int argc, const Eterm *argv);
+void erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep); /* erl_nif.c */
+ERTS_GLB_INLINE ErtsNativeFunc *erts_get_proc_nfunc(Process *c_p, int extra);
+ERTS_GLB_INLINE int erts_setup_nfunc_rootset(Process* proc, Eterm** objv,
+ Uint* nobj);
+ERTS_GLB_INLINE int erts_check_nfunc_in_area(Process *p,
+ char *start, Uint size);
+ERTS_GLB_INLINE void erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep,
+ Eterm result);
+ERTS_GLB_INLINE void erts_nfunc_restore_error(Process* c_p,
+ BeamInstr **pc,
+ Eterm *reg,
+ ErtsCodeMFA **nif_mfa);
ERTS_GLB_INLINE Process *erts_proc_shadow2real(Process *c_p);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE NifExport *
-erts_get_proc_nif_export(Process *c_p, int argc)
+ERTS_GLB_INLINE ErtsNativeFunc *
+erts_get_proc_nfunc(Process *c_p, int argc)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
if (!nep || (nep->argc < 0 && nep->argv_size < argc))
- return erts_new_proc_nif_export(c_p, argc);
+ return erts_new_proc_nfunc(c_p, argc);
return nep;
}
/*
* If a process has saved arguments, they need to be part of the GC
* rootset. The function below is called from setup_rootset() in
- * erl_gc.c. Any exception term saved in the NifExport is also made
+ * erl_gc.c. Any exception term saved in the ErtsNativeFunc is also made
* part of the GC rootset here; it always resides in rootset[0].
*/
ERTS_GLB_INLINE int
-erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
+erts_setup_nfunc_rootset(Process* proc, Eterm** objv, Uint* nobj)
{
- NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ErtsNativeFunc* ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
if (!ep || ep->argc <= 0)
return 0;
@@ -121,18 +107,16 @@ erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
}
/*
- * Check if nif export points into code area...
+ * Check if native func wrapper points into code area...
*/
ERTS_GLB_INLINE int
-erts_check_nif_export_in_area(Process *p, char *start, Uint size)
+erts_check_nfunc_in_area(Process *p, char *start, Uint size)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(p);
if (!nep || nep->argc < 0)
return 0;
if (ErtsInArea(nep->pc, start, size))
return 1;
- if (ErtsInArea(nep->cp, start, size))
- return 1;
if (ErtsInArea(nep->mfa, start, size))
return 1;
if (ErtsInArea(nep->current, start, size))
@@ -141,7 +125,7 @@ erts_check_nif_export_in_area(Process *p, char *start, Uint size)
}
ERTS_GLB_INLINE void
-erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
+erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm result)
{
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data()));
ERTS_LC_ASSERT(!(c_p->static_flags
@@ -151,43 +135,21 @@ erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
c_p->current = ep->current;
ep->argc = -1; /* Unused nif-export marker... */
- if (ep->trace)
- erts_nif_export_restore_trace(c_p, result, ep);
}
ERTS_GLB_INLINE void
-erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
+erts_nfunc_restore_error(Process* c_p, BeamInstr **pc,
Eterm *reg, ErtsCodeMFA **nif_mfa)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
int ix;
ASSERT(nep);
*pc = nep->pc;
- c_p->cp = nep->cp;
*nif_mfa = nep->mfa;
for (ix = 0; ix < nep->argc; ix++)
reg[ix] = nep->argv[ix];
- erts_nif_export_restore(c_p, nep, THE_NON_VALUE);
-}
-
-ERTS_GLB_INLINE int
-erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (is_non_value(result) && c_p->freason == TRAP) {
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- if (nep && nep->argc >= 0) {
- erts_nif_export_save_trace(c_p, nep, applying, ep,
- cp, flags, flags_meta,
- I, meta_tracer);
- return 1;
- }
- }
- return 0;
+ erts_nfunc_restore(c_p, nep, THE_NON_VALUE);
}
ERTS_GLB_INLINE Process *
@@ -210,10 +172,10 @@ erts_proc_shadow2real(Process *c_p)
#if defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__)
#define ERTS_NFUNC_SCHED_INTERNALS__
-#define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \
- (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \
- BeamIsOpCode(*(I), op_call_nif)), \
- ((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0]))))
+#define ERTS_I_BEAM_OP_TO_NFUNC(I) \
+ (ASSERT(BeamIsOpCode(*(I), op_call_bif_W) || \
+ BeamIsOpCode(*(I), op_call_nif_WWW)), \
+ ((ErtsNativeFunc *) (((char *) (I)) - offsetof(ErtsNativeFunc, trampoline.call_op))))
#include "erl_message.h"
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 27959ba2bb..71165f5ff8 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -74,12 +74,25 @@
* 'handle'.
*/
struct erl_module_nif {
+ erts_refc_t refc; /* References to this struct
+ * +1 erl_module_instance (loaded Erlang code)
+ * +1 "dlopen" (loaded native code)
+ * +1 scheduled load finisher
+ * +1 for each owned resource type
+ */
+ erts_mtx_t load_mtx; /* protects load finish from unload */
+ struct ErtsNifFinish_* finish;
+ ErtsThrPrgrLaterOp lop;
+
void* priv_data;
void* handle; /* "dlopen" */
struct enif_entry_t entry;
- erts_refc_t rt_cnt; /* number of resource types */
- erts_refc_t rt_dtor_cnt; /* number of resource types with destructors */
- Module* mod; /* Can be NULL if orphan with dtor-resources left */
+ erts_refc_t dynlib_refc; /* References to loaded native code
+ +1 erl_module_instance
+ +1 for each owned resource type with callbacks
+ +1 for each ongoing dirty NIF call
+ */
+ Module* mod; /* Can be NULL if purged and dynlib_refc > 0 */
ErlNifFunc _funcs_copy_[1]; /* only used for old libs */
};
@@ -309,10 +322,10 @@ void erts_post_nif(ErlNifEnv* env)
/*
- * Initialize a NifExport struct. Create it if needed and store it in the
+ * Initialize a ErtsNativeFunc struct. Create it if needed and store it in the
* proc. The direct_fp function is what will be invoked by op_call_nif, and
* the indirect_fp function, if not NULL, is what the direct_fp function
- * will call. If the allocated NifExport isn't enough to hold all of argv,
+ * will call. If the allocated ErtsNativeFunc isn't enough to hold all of argv,
* allocate a larger one. Save 'current' and registers if first time this
* call is scheduled.
*/
@@ -321,7 +334,7 @@ static ERTS_INLINE ERL_NIF_TERM
schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
Eterm mod, Eterm func_name, int argc, const ERL_NIF_TERM argv[])
{
- NifExport *ep;
+ ErtsNativeFunc *ep;
Process *c_p, *dirty_shadow_proc;
execution_state(env, &c_p, NULL);
@@ -332,16 +345,16 @@ schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- ep = erts_nif_export_schedule(c_p, dirty_shadow_proc,
+ ep = erts_nfunc_schedule(c_p, dirty_shadow_proc,
c_p->current,
- c_p->cp,
- BeamOpCodeAddr(op_call_nif),
+ cp_val(c_p->stop[0]),
+ BeamOpCodeAddr(op_call_nif_WWW),
direct_fp, indirect_fp,
mod, func_name,
argc, (const Eterm *) argv);
if (!ep->m) {
/* First time this call is scheduled... */
- erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1);
+ erts_refc_inc(&env->mod_nif->dynlib_refc, 2);
ep->m = env->mod_nif;
}
return (ERL_NIF_TERM) THE_NON_VALUE;
@@ -356,7 +369,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
{
int exiting;
ERL_NIF_TERM *argv = (ERL_NIF_TERM *) reg;
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsCodeMFA *codemfa = erts_code_to_codemfa(I);
NativeFunPtr dirty_nif = (NativeFunPtr) I[1];
ErlNifEnv env;
@@ -364,7 +377,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#ifdef DEBUG
erts_aint32_t state = erts_atomic32_read_nob(&c_p->state);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
ASSERT(!c_p->scheduler_data);
ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING)
@@ -815,7 +828,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
}
#endif
if (have_seqtrace(stoken)) {
- seq_trace_update_send(c_p);
+ seq_trace_update_serial(c_p);
seq_trace_output(stoken, msg, SEQ_TRACE_SEND,
rp->common.id, c_p);
}
@@ -2189,7 +2202,10 @@ int enif_vsnprintf(char* buffer, size_t size, const char *format, va_list ap)
** Memory managed (GC'ed) "resource" objects **
***********************************************************/
-/* dummy node in circular list */
+/*
+ * Sentinel node in circular list of all resource types.
+ * List protected by code_write_permission.
+ */
struct enif_resource_type_t resource_type_list;
static ErlNifResourceType* find_resource_type(Eterm module, Eterm name)
@@ -2209,16 +2225,26 @@ static ErlNifResourceType* find_resource_type(Eterm module, Eterm name)
#define in_area(ptr,start,nbytes) \
((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
-static ERTS_INLINE int rt_have_callbacks(ErlNifResourceType* rt)
+static ERTS_INLINE int rt_have_callbacks(ErlNifResourceTypeInit* rti)
{
- return rt->dtor != NULL;
+ return rti->dtor != NULL;
+}
+
+static void deref_nifmod(struct erl_module_nif* lib)
+{
+ if (erts_refc_dectest(&lib->refc, 0) == 0) {
+ ASSERT(lib->handle == NULL);
+ ASSERT(lib->mod == NULL);
+ erts_free(ERTS_ALC_T_NIF, lib);
+ }
}
-static void close_lib(struct erl_module_nif* lib)
+static void close_dynlib(struct erl_module_nif* lib)
{
ASSERT(lib != NULL);
+ ASSERT(lib->mod == NULL);
ASSERT(lib->handle != NULL);
- ASSERT(erts_refc_read(&lib->rt_dtor_cnt,0) == 0);
+ ASSERT(erts_refc_read(&lib->dynlib_refc,0) == 0);
if (lib->entry.unload != NULL) {
struct enif_msg_environment_t msg_env;
@@ -2228,24 +2254,56 @@ static void close_lib(struct erl_module_nif* lib)
}
if (!erts_is_static_nif(lib->handle))
erts_sys_ddll_close(lib->handle);
+
lib->handle = NULL;
+ deref_nifmod(lib);
}
static void steal_resource_type(ErlNifResourceType* type)
{
struct erl_module_nif* lib = type->owner;
- if (rt_have_callbacks(type)
- && erts_refc_dectest(&lib->rt_dtor_cnt, 0) == 0
- && lib->mod == NULL) {
- /* last type with destructor gone, close orphan lib */
-
- close_lib(lib);
- }
- if (erts_refc_dectest(&lib->rt_cnt, 0) == 0
- && lib->mod == NULL) {
- erts_free(ERTS_ALC_T_NIF, lib);
+ if (rt_have_callbacks(&type->fn_real)
+ && erts_refc_dectest(&lib->dynlib_refc, 0) == 0) {
+ /* last resource type with callbacks gone, close purged lib */
+ close_dynlib(lib);
}
+ deref_nifmod(lib);
+}
+
+static erts_rwmtx_t erts_nif_call_tab_lock;
+
+static void resource_dtor_during_takeover(ErlNifEnv* env, void* obj)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ if (rt->fn_real.dtor)
+ rt->fn_real.dtor(env, obj);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+}
+static void resource_stop_during_takeover(ErlNifEnv* env, void* obj,
+ ErlNifEvent e, int is_direct_call)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ ASSERT(rt->fn_real.stop);
+ rt->fn_real.stop(env, obj, e, is_direct_call);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+}
+static void resource_down_during_takeover(ErlNifEnv* env, void* obj,
+ ErlNifPid* pid, ErlNifMonitor* mon)
+{
+ ErtsResource* resource = DATA_TO_RESOURCE(obj);
+ ErlNifResourceType* rt = resource->type;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ ASSERT(rt->fn_real.down);
+ rt->fn_real.down(env, obj, pid, mon);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
}
static void resource_dtor_nop(ErlNifEnv* env, void* obj)
@@ -2278,7 +2336,7 @@ ErlNifResourceType* open_resource_type(ErlNifEnv* env,
ErlNifResourceFlags op = flags;
Eterm module_am, name_am;
- ASSERT(erts_thr_progress_is_blocking());
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
module_am = make_atom(env->mod_nif->mod->module);
name_am = enif_make_atom(env, name_str);
@@ -2292,7 +2350,8 @@ ErlNifResourceType* open_resource_type(ErlNifEnv* env,
erts_refc_init(&type->refc, 1);
op = ERL_NIF_RT_CREATE;
#ifdef DEBUG
- type->dtor = (void*)1;
+ type->fn.dtor = (void*)1;
+ type->fn_real.dtor = (void*)1;
type->owner = (void*)2;
type->prev = (void*)3;
type->next = (void*)4;
@@ -2355,14 +2414,16 @@ enif_open_resource_type_x(ErlNifEnv* env,
env->mod_nif->entry.sizeof_ErlNifResourceTypeInit);
}
-static void commit_opened_resource_types(struct erl_module_nif* lib)
+static void prepare_opened_rt(struct erl_module_nif* lib)
{
- while (opened_rt_list) {
- struct opened_resource_type* ort = opened_rt_list;
+ struct opened_resource_type* ort = opened_rt_list;
+ while (ort) {
ErlNifResourceType* type = ort->type;
if (ort->op == ERL_NIF_RT_CREATE) {
+ type->fn = ort->new_callbacks;
+ type->fn_real = ort->new_callbacks;
type->prev = &resource_type_list;
type->next = resource_type_list.next;
type->next->prev = type;
@@ -2370,20 +2431,55 @@ static void commit_opened_resource_types(struct erl_module_nif* lib)
}
else { /* ERL_NIF_RT_TAKEOVER */
steal_resource_type(type);
+
+ /*
+ * Prepare for atomic change of callbacks with lock-wrappers
+ */
+ type->fn.dtor = resource_dtor_during_takeover;
+ type->fn.stop = resource_stop_during_takeover;
+ type->fn.down = resource_down_during_takeover;
}
+ type->owner = lib;
- type->owner = lib;
- type->dtor = ort->new_callbacks.dtor;
- type->stop = ort->new_callbacks.stop;
- type->down = ort->new_callbacks.down;
+ if (rt_have_callbacks(&ort->new_callbacks))
+ erts_refc_inc(&lib->dynlib_refc, 2);
+ erts_refc_inc(&lib->refc, 2);
- if (rt_have_callbacks(type)) {
- erts_refc_inc(&lib->rt_dtor_cnt, 1);
- }
- erts_refc_inc(&lib->rt_cnt, 1);
+ ort = ort->next;
+ }
+}
- opened_rt_list = ort->next;
- erts_free(ERTS_ALC_T_TMP, ort);
+/*
+ * Do atomic change from old to new callbacks
+ */
+static void commit_opened_rt(void)
+{
+ struct opened_resource_type* ort = opened_rt_list;
+
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_nif_call_tab_lock));
+
+ while (ort) {
+ if (ort->op == ERL_NIF_RT_TAKEOVER) {
+ ort->type->fn_real = ort->new_callbacks;
+ }
+ ort = ort->next;
+ }
+}
+
+/*
+ * Cleanup and let callbacks be called directly without locking
+ */
+static void cleanup_opened_rt(void)
+{
+ struct opened_resource_type* ort = opened_rt_list;
+
+ while (opened_rt_list) {
+ ort = opened_rt_list;
+ if (ort->op == ERL_NIF_RT_TAKEOVER) {
+ ort->type->fn = ort->new_callbacks;
+ }
+ opened_rt_list = ort->next;
+ erts_free(ERTS_ALC_T_TMP, ort);
}
}
@@ -2494,7 +2590,7 @@ static void run_resource_dtor(void* vbin)
ErtsMonitor *root;
Uint refc;
- ASSERT(type->down);
+ ASSERT(type->fn.down);
erts_mtx_lock(&rm->lock);
ASSERT(erts_refc_read(&bin->intern.refc, 0) == 0);
kill = !rmon_is_dying(rm);
@@ -2523,10 +2619,10 @@ static void run_resource_dtor(void* vbin)
erts_mtx_destroy(&rm->lock);
}
- if (type->dtor != NULL) {
+ if (type->fn.dtor != NULL) {
struct enif_msg_environment_t msg_env;
pre_nif_noproc(&msg_env, type->owner, NULL);
- type->dtor(&msg_env.env, resource->data);
+ type->fn.dtor(&msg_env.env, resource->data);
post_nif_noproc(&msg_env);
}
if (erts_refc_dectest(&type->refc, 0) == 0) {
@@ -2543,9 +2639,9 @@ void erts_resource_stop(ErtsResource* resource, ErlNifEvent e,
int is_direct_call)
{
struct enif_msg_environment_t msg_env;
- ASSERT(resource->type->stop);
+ ASSERT(resource->type->fn.stop);
pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->stop(&msg_env.env, resource->data, e, is_direct_call);
+ resource->type->fn.stop(&msg_env.env, resource->data, e, is_direct_call);
post_nif_noproc(&msg_env);
}
@@ -2556,7 +2652,7 @@ void erts_nif_demonitored(ErtsResource* resource)
int free_me;
ASSERT(rmp);
- ASSERT(resource->type->down);
+ ASSERT(resource->type->fn.down);
erts_mtx_lock(&rmp->lock);
free_me = ((rmon_refc_dec_read(rmp) == 0) & !!rmon_is_dying(rmp));
@@ -2590,7 +2686,7 @@ void erts_fire_nif_monitor(ErtsMonitor *tmon)
omon = &mdp->origin;
ASSERT(rmp);
- ASSERT(resource->type->down);
+ ASSERT(resource->type->fn.down);
erts_mtx_lock(&rmp->lock);
@@ -2617,7 +2713,7 @@ void erts_fire_nif_monitor(ErtsMonitor *tmon)
erts_ref_to_driver_monitor(mdp->ref, &nif_monitor);
nif_pid.pid = omon->other.item;
pre_nif_noproc(&msg_env, resource->type->owner, NULL);
- resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
+ resource->type->fn.down(&msg_env.env, resource->data, &nif_pid, &nif_monitor);
post_nif_noproc(&msg_env);
erts_bin_release(&bin->binary);
@@ -2634,7 +2730,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
ErtsResource* resource;
size_t monitors_offs;
- if (type->down) {
+ if (type->fn.down) {
/* Put ErtsResourceMonitors after user data and properly aligned */
monitors_offs = ((data_sz + ERTS_ALLOC_ALIGN_BYTES - 1)
& ~((size_t)ERTS_ALLOC_ALIGN_BYTES - 1));
@@ -2656,7 +2752,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
erts_refc_init(&resource->nif_refc, 1);
#endif
erts_refc_inc(&resource->type->refc, 2);
- if (type->down) {
+ if (type->fn.down) {
resource->monitors = (ErtsResourceMonitors*) (resource->data + monitors_offs);
erts_mtx_init(&resource->monitors->lock, "resource_monitors", NIL,
ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
@@ -2838,25 +2934,25 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
}
static ERTS_INLINE void
-nif_export_cleanup_nif_mod(NifExport *ep)
+nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
- if (erts_refc_dectest(&ep->m->rt_dtor_cnt, 0) == 0 && ep->m->mod == NULL)
- close_lib(ep->m);
+ if (erts_refc_dectest(&ep->m->dynlib_refc, 0) == 0)
+ close_dynlib(ep->m);
ep->m = NULL;
}
void
-erts_nif_export_cleanup_nif_mod(NifExport *ep)
+erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
static ERTS_INLINE void
-nif_export_restore(Process *c_p, NifExport *ep, Eterm res)
+nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm res)
{
- erts_nif_export_restore(c_p, ep, res);
+ erts_nfunc_restore(c_p, ep, res);
ASSERT(ep->m);
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
@@ -2873,15 +2969,15 @@ static ERL_NIF_TERM
dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
- nif_export_restore(proc, ep, argv[0]);
+ nfunc_restore(proc, ep, argv[0]);
return argv[0];
}
@@ -2893,21 +2989,22 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
Eterm exception;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
exception = argv[0]; /* argv overwritten by restore below... */
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
ret = enif_raise_exception(env, exception);
- /* Restore orig info for error and clear nif export in handle_error() */
- proc->freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear native func wrapper in
+ * handle_error() */
+ proc->freason |= EXF_RESTORE_NFUNC;
return ret;
}
@@ -2944,7 +3041,7 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
int argc, const ERL_NIF_TERM argv[])
{
Process *proc;
- NifExport *ep;
+ ErtsNativeFunc *ep;
Eterm mod, func;
NativeFunPtr fp;
@@ -2954,12 +3051,11 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
* Called in order to schedule statically determined
* dirty NIF calls...
*
- * Note that 'current' does not point into a NifExport
- * structure; only a structure with similar
- * parts (located in code).
+ * Note that 'current' does not point into a ErtsNativeFunc
+ * structure; only a structure with similar parts (located in code).
*/
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
mod = proc->current->module;
func = proc->current->function;
fp = (NativeFunPtr) ep->func;
@@ -2987,7 +3083,6 @@ static_schedule_dirty_cpu_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return static_schedule_dirty_nif(env, ERTS_PSFLG_DIRTY_CPU_PROC, argc, argv);
}
-
/*
* NIF execution wrapper used by enif_schedule_nif() for regular NIFs. It
* calls the actual NIF, restores original NIF MFA if necessary, and
@@ -2998,12 +3093,12 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
NativeFunPtr fp;
- NifExport* ep;
+ ErtsNativeFunc* ep;
ERL_NIF_TERM result;
execution_state(env, &proc, NULL);
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
fp = ep->func;
ASSERT(ep);
ASSERT(!env->exception_thrown);
@@ -3016,20 +3111,20 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
result = (*fp)(env, argc, argv);
- ASSERT(ep == ERTS_PROC_GET_NIF_TRAP_EXPORT(proc));
+ ASSERT(ep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc));
if (is_value(result) || proc->freason != TRAP) {
/* Done (not rescheduled)... */
ASSERT(ep->func == ERTS_DBG_NIF_NOT_SCHED_MARKER);
if (!env->exception_thrown)
- nif_export_restore(proc, ep, result);
+ nfunc_restore(proc, ep, result);
else {
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
/*
* Restore orig info for error and clear nif
* export in handle_error()
*/
- proc->freason |= EXF_RESTORE_NIF;
+ proc->freason |= EXF_RESTORE_NFUNC;
}
}
@@ -3439,14 +3534,14 @@ int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid,
ASSERT(ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->magic_binary.destructor
== NIF_RESOURCE_DTOR);
ASSERT(erts_refc_read(&ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->binary.intern.refc, 0) != 0);
- ASSERT(!rsrc->monitors == !rsrc->type->down);
+ ASSERT(!rsrc->monitors == !rsrc->type->fn.down);
rm = rsrc->monitors;
if (!rm) {
- ASSERT(!rsrc->type->down);
+ ASSERT(!rsrc->type->fn.down);
return -1;
}
- ASSERT(rsrc->type->down);
+ ASSERT(rsrc->type->fn.down);
if (target_pid->pid == am_undefined)
return 1;
@@ -3996,7 +4091,7 @@ void erts_add_taint(Eterm mod_atom)
struct tainted_module_t *first, *t;
ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_driver_list_lock)
- || erts_thr_progress_is_blocking());
+ || erts_has_code_write_permission());
first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
for (t=first ; t; t=t->next) {
@@ -4008,7 +4103,7 @@ void erts_add_taint(Eterm mod_atom)
if (t != NULL) {
t->module_atom = mod_atom;
t->next = first;
- erts_atomic_set_nob(&first_taint, (erts_aint_t)t);
+ erts_atomic_set_relb(&first_taint, (erts_aint_t)t);
}
}
@@ -4019,7 +4114,7 @@ Eterm erts_nif_taints(Process* p)
Eterm list = NIL;
Eterm* hp;
- first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint);
+ first = (struct tainted_module_t*) erts_atomic_read_acqb(&first_taint);
for (t=first ; t!=NULL; t=t->next) {
cnt++;
}
@@ -4091,6 +4186,8 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
bytes += src->num_of_funcs * sizeof(ErlNifFunc);
lib = erts_alloc(ERTS_ALC_T_NIF, bytes);
+ erts_mtx_init(&lib->load_mtx, "nif_load", NIL,
+ ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
dst = &lib->entry;
sys_memcpy(dst, src, offsetof(ErlNifEntry, vm_variant));
@@ -4132,8 +4229,89 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
return lib;
};
+/* load_nif/2 is implemented as an instruction as it needs to know where it
+ * was called from, and it's a pain to get that information in a BIF.
+ *
+ * This is a small stub that rejects apply(erlang, load_nif, [Path, Args]). */
+BIF_RETTYPE load_nif_2(BIF_ALIST_2) {
+ if (BIF_P->flags & F_HIPE_MODE) {
+ BIF_RET(load_nif_error(BIF_P, "notsup",
+ "Calling load_nif from HiPE compiled modules "
+ "not supported"));
+ }
+
+ BIF_RET(load_nif_error(BIF_P, "bad_lib",
+ "load_nif/2 must be explicitly called from the NIF "
+ "module. It cannot be called through apply/3."));
+}
+
+typedef struct {
+ HashBucket bucket;
+ ErtsCodeInfo* code_info;
+ ErtsCodeMFA mfa;
+ BeamInstr beam[4];
+} ErtsNifBeamStub;
+
+typedef struct ErtsNifFinish_ {
+ int nstubs_hashed; /* can be less than 'num_of_funcs' if load failed */
+ ErtsNifBeamStub beam_stubv[1];
+} ErtsNifFinish;
+
+#define sizeof_ErtsNifFinish(N) \
+ (offsetof(ErtsNifFinish, beam_stubv) + (N)*sizeof(ErtsNifBeamStub))
+
+static void load_nif_1st_finisher(void* vlib);
+static void load_nif_2nd_finisher(void* vlib);
+static void load_nif_3rd_finisher(void* vlib);
+static void release_beam_stubs(struct erl_module_nif* lib);
+static void erase_hashed_stubs(ErtsNifFinish*);
+
+struct hash erts_nif_call_tab;
+
+static HashValue nif_call_hash(ErtsNifBeamStub* obj)
+{
+ return ((HashValue)obj->code_info / sizeof(BeamInstr));
+}
+
+static int nif_call_cmp(ErtsNifBeamStub* tmpl, ErtsNifBeamStub* obj)
+{
+ return tmpl->code_info != obj->code_info;
+}
+
+static ErtsNifBeamStub* nif_call_alloc(ErtsNifBeamStub* tmpl)
+{
+ return tmpl;
+}
+
+static void nif_call_free(ErtsNifBeamStub* obj)
+{
+}
+
+static void nif_call_table_init(void)
+{
+ HashFunctions f;
+
+ erts_rwmtx_opt_t rwmtx_opt = ERTS_RWMTX_OPT_DEFAULT_INITER;
+ rwmtx_opt.lived = ERTS_RWMTX_LONG_LIVED;
+
+ erts_rwmtx_init_opt(&erts_nif_call_tab_lock, &rwmtx_opt, "nif_call_tab",
+ NIL, (ERTS_LOCK_FLAGS_PROPERTY_STATIC |
+ ERTS_LOCK_FLAGS_CATEGORY_GENERIC));
+
+ f.hash = (H_FUN) nif_call_hash;
+ f.cmp = (HCMP_FUN) nif_call_cmp;
+ f.alloc = (HALLOC_FUN) nif_call_alloc;
+ f.free = (HFREE_FUN) nif_call_free;
+ f.meta_alloc = (HMALLOC_FUN) erts_alloc;
+ f.meta_free = (HMFREE_FUN) erts_free;
+ f.meta_print = (HMPRINT_FUN) erts_print;
+
+ hash_init(ERTS_ALC_T_NIF, &erts_nif_call_tab, "nif_call_tab", 100, f);
+}
+
+static void patch_call_nif_early(ErlNifEntry*, struct erl_module_instance*);
-BIF_RETTYPE load_nif_2(BIF_ALIST_2)
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args)
{
static const char bad_lib[] = "bad_lib";
static const char upgrade[] = "upgrade";
@@ -4156,42 +4334,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
struct erl_module_instance* this_mi;
struct erl_module_instance* prev_mi;
- if (BIF_P->flags & F_HIPE_MODE) {
- ret = load_nif_error(BIF_P, "notsup", "Calling load_nif from HiPE compiled "
- "modules not supported");
- BIF_RET(ret);
- }
-
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
/* Do not convert the lib name to utf-16le yet, do that in win32 specific code */
/* since lib_name is used in error messages */
encoding = ERL_FILENAME_UTF8;
}
- lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0,
+ lib_name = erts_convert_filename_to_encoding(filename, NULL, 0,
ERTS_ALC_T_TMP, 1, 0, encoding,
NULL, 0);
if (!lib_name) {
- BIF_ERROR(BIF_P, BADARG);
- }
-
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- erts_free(ERTS_ALC_T_TMP, lib_name);
- ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2],
- BIF_P, BIF_ARG_1, BIF_ARG_2);
+ return THE_NON_VALUE;
}
- /* Block system (is this the right place to do it?) */
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_thr_progress_block();
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
-
/* Find calling module */
- ASSERT(BIF_P->current != NULL);
- ASSERT(BIF_P->current->module == am_erlang
- && BIF_P->current->function == am_load_nif
- && BIF_P->current->arity == 2);
- caller = find_function_from_pc(BIF_P->cp);
+ caller = find_function_from_pc(I);
ASSERT(caller != NULL);
mod_atom = caller->module;
ASSERT(is_atom(mod_atom));
@@ -4211,7 +4368,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
this_mi = &module_p->curr;
prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ ret = load_nif_error(c_p, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
goto error;
} else if (module_p->on_load) {
@@ -4225,52 +4382,52 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
if (this_mi->nif != NULL) {
- ret = load_nif_error(BIF_P,"reload","NIF library already loaded"
+ ret = load_nif_error(c_p,"reload","NIF library already loaded"
" (reload disallowed since OTP 20).");
}
else if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
- ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s: '%s'", slogan, errdesc.str);
}
else {
- ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
}
}
else if (init_func == NULL &&
erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) {
- ret = load_nif_error(BIF_P, bad_lib, "Failed to find library init"
+ ret = load_nif_error(c_p, bad_lib, "Failed to find library init"
" function: '%s'", errdesc.str);
}
else if ((taint ? erts_add_taint(mod_atom) : 0,
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
+ ret = load_nif_error(c_p, bad_lib, "Library init-call unsuccessful");
}
else if (entry->major > ERL_NIF_MAJOR_VERSION
|| (entry->major == ERL_NIF_MAJOR_VERSION
&& entry->minor > ERL_NIF_MINOR_VERSION)) {
char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
" recompile the NIF lib or use a newer erts runtime.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts);
+ ret = load_nif_error(c_p, bad_lib, fmt, mod_atom, entry->min_erts);
}
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
char* fmt = "That old NIF library (%d.%d) is not compatible with this "
"erts runtime (%d.%d). Try recompile the NIF lib.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor,
+ ret = load_nif_error(c_p, bad_lib, fmt, entry->major, entry->minor,
ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
- ret = load_nif_error(BIF_P, bad_lib, "Library (%s) not compiled for "
+ ret = load_nif_error(c_p, bad_lib, "Library (%s) not compiled for "
"this vm variant (%s).",
entry->vm_variant, ERL_NIF_VM_VARIANT);
}
else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not"
+ ret = load_nif_error(c_p, bad_lib, "Library module name '%s' does not"
" match calling module '%T'", entry->name, mod_atom);
}
else {
@@ -4278,37 +4435,66 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
entry = &lib->entry; /* Use a guaranteed modern lib entry from now on */
lib->handle = handle;
- erts_refc_init(&lib->rt_cnt, 0);
- erts_refc_init(&lib->rt_dtor_cnt, 0);
+ erts_refc_init(&lib->refc, 2); /* Erlang code + NIF code */
+ erts_refc_init(&lib->dynlib_refc, 1);
ASSERT(opened_rt_list == NULL);
lib->mod = module_p;
- for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
+ lib->finish = erts_alloc(ERTS_ALC_T_NIF,
+ sizeof_ErtsNifFinish(entry->num_of_funcs));
+ lib->finish->nstubs_hashed = 0;
+
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ for (i=0; i < entry->num_of_funcs; i++) {
ErtsCodeInfo** ci_pp;
+ ErtsCodeInfo* ci;
ErlNifFunc* f = &entry->funcs[i];
+ ErtsNifBeamStub* stub = &lib->finish->beam_stubv[i];
+
+ stub->code_info = NULL; /* end marker in case we fail */
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
|| (ci_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
- ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
+ ret = load_nif_error(c_p,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
+ break;
}
- else if (f->flags != 0 &&
- f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND &&
- f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) {
- ret = load_nif_error(BIF_P, bad_lib,
- "Illegal flags field value %d for NIF %T:%s/%u",
+ ci = *ci_pp;
+
+ if (f->flags != 0 &&
+ f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND &&
+ f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) {
+
+ ret = load_nif_error(c_p, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u",
f->flags, mod_atom, f->name, f->arity);
+ break;
}
- else if (erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
- < BEAM_NIF_MIN_FUNC_SZ)
- {
- ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif"
- " in module (%T:%s/%u too small)",
- mod_atom, f->name, f->arity);
- }
- /*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n",
- mod_atom, f->name, f->arity);*/
+
+ ASSERT(erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
+ >= BEAM_NATIVE_MIN_FUNC_SZ);
+
+ stub->code_info = ci;
+ stub->mfa = ci->mfa;
+ if (hash_put(&erts_nif_call_tab, stub) != stub) {
+ ret = load_nif_error(c_p, bad_lib, "Duplicate NIF entry for %T:%s/%u",
+ mod_atom, f->name, f->arity);
+ break;
+ }
+ lib->finish->nstubs_hashed++;
+
+ stub->beam[0] = BeamOpCodeAddr(op_call_nif_WWW);
+ stub->beam[2] = (BeamInstr) lib;
+ if (f->flags) {
+ stub->beam[3] = (BeamInstr) f->fptr;
+ stub->beam[1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
+ (BeamInstr) static_schedule_dirty_io_nif :
+ (BeamInstr) static_schedule_dirty_cpu_nif;
+ }
+ else
+ stub->beam[1] = (BeamInstr) f->fptr;
}
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
+ ASSERT(lib->finish->nstubs_hashed == lib->entry.num_of_funcs);
}
if (ret != am_ok) {
@@ -4324,67 +4510,71 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
- ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
+ ret = load_nif_error(c_p, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2);
+ /*
+ * Go single scheduler during upgrade callback.
+ * Todo: Fix better solution with suspending callers and waiting for
+ * all calls to return (including dirty).
+ * Note that erts_thr_progress_block() will not block dirty NIFs.
+ */
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_thr_progress_block();
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, args);
erts_post_nif(&env);
+ erts_thr_progress_unblock();
if (veto) {
prev_mi->nif->priv_data = prev_old_data;
- ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
}
else if (entry->load != NULL) { /********* Initial load ***********/
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->load(&env, &lib->priv_data, args);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, "load", "Library load-call unsuccessful (%d).", veto);
}
}
- if (ret == am_ok) {
- commit_opened_resource_types(lib);
+ if (ret == am_ok) {
/*
- ** Everything ok, patch the beam code with op_call_nif
- */
-
+ * Everything ok, make NIF code callable.
+ */
this_mi->nif = lib;
- for (i=0; i < entry->num_of_funcs; i++)
- {
- ErlNifFunc* f = &entry->funcs[i];
- ErtsCodeInfo* ci;
- BeamInstr *code_ptr;
+ prepare_opened_rt(lib);
+ /*
+ * The call table lock will make sure all NIFs and callbacks in module
+ * are made accessible atomically.
+ */
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ commit_opened_rt();
+ patch_call_nif_early(entry, this_mi);
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
- erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
- ci = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
- code_ptr = erts_codeinfo_to_code(ci);
+ cleanup_opened_rt();
- if (ci->u.gen_bp == NULL) {
- code_ptr[0] = BeamOpCodeAddr(op_call_nif);
- }
- else { /* Function traced, patch the original instruction word */
- GenericBp* g = ci->u.gen_bp;
- ASSERT(BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint));
- g->orig_instr = BeamOpCodeAddr(op_call_nif);
- }
- if (f->flags) {
- code_ptr[3] = (BeamInstr) f->fptr;
- code_ptr[1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
- (BeamInstr) static_schedule_dirty_io_nif :
- (BeamInstr) static_schedule_dirty_cpu_nif;
- }
- else
- code_ptr[1] = (BeamInstr) f->fptr;
- code_ptr[2] = (BeamInstr) lib;
- }
+ /*
+ * Now we wait thread progress, to make sure no process is still
+ * executing the beam code of the NIFs, before we can patch in the
+ * final fast multi word call_nif_WWW instructions.
+ */
+ erts_refc_inc(&lib->refc, 2);
+ erts_schedule_thr_prgr_later_op(load_nif_1st_finisher, lib,
+ &lib->lop);
}
else {
error:
rollback_opened_resource_types();
ASSERT(ret != am_ok);
if (lib != NULL) {
+ if (lib->finish != NULL) {
+ erase_hashed_stubs(lib->finish);
+ erts_free(ERTS_ALC_T_NIF, lib->finish);
+ }
erts_free(ERTS_ALC_T_NIF, lib);
}
if (handle != NULL && !erts_is_static_nif(handle)) {
@@ -4393,25 +4583,208 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_sys_ddll_free_error(&errdesc);
}
- erts_thr_progress_unblock();
- erts_release_code_write_permission();
erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_RET(ret);
}
+/*
+ * Write 'call_nif_early' as the first beam instruction for all NIFs
+ * which will make them callable.
+ *
+ * The 'call_nif_early' is a one word beam instruction which uses a lock
+ * protected hash lookup to get its "arguments". This guarantees an atomically
+ * safe publication of all NIFs in the module.
+ */
+static void patch_call_nif_early(ErlNifEntry* entry,
+ struct erl_module_instance* this_mi)
+{
+ int i;
+
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
+ ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_nif_call_tab_lock));
+
+ for (i=0; i < entry->num_of_funcs; i++)
+ {
+ ErlNifFunc* f = &entry->funcs[i];
+ BeamInstr volatile *code_ptr;
+ ErtsCodeInfo* ci;
+ Eterm f_atom;
+
+ erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1);
+ ci = *get_func_pp(this_mi->code_hdr, f_atom, f->arity);
+ code_ptr = erts_codeinfo_to_code(ci);
+
+ if (ci->u.gen_bp) {
+ /*
+ * Function traced, patch the original instruction word
+ * Code write permission protects against racing breakpoint writes.
+ */
+ GenericBp* g = ci->u.gen_bp;
+ g->orig_instr = BeamOpCodeAddr(op_call_nif_early);
+ if (BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint))
+ continue;
+ }
+ else
+ ASSERT(!BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint));
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif_early);
+ }
+}
+
+BeamInstr* erts_call_nif_early(Process* c_p, ErtsCodeInfo* ci)
+{
+ ErtsNifBeamStub* bs;
+ ErtsNifBeamStub tmpl;
+ tmpl.code_info = ci;
+
+ erts_rwmtx_rlock(&erts_nif_call_tab_lock);
+ bs = (ErtsNifBeamStub*) hash_get(&erts_nif_call_tab, &tmpl);
+ erts_rwmtx_runlock(&erts_nif_call_tab_lock);
+
+ ASSERT(bs);
+ return &bs->beam[0];
+}
+
+static void load_nif_1st_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+ ErtsNifFinish* fin;
+ int i;
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ if (fin) {
+ for (i=0; i < lib->entry.num_of_funcs; i++) {
+ BeamInstr* code_ptr;
+ code_ptr = erts_codeinfo_to_code(fin->beam_stubv[i].code_info);
+
+ code_ptr[1] = fin->beam_stubv[i].beam[1]; /* called function */
+ code_ptr[2] = fin->beam_stubv[i].beam[2]; /* erl_module_nif */
+ if (lib->entry.funcs[i].flags)
+ code_ptr[3] = fin->beam_stubv[i].beam[3]; /* real NIF */
+ }
+ }
+ erts_mtx_unlock(&lib->load_mtx);
+
+ if (fin) {
+ /*
+ * A second thread progress to get a memory barrier between the
+ * arguments of call_nif_WWW (written above) and the instruction word
+ * itself.
+ */
+ erts_schedule_thr_prgr_later_op(load_nif_2nd_finisher, lib,
+ &lib->lop);
+ }
+ else { /* Unloaded */
+ deref_nifmod(lib);
+ }
+}
+
+static void load_nif_2nd_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+ ErtsNifFinish* fin;
+ int i;
+
+ /*
+ * We seize code write permission only to avoid any trace breakpoints
+ * to change while we patch the op_call_nif_WWW instruction.
+ */
+ if (!erts_try_seize_code_write_permission_aux(load_nif_2nd_finisher, vlib)) {
+ return;
+ }
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ if (fin) {
+ for (i=0; i < lib->entry.num_of_funcs; i++) {
+ ErtsCodeInfo *ci = fin->beam_stubv[i].code_info;
+ BeamInstr volatile *code_ptr = erts_codeinfo_to_code(ci);
+
+ if (ci->u.gen_bp) {
+ /*
+ * Function traced, patch the original instruction word
+ */
+ GenericBp* g = ci->u.gen_bp;
+ ASSERT(g->orig_instr == BeamOpCodeAddr(op_call_nif_early));
+ g->orig_instr = BeamOpCodeAddr(op_call_nif_WWW);
+ if (BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint))
+ continue;
+ }
+ ASSERT(code_ptr[0] == BeamOpCodeAddr(op_call_nif_early));
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif_WWW);
+ }
+ }
+ erts_mtx_unlock(&lib->load_mtx);
+
+ erts_release_code_write_permission();
+
+ if (fin) {
+ UWord bytes = sizeof_ErtsNifFinish(lib->entry.num_of_funcs);
+ /*
+ * A third and final thread progress, to make sure no one is executing
+ * the call_nif_early instructions anymore, before we can deallocate
+ * the beam stubs.
+ */
+ erts_schedule_thr_prgr_later_cleanup_op(load_nif_3rd_finisher, lib,
+ &lib->lop,
+ bytes);
+ }
+ else { /* Unloaded */
+ deref_nifmod(lib);
+ }
+}
+
+static void load_nif_3rd_finisher(void* vlib)
+{
+ struct erl_module_nif* lib = (struct erl_module_nif*) vlib;
+
+ release_beam_stubs(lib);
+ deref_nifmod(lib);
+}
+
+static void release_beam_stubs(struct erl_module_nif* lib)
+{
+ ErtsNifFinish* fin;
+
+ erts_mtx_lock(&lib->load_mtx);
+ fin = lib->finish;
+ lib->finish = NULL;
+ erts_mtx_unlock(&lib->load_mtx);
+
+ if (fin) {
+ erase_hashed_stubs(fin);
+ erts_free(ERTS_ALC_T_NIF, fin);
+ }
+}
+
+static void erase_hashed_stubs(ErtsNifFinish* fin)
+{
+ int i;
+
+ erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
+ for (i=0; i < fin->nstubs_hashed; i++) {
+ void* erased = hash_erase(&erts_nif_call_tab, &fin->beam_stubv[i]);
+ ASSERT(erased); (void) erased;
+ }
+ erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
+}
+
void
erts_unload_nif(struct erl_module_nif* lib)
{
ErlNifResourceType* rt;
ErlNifResourceType* next;
- ASSERT(erts_thr_progress_is_blocking());
+
ASSERT(lib != NULL);
ASSERT(lib->mod != NULL);
+ ERTS_LC_ASSERT(erts_has_code_write_permission());
erts_tracer_nif_clear();
+ release_beam_stubs(lib);
+
for (rt = resource_type_list.next;
rt != &resource_type_list;
rt = next) {
@@ -4423,25 +4796,19 @@ erts_unload_nif(struct erl_module_nif* lib)
rt->next = NULL;
rt->prev = NULL;
if (erts_refc_dectest(&rt->refc, 0) == 0) {
- if (rt_have_callbacks(rt)) {
- erts_refc_dec(&lib->rt_dtor_cnt, 0);
- }
- erts_refc_dec(&lib->rt_cnt, 0);
+ if (rt_have_callbacks(&rt->fn_real))
+ erts_refc_dec(&lib->dynlib_refc, 1);
+ erts_refc_dec(&lib->refc, 1);
erts_free(ERTS_ALC_T_NIF, rt);
}
}
}
- if (erts_refc_read(&lib->rt_dtor_cnt, 0) == 0) {
- close_lib(lib);
- if (erts_refc_read(&lib->rt_cnt, 0) == 0) {
- erts_free(ERTS_ALC_T_NIF, lib);
- return;
- }
- }
- else {
- ASSERT(erts_refc_read(&lib->rt_cnt, 1) > 0);
- }
- lib->mod = NULL; /* orphan lib */
+ lib->mod = NULL; /* purged Elang module */
+
+ if (erts_refc_dectest(&lib->dynlib_refc, 0) == 0)
+ close_dynlib(lib);
+
+ deref_nifmod(lib);
}
void erl_nif_init()
@@ -4451,11 +4818,13 @@ void erl_nif_init()
resource_type_list.next = &resource_type_list;
resource_type_list.prev = &resource_type_list;
- resource_type_list.dtor = NULL;
+ resource_type_list.fn.dtor = NULL;
+ resource_type_list.fn_real.dtor = NULL;
resource_type_list.owner = NULL;
resource_type_list.module = THE_NON_VALUE;
resource_type_list.name = THE_NON_VALUE;
+ nif_call_table_init();
}
int erts_nif_get_funcs(struct erl_module_nif* mod,
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 51e6a4dc40..abf833f318 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -176,9 +176,10 @@ dist_table_alloc(void *dep_tmpl)
erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL);
dep->connection_id = 0;
dep->state = ERTS_DE_STATE_IDLE;
- dep->flags = 0;
+ dep->pending_nodedown = 0;
+ dep->suspended_nodeup = NULL;
+ dep->dflags = 0;
dep->opts = 0;
- dep->version = 0;
dep->mld = NULL;
@@ -201,7 +202,6 @@ dist_table_alloc(void *dep_tmpl)
erts_port_task_handle_init(&dep->dist_cmd);
dep->send = NULL;
dep->cache = NULL;
- dep->transcode_ctx = NULL;
dep->sequences = NULL;
/* Link in */
@@ -634,7 +634,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
else {
ASSERT(dep->state != ERTS_DE_STATE_IDLE);
ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid));
- if (dep->flags & DFLAG_PUBLISHED) {
+ if (dep->dflags & DFLAG_PUBLISHED) {
ASSERT(erts_no_of_visible_dist_entries > 0);
erts_no_of_visible_dist_entries--;
head = &erts_visible_dist_entries;
@@ -658,7 +658,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep)
dep->next->prev = dep->prev;
dep->state = ERTS_DE_STATE_IDLE;
- dep->flags = 0;
+ dep->dflags = 0;
dep->opts = 0;
dep->prev = NULL;
dep->cid = NIL;
@@ -700,7 +700,7 @@ erts_set_dist_entry_pending(DistEntry *dep)
erts_no_of_not_connected_dist_entries--;
dep->state = ERTS_DE_STATE_PENDING;
- dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_NO_MAGIC);
+ dep->dflags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_PENDING_CONNECT);
dep->connection_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK;
ASSERT(!dep->mld);
@@ -719,7 +719,7 @@ erts_set_dist_entry_pending(DistEntry *dep)
}
void
-erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
+erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint64 flags)
{
erts_aint32_t set_qflgs;
@@ -731,6 +731,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
ASSERT(dep != erts_this_dist_entry);
ASSERT(is_nil(dep->cid));
ASSERT(dep->state == ERTS_DE_STATE_PENDING);
+ ASSERT(!dep->pending_nodedown);
ASSERT(is_internal_port(cid) || is_internal_pid(cid));
if(dep->prev) {
@@ -749,7 +750,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags)
erts_no_of_pending_dist_entries--;
dep->state = ERTS_DE_STATE_CONNECTED;
- dep->flags = flags & ~DFLAG_NO_MAGIC;
+ dep->dflags = flags & ~DFLAG_PENDING_CONNECT;
dep->cid = cid;
erts_atomic_set_nob(&dep->input_handler,
(erts_aint_t) cid);
@@ -976,7 +977,7 @@ static void print_node(void *venp, void *vpndp)
if(pndp->sysname == NIL) {
erts_print(pndp->to, pndp->to_arg, "Name: %T ", enp->sysname);
}
- erts_print(pndp->to, pndp->to_arg, " %d", enp->creation);
+ erts_print(pndp->to, pndp->to_arg, " %u", enp->creation);
#ifdef DEBUG
erts_print(pndp->to, pndp->to_arg, " (refc=%ld)",
erts_refc_read(&enp->refc, 0));
@@ -1019,7 +1020,7 @@ void erts_print_node_info(fmtfn_t to,
/* ----------------------------------------------------------------------- */
void
-erts_set_this_node(Eterm sysname, Uint creation)
+erts_set_this_node(Eterm sysname, Uint32 creation)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(2 <= de_refc_read(erts_this_dist_entry, 2));
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index ffaafbbbea..f426f46d53 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -23,6 +23,7 @@
typedef struct dist_entry_ DistEntry;
typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
+typedef struct ErtsDistOutputBufsContainer_ ErtsDistOutputBufsContainer;
void erts_ref_dist_entry(DistEntry *dep);
void erts_deref_dist_entry(DistEntry *dep);
@@ -95,26 +96,21 @@ enum dist_entry_state {
struct ErtsDistOutputBuf_ {
#ifdef DEBUG
Uint dbg_pattern;
- byte *ext_startp;
- byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
Binary *bin;
- /* Pointers to the distribution header,
- if NULL the distr header is in the extp */
- byte *hdrp;
- byte *hdr_endp;
- /* Pointers to the ctl + payload */
- byte *extp;
- byte *ext_endp;
- /* Start of payload and hopefull_flags, used by transcode */
- Uint hopefull_flags;
- byte *msg_start;
- /* start of the ext buffer, this is not always the same as extp
- as the atom cache handling can use less then the allotted buffer.
- This value is needed to calculate the size of this output buffer.*/
- byte *ext_start;
+ /*
+ * iov[0] reserved for driver
+ * iov[1] reserved for distribution header
+ * iov[2 ... vsize-1] data
+ */
+ ErlIOVec *eiov;
+};
+struct ErtsDistOutputBufsContainer_ {
+ Sint fragments;
+ byte *extp;
+ ErtsDistOutputBuf obuf[1]; /* longer if fragmented... */
};
typedef struct {
@@ -147,10 +143,11 @@ struct dist_entry_ {
NIL == free */
Uint32 connection_id; /* Connection id incremented on connect */
enum dist_entry_state state;
- Uint32 flags; /* Distribution flags, like hidden,
+ int pending_nodedown;
+ Process* suspended_nodeup;
+ Uint64 dflags; /* Distribution flags, like hidden,
atom cache etc. */
Uint32 opts;
- unsigned long version; /* Protocol version */
ErtsMonLnkDist *mld; /* Monitors and links */
@@ -173,8 +170,6 @@ struct dist_entry_ {
ErtsThrPrgrLaterOp later_op;
- struct transcode_context* transcode_ctx;
-
struct dist_sequences *sequences; /* Ongoing distribution sequences */
};
@@ -261,10 +256,10 @@ Uint erts_dist_table_size(void);
void erts_dist_table_info(fmtfn_t, void *);
void erts_set_dist_entry_not_connected(DistEntry *);
void erts_set_dist_entry_pending(DistEntry *);
-void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint);
+void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint64);
ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
void erts_schedule_delete_node(ErlNode *);
-void erts_set_this_node(Eterm, Uint);
+void erts_set_this_node(Eterm, Uint32);
Uint erts_node_table_size(void);
void erts_init_node_tables(int);
void erts_node_table_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index f8d82a8f98..efccb5fcb6 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -51,7 +51,7 @@
* Note that not all signal are handled using this functionality!
*/
-#define ERTS_SIG_Q_OP_MAX 13
+#define ERTS_SIG_Q_OP_MAX 14
#define ERTS_SIG_Q_OP_EXIT 0 /* Exit signal due to bif call */
#define ERTS_SIG_Q_OP_EXIT_LINKED 1 /* Exit signal due to link break*/
@@ -66,7 +66,8 @@
#define ERTS_SIG_Q_OP_IS_ALIVE 10
#define ERTS_SIG_Q_OP_PROCESS_INFO 11
#define ERTS_SIG_Q_OP_SYNC_SUSPEND 12
-#define ERTS_SIG_Q_OP_RPC ERTS_SIG_Q_OP_MAX
+#define ERTS_SIG_Q_OP_RPC 13
+#define ERTS_SIG_Q_OP_DIST_SPAWN_REPLY ERTS_SIG_Q_OP_MAX
#define ERTS_SIG_Q_TYPE_MAX (ERTS_MON_LNK_TYPE_MAX + 5)
@@ -137,6 +138,14 @@ typedef struct {
} ErtsSigDistLinkOp;
typedef struct {
+ Eterm message;
+ Eterm ref;
+ Eterm result;
+ ErtsLink *link;
+ Eterm *patch_point;
+} ErtsDistSpawnReplySigData;
+
+typedef struct {
ErtsSignalCommon common;
Uint flags_on;
Uint flags_off;
@@ -215,6 +224,8 @@ static int handle_trace_change_state(Process *c_p,
ErtsMessage ***next_nm_sig);
static void getting_unlinked(Process *c_p, Eterm unlinker);
static void getting_linked(Process *c_p, Eterm linker);
+static void linking(Process *c_p, Eterm to);
+
static void group_leader_reply(Process *c_p, Eterm to,
Eterm ref, int success);
static int stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
@@ -322,6 +333,17 @@ get_exit_signal_data(ErtsMessage *xsig)
+ xsig->hfrag.used_size);
}
+static ERTS_INLINE ErtsDistSpawnReplySigData *
+get_dist_spawn_reply_data(ErtsMessage *sig)
+{
+ ASSERT(ERTS_SIG_IS_NON_MSG(sig));
+ ASSERT(sig->hfrag.alloc_size > sig->hfrag.used_size);
+ ASSERT((sig->hfrag.alloc_size - sig->hfrag.used_size)*sizeof(UWord)
+ >= sizeof(ErtsDistSpawnReplySigData));
+ return (ErtsDistSpawnReplySigData *) (char *) (&sig->hfrag.mem[0]
+ + sig->hfrag.used_size);
+}
+
static ERTS_INLINE void
destroy_trace_info(ErtsSigTraceInfo *ti)
{
@@ -997,7 +1019,7 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
seq_trace = c_p && have_seqtrace(token);
if (seq_trace)
- seq_trace_update_send(c_p);
+ seq_trace_update_serial(c_p);
#ifdef USE_VM_PROBES
utag_sz = 0;
@@ -1751,6 +1773,104 @@ erts_proc_sig_send_sync_suspend(Process *c_p, Eterm to, Eterm tag, Eterm reply)
}
}
+int
+erts_proc_sig_send_dist_spawn_reply(Eterm node,
+ Eterm ref,
+ Eterm to,
+ ErtsLink *lnk,
+ Eterm result,
+ Eterm token)
+{
+ Uint hsz, ref_sz, result_sz, token_sz;
+ ErtsDistSpawnReplySigData *datap;
+ Eterm msg, ref_copy, result_copy, res_type,
+ token_copy, *hp, *hp_start, *patch_point;
+ ErlHeapFragment *hfrag;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+
+ ASSERT(is_atom(node));
+
+ /*
+ * A respons message to a spawn_request() operation
+ * looks like this:
+ * {Tag, Ref, ok|error, Pid|ErrorAtom}
+ *
+ * Tag is stored in its own heap fragment in the
+ * (pending) monitor struct and can be attached
+ * when creating the resulting message on
+ * reception of this signal.
+ */
+
+ hsz = ref_sz = size_object(ref);
+ hsz += 5 /* 4-tuple */;
+ if (is_atom(result)) {
+ res_type = am_error;
+ result_sz = 0;
+ }
+ else {
+ ASSERT(is_external_pid(result));
+ res_type = am_ok;
+ result_sz = size_object(result);
+ hsz += result_sz;
+ }
+
+ token_sz = is_immed(token) ? 0 : size_object(token);
+ hsz += token_sz;
+
+ hsz += sizeof(ErtsDistSpawnReplySigData)/sizeof(Eterm);
+
+ mp = erts_alloc_message(hsz, &hp);
+ hp_start = hp;
+ hfrag = &mp->hfrag;
+ mp->next = NULL;
+ ohp = &hfrag->off_heap;
+
+ ref_copy = copy_struct(ref, ref_sz, &hp, ohp);
+ result_copy = (is_atom(result)
+ ? result
+ : copy_struct(result, result_sz, &hp, ohp));
+ msg = TUPLE4(hp,
+ am_undefined,
+ ref_copy,
+ res_type,
+ result_copy);
+
+ patch_point = &hp[1];
+ ASSERT(*patch_point == am_undefined);
+
+ hp += 5;
+
+ token_copy = (!token_sz
+ ? token
+ : copy_struct(token, token_sz, &hp, ohp));
+
+ hfrag->used_size = hp - hp_start;
+
+ datap = (ErtsDistSpawnReplySigData *) (char *) hp;
+ datap->message = msg;
+ datap->ref = ref_copy;
+ datap->result = result_copy;
+ datap->link = lnk;
+ datap->patch_point = patch_point;
+
+ ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DIST_SPAWN_REPLY,
+ ERTS_SIG_Q_TYPE_UNDEFINED,
+ 0);
+ ERL_MESSAGE_FROM(mp) = node;
+ ERL_MESSAGE_TOKEN(mp) = token_copy;
+ if (!proc_queue_signal(NULL, to, (ErtsSignal *) mp,
+ ERTS_SIG_Q_OP_DIST_SPAWN_REPLY)) {
+ mp->next = NULL;
+ mp->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(mp) = msg;
+ erts_cleanup_messages(mp);
+ return 0;
+ }
+
+ return !0;
+}
+
Eterm
erts_proc_sig_send_rpc_request(Process *c_p,
Eterm to,
@@ -1901,6 +2021,7 @@ is_alive_response(Process *c_p, ErtsMessage *mp, int is_alive)
}
}
+
static ERTS_INLINE void
adjust_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing, int setup)
{
@@ -2303,17 +2424,14 @@ static int
convert_to_down_message(Process *c_p,
ErtsMessage *sig,
ErtsMonitorData *mdp,
+ ErtsMonitor **omon,
Uint16 mon_type,
ErtsMessage ***next_nm_sig)
{
- /*
- * Create a 'DOWN' message and replace the signal
- * with it...
- */
int cnt = 0;
Eterm node = am_undefined;
ErtsMessage *mp;
- ErtsProcLocks locks;
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
Uint hsz;
Eterm *hp, ref, from, type, reason;
ErlOffHeap *ohp;
@@ -2322,96 +2440,170 @@ convert_to_down_message(Process *c_p,
ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME)
== (mdp->target.flags & ERTS_ML_FLGS_SAME));
- hsz = 6; /* 5-tuple */
+ /* reason is mdp->target.other.item */
+ reason = mdp->target.other.item;
+ ASSERT(is_immed(reason));
+ ASSERT(&mdp->origin == *omon);
+
+ if (mdp->origin.flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ /*
+ * Create a spawn_request() error message and replace
+ * the signal with it...
+ */
+ Eterm tag;
+ ErtsMonitorDataExtended *mdep;
- if (mdp->origin.flags & ERTS_ML_FLG_NAME)
- hsz += 3; /* reg name 2-tuple */
- else {
- ASSERT(is_pid(mdp->origin.other.item)
- || is_internal_port(mdp->origin.other.item));
- hsz += NC_HEAP_SIZE(mdp->origin.other.item);
- }
+ /* Should only happen when connection breaks... */
+ ASSERT(reason == am_noconnection);
- ASSERT(is_ref(mdp->ref));
- hsz += NC_HEAP_SIZE(mdp->ref);
+ if (mdp->origin.flags & (ERTS_ML_FLG_SPAWN_ABANDONED
+ | ERTS_ML_FLG_SPAWN_NO_EMSG)) {
+ /*
+ * Operation has been been abandoned or
+ * error message has been disabled...
+ */
+ erts_monitor_release(*omon);
+ *omon = NULL;
+ return 1;
+ }
- locks = ERTS_PROC_LOCK_MAIN;
+ cnt += 4;
- /* reason is mdp->target.other.item */
- reason = mdp->target.other.item;
- ASSERT(is_immed(reason));
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ hsz = 5; /* 4-tuple */
- mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
- if (locks != ERTS_PROC_LOCK_MAIN)
- erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
- cnt += 4;
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ /*
+ * The tag to patch into the resulting message
+ * is stored in mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ */
+ if (is_immed(mdep->u.name))
+ tag = mdep->u.name;
+ else {
+ ErlHeapFragment *tag_hfrag;
+ tag_hfrag = (ErlHeapFragment *) cp_val(mdep->u.name);
+ tag = tag_hfrag->mem[0];
+ /* Save heap fragment of tag in message... */
+ if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG) {
+ tag_hfrag->next = mp->hfrag.next;
+ mp->hfrag.next = tag_hfrag;
+ }
+ else {
+ tag_hfrag->next = mp->data.heap_frag;
+ mp->data.heap_frag = tag_hfrag;
+ }
+ }
+
+ /* Restore to normal monitor */
+ mdep->u.name = NIL;
+ mdp->origin.flags &= ~ERTS_ML_FLGS_SPAWN;
- ref = STORE_NC(&hp, ohp, mdp->ref);
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ ERL_MESSAGE_TERM(mp) = TUPLE4(hp, tag, ref, am_error, reason);
- if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
- from = STORE_NC(&hp, ohp, mdp->origin.other.item);
}
else {
- ErtsMonitorDataExtended *mdep;
- ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
- mdep = (ErtsMonitorDataExtended *) mdp;
- ASSERT(is_atom(mdep->u.name));
- if (mdep->dist)
- node = mdep->dist->nodename;
- else
- node = erts_this_dist_entry->sysname;
- from = TUPLE2(hp, mdep->u.name, node);
- hp += 3;
- }
+ /*
+ * Create a 'DOWN' message and replace the signal
+ * with it...
+ */
- ASSERT(mdp->origin.type == mon_type);
- switch (mon_type) {
- case ERTS_MON_TYPE_PORT:
- type = am_port;
- if (mdp->origin.other.item == am_undefined) {
- /* failed by name... */
- ERL_MESSAGE_FROM(mp) = am_system;
- }
+ hsz = 6; /* 5-tuple */
+
+ if (mdp->origin.flags & ERTS_ML_FLG_NAME)
+ hsz += 3; /* reg name 2-tuple */
else {
- ASSERT(is_internal_port(mdp->origin.other.item));
- ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ ASSERT(is_pid(mdp->origin.other.item)
+ || is_internal_port(mdp->origin.other.item));
+ hsz += NC_HEAP_SIZE(mdp->origin.other.item);
}
- break;
- case ERTS_MON_TYPE_PROC:
- type = am_process;
- if (mdp->origin.other.item == am_undefined) {
- /* failed by name... */
- ERL_MESSAGE_FROM(mp) = am_system;
+
+ ASSERT(is_ref(mdp->ref));
+ hsz += NC_HEAP_SIZE(mdp->ref);
+
+ mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp);
+
+ if (locks != ERTS_PROC_LOCK_MAIN)
+ erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN);
+
+ cnt += 4;
+
+ ref = STORE_NC(&hp, ohp, mdp->ref);
+
+ if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) {
+ from = STORE_NC(&hp, ohp, mdp->origin.other.item);
}
else {
- ASSERT(is_internal_pid(mdp->origin.other.item));
- ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
- }
- break;
- case ERTS_MON_TYPE_DIST_PROC:
- type = am_process;
- if (node == am_undefined) {
ErtsMonitorDataExtended *mdep;
ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
mdep = (ErtsMonitorDataExtended *) mdp;
- ASSERT(mdep->dist);
- node = mdep->dist->nodename;
+ ASSERT(is_atom(mdep->u.name));
+ if (mdep->dist)
+ node = mdep->dist->nodename;
+ else
+ node = erts_this_dist_entry->sysname;
+ from = TUPLE2(hp, mdep->u.name, node);
+ hp += 3;
+ }
+
+ ASSERT(mdp->origin.type == mon_type);
+ switch (mon_type) {
+ case ERTS_MON_TYPE_PORT:
+ type = am_port;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_port(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_PROC:
+ type = am_process;
+ if (mdp->origin.other.item == am_undefined) {
+ /* failed by name... */
+ ERL_MESSAGE_FROM(mp) = am_system;
+ }
+ else {
+ ASSERT(is_internal_pid(mdp->origin.other.item));
+ ERL_MESSAGE_FROM(mp) = mdp->origin.other.item;
+ }
+ break;
+ case ERTS_MON_TYPE_DIST_PROC:
+ type = am_process;
+ if (node == am_undefined) {
+ ErtsMonitorDataExtended *mdep;
+ ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED);
+ mdep = (ErtsMonitorDataExtended *) mdp;
+ ASSERT(mdep->dist);
+ node = mdep->dist->nodename;
+ }
+ ASSERT(is_atom(node) && node != am_undefined);
+ ERL_MESSAGE_FROM(mp) = node;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected monitor type");
+ type = am_undefined;
+ ERL_MESSAGE_FROM(mp) = am_undefined;
+ break;
}
- ASSERT(is_atom(node) && node != am_undefined);
- ERL_MESSAGE_FROM(mp) = node;
- break;
- default:
- ERTS_INTERNAL_ERROR("Unexpected monitor type");
- type = am_undefined;
- ERL_MESSAGE_FROM(mp) = am_undefined;
- break;
- }
- ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
- type, from, reason);
- hp += 6;
+ ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref,
+ type, from, reason);
+ hp += 6;
+
+ }
ERL_MESSAGE_TOKEN(mp) = am_undefined;
/* Replace original signal with the exit message... */
@@ -3154,6 +3346,308 @@ erts_proc_sig_handle_pending_suspend(Process *c_p)
ERTS_PROC_SET_PENDING_SUSPEND(c_p, NULL);
}
+static int
+handle_dist_spawn_reply(Process *c_p, ErtsSigRecvTracing *tracing,
+ ErtsMessage *sig, ErtsMessage ***next_nm_sig)
+{
+
+ ErtsDistSpawnReplySigData *datap = get_dist_spawn_reply_data(sig);
+ ErtsMonitorDataExtended *mdep;
+ Eterm msg = datap->message;
+ Eterm result = datap->result;
+ ErtsMonitor *omon;
+ int adjust_monitor;
+ ErlHeapFragment *tag_hfrag = NULL;
+ int convert_to_message = !0;
+ int cnt = 1;
+
+ ASSERT(is_atom(result) || is_external_pid(result));
+ ASSERT(is_atom(result) || size_object(result) == EXTERNAL_THING_HEAD_SIZE + 1);
+
+ omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), datap->ref);
+
+ if (!omon || !(omon->flags & ERTS_ML_FLG_SPAWN_PENDING)) {
+ /* Stale reply; remove link that was setup... */
+ ErtsLink *lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;;
+ erts_cleanup_messages(sig);
+ return ++cnt;
+ }
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+
+#ifdef DEBUG
+ {
+ Eterm *tp;
+ int i;
+ ASSERT(erts_monitor_is_in_table(omon));
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+ if (is_atom(result)) {
+ ASSERT(!datap->link);
+ }
+ else {
+ ASSERT(!datap->link || (omon->flags & ERTS_ML_FLG_SPAWN_LINK));
+ ASSERT(!(omon->flags & ERTS_ML_FLG_SPAWN_LINK) || datap->link);
+ }
+ ASSERT(omon->other.item == am_pending);
+ ASSERT(is_tuple_arity(datap->message, 4));
+ tp = tuple_val(datap->message);
+ ASSERT(tp[1] == am_undefined); /* patch point */
+ ASSERT(is_internal_ref(tp[2]));
+ ASSERT((tp[3] == am_ok && is_external_pid(tp[4]))
+ || (tp[3] == am_error && is_atom(tp[4])));
+ for (i = 0; i < EXTERNAL_THING_HEAD_SIZE + 1; i++) {
+ ASSERT(is_non_value(mdep->heap[i]));
+ }
+ }
+#endif
+
+ /*
+ * The tag to patch into the resulting message
+ * is stored in mdep->u.name via a little trick
+ * (see pending_flag in erts_monitor_create()).
+ */
+ if (is_immed(mdep->u.name)) {
+ tag_hfrag = NULL;
+ *datap->patch_point = mdep->u.name;
+ }
+ else {
+ tag_hfrag = (ErlHeapFragment *) cp_val(mdep->u.name);
+ *datap->patch_point = tag_hfrag->mem[0];
+ }
+ mdep->u.name = NIL; /* Restore to normal monitor */
+
+ if (is_atom(result)) { /* Spawn error; cleanup... */
+ /* Dist code should not have created a link on failure... */
+
+ ASSERT(is_not_atom(result) || !datap->link);
+ /* delete monitor structure... */
+ adjust_monitor = 0;
+ if (omon->flags & (ERTS_ML_FLG_SPAWN_ABANDONED
+ | ERTS_ML_FLG_SPAWN_NO_EMSG))
+ convert_to_message = 0;
+ }
+ else if (omon->flags & ERTS_ML_FLG_SPAWN_ABANDONED) {
+ /*
+ * Spawn operation has been abandoned and
+ * link option was passed. Send exit signal
+ * with exit reason 'abandoned'...
+ */
+ DistEntry *dep;
+ ErtsMonLnkDist *dist;
+ ErtsMonitorDataExtended *mdep;
+ ErtsLink *lnk;
+
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ dist = mdep->dist;
+
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_LINK);
+
+ lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk;
+ dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+
+ ASSERT(is_external_pid(result));
+ dep = external_pid_dist_entry(result);
+
+ if (dep != erts_this_dist_entry && dist->nodename == dep->sysname) {
+ ErtsDSigSendContext ctx;
+ int code = erts_dsig_prepare(&ctx, dep, c_p, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (dist->connection_id == ctx.connection_id) {
+ code = erts_dsig_send_exit_tt(&ctx,
+ c_p->common.id,
+ result,
+ am_abandoned,
+ SEQ_TRACE_TOKEN(c_p));
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ /* delete monitor structure... */
+ adjust_monitor = 0;
+ /* drop message... */
+ convert_to_message = 0;
+ }
+ else {
+ /* Success... */
+ ASSERT(is_external_pid(result));
+
+ if (omon->flags & ERTS_ML_FLG_SPAWN_NO_SMSG)
+ convert_to_message = 0;
+
+ if (datap->link) {
+ cnt++;
+ erts_link_tree_insert(&ERTS_P_LINKS(c_p), datap->link);
+ if (tracing->procs)
+ linking(c_p, result);
+ }
+
+ adjust_monitor = !!(omon->flags & ERTS_ML_FLG_SPAWN_MONITOR);
+ if (adjust_monitor) {
+ /*
+ * Insert the actual pid of spawned process
+ * in origin part of monitor...
+ */
+ ErlOffHeap oh;
+ ErtsMonitorDataExtended *mdep;
+ Eterm *hp;
+ mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon);
+ hp = &(mdep)->heap[0];
+ omon->flags &= ~ERTS_ML_FLGS_SPAWN;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = mdep->uptr.ohhp;
+ omon->other.item = copy_struct(result,
+ EXTERNAL_THING_HEAD_SIZE + 1,
+ &hp, &oh);
+ mdep->uptr.ohhp = oh.first;
+ cnt += 2;
+ }
+ }
+
+ if (!adjust_monitor) {
+ /*
+ * Delete monitor; either spawn error
+ * or no monitor requested...
+ */
+ ErtsMonitorData *mdp = erts_monitor_to_data(omon);
+
+ omon->flags &= ~ERTS_ML_FLGS_SPAWN;
+
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon);
+
+ if (erts_monitor_dist_delete(&mdp->target))
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ cnt += 2;
+ }
+
+ if (convert_to_message) {
+ convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig);
+ if (tag_hfrag) {
+ /* Save heap fragment of tag in message... */
+ tag_hfrag->next = sig->hfrag.next;
+ sig->hfrag.next = tag_hfrag;
+ }
+ erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ else {
+ remove_nm_sig(c_p, sig, next_nm_sig);
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;;
+ erts_cleanup_messages(sig);
+ if (tag_hfrag) {
+ tag_hfrag->next = NULL;
+ free_message_buffer(tag_hfrag);
+ }
+ }
+ return cnt;
+}
+
+static int
+handle_dist_spawn_reply_exiting(Process *c_p,
+ ErtsMessage *sig,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason)
+{
+ ErtsDistSpawnReplySigData *datap = get_dist_spawn_reply_data(sig);
+ Eterm result = datap->result;
+ Eterm msg = datap->message;
+ ErtsMonitorData *mdp;
+ ErtsMonitor *omon;
+ int cnt = 1;
+
+ ASSERT(is_atom(result) || is_external_pid(result));
+ ASSERT(is_atom(result) || size_object(result) == EXTERNAL_THING_HEAD_SIZE + 1);
+
+ omon = erts_monitor_tree_lookup(*pend_spawn_mon_pp, datap->ref);
+ if (!omon) {
+ /* May happen when connection concurrently close... */
+ ErtsLink *lnk = datap->link;
+ if (lnk) {
+ ErtsLinkData *ldp;
+ ErtsLink *dlnk = erts_link_to_other(lnk, &ldp);
+ if (erts_link_dist_delete(dlnk))
+ erts_link_release_both(ldp);
+ else
+ erts_link_release(lnk);
+ }
+ cnt++;
+ }
+ else {
+ ASSERT(omon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+ ASSERT(!datap->link || is_external_pid(result));
+
+ erts_monitor_tree_delete(pend_spawn_mon_pp, omon);
+ mdp = erts_monitor_to_data(omon);
+
+ if (!erts_dist_pend_spawn_exit_delete(&mdp->target))
+ mdp = NULL; /* Connection closed/closing... */
+ cnt++;
+
+ if (is_external_pid(result)) {
+ if ((omon->flags & ERTS_ML_FLG_SPAWN_MONITOR) && mdp) {
+ ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp;
+ erts_proc_exit_dist_demonitor(c_p,
+ external_pid_dist_entry(result),
+ mdep->dist->connection_id,
+ datap->ref,
+ result);
+ cnt++;
+ }
+ ASSERT(!datap->link || (omon->flags & ERTS_ML_FLG_SPAWN_LINK));
+ ASSERT(!(omon->flags & ERTS_ML_FLG_SPAWN_LINK) || datap->link);
+
+ if (datap->link) {
+ /* This link exit *should* have actual reason... */
+ ErtsProcExitContext pectxt = {c_p, reason};
+ /* unless operation has been abandoned... */
+ if (omon->flags & ERTS_ML_FLG_SPAWN_ABANDONED)
+ pectxt.reason = am_abandoned;
+ erts_proc_exit_handle_link(datap->link, (void *) &pectxt, -1);
+ cnt++;
+ }
+ }
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(omon);
+ cnt++;
+ }
+ sig->data.attached = ERTS_MSG_COMBINED_HFRAG;
+ ERL_MESSAGE_TERM(sig) = msg;
+ sig->next = NULL;
+ erts_cleanup_messages(sig);
+ cnt++;
+ return cnt;
+}
+
/*
* Called in order to handle incoming signals.
*/
@@ -3266,7 +3760,7 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
omon = &mdp->origin;
erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
omon);
- cnt += convert_to_down_message(c_p, sig, mdp,
+ cnt += convert_to_down_message(c_p, sig, mdp, &omon,
type, next_nm_sig);
}
break;
@@ -3282,13 +3776,13 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
xsigd->u.ref);
if (omon) {
ASSERT(erts_monitor_is_origin(omon));
+ erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
+ omon);
if (omon->type == ERTS_MON_TYPE_DIST_PROC) {
mdp = erts_monitor_to_data(omon);
if (erts_monitor_dist_delete(&mdp->target))
tmon = &mdp->target;
}
- erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p),
- omon);
cnt += convert_prepared_down_message(c_p, sig,
xsigd->message,
next_nm_sig);
@@ -3585,6 +4079,13 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
break;
}
+
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY: {
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ cnt += handle_dist_spawn_reply(c_p, &tracing, sig, next_nm_sig);
+ ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig);
+ break;
+ }
default:
ERTS_INTERNAL_ERROR("Unknown signal");
@@ -3780,7 +4281,9 @@ stretch_limit(Process *c_p, ErtsSigRecvTracing *tp,
int
-erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason)
{
int cnt;
Sint limit;
@@ -3862,7 +4365,8 @@ erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
break;
case ERTS_SIG_Q_OP_MONITOR: {
- ErtsProcExitContext pectxt = {c_p, am_noproc, NULL, NULL, NIL};
+ ErtsProcExitContext pectxt = {c_p, am_noproc, NULL, NULL,
+ NULL, NULL, NIL, 0};
erts_proc_exit_handle_monitor((ErtsMonitor *) sig,
(void *) &pectxt, -1);
cnt += 4;
@@ -3914,6 +4418,13 @@ erts_proc_sig_handle_exit(Process *c_p, Sint *redsp)
break;
}
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY: {
+ cnt += handle_dist_spawn_reply_exiting(c_p, sig,
+ pend_spawn_mon_pp,
+ reason);
+ break;
+ }
+
case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE:
destroy_trace_info((ErtsSigTraceInfo *) sig);
break;
@@ -3986,6 +4497,7 @@ clear_seq_trace_token(ErtsMessage *sig)
break;
case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY:
ERTS_CLEAR_SEQ_TOKEN(sig);
break;
@@ -4060,6 +4572,7 @@ erts_proc_sig_signal_size(ErtsSignal *sig)
case ERTS_SIG_Q_OP_SYNC_SUSPEND:
case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG:
case ERTS_SIG_Q_OP_IS_ALIVE:
+ case ERTS_SIG_Q_OP_DIST_SPAWN_REPLY:
size = ((ErtsMessage *) sig)->hfrag.alloc_size;
size *= sizeof(Eterm);
size += sizeof(ErtsMessage) - sizeof(Eterm);
@@ -4328,6 +4841,13 @@ getting_linked(Process *c_p, Eterm linker)
am_getting_linked, linker);
}
+static void
+linking(Process *c_p, Eterm to)
+{
+ trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p,
+ am_link, to);
+}
+
static ERTS_INLINE void
handle_message_enqueued_tracing(Process *c_p,
ErtsSigRecvTracing *tracing,
diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h
index 2789179b34..b0a5d0dac3 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.h
+++ b/erts/emulator/beam/erl_proc_sig_queue.h
@@ -706,6 +706,14 @@ erts_proc_sig_send_rpc_request(Process *c_p,
Eterm (*func)(Process *, void *, int *, ErlHeapFragment **),
void *arg);
+int
+erts_proc_sig_send_dist_spawn_reply(Eterm node,
+ Eterm ref,
+ Eterm to,
+ ErtsLink *lnk,
+ Eterm result,
+ Eterm token);
+
/*
* End of send operations of currently supported process signals.
*/
@@ -787,7 +795,9 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
* queue.
*/
int
-erts_proc_sig_handle_exit(Process *c_p, Sint *redsp);
+erts_proc_sig_handle_exit(Process *c_p, Sint *redsp,
+ ErtsMonitor **pend_spawn_mon_pp,
+ Eterm reason);
/**
*
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index e9ed4a7407..16d8230533 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -44,7 +44,6 @@
#include "erl_thr_queue.h"
#include "erl_async.h"
#include "dtrace-wrapper.h"
-#include "lttng-wrapper.h"
#include "erl_ptab.h"
#include "erl_bif_unique.h"
#define ERTS_WANT_TIMER_WHEEL_API
@@ -708,10 +707,10 @@ erts_pre_init_process(void)
erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
= ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].get_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].set_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS;
erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].get_locks
= ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS;
@@ -6495,8 +6494,8 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC
|ERTS_PSFLG_DIRTY_CPU_PROC))
- || (BeamIsOpCode(*p->i, op_call_nif)
- || BeamIsOpCode(*p->i, op_apply_bif)));
+ || (BeamIsOpCode(*p->i, op_call_nif_WWW)
+ || BeamIsOpCode(*p->i, op_call_bif_W)));
a = state;
@@ -9554,7 +9553,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
erts_runq_unlock(rq);
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO);
- LTTNG2(scheduler_poll, esdp->no, 1);
erts_check_io(esdp->ssi->psi, ERTS_POLL_NO_TIMEOUT);
ERTS_MSACC_POP_STATE_M();
@@ -11100,8 +11098,13 @@ erts_set_gc_state(Process *c_p, int enable)
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
if (!enable) {
- c_p->flags |= F_DISABLE_GC;
- return 0;
+ /* Strictly speaking it's not illegal to disable the GC when it's
+ * already disabled, but we risk enabling the GC prematurely if (for
+ * example) a BIF were to blindly disable it when trapping and then
+ * re-enable it before returning its result. */
+ ASSERT(!(c_p->flags & F_DISABLE_GC));
+ c_p->flags |= F_DISABLE_GC;
+ return 0;
}
c_p->flags &= ~F_DISABLE_GC;
@@ -11482,6 +11485,180 @@ alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state)
return p;
}
+int
+erts_parse_spawn_opts(ErlSpawnOpts *sop, Eterm opts_list, Eterm *tag,
+ int message_opt)
+{
+ /*
+ * Returns:
+ * - 0 on success
+ * - <0 on badopt
+ * - >0 on badarg (not prober list)
+ */
+ int result = 0;
+ Eterm ap = opts_list;
+
+ if (tag)
+ *tag = am_spawn_reply;
+ /*
+ * Store default values for options.
+ */
+ sop->multi_set = 0;
+ sop->flags = erts_default_spo_flags;
+ sop->min_heap_size = H_MIN_SIZE;
+ sop->min_vheap_size = BIN_VH_MIN_SIZE;
+ sop->max_heap_size = H_MAX_SIZE;
+ sop->max_heap_flags = H_MAX_FLAGS;
+ sop->priority = PRIORITY_NORMAL;
+ sop->max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs);
+ sop->scheduler = 0;
+
+ /*
+ * Walk through the option list.
+ */
+ while (is_list(ap)) {
+ Eterm arg = CAR(list_val(ap));
+ ap = CDR(list_val(ap));
+ if (arg == am_link) {
+ if (sop->flags & SPO_LINK)
+ sop->multi_set = !0;
+ sop->flags |= SPO_LINK;
+ } else if (arg == am_monitor) {
+ if (sop->flags & SPO_MONITOR)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MONITOR;
+ } else if (is_tuple(arg)) {
+ Eterm* tp2 = tuple_val(arg);
+ Eterm val;
+ if (*tp2 != make_arityval(2)) {
+ result = -1;
+ continue;
+ }
+ arg = tp2[1];
+ val = tp2[2];
+ if (arg == am_priority) {
+ if (sop->flags & SPO_PRIORITY)
+ sop->multi_set = !0;
+ sop->flags |= SPO_PRIORITY;
+ if (val == am_max)
+ sop->priority = PRIORITY_MAX;
+ else if (val == am_high)
+ sop->priority = PRIORITY_HIGH;
+ else if (val == am_normal)
+ sop->priority = PRIORITY_NORMAL;
+ else if (val == am_low)
+ sop->priority = PRIORITY_LOW;
+ else
+ result = -1;
+ } else if (arg == am_message_queue_data) {
+ if (sop->flags & (SPO_OFF_HEAP_MSGQ|SPO_ON_HEAP_MSGQ))
+ sop->multi_set = !0;
+ switch (val) {
+ case am_on_heap:
+ sop->flags &= ~SPO_OFF_HEAP_MSGQ;
+ sop->flags |= SPO_ON_HEAP_MSGQ;
+ break;
+ case am_off_heap:
+ sop->flags &= ~SPO_ON_HEAP_MSGQ;
+ sop->flags |= SPO_OFF_HEAP_MSGQ;
+ break;
+ default:
+ result = -1;
+ break;
+ }
+ } else if (arg == am_min_heap_size && is_small(val)) {
+ Sint min_heap_size = signed_val(val);
+ if (sop->flags & SPO_MIN_HEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MIN_HEAP_SIZE;
+ if (min_heap_size < 0) {
+ result = -1;
+ } else if (min_heap_size < H_MIN_SIZE) {
+ sop->min_heap_size = H_MIN_SIZE;
+ } else {
+ sop->min_heap_size = erts_next_heap_size(min_heap_size, 0);
+ }
+ } else if (arg == am_max_heap_size) {
+ if (sop->flags & SPO_MAX_HEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MAX_HEAP_SIZE;
+ if (!erts_max_heap_size(val, &sop->max_heap_size, &sop->max_heap_flags))
+ result = -1;
+ } else if (arg == am_min_bin_vheap_size && is_small(val)) {
+ Sint min_vheap_size = signed_val(val);
+ if (sop->flags & SPO_MIN_VHEAP_SIZE)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MIN_VHEAP_SIZE;
+ if (min_vheap_size < 0) {
+ result = -1;
+ } else if (min_vheap_size < BIN_VH_MIN_SIZE) {
+ sop->min_vheap_size = BIN_VH_MIN_SIZE;
+ } else {
+ sop->min_vheap_size = erts_next_heap_size(min_vheap_size, 0);
+ }
+ } else if (arg == am_fullsweep_after && is_small(val)) {
+ Sint max_gen_gcs = signed_val(val);
+ if (sop->flags & SPO_MAX_GEN_GCS)
+ sop->multi_set = !0;
+ sop->flags |= SPO_MAX_GEN_GCS;
+ if (max_gen_gcs < 0) {
+ result = -1;
+ } else {
+ sop->max_gen_gcs = max_gen_gcs;
+ }
+ } else if (arg == am_scheduler && is_small(val)) {
+ Sint scheduler = signed_val(val);
+ if (sop->flags & SPO_SCHEDULER)
+ sop->multi_set = !0;
+ sop->flags |= SPO_SCHEDULER;
+ if (scheduler < 0 || erts_no_schedulers < scheduler)
+ result = -1;
+ else
+ sop->scheduler = (int) scheduler;
+ } else if (arg == am_reply) {
+ if (!message_opt)
+ result = -1;
+ else if (val == am_error_only) {
+ sop->flags |= SPO_NO_SMSG;
+ sop->flags &= ~SPO_NO_EMSG;
+ }
+ else if (val == am_success_only) {
+ sop->flags &= ~SPO_NO_SMSG;
+ sop->flags |= SPO_NO_EMSG;
+ }
+ else if (val == am_no) {
+ sop->flags |= SPO_NO_SMSG;
+ sop->flags |= SPO_NO_EMSG;
+ }
+ else if (val == am_yes) {
+ sop->flags &= ~SPO_NO_SMSG;
+ sop->flags &= ~SPO_NO_EMSG;
+ }
+ else
+ result = -1;
+ } else if (arg == am_reply_tag) {
+ if (!tag)
+ result = -1;
+ else
+ *tag = val;
+ } else {
+ result = -1;
+ }
+ } else {
+ result = -1;
+ }
+ }
+ if (is_not_nil(ap)) {
+ return 1;
+ }
+
+ if (sop->max_heap_size != 0 && sop->max_heap_size < sop->min_heap_size) {
+ result = -1;
+ }
+
+ return result;
+}
+
Eterm
erl_create_process(Process* parent, /* Parent of process (default group leader). */
Eterm mod, /* Tagged atom for module. */
@@ -11501,6 +11678,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_aint32_t state = 0;
erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL;
ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+ Eterm node_token_heap[6];
+ Eterm group_leader, parent_id, spawn_ref, token;
#ifdef SHCOPY_SPAWN
erts_shcopy_t info;
INITIALIZE_SHCOPY(info);
@@ -11508,8 +11687,25 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_literal_area_t litarea;
INITIALIZE_LITERAL_PURGE_AREA(litarea);
#endif
-
- erts_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
+
+ if (!parent) {
+ token = so->token;
+ group_leader = so->group_leader;
+ parent_id = so->parent_id;
+ spawn_ref = so->mref;
+ }
+ else {
+ token = SEQ_TRACE_TOKEN(parent);
+ erts_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
+ group_leader = parent->group_leader;
+ parent_id = parent->common.id;
+ if (so->flags & (SPO_MONITOR | SPO_ASYNC))
+ spawn_ref = so->mref = erts_make_ref(parent);
+ else if (have_seqtrace(token))
+ spawn_ref = erts_make_ref(parent);
+ else
+ spawn_ref = THE_NON_VALUE;
+ }
/*
* Check for errors.
@@ -11520,6 +11716,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
+ if (arity > MAX_SMALL) {
+ so->error_code = SYSTEM_LIMIT;
+ goto error;
+ }
+
if (so->flags & SPO_USE_ARGS) {
if (so->scheduler) {
int ix = so->scheduler-1;
@@ -11544,14 +11745,21 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ASSERT((qs_flags & FS_ON_HEAP_MSGQ) || (qs_flags & FS_OFF_HEAP_MSGQ));
- if (!rq)
- rq = erts_get_runq_proc(parent, NULL);
+ if (!rq) {
+ if (parent)
+ rq = erts_get_runq_proc(parent, NULL);
+ else {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp->type == ERTS_SCHED_NORMAL);
+ rq = esdp->run_queue;
+ }
+ }
+ ASSERT(rq);
p = alloc_process(rq, bound, state); /* All proc locks are locked by this thread
on success */
if (!p) {
- erts_send_error_to_logger_str(parent->group_leader,
- "Too many processes\n");
+ erts_send_error_to_logger_str(group_leader, "Too many processes\n");
so->error_code = SYSTEM_LIMIT;
goto error;
}
@@ -11561,7 +11769,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#else
arg_size = size_object_litopt(args, &litarea);
#endif
- heap_need = arg_size;
+ heap_need = arg_size + 1; /* Reserve place for continuation pointer */
p->flags = flags;
p->sig_qs.flags = qs_flags;
@@ -11585,18 +11793,14 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->schedule_count = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
- p->u.initial.module = mod;
- p->u.initial.function = func;
- p->u.initial.arity = (Uint) arity;
-
/*
* Must initialize binary lists here before copying binaries to process.
*/
p->off_heap.first = NULL;
p->off_heap.overhead = 0;
- heap_need +=
- IS_CONST(parent->group_leader) ? 0 : NC_HEAP_SIZE(parent->group_leader);
+ if (is_not_immed(group_leader))
+ heap_need += NC_HEAP_SIZE(group_leader);
if (heap_need < p->min_heap_size) {
sz = heap_need = p->min_heap_size;
@@ -11611,7 +11815,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->old_hend = p->old_htop = p->old_heap = NULL;
p->high_water = p->heap;
p->gen_gcs = 0;
- p->stop = p->hend = p->heap + sz;
+ p->hend = p->heap + sz;
+ p->stop = p->hend - 1; /* Reserve place for continuation pointer */
p->htop = p->heap;
p->heap_sz = sz;
p->abandoned_heap = NULL;
@@ -11629,7 +11834,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->current = &p->u.initial;
p->i = (BeamInstr *) beam_apply;
- p->cp = (BeamInstr *) beam_apply+1;
+ p->stop[0] = make_cp(beam_apply + 1);
p->arg_reg = p->def_arg_reg;
p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]);
@@ -11655,16 +11860,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ERTS_P_MONITORS(p) = NULL;
ERTS_P_LT_MONITORS(p) = NULL;
- ASSERT(is_pid(parent->group_leader));
+ ASSERT(is_pid(group_leader));
- if (parent->group_leader == ERTS_INVALID_PID)
+ if (group_leader == ERTS_INVALID_PID)
p->group_leader = p->common.id;
else {
/* Needs to be done after the heap has been set up */
p->group_leader =
- IS_CONST(parent->group_leader)
- ? parent->group_leader
- : STORE_NC(&p->htop, &p->off_heap, parent->group_leader);
+ IS_CONST(group_leader)
+ ? group_leader
+ : STORE_NC(&p->htop, &p->off_heap, group_leader);
}
erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p));
@@ -11692,14 +11897,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->mbuf_sz = 0;
erts_atomic_init_nob(&p->psd, (erts_aint_t) NULL);
p->dictionary = NULL;
- p->seq_trace_lastcnt = 0;
- p->seq_trace_clock = 0;
- SEQ_TRACE_TOKEN(p) = NIL;
#ifdef USE_VM_PROBES
DT_UTAG(p) = NIL;
DT_UTAG_FLAGS(p) = 0;
#endif
- p->parent = (parent->common.id == ERTS_INVALID_PID
+ p->parent = (!parent || parent->common.id == ERTS_INVALID_PID
? NIL
: parent->common.id);
@@ -11715,7 +11917,107 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->fp_exception = 0;
#endif
- if (IS_TRACED(parent)) {
+ /* seq_trace is handled before regular tracing as the latter may touch the
+ * trace token. */
+ if (!have_seqtrace(token)) {
+ SEQ_TRACE_TOKEN(p) = NIL;
+ p->seq_trace_lastcnt = 0;
+ p->seq_trace_clock = 0;
+ }
+ else {
+ Eterm tmp_heap[9]; /* 8-tuple */
+ Eterm seq_msg;
+ Uint token_sz;
+ Eterm *hp;
+
+ if (parent) {
+ seq_trace_update_serial(parent);
+ token = SEQ_TRACE_TOKEN(parent);
+ ASSERT(SEQ_TRACE_T_ARITY(token) == 5);
+ sys_memcpy(&node_token_heap[0],
+ (void *) tuple_val(token),
+ sizeof(Eterm)*6);
+ token = make_tuple(&node_token_heap[0]);
+ }
+
+ ASSERT(SEQ_TRACE_T_ARITY(token) == 5);
+ ASSERT(is_immed(SEQ_TRACE_T_FLAGS(token)));
+ ASSERT(is_immed(SEQ_TRACE_T_SERIAL(token)));
+ ASSERT(is_immed(SEQ_TRACE_T_LASTCNT(token)));
+
+ token_sz = size_object(token);
+
+ hp = HAlloc(p, token_sz);
+ SEQ_TRACE_TOKEN(p) = copy_struct(token, token_sz, &hp, &MSO(p));
+
+ ASSERT((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) ==
+ (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE));
+
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+ if (parent) {
+ /* Simulate spawn_request message... */
+ Eterm tmp_heap2[4];
+ Eterm mfa;
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ mfa = TUPLE3(&tmp_heap2[0], mod, func, make_small(arity)) ;
+ seq_msg = TUPLE8(&tmp_heap[0], am_spawn_request,
+ spawn_ref, parent_id, group_leader,
+ mfa, so->opts, so->tag, args);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND,
+ p->common.id, parent);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ p->common.id, parent);
+
+ /* The counters behave the same way on spawning as they do on messages;
+ * we don't inherit our parent's lastcnt. */
+ p->seq_trace_clock = unsigned_val(SEQ_TRACE_T_SERIAL(token));
+ p->seq_trace_lastcnt = p->seq_trace_clock;
+
+ }
+ else {
+ /*
+ * The spawn request is presented as two messages
+ * in dist case. It is sent as one signal over the
+ * distribution with the argument list as payload.
+ * The payload will be delivered as an ordinary
+ * message of its own, as the first message to the
+ * newly created process (in order to decode it in
+ * in the newly created process). For more info see
+ * erts_internal:dist_spawn_init() in erts_interal.erl.
+ * We expose these as two messages when seq-tracing
+ * in order not having to decode the argument list
+ * here. The remote node has passed a token with
+ * serial bumped twice, i.e., the first message should
+ * use a serial of one less than in the actual token;
+ * adjust serial and then restore it for use with
+ * the argument list message...
+ */
+ Eterm serial;
+ Uint serial_num;
+ ASSERT(eq(SEQ_TRACE_T_SENDER(token), parent_id));
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num--;
+ SEQ_TRACE_T_SERIAL(token) = make_small(serial_num);
+
+ seq_msg = TUPLE6(&tmp_heap[0], am_spawn_request,
+ spawn_ref, parent_id, group_leader,
+ so->mfa, so->opts);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ p->common.id, p);
+
+ /* as on receive... */
+ p->seq_trace_clock = serial_num;
+ p->seq_trace_lastcnt = serial_num;
+
+ /* Restore serial for the argument list message... */
+ SEQ_TRACE_T_SERIAL(token) = serial;
+ }
+ }
+
+ if (parent && IS_TRACED(parent)) {
if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS) {
ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
erts_tracer_replace(&p->common, ERTS_TRACER(parent));
@@ -11736,9 +12038,14 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
}
}
if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) {
- locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ /* The locks may already be released if seq_trace is enabled as
+ * well. */
+ if ((locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE))
+ == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE)) {
+ locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
trace_proc_spawn(parent, am_spawn, p->common.id, mod, func, args);
if (so->flags & SPO_LINK)
trace_proc(parent, locks, parent, am_link, p->common.id);
@@ -11751,49 +12058,187 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
/* This happens when parent was not traced, but child is */
locks &= ~(ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
- erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ if (parent)
+ erts_proc_unlock(parent, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
}
- trace_proc_spawn(p, am_spawned, parent->common.id, mod, func, args);
+ trace_proc_spawn(p, am_spawned, parent_id, mod, func, args);
if (so->flags & SPO_LINK)
- trace_proc(p, locks, p, am_getting_linked, parent->common.id);
+ trace_proc(p, locks, p, am_getting_linked, parent_id);
}
/*
* Check if this process should be initially linked to its parent.
*/
- if (so->flags & SPO_LINK) {
- ErtsLink *lnk;
- ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
- parent->common.id,
- p->common.id);
- lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
- if (lnk) {
- /*
- * This should more or less never happen, but could
- * potentially happen if pid:s wrap...
- */
- erts_link_release(lnk);
+ if (parent) {
+ /* Node local spawn... */
+
+ if (so->flags & SPO_LINK) {
+ ErtsLink *lnk;
+ ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC,
+ parent->common.id,
+ p->common.id);
+ lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a);
+ if (lnk) {
+ /*
+ * This should more or less never happen, but could
+ * potentially happen if pid:s wrap...
+ */
+ erts_link_release(lnk);
+ }
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
}
- erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
- }
- /*
- * Test whether this process should be initially monitored by its parent.
- */
- if (so->flags & SPO_MONITOR) {
- Eterm mref = erts_make_ref(parent);
- ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
- mref,
- parent->common.id,
- p->common.id,
- NIL);
- erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
- erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
- so->mref = mref;
+ /*
+ * Test whether this process should be initially monitored by its parent.
+ */
+ if (so->flags & SPO_MONITOR) {
+ ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC,
+ spawn_ref,
+ parent->common.id,
+ p->common.id,
+ NIL);
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin);
+ erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target);
+ }
+
+ ASSERT(locks & ERTS_PROC_LOCK_MSGQ);
+
+ if (so->flags & SPO_ASYNC) { /* spawn_request() */
+ Eterm *tp;
+
+ ASSERT(is_tuple_arity(so->mfa, 3));
+ tp = tuple_val(so->mfa);
+ ASSERT(is_atom(tp[1]));
+ ASSERT(is_atom(tp[2]));
+ ASSERT(is_small(tp[3]));
+ p->u.initial.module = tp[1];
+ p->u.initial.function = tp[2];
+ p->u.initial.arity = (Uint) unsigned_val(tp[3]);
+
+ ASSERT(is_value(so->tag));
+ if (have_seqtrace(token)) {
+ seq_trace_update_serial(p);
+ token = SEQ_TRACE_TOKEN(p);
+ }
+
+ if (!(so->flags & SPO_NO_SMSG)) {
+ /*
+ * Ensure spawn reply success message reach parent before
+ * any down or exit signals from child...
+ */
+ erts_send_local_spawn_reply(parent, locks, p,
+ so->tag, spawn_ref,
+ p->common.id, token);
+ }
+ }
+ else { /* synchronous spawn */
+
+ p->u.initial.module = mod;
+ p->u.initial.function = func;
+ p->u.initial.arity = (Uint) arity;
+
+ if (have_seqtrace(token)) {
+ /* Simulate spawn reply message... */
+ Eterm tmp_heap[5];
+ Eterm seq_msg;
+ Uint serial;
+
+ seq_trace_update_serial(p);
+ token = SEQ_TRACE_TOKEN(p);
+ serial = SEQ_TRACE_T_SERIAL(token);
+ seq_msg = TUPLE4(&tmp_heap[0], so->tag, spawn_ref,
+ am_ok, p->common.id);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND,
+ parent_id, parent);
+
+ /* Update parent as if receive... */
+ parent->seq_trace_lastcnt = serial;
+ parent->seq_trace_clock = serial;
+ seq_trace_output(token, seq_msg, SEQ_TRACE_RECEIVE,
+ parent_id, parent);
+ }
+
+ }
+
+ erts_proc_unlock(p, locks);
+ erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
}
+ else {
+ /* Distributed spawn */
+ ErtsDSigSendContext ctx;
+ int code;
+ Eterm *tp;
+
+ ASSERT(is_tuple_arity(so->mfa, 3));
+ tp = tuple_val(so->mfa);
+ ASSERT(is_atom(tp[1]));
+ ASSERT(is_atom(tp[2]));
+ ASSERT(is_small(tp[3]));
+ p->u.initial.module = tp[1];
+ p->u.initial.function = tp[2];
+ p->u.initial.arity = (Uint) unsigned_val(tp[3]);
+
+ ASSERT(locks & ERTS_PROC_LOCK_MSGQ);
+ /*
+ * Pass the (on external format) encoded argument list as
+ * *first* message to the process. Note that this message
+ * *must* be first in the message queue of the newly
+ * spawned process!
+ */
+ erts_queue_dist_message(p, locks, so->edep, so->ede_hfrag,
+ token, parent_id);
- erts_proc_unlock(p, locks);
+ erts_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+
+ if (so->flags & SPO_LINK) {
+ ErtsLinkData *ldp;
+ ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC,
+ parent_id, p->common.id);
+ code = erts_link_dist_insert(&ldp->a, so->dist_entry->mld);
+ ASSERT(code);
+ erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b);
+ }
+
+ if (so->flags & SPO_MONITOR) {
+ ErtsMonitorData *mdp;
+ mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC,
+ spawn_ref, parent_id,
+ p->common.id, NIL);
+ code = erts_monitor_dist_insert(&mdp->origin, so->dist_entry->mld);
+ ASSERT(code); (void)code;
+ erts_monitor_tree_insert(&ERTS_P_MONITORS(p), &mdp->target);
+ }
+
+ if (have_seqtrace(token)) {
+ Eterm tmp_heap[5];
+ Eterm seq_msg;
+ seq_trace_update_serial(p);
+ seq_msg = TUPLE4(&tmp_heap[0], so->tag,
+ spawn_ref, am_ok, p->common.id);
+ token = SEQ_TRACE_TOKEN(p);
+ seq_trace_output(token, seq_msg, SEQ_TRACE_SEND, parent_id, p);
+ }
+
+ code = erts_dsig_prepare(&ctx, so->dist_entry, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ if (code == ERTS_DSIG_PREP_CONNECTED) {
+ int dsflags = 0;
+ if (so->flags & SPO_LINK)
+ dsflags |= ERTS_DIST_SPAWN_FLAG_LINK;
+ if (so->flags & SPO_MONITOR)
+ dsflags |= ERTS_DIST_SPAWN_FLAG_MONITOR;
+ code = erts_dsig_send_spawn_reply(&ctx, spawn_ref,
+ parent_id,
+ make_small(dsflags),
+ p->common.id,
+ token);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+
+ erts_proc_unlock(p, locks & ERTS_PROC_LOCK_MAIN);
+ }
res = p->common.id;
@@ -11801,8 +12246,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
* Schedule process for execution.
*/
- erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
-
schedule_process(p, state, 0);
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id));
@@ -11821,11 +12264,65 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
error:
- erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
+ if (parent)
+ erts_proc_unlock(parent, locks & ERTS_PROC_LOCKS_ALL_MINOR);
return res;
}
+void
+erts_send_local_spawn_reply(Process *parent, ErtsProcLocks parent_locks,
+ Process *child, Eterm tag, Eterm ref,
+ Eterm result, Eterm token)
+{
+ ErtsMessage *mp;
+ ErlOffHeap *ohp;
+ Eterm *hp;
+ Eterm msg, ref_copy, ref_sz, tag_copy, tag_sz,
+ token_copy, token_sz, type;
+ ErtsProcLocks locks = parent_locks;
+
+ type = child ? am_ok : am_error;
+
+ if (have_seqtrace(token) && child)
+ token_sz = size_object(token);
+ else {
+ token_copy = token = NIL;
+ token_sz = 0;
+ }
+
+ ref_sz = size_object(ref);
+ tag_sz = is_immed(tag) ? 0 : size_object(tag);
+ mp = erts_alloc_message_heap(parent, &locks,
+ 5 + tag_sz + ref_sz + token_sz,
+ &hp, &ohp);
+ ref_copy = copy_struct(ref, ref_sz, &hp, ohp);
+ tag_copy = is_immed(tag) ? tag : copy_struct(tag, tag_sz, &hp, ohp);
+ msg = TUPLE4(hp, tag_copy, ref_copy, type, result);
+ hp += 5;
+
+ if (have_seqtrace(token)) {
+ token_copy = copy_struct(token, token_sz, &hp, ohp);
+ seq_trace_output(token_copy, msg, SEQ_TRACE_SEND,
+ parent->common.id, parent);
+ }
+
+ if (!child) { /* error reply */
+ ASSERT(is_atom(result));
+ erts_queue_message(parent, parent_locks, mp, msg,
+ erts_this_dist_entry->sysname);
+ }
+ else { /* success reply */
+ ASSERT(child->common.id == result);
+ ERL_MESSAGE_TOKEN(mp) = token_copy;
+ erts_queue_proc_message(child, parent, parent_locks, mp, msg);
+ }
+
+ ASSERT((parent_locks & locks) == parent_locks);
+ if (locks != parent_locks)
+ erts_proc_unlock(parent, locks & ~parent_locks);
+}
+
/*
* Initiates a pseudo process that can be used
* for arithmetic BIFs.
@@ -11902,7 +12399,6 @@ void erts_init_empty_process(Process *p)
p->u.initial.function = 0;
p->u.initial.arity = 0;
p->catches = 0;
- p->cp = NULL;
p->i = NULL;
p->current = NULL;
@@ -11980,7 +12476,6 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
- ASSERT(p->cp == NULL);
ASSERT(p->i == NULL);
ASSERT(p->current == NULL);
@@ -12041,7 +12536,7 @@ delete_process(Process* p)
if (pbt)
erts_free(ERTS_ALC_T_BPD, (void *) pbt);
- erts_destroy_nif_export(p);
+ erts_destroy_nfunc(p);
/* Cleanup psd */
@@ -12261,6 +12756,156 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
return reds_consumed;
}
+static int
+proc_exit_handle_pend_spawn_monitors(ErtsMonitor *mon, void *vctxt, Sint reds)
+{
+ ErtsProcExitContext *ctxt = (ErtsProcExitContext *) vctxt;
+ Process *c_p = ctxt->c_p;
+ Eterm reason = ctxt->reason;
+ Eterm item, *hp;
+ Uint item_sz;
+ int code;
+ ErtsDSigSendContext ctx;
+ ErtsMonitorData *mdp;
+ ErtsMonLnkDist *dist;
+ DistEntry *dep;
+ ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
+
+ ASSERT(c_p->flags & F_DISABLE_GC);
+ ASSERT(erts_monitor_is_origin(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->wait_pend_spawn_monitor);
+ ASSERT(!ctxt->yield);
+
+ ASSERT(mon->flags & ERTS_ML_FLG_SPAWN_PENDING);
+
+ mdp = erts_monitor_to_data(mon);
+ dist = ((ErtsMonitorDataExtended *) mdp)->dist;
+
+ if (!(mon->flags & ERTS_ML_FLG_SPAWN_LINK)) {
+ /* Just cleanup... */
+ if (!erts_dist_pend_spawn_exit_delete(&mdp->target)) {
+ mdp = NULL;
+ }
+ goto done;
+ }
+
+ code = erts_dist_pend_spawn_exit_parent_wait(c_p,
+ ERTS_PROC_LOCK_MAIN,
+ mon);
+ if (code == 0) {
+ /* Connection closing; cleanup... */
+ mdp = NULL;
+ goto done;
+ }
+
+ if (code < 0) {
+ /* We got suspended need to wait for spawn-reply... */
+ ctxt->wait_pend_spawn_monitor = mon;
+ ctxt->yield = !0;
+ return reds;
+ }
+
+ ASSERT(is_external_pid(mon->other.item) || is_atom(mon->other.item));
+
+ /* If other.item is an atom the spawn failed... */
+
+ if (is_not_external_pid(mon->other.item))
+ goto done; /* Cleanup */
+
+ if (mon->flags & ERTS_ML_FLG_SPAWN_ABANDONED)
+ reason = am_abandoned;
+
+ /* Send exit signal... */
+ dep = external_pid_dist_entry(mon->other.item);
+
+ code = erts_dsig_prepare(&ctx, dep, c_p, ERTS_PROC_LOCK_MAIN,
+ ERTS_DSP_NO_LOCK, 0, 0, 0);
+
+ ctx.reds = (Sint) (reds * TERM_TO_BINARY_LOOP_FACTOR);
+
+ switch (code) {
+ case ERTS_DSIG_PREP_NOT_ALIVE:
+ case ERTS_DSIG_PREP_NOT_CONNECTED:
+ break;
+ case ERTS_DSIG_PREP_PENDING:
+ case ERTS_DSIG_PREP_CONNECTED:
+ if (dist->connection_id != ctx.connection_id)
+ break;
+ erts_factory_proc_init(&factory, c_p);
+ item_sz = size_object(mon->other.item);
+ hp = erts_produce_heap(&factory, item_sz, 0);
+ item = copy_struct(mon->other.item, item_sz, &hp, factory.off_heap);
+ erts_factory_close(&factory);
+ code = erts_dsig_send_exit_tt(&ctx,
+ c_p->common.id,
+ item,
+ reason,
+ SEQ_TRACE_TOKEN(c_p));
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ switch (code) {
+ case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_CONTINUE:
+ ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_OK:
+ break;
+ case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
+ break;
+ default:
+ ASSERT(! "Invalid dsig send exit result");
+ break;
+ }
+ break;
+ default:
+ ASSERT(! "Invalid dsig prep exit result");
+ break;
+ }
+
+done:
+
+ if (mdp)
+ erts_monitor_release_both(mdp);
+ else
+ erts_monitor_release(mon);
+
+ return reds_consumed;
+}
+
+void
+erts_proc_exit_dist_demonitor(Process *c_p, DistEntry *dep, Uint32 conn_id,
+ Eterm ref, Eterm watched)
+{
+ ErtsDSigSendContext ctx;
+ int code;
+
+ ASSERT(is_internal_ref(ref));
+ ASSERT(is_atom(watched) || is_external_pid(watched));
+
+ code = erts_dsig_prepare(&ctx, dep, NULL, 0,
+ ERTS_DSP_NO_LOCK, 1, 1, 0);
+ switch (code) {
+ case ERTS_DSIG_PREP_CONNECTED:
+ case ERTS_DSIG_PREP_PENDING:
+ if (conn_id == ctx.connection_id) {
+ code = erts_dsig_send_demonitor(&ctx,
+ c_p->common.id,
+ watched,
+ ref);
+ ASSERT(code == ERTS_DSIG_SEND_OK);
+ }
+ default:
+ break;
+ }
+}
+
int
erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
{
@@ -12381,10 +13026,15 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
case ERTS_MON_TYPE_DIST_PROC: {
ErtsMonLnkDist *dist;
DistEntry *dep;
- ErtsDSigSendContext ctx;
- int code;
Eterm watched;
+ if (mon->flags & ERTS_ML_FLG_SPAWN_PENDING) {
+ if (!erts_dist_pend_spawn_exit_parent_setup(mon))
+ break; /* Drop it... */
+ erts_monitor_tree_insert(&ctxt->pend_spawn_monitors, mon);
+ return 1;
+ }
+
mdp = erts_monitor_to_data(mon);
dist = ((ErtsMonitorDataExtended *) mdp)->dist;
ASSERT(dist);
@@ -12398,21 +13048,8 @@ erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
ASSERT(is_external_pid(watched));
dep = external_pid_dist_entry(watched);
}
- code = erts_dsig_prepare(&ctx, dep, NULL, 0,
- ERTS_DSP_NO_LOCK, 1, 1, 0);
- switch (code) {
- case ERTS_DSIG_PREP_CONNECTED:
- case ERTS_DSIG_PREP_PENDING:
- if (dist->connection_id == ctx.connection_id) {
- code = erts_dsig_send_demonitor(&ctx,
- c_p->common.id,
- watched,
- mdp->ref);
- ASSERT(code == ERTS_DSIG_SEND_OK);
- }
- default:
- break;
- }
+ erts_proc_exit_dist_demonitor(c_p, dep, dist->connection_id,
+ mdp->ref, watched);
if (!erts_monitor_dist_delete(&mdp->target))
mdp = NULL;
res = 100;
@@ -12676,6 +13313,7 @@ enum continue_exit_phase {
ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG,
ERTS_CONTINUE_EXIT_DIST_LINKS,
ERTS_CONTINUE_EXIT_DIST_MONITORS,
+ ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS,
ERTS_CONTINUE_EXIT_DONE,
};
@@ -12925,6 +13563,8 @@ restart:
trap_state->pectxt.reason = trap_state->reason;
trap_state->pectxt.dist_links = NULL;
trap_state->pectxt.dist_monitors = NULL;
+ trap_state->pectxt.pend_spawn_monitors = NULL;
+ trap_state->pectxt.wait_pend_spawn_monitor = NULL;
trap_state->pectxt.dist_state = NIL;
trap_state->pectxt.yield = 0;
@@ -12981,8 +13621,11 @@ restart:
case ERTS_CONTINUE_EXIT_HANDLE_PROC_SIG: {
Sint r = reds;
- if (!erts_proc_sig_handle_exit(p, &r))
+ if (!erts_proc_sig_handle_exit(p, &r,
+ &trap_state->pectxt.pend_spawn_monitors,
+ trap_state->reason)) {
goto yield;
+ }
reds -= r;
@@ -13047,6 +13690,31 @@ restart:
if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
+ trap_state->phase = ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS;
+ }
+ case ERTS_CONTINUE_EXIT_DIST_PEND_SPAWN_MONITORS: {
+
+ if (is_not_nil(trap_state->pectxt.dist_state))
+ goto continue_dist_send;
+
+ if (trap_state->pectxt.wait_pend_spawn_monitor) {
+ ErtsMonitor *mon = trap_state->pectxt.wait_pend_spawn_monitor;
+ trap_state->pectxt.wait_pend_spawn_monitor = NULL;
+ reds -= (proc_exit_handle_pend_spawn_monitors(
+ mon, (void *) &trap_state->pectxt, reds));
+ if (reds <= 0 || trap_state->pectxt.yield)
+ goto yield;
+ }
+
+ reds = erts_monitor_tree_foreach_delete_yielding(
+ &trap_state->pectxt.pend_spawn_monitors,
+ proc_exit_handle_pend_spawn_monitors,
+ (void *) &trap_state->pectxt,
+ &trap_state->yield_state,
+ reds);
+ if (reds <= 0 || trap_state->pectxt.yield)
+ goto yield;
+
trap_state->phase = ERTS_CONTINUE_EXIT_DONE;
}
case ERTS_CONTINUE_EXIT_DONE: {
@@ -13266,9 +13934,6 @@ erts_program_counter_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
state = erts_atomic32_read_acqb(&p->state);
if (!(state & (ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
@@ -13545,9 +14210,6 @@ static void print_current_process_info(fmtfn_t to, void *to_arg,
erts_print(to, to_arg, "Current Process Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "Current Process CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
/* Getting this stacktrace can segfault if we are very very
unlucky if called while a process is being garbage collected.
@@ -13579,7 +14241,7 @@ static void print_current_process_info(fmtfn_t to, void *to_arg,
*
* A BIF that calls this should make sure to schedule out to never come back:
* erts_halt(code);
- * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL);
+ * ERTS_BIF_YIELD1(&bif_trap_export[BIF_erlang_halt_1], BIF_P, NIL);
*/
void erts_halt(int code)
{
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index d311122381..f38008004f 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -812,7 +812,7 @@ erts_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_SCHED_ID 2
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
-#define ERTS_PSD_NIF_TRAP_EXPORT 5
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER 5
#define ERTS_PSD_ETS_OWNED_TABLES 6
#define ERTS_PSD_ETS_FIXED_TABLES 7
#define ERTS_PSD_DIST_ENTRY 8
@@ -849,8 +849,8 @@ typedef struct {
#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS ERTS_PROC_LOCK_STATUS
#define ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS ERTS_PROC_LOCK_STATUS
@@ -975,7 +975,6 @@ struct process {
unsigned max_arg_reg; /* Maximum number of argument registers available. */
Eterm def_arg_reg[6]; /* Default array for argument registers. */
- BeamInstr* cp; /* (untagged) Continuation pointer (for threaded code). */
BeamInstr* i; /* Program counter for threaded code. */
Sint catches; /* Number of catches on stack */
Sint fcalls; /*
@@ -1326,12 +1325,46 @@ void erts_check_for_holes(Process* p);
* Possible flags for the flags field in ErlSpawnOpts below.
*/
-#define SPO_LINK 1
-#define SPO_USE_ARGS 2
-#define SPO_MONITOR 4
-#define SPO_SYSTEM_PROC 8
-#define SPO_OFF_HEAP_MSGQ 16
-#define SPO_ON_HEAP_MSGQ 32
+#define SPO_IX_LINK 0
+#define SPO_IX_MONITOR 1
+#define SPO_IX_SYSTEM_PROC 2
+#define SPO_IX_OFF_HEAP_MSGQ 3
+#define SPO_IX_ON_HEAP_MSGQ 4
+#define SPO_IX_MIN_HEAP_SIZE 5
+#define SPO_IX_MIN_VHEAP_SIZE 6
+#define SPO_IX_PRIORITY 7
+#define SPO_IX_MAX_GEN_GCS 8
+#define SPO_IX_MAX_HEAP_SIZE 9
+#define SPO_IX_SCHEDULER 10
+#define SPO_IX_ASYNC 11
+#define SPO_IX_NO_SMSG 12
+#define SPO_IX_NO_EMSG 13
+
+#define SPO_NO_INDICES (SPO_IX_ASYNC+1)
+
+#define SPO_LINK (1 << SPO_IX_LINK)
+#define SPO_MONITOR (1 << SPO_IX_MONITOR)
+#define SPO_SYSTEM_PROC (1 << SPO_IX_SYSTEM_PROC)
+#define SPO_OFF_HEAP_MSGQ (1 << SPO_IX_OFF_HEAP_MSGQ)
+#define SPO_ON_HEAP_MSGQ (1 << SPO_IX_ON_HEAP_MSGQ)
+#define SPO_MIN_HEAP_SIZE (1 << SPO_IX_MIN_HEAP_SIZE)
+#define SPO_MIN_VHEAP_SIZE (1 << SPO_IX_MIN_VHEAP_SIZE)
+#define SPO_PRIORITY (1 << SPO_IX_PRIORITY)
+#define SPO_MAX_GEN_GCS (1 << SPO_IX_MAX_GEN_GCS)
+#define SPO_MAX_HEAP_SIZE (1 << SPO_IX_MAX_HEAP_SIZE)
+#define SPO_SCHEDULER (1 << SPO_IX_SCHEDULER)
+#define SPO_ASYNC (1 << SPO_IX_ASYNC)
+#define SPO_NO_SMSG (1 << SPO_IX_NO_SMSG)
+#define SPO_NO_EMSG (1 << SPO_IX_NO_EMSG)
+
+#define SPO_MAX_FLAG SPO_NO_EMSG
+
+#define SPO_USE_ARGS \
+ (SPO_MIN_HEAP_SIZE \
+ | SPO_PRIORITY \
+ | SPO_MAX_GEN_GCS \
+ | SPO_MAX_HEAP_SIZE \
+ | SPO_SCHEDULER)
extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
@@ -1341,7 +1374,22 @@ extern int ERTS_WRITE_UNLIKELY(erts_default_spo_flags);
typedef struct {
int flags;
int error_code; /* Error code returned from create_process(). */
- Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given). */
+ Eterm mref; /* Monitor ref returned (if SPO_MONITOR was given).
+ (output if local; input if distributed) */
+
+ int multi_set;
+
+ Eterm tag; /* If SPO_ASYNC */
+ Eterm opts; /* Option list for seq-trace... */
+
+ /* Input fields used for distributed spawn only */
+ Eterm parent_id;
+ Eterm group_leader;
+ Eterm mfa;
+ DistEntry *dist_entry;
+ ErtsDistExternal *edep;
+ ErlHeapFragment *ede_hfrag;
+ Eterm token;
/*
* The following items are only initialized if the SPO_USE_ARGS flag is set.
@@ -1354,6 +1402,7 @@ typedef struct {
Uint max_heap_size; /* Maximum heap size in words */
Uint max_heap_flags; /* Maximum heap flags (kill | log) */
int scheduler;
+
} ErlSpawnOpts;
/*
@@ -1541,6 +1590,7 @@ extern int erts_system_profile_ts_type;
#define SEQ_TRACE_SEND (1 << 0)
#define SEQ_TRACE_RECEIVE (1 << 1)
#define SEQ_TRACE_PRINT (1 << 2)
+/* (This three-bit gap contains the timestamp.) */
#define ERTS_SEQ_TRACE_FLAGS_TS_TYPE_SHIFT 3
@@ -1852,6 +1902,12 @@ Eterm erts_bind_schedulers(Process *c_p, Eterm how);
ErtsRunQueue *erts_schedid2runq(Uint);
Process *erts_schedule(ErtsSchedulerData *, Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
+int erts_parse_spawn_opts(ErlSpawnOpts *sop, Eterm opts_list, Eterm *tag,
+ int success_message_opt);
+void
+erts_send_local_spawn_reply(Process *parent, ErtsProcLocks parent_locks,
+ Process *child, Eterm tag, Eterm ref,
+ Eterm result, Eterm token);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
void erts_set_self_exiting(Process *, Eterm);
void erts_do_exit_process(Process*, Eterm);
@@ -1882,12 +1938,17 @@ typedef struct {
Eterm reason;
ErtsLink *dist_links;
ErtsMonitor *dist_monitors;
+ ErtsMonitor *pend_spawn_monitors;
+ ErtsMonitor *wait_pend_spawn_monitor;
Eterm dist_state;
int yield;
} ErtsProcExitContext;
int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds);
int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds);
+void erts_proc_exit_dist_demonitor(Process *c_p, DistEntry *dep, Uint32 conn_id,
+ Eterm ref, Eterm watched);
+
Eterm erts_get_process_priority(erts_aint32_t state);
Eterm erts_set_process_priority(Process *p, Eterm prio);
@@ -1917,6 +1978,7 @@ Uint erts_debug_nbalance(void);
#define ERTS_DEBUG_WAIT_COMPLETED_DEALLOCATIONS (1 << 0)
#define ERTS_DEBUG_WAIT_COMPLETED_TIMER_CANCELLATIONS (1 << 1)
#define ERTS_DEBUG_WAIT_COMPLETED_AUX_WORK (1 << 2)
+#define ERTS_DEBUG_WAIT_COMPLETED_THREAD_PROGRESS (1 << 3)
int erts_debug_wait_completed(Process *c_p, int flags);
@@ -2089,10 +2151,10 @@ erts_psd_set(Process *p, int ix, void *data)
#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \
((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
-#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \
- erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)
-#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \
- erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#define ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(P) \
+ erts_psd_get((P), ERTS_PSD_NFUNC_TRAP_WRAPPER)
+#define ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(P, NTE) \
+ erts_psd_set((P), ERTS_PSD_NFUNC_TRAP_WRAPPER, (void *) (NTE))
#define ERTS_PROC_GET_DIST_ENTRY(P) \
((DistEntry *) erts_psd_get((P), ERTS_PSD_DIST_ENTRY))
@@ -2696,7 +2758,9 @@ ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi);
void erts_aux_thread_poke(void);
ERTS_GLB_INLINE Uint32 erts_sched_local_random_hash_64_to_32_shift(Uint64 key);
ERTS_GLB_INLINE Uint32 erts_sched_local_random(Uint additional_seed);
-
+#ifdef DEBUG
+ERTS_GLB_INLINE float erts_sched_local_random_float(Uint additional_seed);
+#endif
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -2746,6 +2810,20 @@ Uint32 erts_sched_local_random(Uint additional_seed)
return erts_sched_local_random_hash_64_to_32_shift(seed);
}
+#ifdef DEBUG
+
+/*
+ * This function returns a random float between 0.0 and 1.0.
+ */
+ERTS_GLB_INLINE
+float erts_sched_local_random_float(Uint additional_seed)
+{
+ Uint32 rnd = erts_sched_local_random(additional_seed);
+ return (float)(((double)rnd)/((double)ERTS_UINT32_MAX));
+}
+
+#endif /* #ifdef DEBUG */
+
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index cc483a2148..f1f066be46 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -830,7 +830,7 @@ trace_receive(Process* receiver,
}
int
-seq_trace_update_send(Process *p)
+seq_trace_update_serial(Process *p)
{
ErtsTracer seq_tracer = erts_get_system_seq_tracer();
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
@@ -853,6 +853,18 @@ seq_trace_update_send(Process *p)
return 1;
}
+void
+erts_seq_trace_update_node_token(Eterm token)
+{
+ Eterm serial;
+ Uint serial_num;
+ SEQ_TRACE_T_SENDER(token) = erts_this_dist_entry->sysname;
+ serial = SEQ_TRACE_T_SERIAL(token);
+ serial_num = unsigned_val(serial);
+ serial_num++;
+ SEQ_TRACE_T_SERIAL(token) = make_small(serial_num);
+}
+
/* Send a sequential trace message to the sequential tracer.
* p is the caller (which contains the trace token),
@@ -929,6 +941,9 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
#undef LOCAL_HEAP_SIZE
}
+
+
+
/* Send {trace_ts, Pid, return_to, {Mod, Func, Arity}, Timestamp}
* or {trace, Pid, return_to, {Mod, Func, Arity}}
*/
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index af38ef52db..2c5adf3198 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -142,12 +142,6 @@ void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
int erts_trace_flags(Eterm List,
Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp);
-Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp);
#define ERTS_CHK_PEND_TRACE_MSGS(ESDP) \
@@ -163,7 +157,10 @@ seq_trace_output_generic((token), (msg), (type), (receiver), NULL, (exitfrom))
void seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
Eterm receiver, Process *process, Eterm exitfrom);
-int seq_trace_update_send(Process *process);
+/* Bump the sequence number if tracing is enabled; must be used before sending
+ * send trace messages. */
+int seq_trace_update_serial(Process *process);
+void erts_seq_trace_update_node_token(Eterm token);
Eterm erts_seq_trace(Process *process,
Eterm atom_type, Eterm atom_true_or_false,
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 430ac305c5..895f8e5e27 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -70,6 +70,7 @@ int erts_fit_in_bits_uint(Uint);
Sint erts_list_length(Eterm);
int erts_is_builtin(Eterm, Eterm, int);
Uint32 make_hash2(Eterm);
+Uint32 trapping_make_hash2(Eterm, Eterm*, struct process*);
Uint32 make_hash(Eterm);
Uint32 make_internal_hash(Eterm, Uint32 salt);
@@ -130,15 +131,28 @@ Sint erts_cmp_compound(Eterm, Eterm, int, int);
#define CMP_GT(a,b) ((a) != (b) && CMP((a),(b)) > 0)
#define CMP_EQ_ACTION(X,Y,Action) \
- if ((X) != (Y)) { CMP_SPEC((X),(Y),!=,Action,1); }
+ if ((X) != (Y)) { EQ_SPEC((X),(Y),!=,Action); }
#define CMP_NE_ACTION(X,Y,Action) \
- if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),==,Action,1); }
-#define CMP_GE_ACTION(X,Y,Action) \
- if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action,0); }
+ if ((X) == (Y)) { Action; } else { EQ_SPEC((X),(Y),==,Action); }
+
+#define EQ_SPEC(X,Y,Op,Action) \
+ if (is_both_immed(X, Y)) { \
+ if (X Op Y) { Action; }; \
+ } else if (is_float(X) && is_float(Y)) { \
+ FloatDef af, bf; \
+ GET_DOUBLE(X, af); \
+ GET_DOUBLE(Y, bf); \
+ if (af.fd Op bf.fd) { Action; }; \
+ } else { \
+ if (erts_cmp_compound(X,Y,0,1) Op 0) { Action; }; \
+ }
+
+#define CMP_GE_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_SPEC((X),(Y),<,Action); }
#define CMP_LT_ACTION(X,Y,Action) \
- if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action,0); }
+ if ((X) == (Y)) { Action; } else { CMP_SPEC((X),(Y),>=,Action); }
-#define CMP_SPEC(X,Y,Op,Action,EqOnly) \
+#define CMP_SPEC(X,Y,Op,Action) \
if (is_atom(X) && is_atom(Y)) { \
if (erts_cmp_atoms(X, Y) Op 0) { Action; }; \
} else if (is_both_small(X, Y)) { \
@@ -149,7 +163,26 @@ Sint erts_cmp_compound(Eterm, Eterm, int, int);
GET_DOUBLE(Y, bf); \
if (af.fd Op bf.fd) { Action; }; \
} else { \
- if (erts_cmp_compound(X,Y,0,EqOnly) Op 0) { Action; }; \
+ if (erts_cmp_compound(X,Y,0,0) Op 0) { Action; }; \
+ }
+
+/*
+ * When either operand for is_lt or is_ge is a literal, that literal is
+ * almost always an integer and almost never an atom. Therefore, only
+ * special case the comparison of small integers before calling the
+ * general compare function.
+ */
+
+#define CMP_GE_LITERAL_ACTION(X,Y,Action) \
+ if ((X) != (Y)) { CMP_LITERAL_SPEC((X),(Y),<,Action); }
+#define CMP_LT_LITERAL_ACTION(X,Y,Action) \
+ if ((X) == (Y)) { Action; } else { CMP_LITERAL_SPEC((X),(Y),>=,Action); }
+
+#define CMP_LITERAL_SPEC(X,Y,Op,Action) \
+ if (is_both_small(X, Y)) { \
+ if (signed_val(X) Op signed_val(Y)) { Action; }; \
+ } else { \
+ if (erts_cmp_compound(X,Y,0,0) Op 0) { Action; }; \
}
#define erts_float_comp(x,y) (((x)<(y)) ? -1 : (((x)==(y)) ? 0 : 1))
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index 8792138d53..2af2af2b37 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -176,7 +176,7 @@ provider erlang {
* Fired whenever a user function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
* @param depth the stack depth
*/
probe function__return(char *p, char *mfa, int depth);
@@ -193,7 +193,7 @@ provider erlang {
* Fired whenever a Built In Function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
*/
probe bif__return(char *p, char *mfa);
@@ -209,7 +209,7 @@ provider erlang {
* Fired whenever a Native Function returns.
*
* @param p the PID (string form) of the process
- * @param mfa the m:f/a of the function
+ * @param mfa the m:f/a of the function being returned from
*/
probe nif__return(char *p, char *mfa);
@@ -617,29 +617,6 @@ provider erlang {
probe driver__stop_select(char *name);
- /* Async driver pool */
-
- /**
- * Show the post-add length of the async driver thread pool member's queue.
- *
- * NOTE: The port name is not available: additional lock(s) must
- * be acquired in order to get the port name safely in an SMP
- * environment. The same is true for the aio__pool_get probe.
- *
- * @param port the Port (string form)
- * @param new queue length
- */
- probe aio_pool__add(char *, int);
-
- /**
- * Show the post-get length of the async driver thread pool member's queue.
- *
- * @param port the Port (string form)
- * @param new queue length
- */
- probe aio_pool__get(char *, int);
-
-
/*
* The set of probes called by the erlang tracer nif backend. In order
* to receive events on these you both have to enable tracing in erlang
@@ -678,16 +655,6 @@ provider erlang {
*/
/*
- * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other
- * releases), the sched-thread-id will be the same as the
- * work-thread-id: erl_async.c's async_main() function
- * will call the asynchronous invoke function and then
- * immediately call the drivers ready_async function while
- * inside the same I/O worker pool thread.
- * For R14B03's source, see erl_async.c lines 302-317.
- */
-
-/*
* The set of probes for use by Erlang code ... moved to here from
* lib/runtime_tools/c_src/dtrace_user.d until a more portable solution
* is found. This move pollutes the Erlang VM with functions that are
diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h
index 9b93d77f6e..213830e6fd 100644
--- a/erts/emulator/beam/erlang_lttng.h
+++ b/erts/emulator/beam/erlang_lttng.h
@@ -30,21 +30,6 @@
#include <lttng/tracepoint.h>
-/* Schedulers */
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- scheduler_poll,
- TP_ARGS(
- int, id,
- int, runnable
- ),
- TP_FIELDS(
- ctf_integer(int, scheduler, id)
- ctf_integer(int, runnable, runnable)
- )
-)
-
#ifndef LTTNG_CARRIER_STATS
#define LTTNG_CARRIER_STATS
typedef struct {
@@ -292,35 +277,6 @@ TRACEPOINT_EVENT(
)
)
-/* Async pool */
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- aio_pool_get,
- TP_ARGS(
- char*, port,
- int, length
- ),
- TP_FIELDS(
- ctf_string(port, port)
- ctf_integer(int, length, length)
- )
-)
-
-TRACEPOINT_EVENT(
- org_erlang_otp,
- aio_pool_put,
- TP_ARGS(
- char*, port,
- int, length
- ),
- TP_FIELDS(
- ctf_string(port, port)
- ctf_integer(int, length, length)
- )
-)
-
-
/* Memory Allocator */
TRACEPOINT_EVENT(
diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h
index 64c08b1570..cc80fd09df 100644
--- a/erts/emulator/beam/error.h
+++ b/erts/emulator/beam/error.h
@@ -66,13 +66,13 @@
#define EXF_OFFSET EXTAG_BITS
#define EXF_BITS 7
-#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
-#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
-#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
-#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
-#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
-#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
-#define EXF_RESTORE_NIF (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
+#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
+#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
+#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
+#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
+#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
+#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
+#define EXF_RESTORE_NFUNC (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
#define EXC_FLAGBITS (((1<<(EXF_BITS+EXF_OFFSET))-1) \
& ~((1<<(EXF_OFFSET))-1))
@@ -155,10 +155,8 @@
/* No matching try clause */
#define EXC_NOTSUP ((17 << EXC_OFFSET) | EXC_ERROR)
/* Not supported */
-
#define EXC_BADMAP ((18 << EXC_OFFSET) | EXC_ERROR)
/* Bad map */
-
#define EXC_BADKEY ((19 << EXC_OFFSET) | EXC_ERROR)
/* Bad key in map */
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 946ffeffb8..af1b1c2892 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -129,14 +129,17 @@ export_alloc(struct export_entry* tmpl_e)
obj->info.mfa.module = tmpl->info.mfa.module;
obj->info.mfa.function = tmpl->info.mfa.function;
obj->info.mfa.arity = tmpl->info.mfa.arity;
- obj->beam[0] = 0;
+ obj->bif_number = -1;
+ obj->is_bif_traced = 0;
+
+ memset(&obj->trampoline, 0, sizeof(obj->trampoline));
+
if (BeamOpsAreInitialized()) {
- obj->beam[0] = BeamOpCodeAddr(op_call_error_handler);
+ obj->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
}
- obj->beam[1] = 0;
for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
- obj->addressv[ix] = obj->beam;
+ obj->addressv[ix] = obj->trampoline.raw;
blob->entryv[ix].slot.index = -1;
blob->entryv[ix].ep = &blob->exp;
@@ -196,6 +199,19 @@ init_export_table(void)
}
}
+static struct export_entry* init_template(struct export_templ* templ,
+ Eterm m, Eterm f, unsigned a)
+{
+ templ->entry.ep = &templ->exp;
+ templ->entry.slot.index = -1;
+ templ->exp.info.mfa.module = m;
+ templ->exp.info.mfa.function = f;
+ templ->exp.info.mfa.arity = a;
+ templ->exp.bif_number = -1;
+ templ->exp.is_bif_traced = 0;
+ return &templ->entry;
+}
+
/*
* Return a pointer to the export entry for the given function,
* or NULL otherwise. Notes:
@@ -214,41 +230,15 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix);
Export*
erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
- HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a);
- int ix;
- HashBucket* b;
-
- ix = hval % export_tables[code_ix].htable.size;
- b = export_tables[code_ix].htable.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b != (HashBucket*) 0) {
- Export* ep = ((struct export_entry*) b)->ep;
- if (ep->info.mfa.module == m &&
- ep->info.mfa.function == f &&
- ep->info.mfa.arity == a) {
- return ep;
- }
- b = b->next;
- }
+ struct export_templ templ;
+ struct export_entry *ee =
+ hash_fetch(&export_tables[code_ix].htable,
+ init_template(&templ, m, f, a),
+ (H_FUN)export_hash, (HCMP_FUN)export_cmp);
+ if (ee) return ee->ep;
return NULL;
}
-static struct export_entry* init_template(struct export_templ* templ,
- Eterm m, Eterm f, unsigned a)
-{
- templ->entry.ep = &templ->exp;
- templ->entry.slot.index = -1;
- templ->exp.info.mfa.module = m;
- templ->exp.info.mfa.function = f;
- templ->exp.info.mfa.arity = a;
- return &templ->entry;
-}
-
-
/*
* Find the export entry for a loaded function.
* Returns a NULL pointer if the given function is not loaded, or
@@ -268,8 +258,8 @@ erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
if (ee == NULL ||
- (ee->ep->addressv[code_ix] == ee->ep->beam &&
- ! BeamIsOpCode(ee->ep->beam[0], op_i_generic_breakpoint))) {
+ (ee->ep->addressv[code_ix] == ee->ep->trampoline.raw &&
+ ! BeamIsOpCode(ee->ep->trampoline.op, op_i_generic_breakpoint))) {
return NULL;
}
return ee->ep;
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index ae8dfa4cf8..91c4844d20 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -31,24 +31,72 @@
typedef struct export
{
- void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */
-
- ErtsCodeInfo info; /* MUST be just before beam[] */
-
- /*
- * beam[0]: This entry is 0 unless the 'addressv' field points to it.
- * Threaded code instruction to load function
- * (em_call_error_handler), execute BIF (em_apply_bif),
- * or a breakpoint instruction (op_i_generic_breakpoint).
- * beam[1]: Function pointer to BIF function (for BIFs only),
- * or pointer to threaded code if the module has an
- * on_load function that has not been run yet, or pointer
- * to code if function beam[0] is a breakpoint instruction.
- * Otherwise: 0.
- */
- BeamInstr beam[2];
+ /* Pointer to code for function. */
+ void* addressv[ERTS_NUM_CODE_IX];
+
+ /* Index into bif_table[], or -1 if not a BIF. */
+ int bif_number;
+ /* Non-zero if this is a BIF that's traced. */
+ int is_bif_traced;
+
+ /* This is a small trampoline function that can be used for lazy code
+ * loading, global call tracing, and so on. It's only valid when
+ * addressv points to it and should otherwise be left zeroed.
+ *
+ * Needless to say, the order of the fields below is significant. */
+ ErtsCodeInfo info;
+ union {
+ BeamInstr op; /* Union discriminant. */
+
+ struct {
+ BeamInstr op; /* op_i_generic_breakpoint */
+ BeamInstr address; /* Address of the original function */
+ } breakpoint;
+
+ /* This is used when a module refers to (imports) a function that
+ * hasn't been loaded yet. Upon loading we create an export entry which
+ * redirects to the error_handler so that the appropriate module will
+ * be loaded when called (or crash).
+ *
+ * This is also used when a module has an on_load callback as we need
+ * to defer all calls until the callback returns. `deferred` contains
+ * the address of the original function in this case, and there's an
+ * awkward condiditon where `deferred` may be set while op is zero. See
+ * erlang:finish_after_on_load/2 for details. */
+ struct {
+ BeamInstr op; /* op_call_error_handler, or 0 during the last
+ * phase of code loading when on_load is
+ * present. See above. */
+ BeamInstr deferred;
+ } not_loaded;
+
+ struct {
+ BeamInstr op; /* op_trace_jump_W */
+ BeamInstr address; /* Address of the traced function */
+ } trace;
+
+ BeamInstr raw[2]; /* For use in address comparisons, should not
+ * be tampered directly. */
+ } trampoline;
} Export;
+#ifdef DEBUG
+#define DBG_CHECK_EXPORT(EP, CX) \
+ do { \
+ if((EP)->addressv[CX] == (EP)->trampoline.raw) { \
+ /* The entry currently points at the trampoline, so the
+ * instructions must be valid. */ \
+ ASSERT(((BeamIsOpCode((EP)->trampoline.op, op_i_generic_breakpoint)) && \
+ (EP)->trampoline.breakpoint.address != 0) || \
+ ((BeamIsOpCode((EP)->trampoline.op, op_trace_jump_W)) && \
+ (EP)->trampoline.trace.address != 0) || \
+ /* (EP)->trampoline.not_loaded.deferred may be zero. */ \
+ (BeamIsOpCode((EP)->trampoline.op, op_call_error_handler))); \
+ } \
+ } while(0)
+#else
+#define DBG_CHECK_EXPORT(EP, CX) ((void)(EP), (void)(CX))
+#endif
void init_export_table(void);
void export_info(fmtfn_t, void *);
@@ -71,9 +119,6 @@ extern erts_mtx_t export_staging_lock;
#define export_staging_unlock() erts_mtx_unlock(&export_staging_lock)
#include "beam_load.h" /* For em_* extern declarations */
-#define ExportIsBuiltIn(EntryPtr) \
-(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->beam) && \
- (BeamIsOpCode((EntryPtr)->beam[0], op_apply_bif)))
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 12eda60527..5d91c1b2cb 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -46,23 +46,26 @@
#include "erl_bits.h"
#include "erl_zlib.h"
#include "erl_map.h"
+#include "erl_proc_sig_queue.h"
+#include "erl_trace.h"
+
+#define PASS_THROUGH 'p'
#define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
#define MAX_STRING_LEN 0xffff
-/* MAX value for the creation field in pid, port and reference
- for the local node and for the current external format.
-
- Larger creation values than this are allowed in external pid, port and refs
- encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT.
- The point here is to prepare for future upgrade to 32-bit creation.
- OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes,
- but do not use big creation values for the local node yet,
- as we still may have to communicate with older nodes.
+/*
+ * MAX value for the creation field in pid, port and reference
+ * for the old PID_EXT, PORT_EXT, REFERENCE_EXT and NEW_REFERENCE_EXT.
+ * Older nodes (OTP 19-22) will send us these so we must be able to decode them.
+ *
+ * From OTP 23 DFLAG_BIG_CREATION is mandatory so this node will always
+ * encode with new big 32-bit creations using NEW_PID_EXT, NEW_PORT_EXT
+ * and NEWER_REFERENCE_EXT.
*/
-#define ERTS_MAX_LOCAL_CREATION (3)
-#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION)
+#define ERTS_MAX_TINY_CREATION (3)
+#define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION)
#undef ERTS_DEBUG_USE_DIST_SEP
#ifdef DEBUG
@@ -98,13 +101,13 @@
static Export term_to_binary_trap_export;
-static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_heap_header** off_heap);
+static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint64, struct erl_off_heap_header** off_heap);
struct TTBEncodeContext_;
-static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint64 dflags,
struct erl_off_heap_header** off_heap, Sint *reds, byte **res);
static int is_external_string(Eterm obj, Uint* lenp);
-static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
-static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
+static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
+static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
struct B2TContext_t;
static byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, byte*, Eterm*, struct B2TContext_t*, int);
static byte* dec_atom(ErtsDistExternal *, byte*, Eterm*);
@@ -112,19 +115,23 @@ static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte t
static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
- Uint flags, Binary *context_b);
+static Eterm erts_term_to_binary_int(Process* p, Sint bif_ix, Eterm Term, Eterm opts, int level,
+ Uint64 dflags, Binary *context_b, int iovec,
+ Uint fragment_size);
-static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned);
-struct TTBSizeContext_;
-static ErtsExtSzRes encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp,
- Eterm obj, unsigned dflags, Sint *reds, Uint *res);
+static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, Uint64);
+static ErtsExtSzRes encode_size_struct_int(TTBSizeContext*, ErtsAtomCacheMap *acmp,
+ Eterm obj, Uint64 dflags, Sint *reds, Uint *res);
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
-static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint32 dflags, Sint reds);
-
-
+static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint64 dflags, Sint reds);
+static byte *hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term,
+ byte *bytes, byte bitoffs, byte bitsize, Uint sz);
+static void hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags,
+ struct erl_off_heap_header** off_heap);
+static void store_in_vec(TTBEncodeContext *ctx, byte *ep, Binary *ohbin, Eterm ohpb,
+ byte *ohp, Uint ohsz);
void erts_init_external(void) {
erts_init_trap_export(&term_to_binary_trap_export,
@@ -221,7 +228,7 @@ erts_destroy_atom_cache_map(ErtsAtomCacheMap *acmp)
}
static ERTS_INLINE void
-insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
+insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags)
{
if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES) {
int ix;
@@ -237,7 +244,7 @@ insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
}
static ERTS_INLINE int
-get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
+get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags)
{
if (!acmp)
return -1;
@@ -257,7 +264,7 @@ get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
}
void
-erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
+erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint64 dflags)
{
if (acmp) {
int long_atoms = 0; /* !0 if one or more atoms are longer than 255. */
@@ -295,10 +302,16 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
}
Uint
-erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp, Uint fragments)
+erts_encode_ext_dist_header_size(TTBEncodeContext *ctx,
+ ErtsAtomCacheMap *acmp,
+ Uint fragments)
{
- if (!acmp)
- return 0;
+ if (ctx->dflags & DFLAG_PENDING_CONNECT) {
+ /* HOPEFUL_DATA + hopefull flags + hopefull ix + payload ix */
+ return 1 + 8 + 4 + 4;
+ }
+ else if (!acmp && !(ctx->dflags & DFLAG_FRAGMENTS))
+ return 1; /* pass through */
else {
int fix_sz
= 1 /* VERSION_MAGIC */
@@ -306,48 +319,82 @@ erts_encode_ext_dist_header_size(ErtsAtomCacheMap *acmp, Uint fragments)
+ 1 /* dist header flags */
+ 1 /* number of internal cache entries */
;
- ASSERT(acmp->hdr_sz >= 0);
+
if (fragments > 1)
fix_sz += 8 /* sequence id */
+ 8 /* number of fragments */
;
- return fix_sz + acmp->hdr_sz;
+ if (acmp) {
+ ASSERT(acmp->hdr_sz >= 0);
+ fix_sz += acmp->hdr_sz;
+ } else {
+ ASSERT(ctx->dflags & DFLAG_FRAGMENTS);
+ }
+
+ return fix_sz;
}
}
-byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp,
+byte *erts_encode_ext_dist_header_setup(TTBEncodeContext *ctx,
+ byte *ctl_ext, ErtsAtomCacheMap *acmp,
Uint fragments, Eterm from)
{
/* Maximum number of atom must be less than the maximum of a 32 bits
unsigned integer. Check is done in erl_init.c, erl_start function. */
- if (!acmp)
- return ctl_ext;
+ if (ctx->dflags & DFLAG_PENDING_CONNECT) {
+ byte *ep = ctl_ext;
+ ep -= 4;
+ ctx->payload_ixp = ep;
+ put_int32(0, ep);
+ ep -= 4;
+ ctx->hopefull_ixp = ep;
+ put_int32(ERTS_NO_HIX, ep);
+ ep -= 8;
+ ctx->hopefull_flagsp = ep;
+ put_int64(0, ep);
+ *--ep = HOPEFUL_DATA;
+ return ep;
+ }
+ else if (!acmp && !(ctx->dflags & DFLAG_FRAGMENTS)) {
+ byte *ep = ctl_ext;
+ *--ep = PASS_THROUGH;
+ return ep;
+ }
else {
int i;
byte *ep = ctl_ext;
- byte dist_hdr_flags = acmp->long_atoms ? ERTS_DIST_HDR_LONG_ATOMS_FLG : 0;
- ASSERT(acmp->hdr_sz >= 0);
- /*
- * Write cache update instructions. Note that this is a purely
- * internal format, never seen on the wire. This section is later
- * rewritten by erts_encode_ext_dist_header_finalize() while updating
- * the cache. We write the header backwards just before the
- * actual term(s).
- */
- for (i = acmp->sz-1; i >= 0; i--) {
- Uint32 aval;
- ASSERT(0 <= acmp->cix[i] && acmp->cix[i] < ERTS_ATOM_CACHE_SIZE);
- ASSERT(i == acmp->cache[acmp->cix[i]].iix);
- ASSERT(is_atom(acmp->cache[acmp->cix[i]].atom));
-
- aval = (Uint32) atom_val(acmp->cache[acmp->cix[i]].atom);
- ep -= 4;
- put_int32(aval, ep);
- ep -= 2;
- put_int16(acmp->cix[i], ep);
- }
- --ep;
- put_int8(acmp->sz, ep);
+ byte dist_hdr_flags = acmp && acmp->long_atoms ? ERTS_DIST_HDR_LONG_ATOMS_FLG : 0;
+ ASSERT(!acmp || acmp->hdr_sz >= 0);
+
+ if (acmp) {
+ /*
+ * Write cache update instructions. Note that this is a purely
+ * internal format, never seen on the wire. This section is later
+ * rewritten by erts_encode_ext_dist_header_finalize() while updating
+ * the cache. We write the header backwards just before the
+ * actual term(s).
+ */
+ for (i = acmp->sz-1; i >= 0; i--) {
+ Uint32 aval;
+ ASSERT(0 <= acmp->cix[i] && acmp->cix[i] < ERTS_ATOM_CACHE_SIZE);
+ ASSERT(i == acmp->cache[acmp->cix[i]].iix);
+ ASSERT(is_atom(acmp->cache[acmp->cix[i]].atom));
+
+ aval = (Uint32) atom_val(acmp->cache[acmp->cix[i]].atom);
+ ep -= 4;
+ put_int32(aval, ep);
+ ep -= 2;
+ put_int16(acmp->cix[i], ep);
+ }
+ --ep;
+ put_int8(acmp->sz, ep);
+ } else {
+ ASSERT(ctx->dflags & DFLAG_FRAGMENTS);
+ /* If we don't have an atom cache but are using a dist header we just put 0
+ in the atom cache size slot */
+ --ep;
+ put_int8(0, ep);
+ }
--ep;
put_int8(dist_hdr_flags, ep);
if (fragments > 1) {
@@ -382,11 +429,9 @@ byte *erts_encode_ext_dist_header_fragment(byte **hdrpp,
}
-#define PASS_THROUGH 'p'
-
Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
DistEntry* dep,
- Uint32 dflags,
+ Uint64 dflags,
Sint reds)
{
byte *ip;
@@ -395,69 +440,34 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
byte dist_hdr_flags;
int long_atoms;
Uint64 seq_id = 0, frag_id = 0;
- register byte *ep = ob->hdrp ? ob->hdrp : ob->extp;
+ register byte *ep = ob->eiov->iov[1].iov_base;
ASSERT(dflags & DFLAG_UTF8_ATOMS);
/*
* The buffer can have different layouts at this point depending on
* what was known when encoded:
*
- * Pending connection: CtrlTerm [, MsgTerm]
+ * Pending connection: HOPEFUL_DATA, HFlgs, HIX, PIX, CtrlTerm [, MsgTerm]
* With atom cache : VERSION_MAGIC, DIST_HEADER, ..., CtrlTerm [, MsgTerm]
* No atom cache : VERSION_MAGIC, CtrlTerm [, VERSION_MAGIC, MsgTerm]
*/
- if (ep[0] != VERSION_MAGIC || dep->transcode_ctx) {
- /*
- * Was encoded without atom cache toward pending connection.
- */
- ASSERT(ep[0] == SMALL_TUPLE_EXT || ep[0] == LARGE_TUPLE_EXT);
-
- if (~dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)
- && ep[0] == SMALL_TUPLE_EXT
- && ep[1] == 4
- && ep[2] == SMALL_INTEGER_EXT
- && (ep[3] == DOP_MONITOR_P ||
- ep[3] == DOP_MONITOR_P_EXIT ||
- ep[3] == DOP_DEMONITOR_P)) {
- /*
- * Receiver does not support process monitoring.
- * Suppress monitor control msg (see erts_dsig_send_monitor)
- * by converting it to an empty (tick) packet.
- */
- ob->ext_endp = ob->extp;
- return reds;
- }
- if (~dflags & (DFLAG_BIT_BINARIES | DFLAG_EXPORT_PTR_TAG
- | DFLAG_DIST_HDR_ATOM_CACHE)) {
- reds = transcode_dist_obuf(ob, dep, dflags, reds);
- if (reds < 0)
- return reds;
- ep = ob->extp;
- }
- if (dflags & DFLAG_DIST_HDR_ATOM_CACHE) {
- /*
- * Encoding was done without atom caching but receiver expects
- * a dist header, so we prepend an empty one.
- */
- *--ep = 0; /* NumberOfAtomCacheRefs */
- *--ep = DIST_HEADER;
- *--ep = VERSION_MAGIC;
- }
- goto done;
- }
- else if (ep[1] != DIST_HEADER && ep[1] != DIST_FRAG_HEADER && ep[1] != DIST_FRAG_CONT) {
- ASSERT(ep[1] == SMALL_TUPLE_EXT || ep[1] == LARGE_TUPLE_EXT);
- ASSERT(!(dflags & DFLAG_DIST_HDR_ATOM_CACHE));
- /* Node without atom cache, 'pass through' needed */
- *--ep = PASS_THROUGH;
- goto done;
+ if (ep[0] == HOPEFUL_DATA)
+ return transcode_dist_obuf(ob, dep, dflags, reds);
+
+ if (ep[0] == PASS_THROUGH) {
+ ASSERT(!(dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)));
+ ASSERT(ob->eiov->iov[1].iov_len == 1);
+ return reds;
}
if (ep[1] == DIST_FRAG_CONT) {
- ep = ob->extp;
- goto done;
- } else if (ep[1] == DIST_FRAG_HEADER) {
+ ASSERT(ep[0] == VERSION_MAGIC);
+ ASSERT(ob->eiov->iov[1].iov_len == 18);
+ return reds;
+ }
+
+ if (ep[1] == DIST_FRAG_HEADER) {
/* skip the seq id and frag id */
seq_id = get_int64(&ep[2]);
ep += 8;
@@ -481,10 +491,7 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
ip = &instr_buf[0];
sys_memcpy((void *) ip, (void *) ep, sz);
ep += sz;
- /* ep now points to the beginning of the control message term */
-#ifdef ERTS_DEBUG_USE_DIST_SEP
- ASSERT(*ep == VERSION_MAGIC);
-#endif
+ ASSERT(ep == (byte *) (ob->eiov->iov[1].iov_base + ob->eiov->iov[1].iov_len));
if (ci > 0) {
Uint32 flgs_buf[((ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTES(
ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES)-1)
@@ -597,49 +604,82 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob,
*--ep = DIST_HEADER;
}
*--ep = VERSION_MAGIC;
-done:
- ob->extp = ep;
- ASSERT((byte*)ob->bin->orig_bytes <= ob->extp && ob->extp < ob->ext_endp);
+
+ sz = ((byte *) ob->eiov->iov[1].iov_base) - ep;
+ ob->eiov->size += sz;
+ ob->eiov->iov[1].iov_len += sz;
+ ob->eiov->iov[1].iov_base = ep;
+
return reds < 0 ? 0 : reds;
}
ErtsExtSzRes
-erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, Uint* szp)
+erts_encode_dist_ext_size(Eterm term,
+ ErtsAtomCacheMap *acmp,
+ TTBSizeContext* ctx,
+ Uint* szp, Sint *redsp,
+ Sint *vlenp, Uint *fragmentsp)
{
Uint sz;
- ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz);
- if (res == ERTS_EXT_SZ_OK) {
+ ErtsExtSzRes res;
+
+ ASSERT(ctx);
+ ASSERT(szp);
+ ASSERT(vlenp);
+ ASSERT(fragmentsp);
+
+ sz = *szp;
+
+ if (!ctx->wstack.wstart) {
+ /*
+ * First call for this 'term'. We might however encode
+ * multiple terms and this might not be the first term
+ * in the sequence. 'ctx' should contain valid info about
+ * about previous terms regarding fragments, and vlen.
+ * 'szp' should contain valid info about the total size
+ * of previous terms.
+ */
+ if (ctx->vlen < 0) {
+ /* First term as well */
+ ctx->vlen = 0;
+ if (ctx->dflags & DFLAG_FRAGMENTS)
+ ctx->fragment_size = ERTS_DIST_FRAGMENT_SIZE;
+ }
+
#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
+ if (!(ctx->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)))
#endif
sz++ /* VERSION_MAGIC */;
- *szp += sz;
}
- return res;
-}
-ErtsExtSzRes
-erts_encode_dist_ext_size_ctx(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
-{
- Uint sz;
- ErtsExtSzRes res = encode_size_struct_int(&ctx->u.sc, ctx->acmp, term,
- ctx->flags, &ctx->reds, &sz);
+ res = encode_size_struct_int(ctx, acmp, term, ctx->dflags, redsp, &sz);
+
if (res == ERTS_EXT_SZ_OK) {
-#ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
-#endif
- sz++ /* VERSION_MAGIC */;
+ Uint total_size, fragments;
+
+ /*
+ * Each fragment use
+ * - one element for driver header
+ * - one element for fragment header
+ * - and (at least) one for data
+ */
+ total_size = sz + ctx->extra_size;
+ fragments = (total_size - 1)/ctx->fragment_size + 1;
- *szp += sz;
+ *szp = sz;
+ *fragmentsp = fragments;
+ *vlenp = ctx->vlen + 3*fragments;
}
+
return res;
}
ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp)
{
- ErtsExtSzRes res = encode_size_struct_int(NULL, NULL, term, dflags,
- NULL, szp);
+ ErtsExtSzRes res;
+ *szp = 0;
+ res = encode_size_struct_int(NULL, NULL, term, dflags, NULL, szp);
(*szp)++ /* VERSION_MAGIC */;
return res;
}
@@ -651,20 +691,44 @@ ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp)
Uint erts_encode_ext_size_ets(Eterm term)
{
- return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS);
+ return encode_size_struct2(NULL, term,
+ TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED);
}
-int erts_encode_dist_ext(Eterm term, byte **ext, Uint32 flags, ErtsAtomCacheMap *acmp,
- TTBEncodeContext* ctx, Sint* reds)
+int erts_encode_dist_ext(Eterm term, byte **ext, Uint64 flags, ErtsAtomCacheMap *acmp,
+ TTBEncodeContext* ctx, Uint *fragmentsp, Sint* reds)
{
- if (!ctx || !ctx->wstack.wstart) {
- #ifndef ERTS_DEBUG_USE_DIST_SEP
- if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
- #endif
+ int res;
+ ASSERT(ctx);
+
+ if (!ctx->wstack.wstart) {
+ ctx->cptr = *ext;
+#ifndef ERTS_DEBUG_USE_DIST_SEP
+ if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_PENDING_CONNECT|DFLAG_FRAGMENTS)))
+#endif
*(*ext)++ = VERSION_MAGIC;
+#ifndef ERTS_DEBUG_USE_DIST_SEP
+ if (flags & DFLAG_PENDING_CONNECT) {
+ Sint payload_ix = ctx->vlen;
+ ASSERT(ctx->payload_ixp);
+ if (payload_ix) {
+ /* we potentially need a version magic on the payload... */
+ (*ext)++;
+ ctx->cptr = *ext;
+ put_int32(payload_ix, ctx->payload_ixp);
+ }
+ }
+#endif
}
- return enc_term_int(ctx, acmp, term, *ext, flags, NULL, reds, ext);
+ res = enc_term_int(ctx, acmp, term, *ext, flags, NULL, reds, ext);
+ if (fragmentsp)
+ *fragmentsp = res == 0 ? ctx->frag_ix + 1 : ctx->frag_ix;
+ if (flags & DFLAG_PENDING_CONNECT) {
+ ASSERT(ctx->hopefull_flagsp);
+ put_int64(ctx->hopefull_flags, ctx->hopefull_flagsp);
+ }
+ return res;
}
void erts_encode_ext(Eterm term, byte **ext)
@@ -681,7 +745,7 @@ void erts_encode_ext(Eterm term, byte **ext)
byte* erts_encode_ext_ets(Eterm term, byte *ep, struct erl_off_heap_header** off_heap)
{
- return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS,
+ return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED,
off_heap);
}
@@ -786,7 +850,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
ASSERT(dep);
erts_de_rlock(dep);
- ASSERT(dep->flags & DFLAG_UTF8_ATOMS);
+ ASSERT(dep->dflags & DFLAG_UTF8_ATOMS);
if ((dep->state != ERTS_DE_STATE_CONNECTED &&
@@ -796,7 +860,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
return ERTS_PREP_DIST_EXT_CLOSED;
}
- if (!(dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)) {
+ if (!(dep->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS))) {
/* Skip PASS_THROUGH */
ext++;
size--;
@@ -826,7 +890,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
edep->data->seq_id = 0;
edep->data->frag_id = 1;
- if (dep->flags & DFLAG_DIST_HDR_ATOM_CACHE)
+ if (dep->dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS))
edep->flags |= ERTS_DIST_EXT_DFLAG_HDR;
if (ep[1] != DIST_HEADER && ep[1] != DIST_FRAG_HEADER && ep[1] != DIST_FRAG_CONT) {
@@ -836,7 +900,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
edep->data->extp = ext;
}
else if (ep[1] == DIST_FRAG_CONT) {
- if (!(dep->flags & DFLAG_FRAGMENTS))
+ if (!(dep->dflags & DFLAG_FRAGMENTS))
goto bad_hdr;
edep->attab.size = 0;
edep->data->extp = ext + 1 + 1 + 8 + 8;
@@ -853,7 +917,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
goto bad_hdr;
if (ep[1] == DIST_FRAG_HEADER) {
- if (!(dep->flags & DFLAG_FRAGMENTS))
+ if (!(dep->dflags & DFLAG_FRAGMENTS))
goto bad_hdr;
edep->data->seq_id = get_int64(&ep[2]);
edep->data->frag_id = get_int64(&ep[2+8]);
@@ -1286,8 +1350,11 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
Eterm Term = tp[1];
Eterm Opts = tp[2];
Eterm bt = tp[3];
+ Eterm bix = tp[4];
+ Sint bif_ix = signed_val(bix);
Binary *bin = erts_magic_ref2bin(bt);
- Eterm res = erts_term_to_binary_int(BIF_P, Term, Opts, 0, 0,bin);
+ Eterm res = erts_term_to_binary_int(BIF_P, bif_ix, Term, Opts,
+ 0, 0,bin, 0, ~((Uint) 0));
if (is_non_value(res)) {
if (erts_set_gc_state(BIF_P, 1)
|| MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) {
@@ -1295,10 +1362,10 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
}
if (Opts == am_undefined)
ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT,
- bif_export[BIF_term_to_binary_1], Term);
+ &bif_trap_export[bif_ix], Term);
else
ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT,
- bif_export[BIF_term_to_binary_2], Term, Opts);
+ &bif_trap_export[bif_ix], Term, Opts);
}
if (is_tuple(res)) {
ASSERT(BIF_P->flags & F_DISABLE_GC);
@@ -1316,8 +1383,10 @@ 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, am_undefined,
- 0, TERM_TO_BINARY_DFLAGS, NULL);
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_term_to_binary_1,
+ BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL, 0,
+ ~((Uint) 0));
if (is_non_value(res)) {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
@@ -1331,22 +1400,43 @@ BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
}
}
-HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2)
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_iovec, 1)
-BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
+BIF_RETTYPE term_to_iovec_1(BIF_ALIST_1)
+{
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_term_to_iovec_1,
+ BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL, !0,
+ ~((Uint) 0));
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ if (is_tuple(res)) {
+ erts_set_gc_state(BIF_P, 0);
+ BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
+ } else {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_RET(res);
+ }
+}
+
+static ERTS_INLINE int
+parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
{
- Process* p = BIF_P;
- Eterm Term = BIF_ARG_1;
- Eterm Flags = BIF_ARG_2;
int level = 0;
+ int iovec = 0;
Uint flags = TERM_TO_BINARY_DFLAGS;
- Eterm res;
+ Uint fsize = ~((Uint) 0); /* one fragment */
- while (is_list(Flags)) {
- Eterm arg = CAR(list_val(Flags));
+ while (is_list(opts)) {
+ Eterm arg = CAR(list_val(opts));
Eterm* tp;
if (arg == am_compressed) {
level = Z_DEFAULT_COMPRESSION;
+ }
+ else if (iovecp && arg == am_iovec) {
+ iovec = !0;
} else if (is_tuple(arg) && *(tp = tuple_val(arg)) == make_arityval(2)) {
if (tp[1] == am_minor_version && is_small(tp[2])) {
switch (signed_val(tp[2])) {
@@ -1360,34 +1450,66 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
flags = TERM_TO_BINARY_DFLAGS | DFLAG_UTF8_ATOMS;
break;
default:
- goto error;
+ return 0; /* badarg */
}
} else if (tp[1] == am_compressed && is_small(tp[2])) {
level = signed_val(tp[2]);
if (!(0 <= level && level < 10)) {
- goto error;
+ return 0; /* badarg */
}
- } else {
- goto error;
+ } else if (fsizep) {
+ if (ERTS_IS_ATOM_STR("fragment", tp[1])) {
+ if (!term_to_Uint(tp[2], &fsize))
+ return 0; /* badarg */
+ }
+ else {
+ return 0; /* badarg */
+ }
+ }
+ else {
+ return 0; /* badarg */
}
} else {
- error:
- BIF_ERROR(p, BADARG);
+ return 0; /* badarg */
}
- Flags = CDR(list_val(Flags));
+ opts = CDR(list_val(opts));
}
- if (is_not_nil(Flags)) {
- goto error;
+ if (is_not_nil(opts)) {
+ return 0; /* badarg */
}
- res = erts_term_to_binary_int(p, Term, BIF_ARG_2,
- level, flags, NULL);
+ *flagsp = flags;
+ *levelp = level;
+ if (iovecp)
+ *iovecp = iovec;
+ if (fsizep)
+ *fsizep = fsize;
+
+ return !0; /* ok */
+}
+
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2)
+
+BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
+{
+ int level;
+ Uint flags;
+ Eterm res;
+
+ if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ res = erts_term_to_binary_int(BIF_P, BIF_term_to_binary_2,
+ BIF_ARG_1, BIF_ARG_2,
+ level, flags, NULL, 0,
+ ~((Uint) 0));
if (is_non_value(res)) {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
if (is_tuple(res)) {
- erts_set_gc_state(p, 0);
+ erts_set_gc_state(BIF_P, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
} else {
ASSERT(!(BIF_P->flags & F_DISABLE_GC));
@@ -1395,6 +1517,67 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
}
}
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_iovec, 2)
+
+BIF_RETTYPE term_to_iovec_2(BIF_ALIST_2)
+{
+ int level;
+ Uint flags;
+ Eterm res;
+
+ if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ res = erts_term_to_binary_int(BIF_P, BIF_term_to_iovec_2,
+ BIF_ARG_1, BIF_ARG_2,
+ level, flags, NULL, !0,
+ ~((Uint) 0));
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ if (is_tuple(res)) {
+ erts_set_gc_state(BIF_P, 0);
+ BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
+ } else {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_RET(res);
+ }
+}
+
+Eterm
+erts_debug_term_to_binary(Process *p, Eterm term, Eterm opts)
+{
+ Eterm ret;
+ int level, iovec;
+ Uint flags;
+ Uint fsize;
+
+ if (!parse_t2b_opts(opts, &flags, &level, &iovec, &fsize)) {
+ ERTS_BIF_PREP_ERROR(ret, p, BADARG);
+ }
+ else {
+ Eterm res = erts_term_to_binary_int(p, BIF_term_to_binary_2,
+ term, opts, level, flags,
+ NULL, iovec, fsize);
+
+ if (is_non_value(res)) {
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ ERTS_BIF_PREP_ERROR(ret, p, SYSTEM_LIMIT);
+ }
+ else if (is_tuple(res)) {
+ erts_set_gc_state(p, 0);
+ ERTS_BIF_PREP_TRAP1(ret, &term_to_binary_trap_export,p,res);
+ }
+ else {
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ ERTS_BIF_PREP_RET(ret, res);
+ }
+ }
+ return ret;
+}
+
enum B2TState { /* order is somewhat significant */
B2TPrepare,
@@ -1804,8 +1987,8 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx)
case B2TBadArg:
BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION);
- ASSERT(ctx->bif == bif_export[BIF_binary_to_term_1]
- || ctx->bif == bif_export[BIF_binary_to_term_2]);
+ ASSERT(ctx->bif == &bif_trap_export[BIF_binary_to_term_1]
+ || ctx->bif == &bif_trap_export[BIF_binary_to_term_2]);
if (is_first_call)
ERTS_BIF_PREP_ERROR(ret_val, p, BADARG);
@@ -1886,7 +2069,7 @@ BIF_RETTYPE binary_to_term_1(BIF_ALIST_1)
ctx.flags = 0;
ctx.used_bytes = 0;
ctx.trap_bin = THE_NON_VALUE;
- ctx.bif = bif_export[BIF_binary_to_term_1];
+ ctx.bif = &bif_trap_export[BIF_binary_to_term_1];
ctx.arg[0] = BIF_ARG_1;
ctx.arg[1] = THE_NON_VALUE;
return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx);
@@ -1921,7 +2104,7 @@ BIF_RETTYPE binary_to_term_2(BIF_ALIST_2)
goto error;
ctx.trap_bin = THE_NON_VALUE;
- ctx.bif = bif_export[BIF_binary_to_term_2];
+ ctx.bif = &bif_trap_export[BIF_binary_to_term_2];
ctx.arg[0] = BIF_ARG_1;
ctx.arg[1] = BIF_ARG_2;
return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx);
@@ -1935,7 +2118,7 @@ external_size_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Term = BIF_ARG_1;
- Uint size;
+ Uint size = 0;
switch (erts_encode_ext_size(Term, &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
@@ -1957,7 +2140,7 @@ external_size_1(BIF_ALIST_1)
Eterm
external_size_2(BIF_ALIST_2)
{
- Uint size;
+ Uint size = 0;
Uint flags = TERM_TO_BINARY_DFLAGS;
while (is_list(BIF_ARG_2)) {
@@ -2006,7 +2189,7 @@ external_size_2(BIF_ALIST_2)
}
static Eterm
-erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint flags)
+erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint64 dflags)
{
Eterm bin;
size_t real_size;
@@ -2022,7 +2205,7 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
bytes = erts_alloc(ERTS_ALC_T_TMP, size);
}
- if ((endp = enc_term(NULL, Term, bytes, flags, NULL))
+ if ((endp = enc_term(NULL, Term, bytes, dflags, NULL))
== NULL) {
erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n",
__FILE__, __LINE__, Term);
@@ -2067,7 +2250,7 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
bin = new_binary(p, (byte *)NULL, size);
bytes = binary_bytes(bin);
bytes[0] = VERSION_MAGIC;
- if ((endp = enc_term(NULL, Term, bytes+1, flags, NULL))
+ if ((endp = enc_term(NULL, Term, bytes+1, dflags, NULL))
== NULL) {
erts_exit(ERTS_ERROR_EXIT, "%s, line %d: bad term: %x\n",
__FILE__, __LINE__, Term);
@@ -2082,8 +2265,8 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
}
Eterm
-erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) {
- Uint size;
+erts_term_to_binary(Process* p, Eterm Term, int level, Uint64 flags) {
+ Uint size = 0;
switch (encode_size_struct_int(NULL, NULL, Term, flags, NULL, &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
return THE_NON_VALUE;
@@ -2121,6 +2304,8 @@ static int ttb_context_destructor(Binary *context_bin)
erts_bin_free(context->s.ec.result_bin);
context->s.ec.result_bin = NULL;
}
+ if (context->s.ec.iov)
+ erts_free(ERTS_ALC_T_T2B_VEC, context->s.ec.iov);
break;
case TTBCompress:
erl_zlib_deflate_finish(&(context->s.cc.stream));
@@ -2142,8 +2327,77 @@ static int ttb_context_destructor(Binary *context_bin)
return 1;
}
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
- Uint flags, Binary *context_b)
+Uint
+erts_ttb_iov_size(int use_termv, Sint vlen, Uint fragments)
+{
+ Uint sz;
+ ASSERT(vlen > 0);
+ ASSERT(fragments > 0);
+ sz = sizeof(SysIOVec)*vlen;
+ if (sz % sizeof(void *) != 0)
+ sz += sizeof(void *) - (sz % sizeof(void *));
+ sz += sizeof(ErlDrvBinary *)*vlen;
+ if (use_termv)
+ sz += sizeof(Eterm)*vlen;
+ sz += sizeof(ErlIOVec *)*fragments;
+ sz += sizeof(ErlIOVec)*fragments;
+ if (sz % sizeof(void *) != 0)
+ sz += sizeof(void *) - (sz % sizeof(void *));
+ return sz;
+}
+
+ErlIOVec **
+erts_ttb_iov_init(TTBEncodeContext *ctx, int use_termv, char *ptr,
+ Sint vlen, Uint fragments, Uint fragment_size)
+{
+ int i;
+ ErlIOVec *feiovp;
+ ctx->vlen = 0;
+ ctx->size = 0;
+
+ ctx->iov = (SysIOVec *) ptr;
+ ptr += sizeof(SysIOVec)*vlen;
+ if (((UWord) ptr) % sizeof(void *) != 0)
+ ptr += sizeof(void *) - (((UWord) ptr) % sizeof(void *));
+ ASSERT(((UWord) ptr) % sizeof(void *) == 0);
+
+ ctx->binv = (ErlDrvBinary **) ptr;
+ ptr += sizeof(ErlDrvBinary *)*vlen;
+
+ if (!use_termv)
+ ctx->termv = NULL;
+ else {
+ ctx->termv = (Eterm *) ptr;
+ ptr += sizeof(Eterm)*vlen;
+ }
+
+ ctx->fragment_eiovs = (ErlIOVec **) ptr;
+ ptr += sizeof(ErlIOVec *)*fragments;
+
+ feiovp = (ErlIOVec *) ptr;
+ ptr += sizeof(ErlIOVec)*fragments;
+
+ if (((UWord) ptr) % sizeof(void *) != 0)
+ ptr += sizeof(void *) - (((UWord) ptr) % sizeof(void *));
+ ASSERT(((UWord) ptr) % sizeof(void *) == 0);
+
+ for (i = 0; i < fragments; i++)
+ ctx->fragment_eiovs[i] = &feiovp[i];
+
+ ctx->frag_ix = -1;
+ ctx->fragment_size = fragment_size;
+
+#ifdef DEBUG
+ ctx->cptr = NULL;
+ ctx->debug_fragments = fragments;
+ ctx->debug_vlen = vlen;
+#endif
+ return ctx->fragment_eiovs;
+}
+
+static Eterm erts_term_to_binary_int(Process* p, Sint bif_ix, Eterm Term, Eterm opts,
+ int level, Uint64 dflags, Binary *context_b,
+ int iovec, Uint fragment_size)
{
Eterm *hp;
Eterm res;
@@ -2157,6 +2411,10 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
TTBContext c_buff;
TTBContext *context = &c_buff;
+ ASSERT(bif_ix > 0 && IS_USMALL(!0, bif_ix));
+ ASSERT(bif_ix == BIF_term_to_binary_1 || bif_ix == BIF_term_to_binary_2
+ || bif_ix == BIF_term_to_iovec_1 || bif_ix == BIF_term_to_iovec_2);
+
#define EXPORT_CONTEXT() \
do { \
if (context_b == NULL) { \
@@ -2169,36 +2427,47 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
#define RETURN_STATE() \
do { \
- hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 3); \
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 4); \
c_term = erts_mk_magic_ref(&hp, &MSO(p), context_b); \
- res = TUPLE3(hp, Term, opts, c_term); \
+ res = TUPLE4(hp, Term, opts, c_term, make_small(bif_ix)); \
BUMP_ALL_REDS(p); \
return res; \
} while (0);
-
if (context_b == NULL) {
/* Setup enough to get started */
context->state = TTBSize;
context->alive = 1;
- context->s.sc.wstack.wstart = NULL;
- context->s.sc.flags = flags;
+ ERTS_INIT_TTBSizeContext(&context->s.sc, dflags);
context->s.sc.level = level;
+ context->s.sc.fragment_size = fragment_size;
+ if (!level) {
+ context->s.sc.vlen = iovec ? 0 : -1;
+ context->s.sc.iovec = iovec;
+ }
+ else {
+ context->s.sc.vlen = -1;
+ context->s.sc.iovec = 0;
+ }
} else {
context = ERTS_MAGIC_BIN_DATA(context_b);
- }
+ }
+
/* Initialization done, now we will go through the states */
for (;;) {
switch (context->state) {
case TTBSize:
{
- Uint size;
+ Uint size, fragments = 1;
Binary *result_bin;
- int level;
- Uint flags;
- /* Try for fast path */
+ int level = context->s.sc.level;
+ Sint vlen;
+ iovec = context->s.sc.iovec;
+ fragment_size = context->s.sc.fragment_size;
+ size = 1; /* VERSION_MAGIC */
switch (encode_size_struct_int(&context->s.sc, NULL, Term,
- context->s.sc.flags, &reds, &size)) {
+ context->s.sc.dflags, &reds,
+ &size)) {
case ERTS_EXT_SZ_SYSTEM_LIMIT:
BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
return THE_NON_VALUE;
@@ -2209,14 +2478,23 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
case ERTS_EXT_SZ_OK:
break;
}
- ++size; /* VERSION_MAGIC */
/* Move these to next state */
- flags = context->s.sc.flags;
- level = context->s.sc.level;
- if (size <= ERL_ONHEAP_BIN_LIMIT) {
+ dflags = context->s.sc.dflags;
+ vlen = context->s.sc.vlen;
+ if (vlen >= 0) {
+ Uint total_size = size + context->s.sc.extra_size;
+ fragments = (total_size - 1)/fragment_size + 1;
+ vlen += 3*fragments;
+ ASSERT(vlen);
+ }
+ else if (size <= ERL_ONHEAP_BIN_LIMIT) {
/* Finish in one go */
res = erts_term_to_binary_simple(p, Term, size,
- level, flags);
+ level, dflags);
+ if (iovec) {
+ Eterm *hp = HAlloc(p, 2);
+ res = CONS(hp, res, NIL);
+ }
BUMP_REDS(p, 1);
return res;
}
@@ -2225,37 +2503,156 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
result_bin->orig_bytes[0] = (byte)VERSION_MAGIC;
/* Next state immediately, no need to export context */
context->state = TTBEncode;
- context->s.ec.flags = flags;
+ ERTS_INIT_TTBEncodeContext(&context->s.ec, dflags);
context->s.ec.level = level;
- context->s.ec.wstack.wstart = NULL;
context->s.ec.result_bin = result_bin;
+ context->s.ec.iovec = iovec;
+ if (vlen >= 0) {
+ Uint sz = erts_ttb_iov_size(!0, vlen, fragments);
+ char *ptr = (char *) erts_alloc(ERTS_ALC_T_T2B_VEC, sz);
+ erts_ttb_iov_init(&context->s.ec, !0, ptr, vlen,
+ fragments, fragment_size);
+ context->s.ec.cptr = (byte *) &result_bin->orig_bytes[0];
+ }
break;
}
case TTBEncode:
{
- byte *endp;
+ byte *endp, *tmp;
byte *bytes = (byte *) context->s.ec.result_bin->orig_bytes;
size_t real_size;
Binary *result_bin;
+ Sint realloc_offset;
+ Uint fragments;
- flags = context->s.ec.flags;
- if (enc_term_int(&context->s.ec, NULL,Term, bytes+1, flags, NULL, &reds, &endp) < 0) {
+ dflags = context->s.ec.dflags;
+ if (enc_term_int(&context->s.ec, NULL,Term, bytes+1, dflags,
+ NULL, &reds, &endp) < 0) {
EXPORT_CONTEXT();
RETURN_STATE();
}
real_size = endp - bytes;
+ tmp = (byte *) &context->s.ec.result_bin->orig_bytes[0];
result_bin = erts_bin_realloc(context->s.ec.result_bin,real_size);
+ realloc_offset = (byte *) &result_bin->orig_bytes[0] - tmp;
level = context->s.ec.level;
BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
if (level == 0 || real_size < 6) { /* We are done */
+ Sint cbin_refc_diff;
+ Eterm result, rb_term, *hp, *hp_end;
+ Uint hsz;
+ int ix;
+ SysIOVec *iov;
+ Eterm *termv;
return_normal:
+ fragments = context->s.ec.frag_ix + 1;
context->s.ec.result_bin = NULL;
context->alive = 0;
if (context_b && erts_refc_read(&context_b->intern.refc,0) == 0) {
erts_bin_free(context_b);
}
- return erts_build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE),
- result_bin);
+ if (!context->s.ec.iov) {
+ hsz = PROC_BIN_SIZE + (iovec ? 2 : 0);
+ hp = HAlloc(p, hsz);
+ result = erts_build_proc_bin(&MSO(p), hp, result_bin);
+ if (iovec) {
+ hp += PROC_BIN_SIZE;
+ result = CONS(hp, result, NIL);
+ }
+ return result;
+ }
+ iovec = context->s.ec.iovec;
+ ASSERT(iovec);
+ iov = context->s.ec.iov;
+ termv = context->s.ec.termv;
+ ASSERT(context->s.ec.vlen <= context->s.ec.debug_vlen);
+ ASSERT(fragments <= context->s.ec.debug_fragments);
+ /* first two elements should be unused */
+ ASSERT(context->s.ec.vlen >= 3*fragments);
+ ASSERT(!iov[0].iov_base && !iov[0].iov_len);
+ ASSERT(!iov[1].iov_base && !iov[1].iov_len);
+
+ hsz = (2 /* cons */
+ + (PROC_BIN_SIZE > ERL_SUB_BIN_SIZE
+ ? PROC_BIN_SIZE
+ : ERL_SUB_BIN_SIZE)); /* max size per vec */
+ hsz *= context->s.ec.vlen - 2*fragments; /* number of vecs */
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ rb_term = THE_NON_VALUE;
+ result = NIL;
+ ASSERT(erts_refc_read(&result_bin->intern.refc, 1) == 1);
+ cbin_refc_diff = -1;
+ for (ix = context->s.ec.vlen - 1; ix > 1; ix--) {
+ Eterm bin_term, pb_term;
+ Uint pb_size;
+ ProcBin *pb;
+ SysIOVec *iovp = &iov[ix];
+ if (!iovp->iov_base)
+ continue; /* empty slot for header */
+ pb_term = termv[ix];
+ if (is_value(pb_term)) {
+ pb_size = binary_size(pb_term);
+ pb = (ProcBin *) binary_val(pb_term);
+ }
+ else {
+ iovp->iov_base = (void *) (((byte *) iovp->iov_base)
+ + realloc_offset);
+ pb_size = result_bin->orig_size;
+ if (is_non_value(rb_term))
+ pb = NULL;
+ else {
+ pb = (ProcBin *) binary_val(rb_term);
+ pb_term = rb_term;
+ }
+ }
+ /*
+ * We intentionally avoid using sub binaries
+ * since the GC might convert those to heap
+ * binaries and by this ruin the nice preparation
+ * for usage of this data as I/O vector in
+ * nifs/drivers.
+ */
+ if (is_value(pb_term) && iovp->iov_len == pb_size)
+ bin_term = pb_term;
+ else {
+ Binary *bin;
+ if (is_value(pb_term)) {
+ bin = ((ProcBin *) binary_val(pb_term))->val;
+ erts_refc_inc(&bin->intern.refc, 2);
+ }
+ else {
+ bin = result_bin;
+ cbin_refc_diff++;
+ }
+ pb = (ProcBin *) (char *) hp;
+ hp += PROC_BIN_SIZE;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = (Uint) iovp->iov_len;
+ pb->next = MSO(p).first;
+ MSO(p).first = (struct erl_off_heap_header*) pb;
+ pb->val = bin;
+ pb->bytes = (byte*) iovp->iov_base;
+ pb->flags = 0;
+ OH_OVERHEAD(&MSO(p), pb->size / sizeof(Eterm));
+ bin_term = make_binary(pb);
+ }
+ result = CONS(hp, bin_term, result);
+ hp += 2;
+ }
+ ASSERT(hp <= hp_end);
+ HRelease(p, hp_end, hp);
+ context->s.ec.iov = NULL;
+ erts_free(ERTS_ALC_T_T2B_VEC, iov);
+ if (cbin_refc_diff) {
+ ASSERT(cbin_refc_diff >= -1);
+ if (cbin_refc_diff > 0)
+ erts_refc_add(&result_bin->intern.refc,
+ cbin_refc_diff, 1);
+ else
+ erts_bin_free(result_bin);
+ }
+ return result;
}
/* Continue with compression... */
/* To make absolutely sure that zlib does not barf on a reallocated context,
@@ -2372,7 +2769,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int lev
*/
static byte*
-enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
+enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint64 dflags)
{
int iix;
int len;
@@ -2380,7 +2777,7 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
ASSERT(is_atom(atom));
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (dflags & DFLAG_ETS_COMPRESSED) {
Uint aval = atom_val(atom);
ASSERT(aval < (1<<24));
if (aval >= (1 << 16)) {
@@ -2457,19 +2854,20 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
/*
* We use this atom as sysname in local pid/port/refs
- * for the ETS compressed format (DFLAG_INTERNAL_TAGS).
+ * for the ETS compressed format
*
*/
#define INTERNAL_LOCAL_SYSNAME am_ErtsSecretAtom
static byte*
-enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
+enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
{
Uint on, os;
- Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS))
+ Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_ETS_COMPRESSED))
? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
Uint32 creation = pid_creation(pid);
- byte* tagp = ep++;
+
+ *ep++ = NEW_PID_EXT;
/* insert atom here containing host and sysname */
ep = enc_atom(acmp, sysname, ep, dflags);
@@ -2481,15 +2879,8 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
ep += 4;
put_int32(os, ep);
ep += 4;
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = PID_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_pid(pid));
- *tagp = NEW_PID_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
return ep;
}
@@ -2609,7 +3000,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
if (tag == PID_EXT) {
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
return NULL;
}
} else {
@@ -2653,7 +3044,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
#define ENC_LAST_ARRAY_ELEMENT ((Eterm) 6)
static byte*
-enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint64 dflags,
struct erl_off_heap_header** off_heap)
{
byte *res;
@@ -2662,7 +3053,8 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
}
static int
-enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
+enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
+ Uint64 dflags,
struct erl_off_heap_header** off_heap, Sint *reds, byte **res)
{
DECLARE_WSTACK(s);
@@ -2673,10 +3065,12 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Eterm val;
FloatDef f;
Sint r = 0;
+ int use_iov = 0;
if (ctx) {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
r = *reds;
+ use_iov = !!ctx->iov;
if (ctx->wstack.wstart) { /* restore saved stacks and byte pointer */
WSTACK_RESTORE(s, &ctx->wstack);
@@ -2867,28 +3261,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case REF_DEF:
case EXTERNAL_REF_DEF: {
Uint32 *ref_num;
- Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj))
+ Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_ref(obj))
? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
Uint32 creation = ref_creation(obj);
- byte* tagp = ep++;
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
erts_magic_ref_save_bin(obj);
+ *ep++ = NEWER_REFERENCE_EXT;
i = ref_no_numbers(obj);
put_int16(i, ep);
ep += 2;
ep = enc_atom(acmp, sysname, ep, dflags);
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = NEW_REFERENCE_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_ref(obj));
- *tagp = NEWER_REFERENCE_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
put_int32(ref_num[j], ep);
@@ -2898,24 +3285,17 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
}
case PORT_DEF:
case EXTERNAL_PORT_DEF: {
- Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj))
+ Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_port(obj))
? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
Uint32 creation = port_creation(obj);
- byte* tagp = ep++;
+ *ep++ = NEW_PORT_EXT;
ep = enc_atom(acmp, sysname, ep, dflags);
j = port_number(obj);
put_int32(j, ep);
ep += 4;
- if (creation <= ERTS_MAX_LOCAL_CREATION) {
- *tagp = PORT_EXT;
- *ep++ = creation;
- } else {
- ASSERT(is_external_port(obj));
- *tagp = NEW_PORT_EXT;
- put_int32(creation, ep);
- ep += 4;
- }
+ put_int32(creation, ep);
+ ep += 4;
break;
}
case LIST_DEF:
@@ -3042,9 +3422,44 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Uint bitsize;
byte* bytes;
byte* data_dst;
+ Uint off_heap_bytesize = 0;
+ Uint off_heap_tail;
+ Eterm pb_term;
+ Binary *pb_val;
+ ASSERT(!(dflags & DFLAG_PENDING_CONNECT) || (ctx && ctx->iov));
+
ERTS_GET_BINARY_BYTES(obj, bytes, bitoffs, bitsize);
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (use_iov) {
+ if (bitoffs == 0) {
+ ProcBin* pb = (ProcBin*) binary_val(obj);
+ off_heap_bytesize = pb->size;
+ if (off_heap_bytesize <= ERL_ONHEAP_BIN_LIMIT)
+ off_heap_bytesize = 0;
+ else {
+ pb_term = obj;
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*)pb;
+ pb_term = sub->orig;
+ pb = (ProcBin*) binary_val(pb_term);
+ }
+ if (pb->thing_word != HEADER_PROC_BIN)
+ off_heap_bytesize = 0;
+ else {
+ if (pb->flags) {
+ char* before_realloc = pb->val->orig_bytes;
+ erts_emasculate_writable_binary(pb);
+ bytes += (pb->val->orig_bytes - before_realloc);
+ ASSERT((byte *) &pb->val->orig_bytes[0] <= bytes
+ && bytes < ((byte *) &pb->val->orig_bytes[0]
+ + pb->val->orig_size));
+ }
+ pb_val = pb->val;
+ }
+ }
+ }
+ }
+ else if (dflags & DFLAG_ETS_COMPRESSED) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint bytesize = pb->size;
if (pb->thing_word == HEADER_SUB_BIN) {
@@ -3087,20 +3502,51 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
j = binary_size(obj);
put_int32(j, ep);
ep += 4;
- data_dst = ep;
- ep += j;
+ if (off_heap_bytesize)
+ off_heap_tail = 0;
+ else {
+ data_dst = ep;
+ ep += j;
+ }
} else if (dflags & DFLAG_BIT_BINARIES) {
/* Bit-level binary. */
- *ep++ = BIT_BINARY_EXT;
- j = binary_size(obj);
- put_int32((j+1), ep);
- ep += 4;
- *ep++ = bitsize;
- ep[j] = 0; /* Zero unused bits at end of binary */
- data_dst = ep;
- ep += j + 1;
- if (ctx)
- ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ j = off_heap_bytesize;
+ if (!j) {
+ pb_val = NULL;
+ pb_term = THE_NON_VALUE;
+ j = binary_size(obj);
+ }
+ data_dst = hopefull_bit_binary(ctx, &ep, pb_val, pb_term,
+ bytes, bitoffs, bitsize, j);
+ if (!data_dst)
+ break; /* off heap binary referred... */
+ ASSERT(!off_heap_bytesize);
+ off_heap_tail = 0;
+ /*
+ * Trailing bits already written by hopefull_bit_binary();
+ * now go copy all whole octets...
+ */
+ bitsize = 0;
+ }
+ else {
+ *ep++ = BIT_BINARY_EXT;
+ j = binary_size(obj);
+ put_int32((j+1), ep);
+ ep += 4;
+ *ep++ = bitsize;
+ if (off_heap_bytesize) {
+ /* trailing bits */
+ ep[0] = 0;
+ copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize);
+ off_heap_tail = 1;
+ }
+ else {
+ ep[j] = 0; /* Zero unused bits at end of binary */
+ data_dst = ep;
+ ep += j + 1;
+ }
+ }
} else {
/*
* Bit-level binary, but the receiver doesn't support it.
@@ -3112,13 +3558,30 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
j = binary_size(obj);
put_int32((j+1), ep);
ep += 4;
- ep[j] = 0; /* Zero unused bits at end of binary */
- data_dst = ep;
- ep += j+1;
- *ep++ = SMALL_INTEGER_EXT;
- *ep++ = bitsize;
+
+ if (off_heap_bytesize) {
+ /* trailing bits */
+ ep[0] = 0;
+ copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize);
+ ep[1] = SMALL_INTEGER_EXT;
+ ep[2] = bitsize;
+ off_heap_tail = 3;
+ }
+ else {
+ ep[j] = 0; /* Zero unused bits at end of binary */
+ data_dst = ep;
+ ep += j+1;
+ *ep++ = SMALL_INTEGER_EXT;
+ *ep++ = bitsize;
+ }
}
- if (ctx && j > r * TERM_TO_BINARY_MEMCPY_FACTOR) {
+ if (off_heap_bytesize) {
+ ASSERT(pb_val);
+ store_in_vec(ctx, ep, pb_val, pb_term,
+ bytes, off_heap_bytesize);
+ ep += off_heap_tail;
+ }
+ else if (ctx && j > r * TERM_TO_BINARY_MEMCPY_FACTOR) {
WSTACK_PUSH5(s, (UWord)data_dst, (UWord)bytes, bitoffs,
ENC_BIN_COPY, 8*j + bitsize);
} else {
@@ -3130,14 +3593,15 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case EXPORT_DEF:
{
Export* exp = *((Export **) (export_val(obj) + 1));
- if ((dflags & DFLAG_EXPORT_PTR_TAG) != 0) {
+ ASSERT(!(dflags & DFLAG_PENDING_CONNECT) || (ctx && ctx->iov));
+ if (dflags & DFLAG_PENDING_CONNECT)
+ hopefull_export(ctx, &ep, exp, dflags, off_heap);
+ else if ((dflags & DFLAG_EXPORT_PTR_TAG) != 0) {
*ep++ = EXPORT_EXT;
ep = enc_atom(acmp, exp->info.mfa.module, ep, dflags);
ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags);
ep = enc_term(acmp, make_small(exp->info.mfa.arity),
ep, dflags, off_heap);
- if (ctx)
- ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
} else {
/* Tag, arity */
*ep++ = SMALL_TUPLE_EXT;
@@ -3187,11 +3651,276 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
if (ctx) {
ASSERT(ctx->wstack.wstart == NULL);
*reds = r;
+ if (use_iov)
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
}
*res = ep;
return 0;
}
+static ERTS_INLINE void
+store_in_vec_aux(Uint *szp,
+ Sint *vlenp,
+ SysIOVec *iov,
+ ErlDrvBinary **binv,
+ Eterm *termv,
+ Binary *bin,
+ Eterm term,
+ byte *ptr,
+ Uint len,
+ Sint *fixp,
+ ErlIOVec **frag_eiovpp,
+ Uint frag_size)
+{
+ ErlDrvBinary *dbin = Binary2ErlDrvBinary(bin);
+ Uint size = 0;
+ int vlen = *vlenp;
+ Uint iov_len;
+ ErlIOVec *feiovp;
+ Sint fix = *fixp;
+
+ ASSERT(((byte *) &bin->orig_bytes[0]) <= ptr);
+ ASSERT(ptr + len <= ((byte *) &bin->orig_bytes[0]) + bin->orig_size);
+
+ if (fix >= 0) {
+ feiovp = frag_eiovpp[fix];
+ ASSERT(0 < feiovp->size);
+ ASSERT(feiovp->size <= frag_size);
+ if (feiovp->size != frag_size) {
+ /* current fragment not full yet... */
+ iov_len = frag_size - feiovp->size;
+ if (len < iov_len)
+ iov_len = len;
+ goto store_iov_data;
+ }
+ }
+
+ while (len) {
+ /* Start new fragment... */
+
+ feiovp = frag_eiovpp[++fix];
+ ASSERT(fix >= 0);
+
+ if (termv) {
+ termv[vlen] = THE_NON_VALUE;
+ termv[vlen+1] = THE_NON_VALUE;
+ }
+
+ feiovp->vsize = 2;
+ feiovp->size = 0;
+ feiovp->iov = &iov[vlen];
+ feiovp->binv = &binv[vlen];
+
+ /* entry for driver header */
+ iov[vlen].iov_base = NULL;
+ iov[vlen].iov_len = 0;
+ binv[vlen] = NULL;
+ vlen++;
+
+ /* entry for dist header */
+ iov[vlen].iov_base = NULL;
+ iov[vlen].iov_len = 0;
+ binv[vlen] = NULL;
+ vlen++;
+
+ iov_len = len < frag_size ? len : frag_size;
+
+ store_iov_data:
+
+ ASSERT(iov_len);
+
+ do {
+ Uint iov_len_left;
+
+ if (iov_len <= MAX_SYSIOVEC_IOVLEN)
+ iov_len_left = 0;
+ else {
+ iov_len_left = iov_len - MAX_SYSIOVEC_IOVLEN;
+ iov_len = MAX_SYSIOVEC_IOVLEN;
+ }
+
+ iov[vlen].iov_base = ptr;
+ iov[vlen].iov_len = iov_len;
+ binv[vlen] = dbin;
+ if (termv)
+ termv[vlen] = term;
+ else
+ erts_refc_inc(&bin->intern.refc, 2);
+ size += iov_len;
+ len -= iov_len;
+ ptr += iov_len;
+ vlen++;
+ feiovp->size += iov_len;
+ feiovp->vsize++;
+
+ iov_len = iov_len_left;
+ } while (iov_len);
+ }
+
+ *fixp = fix;
+ *vlenp = vlen;
+ *szp += size;
+}
+
+static void
+store_in_vec(TTBEncodeContext *ctx,
+ byte *ep,
+ Binary *ohbin,
+ Eterm ohpb,
+ byte *ohp,
+ Uint ohsz)
+{
+ byte *cp = ctx->cptr;
+ if (cp != ep) {
+ /* save data in common binary... */
+ store_in_vec_aux(&ctx->size,
+ &ctx->vlen,
+ ctx->iov,
+ ctx->binv,
+ ctx->termv,
+ ctx->result_bin,
+ THE_NON_VALUE,
+ cp, ep - cp,
+ &ctx->frag_ix,
+ ctx->fragment_eiovs,
+ ctx->fragment_size);
+ ASSERT(ctx->vlen <= ctx->debug_vlen);
+ ASSERT(ctx->frag_ix <= ctx->debug_fragments);
+ ctx->cptr = ep;
+ }
+ if (ohbin) {
+ /* save off-heap binary... */
+ store_in_vec_aux(&ctx->size,
+ &ctx->vlen,
+ ctx->iov,
+ ctx->binv,
+ ctx->termv,
+ ohbin, ohpb, ohp, ohsz,
+ &ctx->frag_ix,
+ ctx->fragment_eiovs,
+ ctx->fragment_size);
+ ASSERT(ctx->vlen <= ctx->debug_vlen);
+ ASSERT(ctx->frag_ix <= ctx->debug_fragments);
+ }
+}
+
+static byte *
+begin_hopefull_data(TTBEncodeContext *ctx, byte *ep)
+{
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
+ ASSERT(ERTS_NO_HIX == (Uint32) get_int32(ctx->hopefull_ixp));
+ put_int32(ctx->vlen, ctx->hopefull_ixp);
+ ctx->hopefull_ixp = ep;
+ put_int32(ERTS_NO_HIX, ep);
+ ep += 4;
+ ctx->cptr = ep;
+ return ep;
+}
+
+static byte *
+end_hopefull_data(TTBEncodeContext *ctx, byte *ep, Uint fallback_size)
+{
+ Uint sz;
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
+ /*
+ * Reserve extra room for fallback if needed. The four
+ * bytes used for hopefull index can be used for
+ * fallback encoding...
+ */
+ sz = ep - ctx->hopefull_ixp;
+ if (fallback_size > sz) {
+ ep += fallback_size - sz;
+ ctx->cptr = ep;
+ }
+ return ep;
+}
+
+static byte *
+hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term,
+ byte *bytes, byte bitoffs, byte bitsize, Uint sz)
+{
+ byte *octets, *ep = *epp;
+
+ ctx->hopefull_flags |= DFLAG_BIT_BINARIES;
+
+ /*
+ * The fallback:
+ *
+ * SMALL_TUPLE_EXT - 1 byte
+ * 2 - 1 byte
+ * BINARY_EXT - 1 byte
+ * whole octet size ('sz') - 4 byte
+ * whole octets - 'sz' bytes
+ * trailing bits - 1 byte
+ * SMALL_INTEGER_EXT - 1 byte
+ * bitsize - 1 byte
+ */
+
+ /* bit binary prelude in one hopefull data element */
+ ep = begin_hopefull_data(ctx, ep);
+ *ep++ = BIT_BINARY_EXT;
+ put_int32((sz+1), ep);
+ ep += 4;
+ *ep++ = bitsize;
+ ep = end_hopefull_data(ctx, ep, 1+1+1+4);
+
+ /* All whole octets... */
+ if (pb_val) {
+ octets = NULL;
+ store_in_vec(ctx, ep, pb_val, pb_term, bytes, sz);
+ }
+ else {
+ /* ... will be copied here afterwards */
+ octets = ep;
+ ep += sz;
+ }
+
+ /* copy trailing bits into new hopefull data element */
+ ep = begin_hopefull_data(ctx, ep);
+ *ep = 0;
+
+ copy_binary_to_buffer(ep, 0, bytes + sz, bitoffs, bitsize);
+ ep++;
+
+ ep = end_hopefull_data(ctx, ep, 1+1+1);
+ *epp = ep;
+
+ return octets;
+}
+
+static void
+hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags,
+ struct erl_off_heap_header** off_heap)
+{
+ Uint fallback_sz;
+ byte *ep = *epp, *mod_start;
+
+ /*
+ * The fallback:
+ *
+ * SMALL_TUPLE_EXT - 1 byte
+ * 2 - 1 byte
+ * module atom... - M bytes
+ * function atom... - F bytes
+ */
+
+ ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG;
+
+ ep = begin_hopefull_data(ctx, ep);
+
+ *ep++ = EXPORT_EXT;
+ mod_start = ep;
+ ep = enc_atom(NULL, exp->info.mfa.module, ep, dflags);
+ ep = enc_atom(NULL, exp->info.mfa.function, ep, dflags);
+ fallback_sz = 2 + (ep - mod_start);
+ ep = enc_term(NULL, make_small(exp->info.mfa.arity),
+ ep, dflags, off_heap);
+
+ ep = end_hopefull_data(ctx, ep, fallback_sz);
+
+ *epp = ep;
+}
+
/** @brief Is it a list of bytes not longer than MAX_STRING_LEN?
* @param lenp out: string length or number of list cells traversed
* @return true/false
@@ -3611,7 +4340,7 @@ dec_term_atom_common:
if (tag == PORT_EXT) {
cre = get_int8(ep);
ep++;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
}
@@ -3658,7 +4387,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
goto ref_ext_common;
@@ -3672,7 +4401,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_valid_creation(cre)) {
+ if (!is_tiny_creation(cre)) {
goto error;
}
r0 = get_int32(ep);
@@ -4067,73 +4796,6 @@ dec_term_atom_common:
next = &(funp->creator);
break;
}
- case FUN_EXT:
- {
- ErlFunThing* funp = (ErlFunThing *) hp;
- Eterm module;
- Sint old_uniq;
- Sint old_index;
- unsigned num_free;
- int i;
- Eterm temp;
-
- num_free = get_int32(ep);
- ep += 4;
- hp += ERL_FUN_SIZE;
- hp += num_free;
- factory->hp = hp;
- funp->thing_word = HEADER_FUN;
- funp->num_free = num_free;
- *objp = make_fun(funp);
-
- /* Creator pid */
- if ((*ep != PID_EXT && *ep != NEW_PID_EXT)
- || (ep = dec_pid(edep, factory, ep+1,
- &funp->creator, *ep))==NULL) {
- goto error;
- }
-
- /* Module */
- if ((ep = dec_atom(edep, ep, &module)) == NULL) {
- goto error;
- }
-
- /* Index */
- if ((ep = dec_term(edep, factory, ep, &temp, NULL, 0)) == NULL) {
- goto error;
- }
- if (!is_small(temp)) {
- goto error;
- }
- old_index = unsigned_val(temp);
-
- /* Uniq */
- if ((ep = dec_term(edep, factory, ep, &temp, NULL, 0)) == NULL) {
- goto error;
- }
- if (!is_small(temp)) {
- goto error;
- }
-
- /*
- * It is safe to link the fun into the fun list only when
- * no more validity tests can fail.
- */
- funp->next = factory->off_heap->first;
- factory->off_heap->first = (struct erl_off_heap_header*)funp;
- old_uniq = unsigned_val(temp);
-
- funp->fe = erts_put_fun_entry(module, old_uniq, old_index);
- funp->arity = funp->fe->address[-1] - num_free;
- hp = factory->hp;
-
- /* Environment */
- for (i = num_free-1; i >= 0; i--) {
- funp->env[i] = (Eterm) next;
- next = funp->env + i;
- }
- break;
- }
case ATOM_INTERNAL_REF2:
n = get_int16(ep);
ep += 2;
@@ -4302,10 +4964,11 @@ error_hamt:
(except for cached atoms) */
static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
Eterm obj,
- unsigned dflags) {
- Uint size;
+ Uint64 dflags) {
+ Uint size = 0;
ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj,
- dflags, NULL, &size);
+ dflags, NULL,
+ &size);
/*
* encode_size_struct2() only allowed when
* we know the result will always be OK!
@@ -4316,18 +4979,23 @@ static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
static ErtsExtSzRes
encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
- unsigned dflags, Sint *reds, Uint *res)
+ Uint64 dflags, Sint *reds, Uint *res)
{
DECLARE_WSTACK(s);
Uint m, i, arity;
- Uint result = 0;
+ Uint result = *res;
Sint r = 0;
+ int vlen = -1;
if (ctx) {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
r = *reds;
- if (ctx->wstack.wstart) { /* restore saved stack */
+ vlen = ctx->vlen;
+
+ if (!ctx->wstack.wstart)
+ ctx->last_result = result;
+ else { /* restore saved stack */
WSTACK_RESTORE(s, &ctx->wstack);
result = ctx->result;
obj = ctx->obj;
@@ -4346,6 +5014,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
*reds = 0;
ctx->obj = obj;
ctx->result = result;
+ ctx->vlen = vlen;
WSTACK_SAVE(s, &ctx->wstack);
return ERTS_EXT_SZ_YIELD;
}
@@ -4354,7 +5023,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result++;
break;
case ATOM_DEF:
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ if (dflags & DFLAG_ETS_COMPRESSED) {
if (atom_val(obj) >= (1<<16)) {
result += 1 + 3;
}
@@ -4408,30 +5077,21 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + 1 + i; /* tag,size,sign,digits */
break;
case EXTERNAL_PID_DEF:
- if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case PID_DEF:
result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) +
- 4 + 4 + 1);
+ 4 + 4 + 4);
break;
case EXTERNAL_REF_DEF:
- if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case REF_DEF:
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
i = ref_no_numbers(obj);
result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) +
- 1 + 4*i);
+ 4 + 4*i);
break;
case EXTERNAL_PORT_DEF:
- if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION)
- result += 3;
- /*fall through*/
case PORT_DEF:
result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
- 4 + 1);
+ 4 + 4);
break;
case LIST_DEF: {
int is_str = is_external_string(obj, &m);
@@ -4525,39 +5185,131 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
break;
case BINARY_DEF: {
ProcBin* pb = (ProcBin*) binary_val(obj);
- Uint tot_bytes = pb->size;
- if (!(dflags & DFLAG_INTERNAL_TAGS)) {
-#ifdef ARCH_64
- if (tot_bytes >= (Uint) 0xffffffff) {
- if (pb->thing_word == HEADER_SUB_BIN) {
- ErlSubBin* sub = (ErlSubBin*) pb;
- tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
- }
- if (tot_bytes > (Uint) 0xffffffff) {
- WSTACK_DESTROY(s);
- return ERTS_EXT_SZ_SYSTEM_LIMIT;
- }
- }
-#endif
- }
- else {
+ Uint bin_size = pb->size;
+ byte bitoffs = 0;
+ byte bitsize = 0;
+ if (dflags & DFLAG_ETS_COMPRESSED) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint sub_extra = 0;
if (pb->thing_word == HEADER_SUB_BIN) {
ErlSubBin* sub = (ErlSubBin*) pb;
+ bitoffs = sub->bitoffs;
+ bitsize = sub->bitsize;
pb = (ProcBin*) binary_val(sub->orig);
sub_extra = 2; /* bitoffs and bitsize */
- tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ bin_size += (bitoffs + bitsize + 7) / 8;
}
if (pb->thing_word == HEADER_PROC_BIN
- && heap_bin_size(tot_bytes) > PROC_BIN_SIZE) {
+ && heap_bin_size(bin_size) > PROC_BIN_SIZE) {
result += 1 + sub_extra + sizeof(ProcBin);
break;
}
+ }
+ else {
+#ifdef ARCH_64
+ if (bin_size >= (Uint) 0xffffffff) {
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ bin_size += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ }
+ if (bin_size > (Uint) 0xffffffff) {
+ WSTACK_DESTROY(s);
+ return ERTS_EXT_SZ_SYSTEM_LIMIT;
+ }
+ }
+#endif
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ bitoffs = sub->bitoffs;
+ bitsize = sub->bitsize;
+ pb = (ProcBin*) binary_val(sub->orig);
+ }
+ if (vlen >= 0) {
+ Uint csz;
+ if (pb->thing_word == HEADER_PROC_BIN
+ && bitoffs == 0
+ && bin_size > ERL_ONHEAP_BIN_LIMIT) {
+ Uint trailing_result;
+ if (bitsize == 0) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */);
+ trailing_result = 0;
+ }
+ else if (dflags & DFLAG_BIT_BINARIES) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */
+ + 1 /* trailing bitsize */);
+ trailing_result = 1 /* trailing bits */;
+ }
+ else {
+ /* sigh... */
+ result += (1 /* SMALL_TUPLE_EXT */
+ + 1 /* 2 tuple size */
+ + 1 /* BINARY_EXT */
+ + 4 /* binary size */);
+ trailing_result = (1 /* SMALL_INTEGER_EXT */
+ + 1 /* bitsize */);
+ }
+ csz = result - ctx->last_result;
+ ctx->last_result = result;
+ result += trailing_result;
+ vlen += 2; /* data leading up to binary and binary */
+
+ /* potentially multiple elements leading up to binary */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ /* potentially multiple elements for binary */
+ vlen += bin_size/MAX_SYSIOVEC_IOVLEN;
+ ctx->extra_size += bin_size;
+
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ ASSERT(dflags & DFLAG_BIT_BINARIES);
+ vlen += 2; /* for hopefull prolog and epilog */
+ result += (4 /* for hopefull prolog (see below) */
+ + 4); /* for hopefull epilog (see below) */
+ ctx->last_result = result;
+ }
+ break;
+ }
+ }
}
- result += 1 + 4 + binary_size(obj) +
- 5; /* For unaligned binary */
+
+ if (bitsize == 0) {
+ result += (1 /* BIT_BINARY_EXT */
+ + 4 /* size */
+ + bin_size);
+ }
+ else if (dflags & DFLAG_PENDING_CONNECT) {
+ Uint csz = result - ctx->last_result;
+ ASSERT(dflags & DFLAG_BIT_BINARIES);
+ /* potentially multiple elements leading up to binary */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ vlen++; /* hopefull prolog */
+ /*
+ * Size for hopefull prolog is max of
+ * - fallback: 1 + 1 + 1 + 4
+ * - hopfull index + bit binary prolog: 4 + 1 + 4 + 1
+ */
+ result += 4 + 1 + 4 + 1;
+ /* potentially multiple elements for binary */
+ vlen += bin_size/MAX_SYSIOVEC_IOVLEN + 1;
+ result += bin_size;
+ vlen++; /* hopefull epiolog */
+ /*
+ * Size for hopefull epiolog is max of
+ * - fallback: 1 + 1 + 1
+ * - hopfull index + bit binary epilog: 4 + 1
+ */
+ result += 4 + 1;
+ ctx->last_result = result;
+ }
+ else if (dflags & DFLAG_BIT_BINARIES) {
+ result += 1 + 4 + 1 + bin_size + 1;
+ }
+ else {
+ /* Sigh... */
+ result += 1 + 1 + 1 + 4 + bin_size + 1 + 1 + 1;
+ }
break;
}
case FUN_DEF:
@@ -4584,10 +5336,25 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
case EXPORT_DEF:
{
Export* ep = *((Export **) (export_val(obj) + 1));
+ Uint tmp_result = result;
result += 1;
result += encode_size_struct2(acmp, ep->info.mfa.module, dflags);
result += encode_size_struct2(acmp, ep->info.mfa.function, dflags);
result += encode_size_struct2(acmp, make_small(ep->info.mfa.arity), dflags);
+ if (dflags & DFLAG_PENDING_CONNECT) {
+ Uint csz;
+ /*
+ * Fallback is 1 + 1 + Module size + Function size, that is,
+ * the hopefull index + hopefull encoding is larger...
+ */
+ ASSERT(dflags & DFLAG_EXPORT_PTR_TAG);
+ csz = tmp_result - ctx->last_result;
+ /* potentially multiple elements leading up to hopefull entry */
+ vlen += csz/MAX_SYSIOVEC_IOVLEN;
+ vlen++; /* hopefull entry */
+ result += 4; /* hopefull index */
+ ctx->last_result = result;
+ }
}
break;
@@ -4630,6 +5397,14 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
if (ctx) {
ASSERT(ctx->wstack.wstart == NULL);
*reds = r < 0 ? 0 : r;
+
+ if (vlen >= 0) {
+ Uint csz;
+ csz = result - ctx->last_result;
+ if (csz)
+ vlen += csz/MAX_SYSIOVEC_IOVLEN + 1;
+ ctx->vlen = vlen;
+ }
}
*res = result;
return ERTS_EXT_SZ_OK;
@@ -4898,9 +5673,6 @@ init_done:
total_size = get_int32(ep);
CHKSIZE(total_size);
ep += 1+16+4+4;
- /*FALLTHROUGH*/
-
- case FUN_EXT:
CHKSIZE(4);
num_free = get_int32(ep);
ep += 4;
@@ -4911,6 +5683,12 @@ init_done:
heap_size += ERL_FUN_SIZE + num_free;
break;
}
+ case FUN_EXT:
+ /*
+ * OTP 23: No longer support decoding the old fun
+ * representation.
+ */
+ goto error;
case ATOM_INTERNAL_REF2:
SKIP(2+atom_extra_skip);
atom_extra_skip = 0;
@@ -4968,181 +5746,374 @@ error:
#undef CHKSIZE
}
+#define ERTS_TRANSCODE_REDS_FACT 4
-struct transcode_context {
- enum {
- TRANSCODE_DEC_MSG_SIZE,
- TRANSCODE_DEC_MSG,
- TRANSCODE_ENC_CTL,
- TRANSCODE_ENC_MSG
- }state;
- Eterm ctl_term;
- Eterm* ctl_heap;
- ErtsHeapFactory ctl_factory;
- Eterm* msg_heap;
- B2TContext b2t;
- TTBEncodeContext ttb;
-#ifdef DEBUG
- ErtsDistOutputBuf* dbg_ob;
-#endif
-};
-
-void transcode_free_ctx(DistEntry* dep)
+Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
+ DistEntry* dep,
+ Uint64 dflags,
+ Sint reds)
{
- struct transcode_context* ctx = dep->transcode_ctx;
+ ErlIOVec* eiov = ob->eiov;
+ SysIOVec* iov = eiov->iov;
+ byte *hdr;
+ Uint64 hopefull_flags;
+ Uint32 hopefull_ix, payload_ix;
+ Sint start_r, r;
+ Uint new_len;
+ byte *ep;
- erts_factory_close(&ctx->ctl_factory);
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->ctl_heap);
+ if (reds < 0)
+ return reds;
- if (ctx->msg_heap) {
- erts_factory_close(&ctx->b2t.u.dc.factory);
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx->msg_heap);
+ /*
+ * HOPEFUL_DATA header always present in io vector
+ * element 1:
+ *
+ * +---+--------------+-----------+----------+
+ * |'H'|Hopefull Flags|Hopefull IX|Payload IX|
+ * +---+--------------+-----------+----------+
+ * 1 8 4 4
+ *
+ * Hopefull flags: Flags corresponding to actual
+ * hopefull encodings in this
+ * buffer.
+ * Hopefull IX: Vector index of first hopefull
+ * encoding. Each hopefull encoding
+ * is preceeded by 4 bytes containing
+ * next vector index of hopefull
+ * encoding. ERTS_NO_HIX marks the
+ * end.
+ * Payload IX: Vector index of the beginning
+ * of the payload if there is
+ * one; otherwise, zero.
+ */
+ hdr = (byte *) iov[1].iov_base;
+
+ ASSERT(HOPEFUL_DATA == *((byte *)iov[1].iov_base));
+ ASSERT(iov[1].iov_len == 1+8+4+4);
+
+ /* Control message always begin in vector element 2 */
+ ep = iov[2].iov_base;
+ ASSERT(ep[0] == SMALL_TUPLE_EXT || ep[0] == LARGE_TUPLE_EXT);
+
+ if (~dflags & (DFLAG_DIST_MONITOR | DFLAG_DIST_MONITOR_NAME)
+ && ep[0] == SMALL_TUPLE_EXT
+ && ep[1] == 4
+ && ep[2] == SMALL_INTEGER_EXT
+ && (ep[3] == DOP_MONITOR_P ||
+ ep[3] == DOP_MONITOR_P_EXIT ||
+ ep[3] == DOP_DEMONITOR_P)) {
+ /*
+ * Receiver does not support process monitoring.
+ * Suppress monitor control msg (see erts_dsig_send_monitor)
+ * by converting it to an empty (tick) packet.
+ */
+ ob->eiov->vsize = 1;
+ ob->eiov->size = 0;
+ return reds;
}
- erts_free(ERTS_ALC_T_DIST_TRANSCODE, ctx);
- dep->transcode_ctx = NULL;
-}
-Sint transcode_dist_obuf(ErtsDistOutputBuf* ob,
- DistEntry* dep,
- Uint32 dflags,
- Sint reds)
-{
- Sint hsz;
- byte* decp;
- const int have_msg = !!ob->msg_start;
- int i;
- struct transcode_context* ctx = dep->transcode_ctx;
+ hdr++;
+ hopefull_flags = get_int64(hdr);
- if (!ctx) { /* first call for 'ob' */
- ASSERT(!(ob->hopefull_flags & ~(Uint)(DFLAG_BIT_BINARIES |
- DFLAG_EXPORT_PTR_TAG)));
- if (~dflags & ob->hopefull_flags) {
- /*
- * Receiver does not support bitstrings and/or export funs
- * and output buffer contains such message tags (hopefull_flags).
- * Must transcode control and message terms to use tuple fallbacks.
- */
- ctx = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, sizeof(struct transcode_context));
- dep->transcode_ctx = ctx;
- #ifdef DEBUG
- ctx->dbg_ob = ob;
- #endif
-
- hsz = decoded_size(ob->extp, ob->ext_endp, 0, NULL);
- ctx->ctl_heap = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, hsz*sizeof(Eterm));
- erts_factory_tmp_init(&ctx->ctl_factory, ctx->ctl_heap, hsz, ERTS_ALC_T_DIST_TRANSCODE);
- ctx->msg_heap = NULL;
-
- decp = dec_term(NULL, &ctx->ctl_factory, ob->extp, &ctx->ctl_term, NULL, 0);
- if (have_msg) {
- ASSERT(decp == ob->msg_start); (void)decp;
- ctx->b2t.u.sc.ep = NULL;
- ctx->b2t.state = B2TSize;
- ctx->b2t.aligned_alloc = NULL;
- ctx->b2t.b2ts.exttmp = 0;
- ctx->state = TRANSCODE_DEC_MSG_SIZE;
- }
- else {
- ASSERT(decp == ob->ext_endp);
- ctx->state = TRANSCODE_ENC_CTL;
- }
+ hdr += 8;
+ hopefull_ix = get_int32(hdr);
+
+ if ((~dflags & DFLAG_SPAWN)
+ && ep[0] == SMALL_TUPLE_EXT
+ && ((ep[1] == 6
+ && ep[2] == SMALL_INTEGER_EXT
+ && ep[3] == DOP_SPAWN_REQUEST)
+ || (ep[1] == 8
+ && ep[2] == SMALL_INTEGER_EXT
+ && ep[3] == DOP_SPAWN_REQUEST_TT))) {
+ /*
+ * Receiver does not support distributed spawn. Convert
+ * this packet to an empty (tick) packet, and inform
+ * spawning process that this is not supported...
+ */
+ ErtsHeapFactory factory;
+ Eterm ctl_msg, ref, pid, token, *tp, *hp;
+ Uint buf_sz;
+ byte *buf_start, *buf_end;
+ byte *ptr;
+ Uint hsz;
+
+ hdr += 4;
+ payload_ix = get_int32(hdr);
+ ASSERT(payload_ix >= 3);
+
+ if (payload_ix == 3) {
+ /* The whole control message is in iov[2].iov_base */
+ buf_sz = (Uint) iov[2].iov_len;
+ buf_start = (byte *) iov[2].iov_base;
+ buf_end = buf_start + buf_sz;
}
- else if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) {
- /*
- * No need for full transcoding, but primitive receiver (erl_/jinterface)
- * expects VERSION_MAGIC before both control and message terms.
- */
- if (ob->msg_start) {
- Sint ctl_bytes = ob->msg_start - ob->extp;
- ASSERT(ob->extp < ob->msg_start && ob->msg_start < ob->ext_endp);
- /* Move control term back 1 byte to make room */
- sys_memmove(ob->extp-1, ob->extp, ctl_bytes);
- *--(ob->msg_start) = VERSION_MAGIC;
- --(ob->extp);
- reds -= ctl_bytes / (B2T_BYTES_PER_REDUCTION * B2T_MEMCPY_FACTOR);
+ else {
+ /* Control message over multiple buffers... */
+ int ix;
+ buf_sz = 0;
+ for (ix = 2; ix < payload_ix; ix++)
+ buf_sz += iov[ix].iov_len;
+ ptr = buf_start = erts_alloc(ERTS_ALC_T_TMP, buf_sz);
+ buf_end = buf_start + buf_sz;
+ for (ix = 2; ix < payload_ix; ix++) {
+ sys_memcpy((void *) ptr,
+ (void *) iov[ix].iov_base,
+ iov[ix].iov_len);
+ ptr += iov[ix].iov_len;
}
- *--(ob->extp) = VERSION_MAGIC;
- goto done;
}
- else
- goto done;
- }
- else { /* continue after yield */
- ASSERT(ctx->dbg_ob == ob);
- }
- ctx->b2t.reds = reds * B2T_BYTES_PER_REDUCTION;
- switch (ctx->state) {
- case TRANSCODE_DEC_MSG_SIZE:
- hsz = decoded_size(ob->msg_start, ob->ext_endp, 0, &ctx->b2t);
- if (ctx->b2t.state == B2TSize) {
- return -1;
+ hsz = decoded_size(buf_start, buf_end, 0, NULL);
+ hp = erts_alloc(ERTS_ALC_T_TMP, hsz*sizeof(Eterm));
+ erts_factory_tmp_init(&factory, hp, hsz, ERTS_ALC_T_TMP);
+
+ ptr = dec_term(NULL, &factory, buf_start, &ctl_msg, NULL, 0);
+ ASSERT(ptr); (void)ptr;
+
+ ASSERT(is_tuple_arity(ctl_msg, 6)
+ || is_tuple_arity(ctl_msg, 8));
+ tp = tuple_val(ctl_msg);
+ ASSERT(tp[1] == make_small(DOP_SPAWN_REQUEST)
+ || tp[1] == make_small(DOP_SPAWN_REQUEST_TT));
+
+ ref = tp[2];
+ pid = tp[3];
+ if (tp[1] == make_small(DOP_SPAWN_REQUEST))
+ token = NIL;
+ else {
+ token = tp[8];
+ erts_seq_trace_update_node_token(token);
}
- ASSERT(ctx->b2t.state == B2TDecodeInit);
- ctx->msg_heap = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, hsz*sizeof(Eterm));
- ctx->b2t.u.dc.ep = ob->msg_start;
- ctx->b2t.u.dc.res = (Eterm) NULL;
- ctx->b2t.u.dc.next = &ctx->b2t.u.dc.res;
- erts_factory_tmp_init(&ctx->b2t.u.dc.factory,
- ctx->msg_heap, hsz, ERTS_ALC_T_DIST_TRANSCODE);
- ctx->b2t.u.dc.flat_maps.wstart = NULL;
- ctx->b2t.u.dc.hamt_array.pstart = NULL;
- ctx->b2t.state = B2TDecode;
-
- ctx->state = TRANSCODE_DEC_MSG;
- case TRANSCODE_DEC_MSG:
- if (ctx->b2t.reds <= 0)
- ctx->b2t.reds = 1;
- decp = dec_term(NULL, NULL, NULL, NULL, &ctx->b2t, 0);
- if (ctx->b2t.state < B2TDone) {
- return -1;
- }
- ASSERT(ctx->b2t.state == B2TDone);
- ASSERT(decp && decp <= ob->ext_endp);
- reds = ctx->b2t.reds / B2T_BYTES_PER_REDUCTION;
- b2t_destroy_context(&ctx->b2t);
-
- ctx->state = TRANSCODE_ENC_CTL;
- case TRANSCODE_ENC_CTL:
- if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) {
- ASSERT(!(dflags & DFLAG_NO_MAGIC));
- ob->extp -= 2; /* VERSION_MAGIC x 2 */
- }
- ob->ext_endp = ob->extp;
- i = erts_encode_dist_ext(ctx->ctl_term, &ob->ext_endp, dflags,
- NULL, NULL, NULL);
- ASSERT(i == 0); (void)i;
- ASSERT(ob->ext_endp <= ob->alloc_endp);
+ ASSERT(is_internal_ordinary_ref(tp[2]));
+ ASSERT(is_internal_pid(tp[3]));
+
+ (void) erts_proc_sig_send_dist_spawn_reply(dep->sysname,
+ ref, pid,
+ NULL, am_notsup,
+ token);
+
+ erts_factory_close(&factory);
+ erts_free(ERTS_ALC_T_TMP, hp);
+ if (buf_start != (byte *) iov[2].iov_base)
+ erts_free(ERTS_ALC_T_TMP, buf_start);
+
+ ob->eiov->vsize = 1;
+ ob->eiov->size = 0;
+
+ reds -= 4;
+
+ if (reds < 0)
+ return 0;
+ return reds;
+ }
+
+ start_r = r = reds*ERTS_TRANSCODE_REDS_FACT;
- if (!have_msg) {
- break;
+ if (~dflags & hopefull_flags) {
+
+ while (hopefull_ix != ERTS_NO_HIX) {
+ Uint32 new_hopefull_ix;
+
+ if (r <= 0) { /* yield... */
+ /* save current hopefull_ix... */
+ ep = (byte *) iov[1].iov_base;
+ ep += 5;
+ put_int32(hopefull_ix, ep);
+ return -1;
+ }
+
+ /* Read next hopefull index */
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ ep -= 4;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ switch (*ep) {
+
+ case EXPORT_EXT: {
+ byte *start_ep, *end_ep;
+ Eterm module, function;
+ if (!(hopefull_flags & DFLAG_EXPORT_PTR_TAG))
+ break;
+ /* Read original encoding... */
+ ep++;
+ start_ep = ep;
+ ep = dec_atom(NULL, ep, &module);
+ ASSERT(ep && is_atom(module));
+ ep = dec_atom(NULL, ep, &function);
+ ASSERT(ep && is_atom(function));
+ end_ep = ep;
+ ASSERT(*ep == SMALL_INTEGER_EXT
+ || *ep == INTEGER_EXT
+ || *ep == SMALL_BIG_EXT
+ || *ep == LARGE_BIG_EXT);
+
+ /*
+ * module and function atoms are encoded
+ * between start_ep and end_ep. Prepend a
+ * 2-tuple tag before the atoms and
+ * remove arity at end.
+ */
+
+ /* write fallback */
+
+ ep = start_ep;
+ ep--;
+ put_int8(2, ep);
+ ep--;
+ *ep = SMALL_TUPLE_EXT;
+
+ iov[hopefull_ix].iov_base = ep;
+
+ /* Update iov sizes... */
+ new_len = end_ep - ep;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+ break;
+ }
+
+ case BIT_BINARY_EXT: {
+ Uint bin_sz;
+ byte bitsize, epilog_byte;
+ ASSERT(hopefull_ix != ERTS_NO_HIX);
+ if (!(hopefull_flags & DFLAG_BIT_BINARIES)) {
+ /* skip to epilog... */
+ hopefull_ix = new_hopefull_ix;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ ep -= 4;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+ break;
+ }
+
+ /* read original encoded prolog... */
+ ep++;
+ bin_sz = get_int32(ep);
+ ep += 4;
+ bitsize = *ep++;
+
+ /* write fallback prolog... */
+ iov[hopefull_ix].iov_base -= 4;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+
+ *ep++ = SMALL_TUPLE_EXT;
+ *ep++ = 2;
+ *ep++ = BINARY_EXT;
+ put_int32(bin_sz, ep);
+ ep += 4;
+
+ /* Update iov sizes... */
+ new_len = ep - (byte *) iov[hopefull_ix].iov_base;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+#ifdef DEBUG
+ /*
+ * The binary data between the prolog and the
+ * epilog should be of size 'bin_sz - 1' and
+ * exists in the iov elements between prolog
+ * and epilog...
+ */
+ {
+ Uint ix, debug_bin_sz = 0;
+ for (ix = hopefull_ix+1; ix < new_hopefull_ix; ix++)
+ debug_bin_sz += iov[ix].iov_len;
+ ASSERT(debug_bin_sz == bin_sz - 1);
+ }
+#endif
+ /* jump to epilog... */
+ hopefull_ix = new_hopefull_ix;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+
+ /* read original encoded epilog... */
+ epilog_byte = *ep;
+
+ ASSERT(1 == iov[hopefull_ix].iov_len);
+
+ iov[hopefull_ix].iov_base -= 4;
+ ep = (byte *) iov[hopefull_ix].iov_base;
+ new_hopefull_ix = get_int32(ep);
+ ASSERT(new_hopefull_ix == ERTS_NO_HIX
+ || (hopefull_ix < new_hopefull_ix
+ && new_hopefull_ix < eiov->vsize));
+
+ /* write fallback epilog... */
+
+ *ep++ = epilog_byte;
+ *ep++ = SMALL_INTEGER_EXT;
+ *ep++ = bitsize;
+
+ /* Update iov sizes... */
+ new_len = ep - (byte *) iov[hopefull_ix].iov_base;
+ eiov->size -= iov[hopefull_ix].iov_len;
+ eiov->size += new_len;
+ iov[hopefull_ix].iov_len = new_len;
+ r--;
+ break;
+ }
+
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected external tag");
+ break;
+ }
+
+ hopefull_ix = new_hopefull_ix;
+ r--;
}
- ob->msg_start = ob->ext_endp;
- ctx->ttb.wstack.wstart = NULL;
- ctx->ttb.flags = dflags;
- ctx->ttb.hopefull_flags = 0;
- ctx->ttb.level = 0;
-
- ctx->state = TRANSCODE_ENC_MSG;
- case TRANSCODE_ENC_MSG:
- reds *= TERM_TO_BINARY_LOOP_FACTOR;
- if (erts_encode_dist_ext(ctx->b2t.u.dc.res, &ob->ext_endp, dflags, NULL,
- &ctx->ttb, &reds)) {
- return -1;
+ }
+
+ /*
+ * Replace hopefull data header with actual header...
+ */
+ ep = (byte *) iov[1].iov_base;
+ eiov->size -= iov[1].iov_len;
+
+ if (dflags & (DFLAG_DIST_HDR_ATOM_CACHE|DFLAG_FRAGMENTS)) {
+ /*
+ * Encoding was done without atom caching but receiver expects
+ * a dist header, so we prepend an empty one.
+ */
+ *ep++ = VERSION_MAGIC;
+ *ep++ = DIST_HEADER;
+ *ep++ = 0; /* NumberOfAtomCacheRefs */
+ }
+ else {
+ hdr += 4;
+ payload_ix = get_int32(hdr);
+
+ if (payload_ix) {
+ ASSERT(0 < payload_ix && payload_ix < eiov->vsize);
+ /* Prepend version magic on payload. */
+ iov[payload_ix].iov_base--;
+ *((byte *) iov[payload_ix].iov_base) = VERSION_MAGIC;
+ iov[payload_ix].iov_len++;
+ eiov->size++;
+ r--;
}
- reds /= TERM_TO_BINARY_LOOP_FACTOR;
- ASSERT(ob->ext_endp <= ob->alloc_endp);
- ASSERT(!ctx->ttb.hopefull_flags);
+ *ep++ = PASS_THROUGH;
+ *ep++ = VERSION_MAGIC;
}
- transcode_free_ctx(dep);
-done:
- if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE))
- *--(ob->extp) = PASS_THROUGH;
+ iov[1].iov_len = ep - (byte *) iov[1].iov_base;
+ eiov->size += iov[1].iov_len;
- if (reds < 0)
- reds = 0;
+ r--;
+
+ /* done... */
+ reds -= (start_r - r)/ERTS_TRANSCODE_REDS_FACT + 1;
+ if (reds < 0)
+ return 0;
return reds;
}
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index e362a6c81f..bc006f83e2 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -60,6 +60,7 @@
#define DIST_HEADER 'D'
#define DIST_FRAG_HEADER 'E'
#define DIST_FRAG_CONT 'F'
+#define HOPEFUL_DATA 'H'
#define ATOM_CACHE_REF 'R'
#define ATOM_INTERNAL_REF2 'I'
#define ATOM_INTERNAL_REF3 'K'
@@ -90,6 +91,8 @@
#undef ERL_NODE_TABLES_BASIC_ONLY
#include "erl_alloc.h"
+#define ERTS_NO_HIX (~((Uint32) 0))
+
#define ERTS_ATOM_CACHE_SIZE 2048
typedef struct cache {
@@ -153,16 +156,19 @@ typedef struct {
/* -------------------------------------------------------------------------- */
+struct TTBSizeContext_;
+struct TTBEncodeContext_;
void erts_init_atom_cache_map(ErtsAtomCacheMap *);
void erts_reset_atom_cache_map(ErtsAtomCacheMap *);
void erts_destroy_atom_cache_map(ErtsAtomCacheMap *);
-void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32);
+void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint64);
-Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *, Uint);
-byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm);
+Uint erts_encode_ext_dist_header_size(struct TTBEncodeContext_ *ctx, ErtsAtomCacheMap *, Uint);
+byte *erts_encode_ext_dist_header_setup(struct TTBEncodeContext_ *ctx, byte *,
+ ErtsAtomCacheMap *, Uint, Eterm);
byte *erts_encode_ext_dist_header_fragment(byte **, Uint, Eterm);
-Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds);
+Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint64 dflags, Sint reds);
struct erts_dsig_send_context;
typedef enum {
@@ -171,12 +177,13 @@ typedef enum {
ERTS_EXT_SZ_SYSTEM_LIMIT
} ErtsExtSzRes;
-ErtsExtSzRes erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
-ErtsExtSzRes erts_encode_dist_ext_size_ctx(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
-struct TTBEncodeContext_;
-int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *,
- struct TTBEncodeContext_ *, Sint* reds);
-
+ErtsExtSzRes erts_encode_dist_ext_size(Eterm term, ErtsAtomCacheMap *acmp,
+ struct TTBSizeContext_ *ctx,
+ Uint* szp, Sint *redsp,
+ Sint *vlenp, Uint *fragments);
+int erts_encode_dist_ext(Eterm, byte **, Uint64, ErtsAtomCacheMap *,
+ struct TTBEncodeContext_ *, Uint *,
+ Sint *);
ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp);
ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp);
Uint erts_encode_ext_size_ets(Eterm);
@@ -207,7 +214,8 @@ Sint erts_decode_ext_size_ets(byte*, Uint);
Eterm erts_decode_ext(ErtsHeapFactory*, byte**, Uint32 flags);
Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*);
-Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags);
+Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint64 flags);
+Eterm erts_debug_term_to_binary(Process *p, Eterm term, Eterm opts);
Sint erts_binary2term_prepare(ErtsBinary2TermState *, byte *, Sint);
void erts_binary2term_abort(ErtsBinary2TermState *);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 8a04a4c7f6..b9af01ee57 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -74,9 +74,8 @@ struct enif_resource_type_t
struct enif_resource_type_t* next; /* list of all resource types */
struct enif_resource_type_t* prev;
struct erl_module_nif* owner; /* that created this type and thus implements the destructor*/
- ErlNifResourceDtor* dtor; /* user destructor function */
- ErlNifResourceStop* stop;
- ErlNifResourceDown* down;
+ ErlNifResourceTypeInit fn;
+ ErlNifResourceTypeInit fn_real;
erts_refc_t refc; /* num of resources of this type (HOTSPOT warning)
+1 for active erl_module_nif */
Eterm module;
@@ -110,6 +109,7 @@ typedef struct ErtsResource_
extern Eterm erts_bld_resource_ref(Eterm** hp, ErlOffHeap*, ErtsResource*);
+extern BeamInstr* erts_call_nif_early(Process* c_p, ErtsCodeInfo* ci);
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
@@ -122,6 +122,10 @@ void erts_nif_demonitored(ErtsResource* resource);
extern void erts_add_taint(Eterm mod_atom);
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
+
+/* Loads the specified NIF. The caller must have code write permission. */
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args);
+
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
extern int erts_nif_get_funcs(struct erl_module_nif*,
@@ -885,6 +889,8 @@ void erts_bif_info_init(void);
/* bif.c */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address);
+
void erts_queue_monitor_message(Process *,
ErtsProcLocks*,
Eterm,
@@ -926,6 +932,8 @@ void erts_queue_release_literals(Process *c_p, ErtsLiteralArea* literals);
#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
(sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
+#define ERTS_LITERAL_AREA_SIZE(AP) \
+ (ERTS_LITERAL_AREA_ALLOC_SIZE((AP)->end - (AP)->start))
extern erts_atomic_t erts_copy_literal_area__;
#define ERTS_COPY_LITERAL_AREA() \
@@ -1135,6 +1143,12 @@ extern int is_node_name_atom(Eterm a);
extern int erts_net_message(Port *, DistEntry *, Uint32 conn_id,
byte *, ErlDrvSizeT, Binary *, byte *, ErlDrvSizeT);
+int erts_dist_pend_spawn_exit_delete(ErtsMonitor *mon);
+int erts_dist_pend_spawn_exit_parent_setup(ErtsMonitor *mon);
+int erts_dist_pend_spawn_exit_parent_wait(Process *c_p,
+ ErtsProcLocks locks,
+ ErtsMonitor *mon);
+
extern void init_dist(void);
extern int stop_dist(void);
@@ -1152,6 +1166,7 @@ void erts_dirty_process_main(ErtsSchedulerData *);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) ERTS_NOINLINE;
/* erl_init.c */
@@ -1291,7 +1306,6 @@ Sint erts_binary_set_loop_limit(Sint limit);
/* erl_bif_persistent.c */
void erts_init_bif_persistent_term(void);
-Uint erts_persistent_term_count(void);
void erts_init_persistent_dumping(void);
extern ErtsLiteralArea** erts_persistent_areas;
extern Uint erts_num_persistent_areas;
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 8954dbb06c..177b7cc3d1 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -30,37 +30,19 @@
#include "hash.h"
/*
-** List of sizes (all are primes)
-*/
-static const int h_size_table[] = {
- 2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
- 1201, 1597,
- 2411, 3203,
- 4813, 6421,
- 9643, 12853,
- 19289, 25717,
- 51437,
- 102877,
- 205759,
- 411527,
- 823117,
- 1646237,
- 3292489,
- 6584983,
- 13169977,
- 26339969,
- 52679969,
- -1
-};
-
-/*
** Get info about hash
**
*/
+#define MAX_SHIFT (ERTS_SIZEOF_TERM * 8)
+
+static int hash_get_slots(Hash *h) {
+ return UWORD_CONSTANT(1) << (MAX_SHIFT - h->shift);
+}
+
void hash_get_info(HashInfo *hi, Hash *h)
{
- int size = h->size;
+ int size = hash_get_slots(h);
int i;
int max_depth = 0;
int objects = 0;
@@ -84,7 +66,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
ASSERT(objects == h->nobjs);
hi->name = h->name;
- hi->size = h->size;
+ hi->size = hash_get_slots(h);
hi->used = used;
hi->objs = h->nobjs;
hi->depth = max_depth;
@@ -118,15 +100,15 @@ hash_table_sz(Hash *h)
int i;
for(i=0;h->name[i];i++);
i++;
- return sizeof(Hash) + h->size*sizeof(HashBucket*) + i;
+ return sizeof(Hash) + hash_get_slots(h)*sizeof(HashBucket*) + i;
}
static ERTS_INLINE void set_thresholds(Hash* h)
{
- h->grow_threshold = (8*h->size)/5; /* grow at 160% load */
- if (h->size_ix > h->min_size_ix)
- h->shrink_threshold = h->size / 5; /* shrink at 20% load */
+ h->grow_threshold = (8*hash_get_slots(h))/5; /* grow at 160% load */
+ if (h->shift < h->max_shift)
+ h->shrink_threshold = hash_get_slots(h) / 5; /* shrink at 20% load */
else
h->shrink_threshold = -1; /* never shrink below inital size */
}
@@ -138,29 +120,27 @@ static ERTS_INLINE void set_thresholds(Hash* h)
Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
{
int sz;
- int ix = 0;
+ int shift = 1;
h->meta_alloc_type = type;
- while (h_size_table[ix] != -1 && h_size_table[ix] < size)
- ix++;
- if (h_size_table[ix] == -1)
- return NULL;
-
- size = h_size_table[ix];
- sz = size*sizeof(HashBucket*);
+ while ((UWORD_CONSTANT(1) << shift) < size)
+ shift++;
- h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
-
- memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
- h->size = size;
- h->size_ix = ix;
- h->min_size_ix = ix;
+ h->shift = MAX_SHIFT - shift;
+ h->max_shift = h->shift;
h->nobjs = 0;
set_thresholds(h);
+
+ sz = hash_get_slots(h) * sizeof(HashBucket*);
+ h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
+ memzero(h->bucket, sz);
+
+ ASSERT(h->shift > 0 && h->shift < 64);
+
return h;
}
@@ -183,7 +163,7 @@ Hash* hash_new(int type, char* name, int size, HashFunctions fun)
*/
void hash_delete(Hash* h)
{
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
int i;
for (i = 0; i < old_size; i++) {
@@ -206,22 +186,20 @@ void hash_delete(Hash* h)
static void rehash(Hash* h, int grow)
{
int sz;
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
HashBucket** new_bucket;
int i;
if (grow) {
- if ((h_size_table[h->size_ix+1]) == -1)
- return;
- h->size_ix++;
+ h->shift--;
}
else {
- if (h->size_ix == 0)
+ if (h->shift == h->max_shift)
return;
- h->size_ix--;
+ h->shift++;
}
- h->size = h_size_table[h->size_ix];
- sz = h->size*sizeof(HashBucket*);
+
+ sz = hash_get_slots(h)*sizeof(HashBucket*);
new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
memzero(new_bucket, sz);
@@ -230,7 +208,7 @@ static void rehash(Hash* h, int grow)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
- int ix = b->hvalue % h->size;
+ Uint ix = hash_get_slot(h, b->hvalue);
b->next = new_bucket[ix];
new_bucket[ix] = b;
b = b_next;
@@ -247,16 +225,7 @@ static void rehash(Hash* h, int grow)
*/
void* hash_get(Hash* h, void* tmpl)
{
- HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
- HashBucket* b = h->bucket[ix];
-
- while(b != (HashBucket*) 0) {
- if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
- return (void*) b;
- b = b->next;
- }
- return (void*) 0;
+ return hash_fetch(h, tmpl, h->fun.hash, h->fun.cmp);
}
/*
@@ -265,7 +234,7 @@ void* hash_get(Hash* h, void* tmpl)
void* hash_put(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
while(b != (HashBucket*) 0) {
@@ -291,7 +260,7 @@ void* hash_put(Hash* h, void* tmpl)
void* hash_erase(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
@@ -323,7 +292,7 @@ void *
hash_remove(Hash *h, void *tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
@@ -343,11 +312,11 @@ hash_remove(Hash *h, void *tmpl)
return NULL;
}
-void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
+void hash_foreach(Hash* h, HFOREACH_FUN func, void *func_arg2)
{
int i;
- for (i = 0; i < h->size; i++) {
+ for (i = 0; i < hash_get_slots(h); i++) {
HashBucket* b = h->bucket[i];
while(b != (HashBucket*) 0) {
(*func)((void *) b, func_arg2);
diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h
index d319aaca83..4e8eb6594b 100644
--- a/erts/emulator/beam/hash.h
+++ b/erts/emulator/beam/hash.h
@@ -18,16 +18,16 @@
* %CopyrightEnd%
*/
-/*
-** General hash functions
-**
-*/
+/**
+ * General hash functions
+ *
+ **/
#ifndef __HASH_H__
#define __HASH_H__
#include "sys.h"
-typedef unsigned long HashValue;
+typedef UWord HashValue;
typedef struct hash Hash;
typedef int (*HCMP_FUN)(void*, void*);
@@ -38,6 +38,7 @@ typedef void (*HFREE_FUN)(void*);
typedef void* (*HMALLOC_FUN)(int,size_t);
typedef void (*HMFREE_FUN)(int,void*);
typedef int (*HMPRINT_FUN)(fmtfn_t,void*,char*, ...);
+typedef void (*HFOREACH_FUN)(void *, void *);
/*
** This bucket must be placed in top of
@@ -75,11 +76,10 @@ struct hash
int is_allocated; /* 0 iff hash structure is on stack or is static */
int meta_alloc_type; /* argument to pass to meta_alloc and meta_free */
char* name; /* Table name (static string, for debugging) */
- int size; /* Number of slots */
+ int shift; /* How much to shift the hash value */
+ int max_shift; /* Never shift more than this value */
int shrink_threshold;
int grow_threshold;
- int size_ix; /* Size index in size table */
- int min_size_ix; /* Never shrink table smaller than this */
int nobjs; /* Number of objects in table */
HashBucket** bucket; /* Vector of bucket pointers (objects) */
};
@@ -96,6 +96,54 @@ void* hash_get(Hash*, void*);
void* hash_put(Hash*, void*);
void* hash_erase(Hash*, void*);
void* hash_remove(Hash*, void*);
-void hash_foreach(Hash*, void (*func)(void *, void *), void *);
+void hash_foreach(Hash*, HFOREACH_FUN, void *);
+
+ERTS_GLB_INLINE Uint hash_get_slot(Hash *h, HashValue hv);
+ERTS_GLB_INLINE void* hash_fetch(Hash *, void*, H_FUN, HCMP_FUN);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Uint
+hash_get_slot(Hash *h, HashValue hv)
+{
+ /* This slot mapping function uses fibonacci hashing in order to
+ * protect itself against a very bad hash function. This is not
+ * a hash function, so the user of hash.h should still spend time
+ * to figure out a good hash function for its data.
+ *
+ * See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
+ * for some thoughts and ideas about fibonacci hashing.
+ */
+
+ /* This is not strictly part of the fibonacci hashing algorithm
+ * but it does help to spread the values of the mapping function better.
+ */
+ hv ^= hv >> h->shift;
+#ifdef ARCH_64
+ /* 2^64 / 1.61803398875 = 11400714819323198485.... */
+ return (UWORD_CONSTANT(11400714819323198485) * hv) >> h->shift;
+#else
+ /* 2^32 / 1.61803398875 = 2654435769.... */
+ return (UWORD_CONSTANT(2654435769) * hv) >> h->shift;
+#endif
+}
+
+ERTS_GLB_INLINE void* hash_fetch(Hash *h, void* tmpl, H_FUN hash, HCMP_FUN cmp)
+{
+ HashValue hval = hash(tmpl);
+ Uint ix = hash_get_slot(h, hval);
+ HashBucket* b = h->bucket[ix];
+ ASSERT(h->fun.hash == hash);
+ ASSERT(h->fun.cmp == cmp);
+
+ while(b != (HashBucket*) 0) {
+ if ((b->hvalue == hval) && (cmp(tmpl, (void*)b) == 0))
+ return (void*) b;
+ b = b->next;
+ }
+ return (void*) 0;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index be1771b037..09d3c24424 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -114,35 +114,26 @@ int index_get(IndexTable* t, void* tmpl)
return -1;
}
-void erts_index_merge(Hash* src, IndexTable* dst)
+static void index_merge_foreach(IndexSlot *p, IndexTable *dst)
{
- int limit = src->size;
- HashBucket** bucket = src->bucket;
- int i;
-
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
- IndexSlot* p;
- int ix;
-
- while (b) {
- Uint sz;
- ix = dst->entries++;
- if (ix >= dst->size) {
- if (ix >= dst->limit) {
- erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
- dst->htable.name, dst->limit);
- }
- sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
- dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
- dst->size += INDEX_PAGE_SIZE;
- }
- p = (IndexSlot*) b;
- p->index = ix;
- dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
- b = b->next;
- }
+ Uint sz;
+ int ix = dst->entries++;
+ if (ix >= dst->size) {
+ if (ix >= dst->limit) {
+ erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
+ dst->htable.name, dst->limit);
+ }
+ sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
+ dst->size += INDEX_PAGE_SIZE;
}
+ p->index = ix;
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
+}
+
+void erts_index_merge(Hash* src, IndexTable* dst)
+{
+ hash_foreach(src, (HFOREACH_FUN)index_merge_foreach, dst);
}
void index_erase_latest_from(IndexTable* t, Uint from_ix)
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index 7cffe7fb5c..c28b5f3443 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -19,7 +19,12 @@
// %CopyrightEnd%
//
-// Stack manipulation instructions
+//
+// Stack manipulation instructions follow.
+//
+// See the comment for AH() in macros.tab for information about
+// the layout of stack frames.
+//
allocate(NeedStack, Live) {
$AH($NeedStack, 0, $Live);
@@ -58,105 +63,170 @@ allocate_heap_zero(NeedStack, NeedHeap, Live) {
deallocate(Deallocate) {
//| -no_prefetch
- SET_CP(c_p, (BeamInstr *) cp_val(*E));
E = ADD_BYTE_OFFSET(E, $Deallocate);
}
-deallocate_return(Deallocate) {
- //| -no_next
- int words_to_pop = $Deallocate;
- SET_I((BeamInstr *) cp_val(*E));
- E = ADD_BYTE_OFFSET(E, words_to_pop);
- CHECK_TERM(x(0));
- DispatchReturn;
+//
+// Micro-benchmarks showed that the deallocate_return instruction
+// became slower when the continuation pointer was moved from
+// the process struct to the stack. The reason seems to be read
+// dependencies, i.e. that the CPU cannot figure out beforehand
+// from which position on the stack the continuation pointer
+// should be fetched.
+//
+// Initializing num_bytes with a constant value seems to restore
+// the lost speed, so we've specialized the instruction for the
+// most common values.
+//
+
+deallocate_return0 := dealloc_ret.n0.execute;
+deallocate_return1 := dealloc_ret.n1.execute;
+deallocate_return2 := dealloc_ret.n2.execute;
+deallocate_return3 := dealloc_ret.n3.execute;
+deallocate_return4 := dealloc_ret.n4.execute;
+deallocate_return := dealloc_ret.var.execute;
+
+dealloc_ret.head() {
+ Uint num_bytes;
}
-move_deallocate_return(Src, Deallocate) {
- x(0) = $Src;
- $deallocate_return($Deallocate);
+dealloc_ret.n0() {
+ num_bytes = (0+1) * sizeof(Eterm);
}
-// Call instructions
+dealloc_ret.n1() {
+ num_bytes = (1+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n2() {
+ num_bytes = (2+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n3() {
+ num_bytes = (3+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n4() {
+ num_bytes = (4+1) * sizeof(Eterm);
+}
+
+dealloc_ret.var(Deallocate) {
+ num_bytes = $Deallocate;
+}
-DISPATCH_REL(CallDest) {
+dealloc_ret.execute() {
//| -no_next
- $SET_I_REL($CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ E = ADD_BYTE_OFFSET(E, num_bytes);
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
-DISPATCH_ABS(CallDest) {
+move_deallocate_return(Src, Deallocate) {
//| -no_next
- SET_I((BeamInstr *) $CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ /*
+ * Explicitly do reads first to mitigate the impact of read
+ * dependencies.
+ */
+
+ Uint bytes_to_pop = $Deallocate;
+ Eterm src = $Src;
+ E = ADD_BYTE_OFFSET(E, bytes_to_pop);
+ x(0) = src;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
+// Call instructions
+
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
move_call(Src, CallDest) {
- x(0) = $Src;
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCH_REL($CallDest);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_last(CallDest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
$DISPATCH_REL($CallDest);
}
move_call_last(Src, CallDest, Deallocate) {
- x(0) = $Src;
- $i_call_last($CallDest, $Deallocate);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_only(CallDest) {
+ //| -no_next
$DISPATCH_REL($CallDest);
}
move_call_only(Src, CallDest) {
- x(0) = $Src;
- $i_call_only($CallDest);
-}
-
-DISPATCHX(Dest) {
//| -no_next
- DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, $Dest);
- // Dispatchx assumes the Export* is in Arg(0)
- I = (&$Dest) - 1;
- Dispatchx();
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_ext(Dest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCHX($Dest);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext(Src, Dest) {
- x(0) = $Src;
- $i_call_ext($Dest);
+i_move_call_ext(Src, CallDest) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_only(Dest) {
- $DISPATCHX($Dest);
+ //| -no_next
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_only(Dest, Src) {
- x(0) = $Src;
- $i_call_ext_only($Dest);
+i_move_call_ext_only(CallDest, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_last(Dest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
- $DISPATCHX($Dest);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_last(Dest, StackOffset, Src) {
- x(0) = $Src;
- $i_call_ext_last($Dest, $StackOffset);
+i_move_call_ext_last(CallDest, Deallocate, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
APPLY(I, Deallocate, Next) {
@@ -167,21 +237,23 @@ APPLY(I, Deallocate, Next) {
}
HANDLE_APPLY_ERROR() {
- I = handle_error(c_p, I, reg, &bif_export[BIF_apply_3]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_apply_3].info.mfa);
goto post_error_handling;
}
i_apply() {
+ //| -no_next
BeamInstr *next;
$APPLY(NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
i_apply_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY(I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -192,6 +264,7 @@ i_apply_last(Deallocate) {
}
i_apply_only() {
+ //| -no_next
BeamInstr *next;
$APPLY(I, 0, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -208,16 +281,18 @@ FIXED_APPLY(Arity, I, Deallocate, Next) {
}
apply(Arity) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
apply_last(Arity, Deallocate) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -237,23 +312,19 @@ HANDLE_APPLY_FUN_ERROR() {
goto find_func_info;
}
-DISPATCH_FUN(I) {
- //| -no_next
- SET_I($I);
- Dispatchfun();
-}
-
i_apply_fun() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_apply_fun_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -264,6 +335,7 @@ i_apply_fun_last(Deallocate) {
}
i_apply_fun_only() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -280,16 +352,18 @@ CALL_FUN(Fun, Next) {
}
i_call_fun(Fun) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_call_fun_last(Fun, Deallocate) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -301,18 +375,12 @@ i_call_fun_last(Fun, Deallocate) {
return() {
//| -no_next
- SET_I(c_p->cp);
- DTRACE_RETURN_FROM_PC(c_p);
-
- /*
- * We must clear the CP to make sure that a stale value do not
- * create a false module dependcy preventing code upgrading.
- * It also means that we can use the CP in stack backtraces.
- */
- c_p->cp = 0;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
CHECK_TERM(r(0));
HEAP_SPACE_VERIFIED(0);
- DispatchReturn;
+
+ $DISPATCH_RETURN();
}
get_list(Src, Hd, Tl) {
@@ -478,16 +546,21 @@ i_make_fun(FunP, NumFree) {
}
move_trim(Src, Dst, Words) {
- Uint cp = E[0];
$Dst = $Src;
- E += $Words;
- E[0] = cp;
+ $i_trim($Words);
}
i_trim(Words) {
- Uint cp = E[0];
E += $Words;
- E[0] = cp;
+
+ /*
+ * Clear the reserved location for the continuation pointer at
+ * E[0]. This is not strictly necessary for correctness, but if a
+ * GC is triggered before E[0] is overwritten by another
+ * continuation pointer the now dead term at E[0] would be
+ * retained by the GC.
+ */
+ E[0] = NIL;
}
move(Src, Dst) {
@@ -599,9 +672,9 @@ move_window5(S1, S2, S3, S4, S5, D) {
move_return(Src) {
//| -no_next
x(0) = $Src;
- SET_I(c_p->cp);
- c_p->cp = 0;
- DispatchReturn;
+ DTRACE_RETURN_FROM_PC(c_p, I);
+ $RETURN();
+ $DISPATCH_RETURN();
}
move_x1(Src) {
@@ -683,10 +756,11 @@ swap(R1, R2) {
$R2 = V;
}
-swap_temp(R1, R2, Tmp) {
- Eterm V = $R1;
- $R1 = $R2;
- $R2 = $Tmp = V;
+swap2(R1, R2, R3) {
+ Eterm V = $R2;
+ $R2 = $R1;
+ $R1 = $R3;
+ $R3 = V;
}
test_heap(Nh, Live) {
@@ -952,6 +1026,14 @@ is_ge(Fail, X, Y) {
CMP_GE_ACTION($X, $Y, $FAIL($Fail));
}
+is_lt_literal(Fail, X, Y) {
+ CMP_LT_LITERAL_ACTION($X, $Y, $FAIL($Fail));
+}
+
+is_ge_literal(Fail, X, Y) {
+ CMP_GE_LITERAL_ACTION($X, $Y, $FAIL($Fail));
+}
+
badarg(Fail) {
$BADARG($Fail);
//| -no_next;
@@ -991,6 +1073,7 @@ catch_end(Y) {
$try_end($Y);
if (is_non_value(r(0))) {
c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
if (x(1) == am_throw) {
r(0) = x(2);
} else {
@@ -1024,6 +1107,7 @@ try_case(Y) {
$try_end($Y);
ASSERT(is_non_value(r(0)));
c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
r(0) = x(1);
x(1) = x(2);
x(2) = x(3);
diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab
index 1b5e5f66b0..802c8aec9a 100644
--- a/erts/emulator/beam/macros.tab
+++ b/erts/emulator/beam/macros.tab
@@ -104,14 +104,136 @@ GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) {
// Make sure that there are NeedStack + NeedHeap + 1 words available
-// on the combined heap/stack segment, then allocates NeedHeap + 1
-// words on the stack and saves CP.
+// on the combined heap/stack segment, then decrement the stack
+// pointer by (NeedStack + 1) words. Finally clear the word reserved
+// for the continuation pointer at the top of the stack.
+//
+// Stack frame layout:
+//
+// +-----------+
+// y(N) | Term |
+// +-----------+
+// .
+// .
+// .
+// +-----------+
+// y(0) | Term |
+// +-----------+
+// E ==> | NIL or CP |
+// +-----------+
+//
+// When the function owning the stack frame is the currently executing
+// function, the word at the top of the stack is NIL. When calling
+// another function, the continuation pointer will be stored in the
+// word at the top of the stack. When returning to the function
+// owning the stack frame, the word at the stack top will again be set
+// to NIL.
+
AH(NeedStack, NeedHeap, Live) {
unsigned needed = $NeedStack + 1;
$GC_TEST(needed, $NeedHeap, $Live);
E -= needed;
- *E = make_cp(c_p->cp);
- c_p->cp = 0;
+ *E = NIL;
+}
+
+
+//
+// Helpers for call instructions
+//
+
+DISPATCH() {
+ BeamInstr dis_next;
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch;
+ }
+}
+
+DISPATCH_ABS(CallDest) {
+ SET_I((BeamInstr *) $CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_EXPORT(Export) {
+ BeamInstr dis_next;
+ Export *ep;
+
+ ep = (Export*)($Export);
+
+ DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep);
+
+ SET_I(ep->addressv[erts_active_code_ix()]);
+ CHECK_ARGS(I);
+ dis_next = *I;
+
+ if (ERTS_UNLIKELY(FCALLS <= 0)) {
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) {
+ save_calls(c_p, ep);
+ } else {
+ goto context_switch;
+ }
+ }
+
+ FCALLS--;
+ Goto(dis_next);
+}
+
+DISPATCH_FUN(I) {
+ BeamInstr dis_next;
+
+ SET_I($I);
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch_fun;
+ }
+}
+
+DISPATCH_REL(CallDest) {
+ $SET_I_REL($CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_RETURN() {
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(*I);
+ } else {
+ c_p->current = NULL;
+ c_p->arity = 1;
+ goto context_switch3;
+ }
+}
+
+// Save the continuation pointer in the reserved slot at the
+// top of the stack as preparation for doing a function call.
+
+SAVE_CONTINUATION_POINTER(IP) {
+ ASSERT(VALID_INSTR(*($IP)));
+ *E = (BeamInstr) ($IP);
+}
+
+// Return to the function whose continuation pointer is stored
+// at the top of the stack and set that word to NIL.
+
+RETURN() {
+ SET_I(cp_val(*E));
+ *E = NIL;
}
NEXT0() {
@@ -167,7 +289,7 @@ BIF_ERROR_ARITY_1(Fail, BIF, Op1) {
}
reg[0] = $Op1;
SWAPOUT;
- I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa);
goto post_error_handling;
}
@@ -179,6 +301,6 @@ BIF_ERROR_ARITY_2(Fail, BIF, Op1, Op2) {
reg[0] = $Op1;
reg[1] = $Op2;
SWAPOUT;
- I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa);
goto post_error_handling;
}
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index b08466c830..3c989b6e60 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -210,7 +210,8 @@ remove_message() {
ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p)));
ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p)));
- ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p)));
+ ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))
+ || is_atom(SEQ_TRACE_TOKEN_SENDER(c_p)));
c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) {
c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index b9d4f6afcc..8c9361c99b 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -74,22 +74,29 @@ trace_jump W
return
-# To ensure that a "move Src x(0)" instruction can be combined with
-# the following call instruction, we need to make sure that there is
-# no line/1 instruction between the move and the call.
#
-# A tail-recursive call to an external function (BIF or non-BIF) will
-# never be saved on the stack, so there is no reason to keep the line
-# instruction.
+# A tail call will not refer to the current function on error unless it's a
+# BIF, so we can omit the line instruction for non-BIFs.
+#
-move S X0=x==0 | line Loc | call_ext Ar Func => \
- line Loc | move S X0 | call_ext Ar Func
-move S X0=x==0 | line Loc | call_ext_last Ar Func D => \
+move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_not_bif D => \
move S X0 | call_ext_last Ar Func D
-move S X0=x==0 | line Loc | call_ext_only Ar Func => \
+move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_not_bif => \
move S X0 | call_ext_only Ar Func
-move S X0=x==0 | line Loc | call Ar Func => \
- line Loc | move S X0 | call Ar Func
+
+move S X0=x==0 | line Loc | call_last Ar Func D => \
+ move S X0 | call_last Ar Func D
+move S X0=x==0 | line Loc | call_only Ar Func => \
+ move S X0 | call_only Ar Func
+
+# To ensure that a "move Src x(0)" instruction can be combined with
+# the following call instruction, we need to make sure that there is
+# no line/1 instruction between the move and the call. (We don't
+# need to match the call instruction, because reordering the move
+# and line instructions would be harmless even if no call instruction
+# follows.)
+
+move S X0=x==0 | line Loc => line Loc | move S X0
line Loc | func_info M F A => func_info M F A | line Loc
@@ -273,26 +280,19 @@ move_jump f c r
# Movement to and from the stack is common.
# Try to pack as much as we can into one instruction.
-# Window move
-move_window/5
-move_window/6
-
# x -> y
-move X1=x Y1=y | move X2=x Y2=y | move X3=x Y3=y | succ(Y1,Y2) | succ(Y2,Y3) => \
- move_window X1 X2 X3 Y1 Y3
-
-move X1=x Y1=y | move X2=x Y2=y | succ(Y1,Y2) => \
+move X1=x Y1=y | move X2=x Y2=y | succ(Y1, Y2) => \
move_window2 X1 X2 Y1
-move_window X1=x X2=x X3=x Y1=y Y3=y | move X4=x Y4=y | succ(Y3,Y4) => \
- move_window X1 X2 X3 X4 Y1 Y4
+move_window2 X1 X2 Y1 | move X3=x Y3=y | offset(Y1, Y3, 2) => \
+ move_window3 X1 X2 X3 Y1
-move_window X1=x X2=x X3=x X4=x Y1=y Y4=y | move X5=x Y5=y | succ(Y4,Y5) => \
- move_window5 X1 X2 X3 X4 X5 Y1
+move_window3 X1 X2 X3 Y1 | move X4=x Y4=y | offset(Y1, Y4, 3) => \
+ move_window4 X1 X2 X3 X4 Y1
-move_window X1=x X2=x X3=x Y1=y Y3=y => move_window3 X1 X2 X3 Y1
-move_window X1=x X2=x X3=x X4=x Y1=y Y4=y => move_window4 X1 X2 X3 X4 Y1
+move_window4 X1 X2 X3 X4 Y1=y | move X5=x Y5=y | offset(Y1, Y5, 4) => \
+ move_window5 X1 X2 X3 X4 X5 Y1
move_window2 x x y
move_window3 x x x y
@@ -324,76 +324,15 @@ move_src_window2 y x x
move_src_window3 y x x x
move_src_window4 y x x x x
-# Swap registers.
-move R1=xy Tmp=x | move R2=xy R1 | move Tmp R2 => swap_temp R1 R2 Tmp
-
-# The compiler uses x(1022) when swapping registers. It will definitely
-# not be used again.
-swap_temp R1 R2 Tmp=x==1022 => swap R1 R2
-
-swap_temp R1 R2 Tmp | move Src Tmp => swap R1 R2 | move Src Tmp
-
-swap_temp R1 R2 Tmp | line Loc | apply Live | is_killed_apply(Tmp, Live) => \
- swap R1 R2 | line Loc | apply Live
-swap_temp R1 R2 Tmp | line Loc | apply_last Live D | is_killed_apply(Tmp, Live) => \
- swap R1 R2 | line Loc | apply_last Live D
-
-swap_temp R1 R2 Tmp | line Loc | call_fun Live | is_killed_by_call_fun(Tmp, Live) => \
- swap R1 R2 | line Loc | call_fun Live
-swap_temp R1 R2 Tmp | make_fun2 OldIndex=u | is_killed_by_make_fun(Tmp, OldIndex) => \
- swap R1 R2 | make_fun2 OldIndex
-
-swap_temp R1 R2 Tmp | line Loc | call Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | line Loc | call Live Addr
-swap_temp R1 R2 Tmp | call_only Live Addr | \
- is_killed(Tmp, Live) => swap R1 R2 | call_only Live Addr
-swap_temp R1 R2 Tmp | call_last Live Addr D | \
- is_killed(Tmp, Live) => swap R1 R2 | call_last Live Addr D
-
-swap_temp R1 R2 Tmp | line Loc | call_ext Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | line Loc | call_ext Live Addr
-swap_temp R1 R2 Tmp | line Loc | call_ext_only Live Addr | \
- is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \
- is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D
-
-swap_temp R1 R2 Tmp | call_ext Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext Live Addr
-swap_temp R1 R2 Tmp | call_ext_only Live Addr | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | call_ext_last Live Addr D | is_killed(Tmp, Live) => \
- swap R1 R2 | call_ext_last Live Addr D
-
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_ext Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_ext Live Addr
-swap_temp R1 R2 Tmp | move Src Any | call_only Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | call_only Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_ext_only Live Addr | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_ext_only Live Addr
-swap_temp R1 R2 Tmp | move Src Any | line Loc | call_fun Live | \
- is_killed(Tmp, Live) | distinct(Tmp, Src) => \
- swap R1 R2 | move Src Any | line Loc | call_fun Live
-
-swap_temp R1 R2 Tmp | line Loc | send | is_killed_by_send(Tmp) => \
- swap R1 R2 | line Loc | send
-
-# swap_temp/3 with Y register operands are rare.
-swap_temp R1 R2=y Tmp => swap R1 R2 | move R2 Tmp
-swap_temp R1=y R2 Tmp => swap R1 R2 | move R2 Tmp
-
swap R1=x R2=y => swap R2 R1
-swap_temp x x x
-
swap xy x
swap y y
+swap R1=x R2=x | swap R3=x R1 => swap2 R1 R2 R3
+
+swap2 x x x
+
# move_shift
move SD=x D=x | move Src=cxy SD=x | distinct(D, Src) => move_shift Src SD D
@@ -450,16 +389,6 @@ move2_par X1=x X2=x X3=x X4=x | move X5=x X6=x => move3 X1 X2 X3 X4 X5 X6
move3 y x y x y x
move3 x x x x x x
-# move_x1, move_x2
-
-move C=aiq X=x==1 => move_x1 C
-move C=aiq X=x==2 => move_x2 C
-
-move n D=y => init D
-
-move_x1 c
-move_x2 c
-
move xy xy
move c xy
move n x
@@ -542,16 +471,27 @@ is_eq_exact f? y y
is_ne_exact f? S S
+# When either operand for is_lt or is_ge is a literal,
+# that literal is almost always an integer and almost never
+# an atom. Therefore we use a specialized instruction when
+# one of the operands is a literal.
+
+is_lt Fail Src=x Lit=c => is_lt_literal Fail Src Lit
+is_lt Fail Lit=c Src=x => is_lt_literal Fail Lit Src
+
is_lt f? x x
-is_lt f? x c
-is_lt f? c x
+is_lt_literal f? x c
+is_lt_literal f? c x
%cold
is_lt f? s s
%hot
+is_ge Fail Src=x Lit=c => is_ge_literal Fail Src Lit
+is_ge Fail Lit=c Src=x => is_ge_literal Fail Lit Src
+
is_ge f? x x
-is_ge f? x c
-is_ge f? c x
+is_ge_literal f? x c
+is_ge_literal f? c x
%cold
is_ge f? s s
%hot
@@ -635,8 +575,9 @@ put_list s s d
%cold
normal_exit
continue_exit
-apply_bif
-call_nif
+call_bif W
+call_nif W W W
+call_nif_early
call_error_handler
error_action_code
return_trace
@@ -657,8 +598,20 @@ move S x==0 | deallocate D | return => move_deallocate_return S D
move_deallocate_return xycn Q
+deallocate u==0 | return => deallocate_return0
+deallocate u==1 | return => deallocate_return1
+deallocate u==2 | return => deallocate_return2
+deallocate u==3 | return => deallocate_return3
+deallocate u==4 | return => deallocate_return4
+
deallocate D | return => deallocate_return D
+deallocate_return0
+deallocate_return1
+deallocate_return2
+deallocate_return3
+deallocate_return4
+
deallocate_return Q
test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
@@ -836,62 +789,22 @@ allocate_init t t? y
# External function and bif calls.
#################################################################
-#
-# The BIFs erts_internal:check_process_code/1 must be called like a function,
-# to ensure that c_p->i (program counter) is set correctly (an ordinary
-# BIF call doesn't set it).
-#
-
-call_ext u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:check_process_code/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext_only Bif
+# Expands into call_light_bif(_only)/2
+call_light_bif/1
+call_light_bif_only/1
+call_light_bif_last/2
#
-# The BIFs erts_internal:garbage_collect/1 must be called like a function,
-# to allow them to invoke the garbage collector. (The stack pointer must
-# be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.)
+# The load_nif/2 BIF is an instruction.
#
-call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif
-#
-# put/2 and erase/1 must be able to do garbage collection, so we must call
-# them like functions.
-#
-
-call_ext u==2 Bif=u$bif:erlang:put/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:put/2 D => i_call_ext_last Bif D
-call_ext_only u==2 Bif=u$bif:erlang:put/2 => i_call_ext_only Bif
-
-call_ext u==1 Bif=u$bif:erlang:erase/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:erase/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erlang:erase/1 => i_call_ext_only Bif
-
-#
-# The process_info/1,2 BIF should be called like a function, to force
-# the emulator to set c_p->current before calling it (a BIF call doesn't
-# set it).
-#
-# In addition, we force the use of a non-tail-recursive call. This will ensure
-# that c_p->cp points into the function making the call.
-#
-
-call_ext u==1 Bif=u$bif:erlang:process_info/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:process_info/1 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==1 Bif=u$bif:erlang:process_info/1 => allocate u Ar | i_call_ext Bif | deallocate_return u
-
-call_ext u==2 Bif=u$bif:erlang:process_info/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:process_info/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:process_info/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
-
-#
-# load_nif/2 also needs to know calling function like process_info
-#
-call_ext u==2 Bif=u$bif:erlang:load_nif/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:load_nif/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
+call_ext u==2 u$func:erlang:load_nif/2 => i_load_nif
+call_ext_last u==2 u$func:erlang:load_nif/2 D => i_load_nif | deallocate_return D
+call_ext_only u==2 u$func:erlang:load_nif/2 => i_load_nif | return
+%cold
+i_load_nif
+%hot
#
# apply/2 is an instruction, not a BIF.
@@ -910,33 +823,6 @@ call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
#
-# The exit/1 and throw/1 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:exit/1 D => call_bif Bif
-call_ext_last u==1 Bif=u$bif:erlang:throw/1 D => call_bif Bif
-
-call_ext_only u==1 Bif=u$bif:erlang:exit/1 => call_bif Bif
-call_ext_only u==1 Bif=u$bif:erlang:throw/1 => call_bif Bif
-
-#
-# The error/1 and error/2 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-# However, they generate stack backtraces, so if the call instruction
-# is call_ext_only/2 instruction, we explicitly do an allocate/2 to store
-# the continuation pointer on the stack.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:error/1 D => call_bif Bif
-call_ext_last u==2 Bif=u$bif:erlang:error/2 D => call_bif Bif
-
-call_ext_only Ar=u==1 Bif=u$bif:erlang:error/1 => \
- allocate u Ar | call_bif Bif
-call_ext_only Ar=u==2 Bif=u$bif:erlang:error/2 => \
- allocate u Ar | call_bif Bif
-
-#
# The yield/0 BIF is an instruction
#
@@ -1048,17 +934,24 @@ call_ext_only u==0 u$func:os:perf_counter/0 => \
i_perf_counter | return
#
-# The general case for BIFs that have no special instructions.
-# A BIF used in the tail must be followed by a return instruction.
+# BIFs like process_info/1,2 require up-to-date information about the current
+# emulator state, which the ordinary call_light_bif instruction doesn't save.
#
-# To make trapping and stack backtraces work correctly, we make sure that
-# the continuation pointer is always stored on the stack.
-call_ext u Bif=u$is_bif => call_bif Bif
+call_ext u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ i_call_ext Bif
+call_ext_last u Bif=u$is_bif D | is_heavy_bif(Bif) => \
+ i_call_ext Bif | deallocate_return D
+call_ext_only Ar=u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ allocate u Ar | i_call_ext Bif | deallocate_return u
-call_ext_last u Bif=u$is_bif D => deallocate D | call_bif_only Bif
+#
+# The general case for BIFs that have no special requirements.
+#
-call_ext_only Ar=u Bif=u$is_bif => call_bif_only Bif
+call_ext u Bif=u$is_bif => call_light_bif Bif
+call_ext_last u Bif=u$is_bif D => call_light_bif_last Bif D
+call_ext_only Ar=u Bif=u$is_bif => call_light_bif_only Bif
#
# Any remaining calls are calls to Erlang functions, not BIFs.
@@ -1083,14 +976,32 @@ i_apply_fun
i_apply_fun_last Q
i_apply_fun_only
+#
+# When a BIF is traced, these instructions make a body call through the export
+# entry instead of calling the BIF directly (setting up a temporary stack frame
+# if needed). We therefore retain the stack frame in call_light_bif_last, and
+# add a deallocate_return after call_light_bif_only to remove the temporary
+# stack frame before returning.
+#
+
+call_light_bif Bif=u$is_bif => \
+ call_light_bif Bif Bif
+
+call_light_bif_last Bif=u$is_bif D => \
+ call_light_bif Bif Bif | deallocate_return D
+
+call_light_bif_only Bif=u$is_bif => \
+ call_light_bif_only Bif Bif | deallocate_return u
+
+call_light_bif b e
+call_light_bif_only b e
+
%cold
-i_hibernate
+i_hibernate
i_perf_counter
-%hot
-call_bif e
-call_bif_only e
+%hot
#
# Calls to non-building and guard BIFs.
@@ -1252,7 +1163,7 @@ i_bs_get_integer_imm Ms Bits Live Fail Flags Y=y => \
i_bs_get_integer_small_imm xy W f? t x
i_bs_get_integer_imm xy W t f? t x
-i_bs_get_integer xy f? t t s d
+i_bs_get_integer xy f? t t S d
i_bs_get_integer_8 xy f? d
i_bs_get_integer_16 xy f? d
@@ -1265,7 +1176,7 @@ bs_get_binary2 Fail=f Ms=xy Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
i_bs_get_binary_imm2 xy f? t W t d
-i_bs_get_binary2 xy f t? s t d
+i_bs_get_binary2 xy f t? S t d
i_bs_get_binary_all2 xy f? t t d
# Fetching float from binaries.
@@ -1302,16 +1213,32 @@ bs_get_tail xy d t
# "slots" in the context itself, which lets us continue matching even after
# we've passed it off to another function.
+bs_start_match4 a==am_no_fail Live=u Src=xy Ctx=d => \
+ bs_start_match3 p Src Live Ctx
+bs_start_match4 Fail=f Live=u Src=xy Ctx=d => \
+ bs_start_match3 Fail Src Live Ctx
+
%if ARCH_64
+
+# This instruction nops on 64-bit platforms
+bs_start_match4 a==am_resume Live Same Same =>
+bs_start_match4 a==am_resume Live Ctx Dst => move Ctx Dst
+
bs_start_match3 Fail Bin Live Ctx | bs_get_position Ctx Pos=x Ignored => \
i_bs_start_match3_gp Bin Live Fail Ctx Pos
-i_bs_start_match3_gp xy t f d x
+i_bs_start_match3_gp xy t j d x
+
+%else
+
+bs_start_match4 a==am_resume Live Ctx Dst => \
+ bs_start_match4 a=am_no_fail Live Ctx Dst
+
%endif
-bs_start_match3 Fail=f ica Live Dst => jump Fail
+bs_start_match3 Fail=j ica Live Dst => jump Fail
bs_start_match3 Fail Bin Live Dst => i_bs_start_match3 Bin Live Fail Dst
-i_bs_start_match3 xy t f d
+i_bs_start_match3 xy t j d
# Match context position instructions. 64-bit assumes that all positions can
# fit into an unsigned small.
@@ -1793,3 +1720,21 @@ i_recv_set
build_stacktrace
raw_raise
+
+#
+# Specialized move instructions. Since they don't require a second
+# instruction, we have intentionally placed them after any other
+# transformation rules that starts with a move instruction in order to
+# produce better code for the transformation engine.
+#
+
+# move_x1, move_x2
+
+move C=aiq X=x==1 => move_x1 C
+move C=aiq X=x==2 => move_x2 C
+
+move n D=y => init D
+
+move_x1 c
+move_x2 c
+
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index 4b526887b5..8f24725326 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -816,9 +816,6 @@ int packet_parse_http(const char* buf, int len, int* statep,
ptr++;
if (--n == 0) return -1;
}
- while (n && SP(ptr)) { /* Skip white space before ':' */
- ptr++; n--;
- }
if (*ptr != ':') {
return -1;
}
@@ -837,7 +834,9 @@ int packet_parse_http(const char* buf, int len, int* statep,
while (n && SP(ptr)) {
ptr++; n--;
}
- return pcb->http_header(arg, name, name_ptr, name_len,
+ return pcb->http_header(arg, name,
+ name_ptr, name_len,
+ buf, name_len,
ptr, n);
}
return -1;
diff --git a/erts/emulator/beam/packet_parser.h b/erts/emulator/beam/packet_parser.h
index 358d650804..b05623efec 100644
--- a/erts/emulator/beam/packet_parser.h
+++ b/erts/emulator/beam/packet_parser.h
@@ -77,8 +77,10 @@ typedef int HttpResponseMessageFn(void* arg, int major, int minor, int status,
typedef int HttpRequestMessageFn(void* arg, const http_atom_t* meth, const char* meth_ptr,
int meth_len, const PacketHttpURI*, int major, int minor);
typedef int HttpEohMessageFn(void *arg);
-typedef int HttpHeaderMessageFn(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len);
+typedef int HttpHeaderMessageFn(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len);
typedef int HttpErrorMessageFn(void* arg, const char* buf, int len);
typedef int SslTlsFn(void* arg, unsigned type, unsigned major, unsigned minor,
const char* data, int len, const char* prefix, int plen);
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index c7e02c6d48..8e44b527a2 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -265,10 +265,8 @@ Eterm
erts_whereis_name_to_id(Process *c_p, Eterm name)
{
Eterm res = am_undefined;
- HashValue hval;
- int ix;
- HashBucket* b;
ErtsProcLocks c_p_locks = 0;
+ RegProc *rp, tmpl;
if (c_p) {
c_p_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
@@ -278,29 +276,14 @@ erts_whereis_name_to_id(Process *c_p, Eterm name)
if (c_p && !c_p_locks)
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- RegProc* rp = (RegProc *) b;
- if (rp->name == name) {
- /*
- * SMP NOTE: No need to lock registered entity since it cannot
- * be removed without acquiring write reg lock and id on entity
- * is read only.
- */
- if (rp->p)
- res = rp->p->common.id;
- else if (rp->pt)
- res = rp->pt->common.id;
- break;
- }
- b = b->next;
+ if (rp) {
+ if (rp->p)
+ res = rp->p->common.id;
+ else if (rp->pt)
+ res = rp->pt->common.id;
}
reg_read_unlock();
@@ -321,10 +304,7 @@ erts_whereis_name(Process *c_p,
Port** port,
int lock_port)
{
- RegProc* rp = NULL;
- HashValue hval;
- int ix;
- HashBucket* b;
+ RegProc* rp = NULL, tmpl;
ErtsProcLocks current_c_p_locks;
Port *pending_port = NULL;
@@ -342,21 +322,8 @@ erts_whereis_name(Process *c_p,
* - current_c_p_locks (either c_p_locks or 0) on c_p
*/
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- if (((RegProc *) b)->name == name) {
- rp = (RegProc *) b;
- break;
- }
- b = b->next;
- }
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
if (proc) {
if (!rp)
@@ -564,18 +531,6 @@ int erts_unregister_name(Process *c_p,
return res;
}
-int process_reg_size(void)
-{
- int size;
- int lock = !ERTS_IS_CRASH_DUMPING;
- if (lock)
- reg_read_lock();
- size = process_reg.size;
- if (lock)
- reg_read_unlock();
- return size;
-}
-
int process_reg_sz(void)
{
int sz;
@@ -592,15 +547,24 @@ int process_reg_sz(void)
#include "bif.h"
+struct registered_foreach_arg {
+ Eterm res;
+ Eterm *hp;
+};
+
+static void
+registered_foreach(RegProc *reg, struct registered_foreach_arg *arg)
+{
+ arg->res = CONS(arg->hp, reg->name, arg->res);
+ arg->hp += 2;
+}
+
/* return a list of the registered processes */
BIF_RETTYPE registered_0(BIF_ALIST_0)
{
- int i;
- Eterm res;
+ struct registered_foreach_arg arg;
Uint need;
- Eterm* hp;
- HashBucket **bucket;
ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(BIF_P);
@@ -608,41 +572,21 @@ BIF_RETTYPE registered_0(BIF_ALIST_0)
if (!proc_locks)
erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- bucket = process_reg.bucket;
-
- /* work out how much heap we need & maybe garb, by scanning through
- the registered process table */
- need = 0;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- need += 2;
- b = b->next;
- }
- }
+ /* work out how much heap we need */
+ need = process_reg.nobjs * 2;
if (need == 0) {
reg_read_unlock();
BIF_RET(NIL);
}
- hp = HAlloc(BIF_P, need);
-
- /* scan through again and make the list */
- res = NIL;
+ /* scan through again and make the list */
+ arg.hp = HAlloc(BIF_P, need);
+ arg.res = NIL;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- RegProc *reg = (RegProc *) b;
-
- res = CONS(hp, reg->name, res);
- hp += 2;
- b = b->next;
- }
- }
+ hash_foreach(&process_reg, (HFOREACH_FUN)registered_foreach, &arg);
reg_read_unlock();
- BIF_RET(res);
+ BIF_RET(arg.res);
}
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index 27a314ca78..c77bd03653 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -41,7 +41,6 @@ typedef struct reg_proc
Eterm name; /* Atom name */
} RegProc;
-int process_reg_size(void);
int process_reg_sz(void);
void init_register_table(void);
void register_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 0d85211be8..3316d9dfde 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -92,6 +92,12 @@
# define ERTS_GLB_INLINE_INCL_FUNC_DEF 0
#endif
+#ifdef __GNUC__
+# define ERTS_NOINLINE __attribute__((__noinline__))
+#else
+# define ERTS_NOINLINE
+#endif
+
#if defined(VALGRIND) && !defined(NO_FPE_SIGNALS)
# define NO_FPE_SIGNALS
#endif
@@ -172,7 +178,8 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# define ERTS_UNLIKELY(BOOL) (BOOL)
#endif
-#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0)
+/* AIX doesn't like this and claims section conflicts */
+#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0) && !defined(_AIX)
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__DARWIN__)
# define ERTS_WRITE_UNLIKELY(X) X __attribute__ ((section ("__DATA,ERTS_LOW_WRITE") ))
#else
@@ -211,7 +218,7 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
#endif
/* 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
+ * but in GNUC it is an attribute 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
@@ -254,6 +261,39 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
#endif
/*
+ * ERTS_GCC_DIAG_ON and ERTS_GCC_DIAG_OFF can be used to temporarly
+ * disable a gcc or clang warning in a file.
+ *
+ * Example:
+ * GCC_DIAG_OFF(unused-function)
+ * static int test(){ return 0;}
+ * GCC_DIAG_ON(unused-function)
+ *
+ * These macros were orginally authored by Jonathan Wakely and has
+ * been modified by Patrick Horgan.
+ *
+ * Source: http://dbp-consulting.com/tutorials/SuppressingGCCWarnings.html
+ *
+ */
+#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+#define ERTS_GCC_DIAG_STR(s) #s
+#define ERTS_GCC_DIAG_JOINSTR(x,y) ERTS_GCC_DIAG_STR(x ## y)
+# define ERTS_GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)
+# define ERTS_GCC_DIAG_PRAGMA(x) ERTS_GCC_DIAG_DO_PRAGMA(GCC diagnostic x)
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
+# define ERTS_GCC_DIAG_OFF(x) ERTS_GCC_DIAG_PRAGMA(push) \
+ ERTS_GCC_DIAG_PRAGMA(ignored ERTS_GCC_DIAG_JOINSTR(-W,x))
+# define ERTS_GCC_DIAG_ON(x) ERTS_GCC_DIAG_PRAGMA(pop)
+# else
+# define ERTS_GCC_DIAG_OFF(x) ERTS_GCC_DIAG_PRAGMA(ignored ERTS_GCC_DIAG_JOINSTR(-W,x))
+# define ERTS_GCC_DIAG_ON(x) ERTS_GCC_DIAG_PRAGMA(warning ERTS_GCC_DIAG_JOINSTR(-W,x))
+# endif
+#else
+# define ERTS_GCC_DIAG_OFF(x)
+# define ERTS_GCC_DIAG_ON(x)
+#endif
+
+/*
* Compile time assert
* (the actual compiler error msg can be a bit confusing)
*/
@@ -666,7 +706,16 @@ typedef struct preload {
*/
typedef Eterm ErtsTracer;
-#include "erl_osenv.h"
+
+/*
+ * This structure contains the rb tree for the erlang osenv copy
+ * see erl_osenv.h for more details.
+ */
+typedef struct __erts_osenv_t {
+ struct __env_rbtnode_t *tree;
+ int variable_count;
+ int content_size;
+} erts_osenv_t;
extern char *erts_default_arg0;
diff --git a/erts/emulator/beam/trace_instrs.tab b/erts/emulator/beam/trace_instrs.tab
index 6bcd12e22e..a6cdbcbc20 100644
--- a/erts/emulator/beam/trace_instrs.tab
+++ b/erts/emulator/beam/trace_instrs.tab
@@ -20,16 +20,15 @@
//
return_trace() {
- ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[0]);
+ ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[1]);
SWAPOUT; /* Needed for shared heap */
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
- erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */);
+ erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+2)/* tracer */);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[2]));
E += 3;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -45,14 +44,12 @@ i_generic_breakpoint() {
}
i_return_time_trace() {
- ErtsCodeInfo *cinfo = (!is_CP(E[0]) ? NULL
- : erts_code_to_codeinfo((BeamInstr*)E[0]));
+ ErtsCodeInfo *cinfo = (!is_CP(E[1]) ? NULL : erts_code_to_codeinfo((BeamInstr*)E[1]));
SWAPOUT;
erts_trace_time_return(c_p, cinfo);
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[1]));
E += 2;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -60,8 +57,10 @@ i_return_time_trace() {
i_return_to_trace() {
if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
Uint *cpp = (Uint*) E;
+ while (is_not_CP(*cpp)) {
+ cpp++;
+ }
for(;;) {
- ASSERT(is_CP(*cpp));
if (IsOpCode(*cp_val(*cpp), return_trace)) {
do
++cpp;
@@ -81,9 +80,8 @@ i_return_to_trace() {
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
}
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[0]));
E += 1;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -109,7 +107,7 @@ i_hibernate() {
goto do_schedule;
} else {
HEAVY_SWAPIN;
- I = handle_error(c_p, I, reg, &bif_export[BIF_hibernate_3]->info.mfa);
+ I = handle_error(c_p, I, reg, &bif_trap_export[BIF_hibernate_3].info.mfa);
goto post_error_handling;
}
//| -no_next
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 9fb37f77fc..f06a4a3d60 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -66,7 +66,7 @@
#undef M_MMAP_THRESHOLD
#undef M_MMAP_MAX
-#if defined(__GLIBC__) && defined(HAVE_MALLOC_H)
+#if (defined(__GLIBC__) || defined(_AIX)) && defined(HAVE_MALLOC_H)
#include <malloc.h>
#endif
@@ -907,7 +907,7 @@ tail_recur:
hash = hash * FUNNY_NUMBER10 + num_free;
hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
+ hash = hash*FUNNY_NUMBER2 + funp->fe->index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
if (num_free > 0) {
if (num_free > 1) {
@@ -1069,54 +1069,237 @@ do { \
#define HCONST 0x9e3779b9UL /* the golden ratio; an arbitrary value */
-static Uint32
-block_hash(byte *k, Uint length, Uint32 initval)
+typedef struct {
+ Uint32 a,b,c;
+} ErtsBlockHashHelperCtx;
+
+#define BLOCK_HASH_BYTES_PER_ITER 12
+
+/* The three functions below are separated into different functions even
+ though they are always used together to make trapping and handling
+ of unaligned binaries easier. Examples of how they are used can be
+ found in block_hash and make_hash2_helper.*/
+static ERTS_INLINE
+void block_hash_setup(Uint32 initval,
+ ErtsBlockHashHelperCtx* ctx /* out parameter */)
+{
+ ctx->a = ctx->b = HCONST;
+ ctx->c = initval; /* the previous hash value */
+}
+
+static ERTS_INLINE
+void block_hash_buffer(byte *buf,
+ Uint buf_length,
+ ErtsBlockHashHelperCtx* ctx /* out parameter */)
{
- Uint32 a,b,c;
- Uint len;
-
- /* Set up the internal state */
- len = length;
- a = b = HCONST;
- c = initval; /* the previous hash value */
-
- while (len >= 12)
- {
- a += (k[0] +((Uint32)k[1]<<8) +((Uint32)k[2]<<16) +((Uint32)k[3]<<24));
- b += (k[4] +((Uint32)k[5]<<8) +((Uint32)k[6]<<16) +((Uint32)k[7]<<24));
- c += (k[8] +((Uint32)k[9]<<8) +((Uint32)k[10]<<16)+((Uint32)k[11]<<24));
- MIX(a,b,c);
- k += 12; len -= 12;
- }
-
- c += length;
- switch(len) /* all the case statements fall through */
- {
- case 11: c+=((Uint32)k[10]<<24);
- case 10: c+=((Uint32)k[9]<<16);
- case 9 : c+=((Uint32)k[8]<<8);
- /* the first byte of c is reserved for the length */
- case 8 : b+=((Uint32)k[7]<<24);
- case 7 : b+=((Uint32)k[6]<<16);
- case 6 : b+=((Uint32)k[5]<<8);
- case 5 : b+=k[4];
- case 4 : a+=((Uint32)k[3]<<24);
- case 3 : a+=((Uint32)k[2]<<16);
- case 2 : a+=((Uint32)k[1]<<8);
- case 1 : a+=k[0];
- /* case 0: nothing left to add */
- }
- MIX(a,b,c);
- return c;
+ Uint len = buf_length;
+ byte *k = buf;
+ ASSERT(buf_length % BLOCK_HASH_BYTES_PER_ITER == 0);
+ while (len >= BLOCK_HASH_BYTES_PER_ITER) {
+ ctx->a += (k[0] +((Uint32)k[1]<<8) +((Uint32)k[2]<<16) +((Uint32)k[3]<<24));
+ ctx->b += (k[4] +((Uint32)k[5]<<8) +((Uint32)k[6]<<16) +((Uint32)k[7]<<24));
+ ctx->c += (k[8] +((Uint32)k[9]<<8) +((Uint32)k[10]<<16)+((Uint32)k[11]<<24));
+ MIX(ctx->a,ctx->b,ctx->c);
+ k += BLOCK_HASH_BYTES_PER_ITER; len -= BLOCK_HASH_BYTES_PER_ITER;
+ }
}
+static ERTS_INLINE
+Uint32 block_hash_final_bytes(byte *buf,
+ Uint buf_length,
+ Uint full_length,
+ ErtsBlockHashHelperCtx* ctx)
+{
+ Uint len = buf_length;
+ byte *k = buf;
+ ctx->c += full_length;
+ switch(len)
+ { /* all the case statements fall through */
+ case 11: ctx->c+=((Uint32)k[10]<<24);
+ case 10: ctx->c+=((Uint32)k[9]<<16);
+ case 9 : ctx->c+=((Uint32)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : ctx->b+=((Uint32)k[7]<<24);
+ case 7 : ctx->b+=((Uint32)k[6]<<16);
+ case 6 : ctx->b+=((Uint32)k[5]<<8);
+ case 5 : ctx->b+=k[4];
+ case 4 : ctx->a+=((Uint32)k[3]<<24);
+ case 3 : ctx->a+=((Uint32)k[2]<<16);
+ case 2 : ctx->a+=((Uint32)k[1]<<8);
+ case 1 : ctx->a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ MIX(ctx->a,ctx->b,ctx->c);
+ return ctx->c;
+}
+
+static
Uint32
-make_hash2(Eterm term)
+block_hash(byte *block, Uint block_length, Uint32 initval)
{
+ ErtsBlockHashHelperCtx ctx;
+ Uint no_bytes_not_in_loop =
+ (block_length % BLOCK_HASH_BYTES_PER_ITER);
+ Uint no_bytes_to_process_in_loop =
+ block_length - no_bytes_not_in_loop;
+ byte *final_bytes = block + no_bytes_to_process_in_loop;
+ block_hash_setup(initval, &ctx);
+ block_hash_buffer(block,
+ no_bytes_to_process_in_loop,
+ &ctx);
+ return block_hash_final_bytes(final_bytes,
+ no_bytes_not_in_loop,
+ block_length,
+ &ctx);
+}
+
+typedef enum {
+ tag_primary_list,
+ arityval_subtag,
+ hamt_subtag_head_flatmap,
+ map_subtag,
+ fun_subtag,
+ neg_big_subtag,
+ sub_binary_subtag_1,
+ sub_binary_subtag_2,
+ hash2_common_1,
+ hash2_common_2,
+ hash2_common_3,
+} ErtsMakeHash2TrapLocation;
+
+typedef struct {
+ int c;
+ Uint32 sh;
+ Eterm* ptr;
+} ErtsMakeHash2Context_TAG_PRIMARY_LIST;
+
+typedef struct {
+ int i;
+ int arity;
+ Eterm* elem;
+} ErtsMakeHash2Context_ARITYVAL_SUBTAG;
+
+typedef struct {
+ Eterm *ks;
+ Eterm *vs;
+ int i;
+ Uint size;
+} ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP;
+
+typedef struct {
+ Eterm* ptr;
+ int i;
+} ErtsMakeHash2Context_MAP_SUBTAG;
+
+typedef struct {
+ Uint num_free;
+ Eterm* bptr;
+} ErtsMakeHash2Context_FUN_SUBTAG;
+
+typedef struct {
+ Eterm* ptr;
+ Uint i;
+ Uint n;
+ Uint32 con;
+} ErtsMakeHash2Context_NEG_BIG_SUBTAG;
+
+typedef struct {
+ byte* bptr;
+ Uint sz;
+ Uint bitsize;
+ Uint bitoffs;
+ Uint no_bytes_processed;
+ ErtsBlockHashHelperCtx block_hash_ctx;
+ /* The following fields are only used when bitoffs != 0 */
+ byte* buf;
+ int done;
+
+} ErtsMakeHash2Context_SUB_BINARY_SUBTAG;
+
+typedef struct {
+ int dummy__; /* Empty structs are not supported on all platforms */
+} ErtsMakeHash2Context_EMPTY;
+
+typedef struct {
+ ErtsMakeHash2TrapLocation trap_location;
+ /* specific to the trap location: */
+ union {
+ ErtsMakeHash2Context_TAG_PRIMARY_LIST tag_primary_list;
+ ErtsMakeHash2Context_ARITYVAL_SUBTAG arityval_subtag;
+ ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP hamt_subtag_head_flatmap;
+ ErtsMakeHash2Context_MAP_SUBTAG map_subtag;
+ ErtsMakeHash2Context_FUN_SUBTAG fun_subtag;
+ ErtsMakeHash2Context_NEG_BIG_SUBTAG neg_big_subtag;
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG sub_binary_subtag_1;
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG sub_binary_subtag_2;
+ ErtsMakeHash2Context_EMPTY hash2_common_1;
+ ErtsMakeHash2Context_EMPTY hash2_common_2;
+ ErtsMakeHash2Context_EMPTY hash2_common_3;
+ } trap_location_state;
+ /* same for all trap locations: */
+ Eterm term;
Uint32 hash;
Uint32 hash_xor_pairs;
- DeclareTmpHeapNoproc(tmp_big,2);
+ ErtsEStack stack;
+} ErtsMakeHash2Context;
+
+static int make_hash2_ctx_bin_dtor(Binary *context_bin) {
+ ErtsMakeHash2Context* context = ERTS_MAGIC_BIN_DATA(context_bin);
+ DESTROY_SAVED_ESTACK(&context->stack);
+ if (context->trap_location == sub_binary_subtag_2 &&
+ context->trap_location_state.sub_binary_subtag_2.buf != NULL) {
+ erts_free(ERTS_ALC_T_PHASH2_TRAP, context->trap_location_state.sub_binary_subtag_2.buf);
+ }
+ return 1;
+}
+/* hash2_save_trap_state is called seldom so we want to avoid inlining */
+static ERTS_NOINLINE
+Eterm hash2_save_trap_state(Eterm state_mref,
+ Uint32 hash_xor_pairs,
+ Uint32 hash,
+ Process* p,
+ Eterm term,
+ Eterm* ESTK_DEF_STACK(s),
+ ErtsEStack s,
+ ErtsMakeHash2TrapLocation trap_location,
+ void* trap_location_state_ptr,
+ size_t trap_location_state_size) {
+ Binary* state_bin;
+ ErtsMakeHash2Context* context;
+ if (state_mref == THE_NON_VALUE) {
+ Eterm* hp;
+ state_bin = erts_create_magic_binary(sizeof(ErtsMakeHash2Context),
+ make_hash2_ctx_bin_dtor);
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin);
+ } else {
+ state_bin = erts_magic_ref2bin(state_mref);
+ }
+ context = ERTS_MAGIC_BIN_DATA(state_bin);
+ context->term = term;
+ context->hash = hash;
+ context->hash_xor_pairs = hash_xor_pairs;
+ ESTACK_SAVE(s, &context->stack);
+ context->trap_location = trap_location;
+ sys_memcpy(&context->trap_location_state,
+ trap_location_state_ptr,
+ trap_location_state_size);
+ erts_set_gc_state(p, 0);
+ BUMP_ALL_REDS(p);
+ return state_mref;
+}
+#undef NOINLINE_HASH2_SAVE_TRAP_STATE
+
+/* Writes back a magic reference to *state_mref_write_back when the
+ function traps */
+static ERTS_INLINE Uint32
+make_hash2_helper(Eterm term_param, const int can_trap, Eterm* state_mref_write_back, Process* p)
+{
+ static const Uint ITERATIONS_PER_RED = 64;
+ Uint32 hash;
+ Uint32 hash_xor_pairs;
+ Eterm term = term_param;
ERTS_UNDEF(hash_xor_pairs, 0);
/* (HCONST * {2, ..., 22}) mod 2^32 */
@@ -1168,12 +1351,63 @@ make_hash2(Eterm term)
#define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
+#define NOT_SSMALL28_HASH(SMALL) \
+ do { \
+ Uint64 t; \
+ Uint32 x, y; \
+ Uint32 con; \
+ if (SMALL < 0) { \
+ con = HCONST_10; \
+ t = (Uint64)(SMALL * (-1)); \
+ } else { \
+ con = HCONST_11; \
+ t = SMALL; \
+ } \
+ x = t & 0xffffffff; \
+ y = t >> 32; \
+ UINT32_HASH_2(x, y, con); \
+ } while(0)
+
#ifdef ARCH_64
# define POINTER_HASH(Ptr, AConst) UINT32_HASH_2((Uint32)(UWord)(Ptr), (((UWord)(Ptr)) >> 32), AConst)
#else
# define POINTER_HASH(Ptr, AConst) UINT32_HASH(Ptr, AConst)
#endif
+#define TRAP_LOCATION_NO_RED(location_name) \
+ do { \
+ if(can_trap && iterations_until_trap <= 0) { \
+ *state_mref_write_back = \
+ hash2_save_trap_state(state_mref, \
+ hash_xor_pairs, \
+ hash, \
+ p, \
+ term, \
+ ESTK_DEF_STACK(s), \
+ s, \
+ location_name, \
+ &ctx, \
+ sizeof(ctx)); \
+ return 0; \
+ L_##location_name: \
+ ctx = context->trap_location_state. location_name; \
+ } \
+ } while(0)
+
+#define TRAP_LOCATION(location_name) \
+ do { \
+ if (can_trap) { \
+ iterations_until_trap--; \
+ TRAP_LOCATION_NO_RED(location_name); \
+ } \
+ } while(0)
+
+#define TRAP_LOCATION_NO_CTX(location_name) \
+ do { \
+ ErtsMakeHash2Context_EMPTY ctx; \
+ TRAP_LOCATION(location_name); \
+ } while(0)
+
/* Optimization. Simple cases before declaration of estack. */
if (primary_tag(term) == TAG_PRIMARY_IMMED1) {
switch (term & _TAG_IMMED1_MASK) {
@@ -1186,51 +1420,94 @@ make_hash2(Eterm term)
break;
case _TAG_IMMED1_SMALL:
{
- Sint x = signed_val(term);
-
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
+ Sint small = signed_val(term);
+ if (SMALL_BITS > 28 && !IS_SSMALL28(small)) {
+ hash = 0;
+ NOT_SSMALL28_HASH(small);
+ return hash;
}
hash = 0;
- SINT32_HASH(x, HCONST);
+ SINT32_HASH(small, HCONST);
return hash;
}
}
};
{
Eterm tmp;
+ long max_iterations = 0;
+ long iterations_until_trap = 0;
+ Eterm state_mref = THE_NON_VALUE;
+ ErtsMakeHash2Context* context = NULL;
DECLARE_ESTACK(s);
-
- UseTmpHeapNoproc(2);
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ if(can_trap){
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ (1103515245 * (ERTS_BIF_REDS_LEFT(p)) + 12345) % 227;
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(p);
+#endif
+ }
+ if (can_trap && is_internal_magic_ref(term)) {
+ Binary* state_bin;
+ state_mref = term;
+ state_bin = erts_magic_ref2bin(state_mref);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(state_bin) == make_hash2_ctx_bin_dtor) {
+ /* Restore state after a trap */
+ context = ERTS_MAGIC_BIN_DATA(state_bin);
+ term = context->term;
+ hash = context->hash;
+ hash_xor_pairs = context->hash_xor_pairs;
+ ESTACK_RESTORE(s, &context->stack);
+ ASSERT(p->flags & F_DISABLE_GC);
+ erts_set_gc_state(p, 1);
+ switch (context->trap_location) {
+ case hash2_common_3: goto L_hash2_common_3;
+ case tag_primary_list: goto L_tag_primary_list;
+ case arityval_subtag: goto L_arityval_subtag;
+ case hamt_subtag_head_flatmap: goto L_hamt_subtag_head_flatmap;
+ case map_subtag: goto L_map_subtag;
+ case fun_subtag: goto L_fun_subtag;
+ case neg_big_subtag: goto L_neg_big_subtag;
+ case sub_binary_subtag_1: goto L_sub_binary_subtag_1;
+ case sub_binary_subtag_2: goto L_sub_binary_subtag_2;
+ case hash2_common_1: goto L_hash2_common_1;
+ case hash2_common_2: goto L_hash2_common_2;
+ }
+ }
+ }
hash = 0;
for (;;) {
switch (primary_tag(term)) {
case TAG_PRIMARY_LIST:
{
- int c = 0;
- Uint32 sh = 0;
- Eterm* ptr = list_val(term);
- while (is_byte(*ptr)) {
+ ErtsMakeHash2Context_TAG_PRIMARY_LIST ctx = {
+ .c = 0,
+ .sh = 0,
+ .ptr = list_val(term)};
+ while (is_byte(*ctx.ptr)) {
/* Optimization for strings. */
- sh = (sh << 8) + unsigned_val(*ptr);
- if (c == 3) {
- UINT32_HASH(sh, HCONST_4);
- c = sh = 0;
+ ctx.sh = (ctx.sh << 8) + unsigned_val(*ctx.ptr);
+ if (ctx.c == 3) {
+ UINT32_HASH(ctx.sh, HCONST_4);
+ ctx.c = ctx.sh = 0;
} else {
- c++;
+ ctx.c++;
}
- term = CDR(ptr);
+ term = CDR(ctx.ptr);
if (is_not_list(term))
break;
- ptr = list_val(term);
+ ctx.ptr = list_val(term);
+ TRAP_LOCATION(tag_primary_list);
}
- if (c > 0)
- UINT32_HASH(sh, HCONST_4);
+ if (ctx.c > 0)
+ UINT32_HASH(ctx.sh, HCONST_4);
if (is_list(term)) {
- tmp = CDR(ptr);
+ tmp = CDR(ctx.ptr);
ESTACK_PUSH(s, tmp);
- term = CAR(ptr);
+ term = CAR(ctx.ptr);
}
}
break;
@@ -1241,34 +1518,39 @@ make_hash2(Eterm term)
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
{
- int i;
- int arity = header_arity(hdr);
- Eterm* elem = tuple_val(term);
- UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
+ ErtsMakeHash2Context_ARITYVAL_SUBTAG ctx = {
+ .i = 0,
+ .arity = header_arity(hdr),
+ .elem = tuple_val(term)};
+ UINT32_HASH(ctx.arity, HCONST_9);
+ if (ctx.arity == 0) /* Empty tuple */
goto hash2_common;
- for (i = arity; ; i--) {
- term = elem[i];
- if (i == 1)
+ for (ctx.i = ctx.arity; ; ctx.i--) {
+ term = ctx.elem[ctx.i];
+ if (ctx.i == 1)
break;
ESTACK_PUSH(s, term);
+ TRAP_LOCATION(arityval_subtag);
}
}
break;
case MAP_SUBTAG:
{
- Eterm* ptr = boxed_val(term) + 1;
Uint size;
- int i;
+ ErtsMakeHash2Context_MAP_SUBTAG ctx = {
+ .ptr = boxed_val(term) + 1,
+ .i = 0};
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_FLATMAP:
{
flatmap_t *mp = (flatmap_t *)flatmap_val(term);
- Eterm *ks = flatmap_get_keys(mp);
- Eterm *vs = flatmap_get_values(mp);
- size = flatmap_get_size(mp);
- UINT32_HASH(size, HCONST_16);
- if (size == 0)
+ ErtsMakeHash2Context_HAMT_SUBTAG_HEAD_FLATMAP ctx = {
+ .ks = flatmap_get_keys(mp),
+ .vs = flatmap_get_values(mp),
+ .i = 0,
+ .size = flatmap_get_size(mp)};
+ UINT32_HASH(ctx.size, HCONST_16);
+ if (ctx.size == 0)
goto hash2_common;
/* We want a portable hash function that is *independent* of
@@ -1281,17 +1563,18 @@ make_hash2(Eterm term)
ESTACK_PUSH(s, HASH_MAP_TAIL);
hash = 0;
hash_xor_pairs = 0;
- for (i = size - 1; i >= 0; i--) {
+ for (ctx.i = ctx.size - 1; ctx.i >= 0; ctx.i--) {
ESTACK_PUSH(s, HASH_MAP_PAIR);
- ESTACK_PUSH(s, vs[i]);
- ESTACK_PUSH(s, ks[i]);
+ ESTACK_PUSH(s, ctx.vs[ctx.i]);
+ ESTACK_PUSH(s, ctx.ks[ctx.i]);
+ TRAP_LOCATION(hamt_subtag_head_flatmap);
}
goto hash2_common;
}
case HAMT_SUBTAG_HEAD_ARRAY:
case HAMT_SUBTAG_HEAD_BITMAP:
- size = *ptr++;
+ size = *ctx.ptr++;
UINT32_HASH(size, HCONST_16);
if (size == 0)
goto hash2_common;
@@ -1303,27 +1586,28 @@ make_hash2(Eterm term)
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
- i = 16;
+ ctx.i = 16;
break;
case HAMT_SUBTAG_HEAD_BITMAP:
case HAMT_SUBTAG_NODE_BITMAP:
- i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ ctx.i = hashmap_bitcount(MAP_HEADER_VAL(hdr));
break;
default:
erts_exit(ERTS_ERROR_EXIT, "bad header");
}
- while (i) {
- if (is_list(*ptr)) {
- Eterm* cons = list_val(*ptr);
+ while (ctx.i) {
+ if (is_list(*ctx.ptr)) {
+ Eterm* cons = list_val(*ctx.ptr);
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, CDR(cons));
ESTACK_PUSH(s, CAR(cons));
}
else {
- ASSERT(is_boxed(*ptr));
- ESTACK_PUSH(s, *ptr);
+ ASSERT(is_boxed(*ctx.ptr));
+ ESTACK_PUSH(s, *ctx.ptr);
}
- i--; ptr++;
+ ctx.i--; ctx.ptr++;
+ TRAP_LOCATION(map_subtag);
}
goto hash2_common;
}
@@ -1344,22 +1628,25 @@ make_hash2(Eterm term)
case FUN_SUBTAG:
{
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
- Uint num_free = funp->num_free;
+ ErtsMakeHash2Context_FUN_SUBTAG ctx = {
+ .num_free = funp->num_free,
+ .bptr = NULL};
UINT32_HASH_2
- (num_free,
+ (ctx.num_free,
atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
HCONST);
UINT32_HASH_2
- (funp->fe->old_index, funp->fe->old_uniq, HCONST);
- if (num_free == 0) {
+ (funp->fe->index, funp->fe->old_uniq, HCONST);
+ if (ctx.num_free == 0) {
goto hash2_common;
} else {
- Eterm* bptr = funp->env + num_free - 1;
- while (num_free-- > 1) {
- term = *bptr--;
+ ctx.bptr = funp->env + ctx.num_free - 1;
+ while (ctx.num_free-- > 1) {
+ term = *ctx.bptr--;
ESTACK_PUSH(s, term);
+ TRAP_LOCATION(fun_subtag);
}
- term = *bptr;
+ term = *ctx.bptr;
}
}
break;
@@ -1367,70 +1654,190 @@ make_hash2(Eterm term)
case HEAP_BINARY_SUBTAG:
case SUB_BINARY_SUBTAG:
{
- byte* bptr;
- unsigned sz = binary_size(term);
+#define BYTE_BITS 8
+ ErtsMakeHash2Context_SUB_BINARY_SUBTAG ctx = {
+ .bptr = 0,
+ /* !!!!!!!!!!!!!!!!!!!! OBS !!!!!!!!!!!!!!!!!!!!
+ *
+ * The size is truncated to 32 bits on the line
+ * below so that the code is compatible with old
+ * versions of the code. This means that hash
+ * values for binaries with a size greater than
+ * 4GB do not take all bytes in consideration.
+ *
+ * !!!!!!!!!!!!!!!!!!!! OBS !!!!!!!!!!!!!!!!!!!!
+ */
+ .sz = (0xFFFFFFFF & binary_size(term)),
+ .bitsize = 0,
+ .bitoffs = 0,
+ .no_bytes_processed = 0
+ };
Uint32 con = HCONST_13 + hash;
- Uint bitoffs;
- Uint bitsize;
-
- ERTS_GET_BINARY_BYTES(term, bptr, bitoffs, bitsize);
- if (sz == 0 && bitsize == 0) {
+ Uint iters_for_bin = MAX(1, ctx.sz / BLOCK_HASH_BYTES_PER_ITER);
+ ERTS_GET_BINARY_BYTES(term, ctx.bptr, ctx.bitoffs, ctx.bitsize);
+ if (ctx.sz == 0 && ctx.bitsize == 0) {
hash = con;
- } else {
- if (bitoffs == 0) {
- hash = block_hash(bptr, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (bptr[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- } else {
- byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP,
- sz + (bitsize != 0));
- erts_copy_bits(bptr, bitoffs, 1, buf, 0, 1, sz*8+bitsize);
- hash = block_hash(buf, sz, con);
- if (bitsize > 0) {
- UINT32_HASH_2(bitsize, (buf[sz] >> (8 - bitsize)),
- HCONST_15);
- }
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- }
+ } else if (ctx.bitoffs == 0 &&
+ (!can_trap ||
+ (iterations_until_trap - iters_for_bin) > 0)) {
+ /* No need to trap while hashing binary */
+ if (can_trap) iterations_until_trap -= iters_for_bin;
+ hash = block_hash(ctx.bptr, ctx.sz, con);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.bptr[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ } else if (ctx.bitoffs == 0) {
+ /* Need to trap while hashing binary */
+ ErtsBlockHashHelperCtx* block_hash_ctx = &ctx.block_hash_ctx;
+ block_hash_setup(con, block_hash_ctx);
+ do {
+ Uint max_bytes_to_process =
+ iterations_until_trap <= 0 ? BLOCK_HASH_BYTES_PER_ITER :
+ iterations_until_trap * BLOCK_HASH_BYTES_PER_ITER;
+ Uint bytes_left = ctx.sz - ctx.no_bytes_processed;
+ Uint even_bytes_left =
+ bytes_left - (bytes_left % BLOCK_HASH_BYTES_PER_ITER);
+ Uint bytes_to_process =
+ MIN(max_bytes_to_process, even_bytes_left);
+ block_hash_buffer(&ctx.bptr[ctx.no_bytes_processed],
+ bytes_to_process,
+ block_hash_ctx);
+ ctx.no_bytes_processed += bytes_to_process;
+ iterations_until_trap -=
+ MAX(1, bytes_to_process / BLOCK_HASH_BYTES_PER_ITER);
+ TRAP_LOCATION_NO_RED(sub_binary_subtag_1);
+ block_hash_ctx = &ctx.block_hash_ctx; /* Restore after trap */
+ } while ((ctx.sz - ctx.no_bytes_processed) >=
+ BLOCK_HASH_BYTES_PER_ITER);
+ hash = block_hash_final_bytes(ctx.bptr +
+ ctx.no_bytes_processed,
+ ctx.sz - ctx.no_bytes_processed,
+ ctx.sz,
+ block_hash_ctx);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.bptr[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ } else if (/* ctx.bitoffs != 0 && */
+ (!can_trap ||
+ (iterations_until_trap - iters_for_bin) > 0)) {
+ /* No need to trap while hashing binary */
+ Uint nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ byte *buf = erts_alloc(ERTS_ALC_T_TMP, nr_of_bytes);
+ Uint nr_of_bits_to_copy = ctx.sz*BYTE_BITS+ctx.bitsize;
+ if (can_trap) iterations_until_trap -= iters_for_bin;
+ erts_copy_bits(ctx.bptr,
+ ctx.bitoffs, 1, buf, 0, 1, nr_of_bits_to_copy);
+ hash = block_hash(buf, ctx.sz, con);
+ if (ctx.bitsize > 0) {
+ UINT32_HASH_2(ctx.bitsize,
+ (buf[ctx.sz] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ erts_free(ERTS_ALC_T_TMP, buf);
+ } else /* ctx.bitoffs != 0 && */ {
+#ifdef DEBUG
+#define BINARY_BUF_SIZE (BLOCK_HASH_BYTES_PER_ITER * 3)
+#else
+#define BINARY_BUF_SIZE (BLOCK_HASH_BYTES_PER_ITER * 256)
+#endif
+#define BINARY_BUF_SIZE_BITS (BINARY_BUF_SIZE*BYTE_BITS)
+ /* Need to trap while hashing binary */
+ ErtsBlockHashHelperCtx* block_hash_ctx = &ctx.block_hash_ctx;
+ Uint nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ ERTS_CT_ASSERT(BINARY_BUF_SIZE % BLOCK_HASH_BYTES_PER_ITER == 0);
+ ctx.buf = erts_alloc(ERTS_ALC_T_PHASH2_TRAP,
+ MIN(nr_of_bytes, BINARY_BUF_SIZE));
+ block_hash_setup(con, block_hash_ctx);
+ do {
+ Uint bytes_left =
+ ctx.sz - ctx.no_bytes_processed;
+ Uint even_bytes_left =
+ bytes_left - (bytes_left % BLOCK_HASH_BYTES_PER_ITER);
+ Uint bytes_to_process =
+ MIN(BINARY_BUF_SIZE, even_bytes_left);
+ Uint nr_of_bits_left =
+ (ctx.sz*BYTE_BITS+ctx.bitsize) -
+ ctx.no_bytes_processed*BYTE_BITS;
+ Uint nr_of_bits_to_copy =
+ MIN(nr_of_bits_left, BINARY_BUF_SIZE_BITS);
+ ctx.done = nr_of_bits_left == nr_of_bits_to_copy;
+ erts_copy_bits(ctx.bptr + ctx.no_bytes_processed,
+ ctx.bitoffs, 1, ctx.buf, 0, 1,
+ nr_of_bits_to_copy);
+ block_hash_buffer(ctx.buf,
+ bytes_to_process,
+ block_hash_ctx);
+ ctx.no_bytes_processed += bytes_to_process;
+ iterations_until_trap -=
+ MAX(1, bytes_to_process / BLOCK_HASH_BYTES_PER_ITER);
+ TRAP_LOCATION_NO_RED(sub_binary_subtag_2);
+ block_hash_ctx = &ctx.block_hash_ctx; /* Restore after trap */
+ } while (!ctx.done);
+ nr_of_bytes = ctx.sz + (ctx.bitsize != 0);
+ hash = block_hash_final_bytes(ctx.buf +
+ (ctx.no_bytes_processed -
+ ((nr_of_bytes-1) / BINARY_BUF_SIZE) * BINARY_BUF_SIZE),
+ ctx.sz - ctx.no_bytes_processed,
+ ctx.sz,
+ block_hash_ctx);
+ if (ctx.bitsize > 0) {
+ Uint last_byte_index =
+ nr_of_bytes - (((nr_of_bytes-1) / BINARY_BUF_SIZE) * BINARY_BUF_SIZE) -1;
+ UINT32_HASH_2(ctx.bitsize,
+ (ctx.buf[last_byte_index] >> (BYTE_BITS - ctx.bitsize)),
+ HCONST_15);
+ }
+ erts_free(ERTS_ALC_T_PHASH2_TRAP, ctx.buf);
+ context->trap_location_state.sub_binary_subtag_2.buf = NULL;
}
goto hash2_common;
+#undef BYTE_BITS
+#undef BINARY_BUF_SIZE
+#undef BINARY_BUF_SIZE_BITS
}
break;
case POS_BIG_SUBTAG:
case NEG_BIG_SUBTAG:
{
- Eterm* ptr = big_val(term);
- Uint i = 0;
- Uint n = BIG_SIZE(ptr);
- Uint32 con = BIG_SIGN(ptr) ? HCONST_10 : HCONST_11;
+ Eterm* big_val_ptr = big_val(term);
+ ErtsMakeHash2Context_NEG_BIG_SUBTAG ctx = {
+ .ptr = big_val_ptr,
+ .i = 0,
+ .n = BIG_SIZE(big_val_ptr),
+ .con = BIG_SIGN(big_val_ptr) ? HCONST_10 : HCONST_11};
#if D_EXP == 16
do {
Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- x += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y += (Uint32)(i < n ? BIG_DIGIT(ptr, i++) : 0) << 16;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ x = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ x += (Uint32)(ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0) << 16;
+ y = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ y += (Uint32)(ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0) << 16;
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#elif D_EXP == 32
do {
Uint32 x, y;
- x = i < n ? BIG_DIGIT(ptr, i++) : 0;
- y = i < n ? BIG_DIGIT(ptr, i++) : 0;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ x = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ y = ctx.i < ctx.n ? BIG_DIGIT(ctx.ptr, ctx.i++) : 0;
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#elif D_EXP == 64
do {
Uint t;
Uint32 x, y;
- ASSERT(i < n);
- t = BIG_DIGIT(ptr, i++);
+ ASSERT(ctx.i < ctx.n);
+ t = BIG_DIGIT(ctx.ptr, ctx.i++);
x = t & 0xffffffff;
y = t >> 32;
- UINT32_HASH_2(x, y, con);
- } while (i < n);
+ UINT32_HASH_2(x, y, ctx.con);
+ TRAP_LOCATION(neg_big_subtag);
+ } while (ctx.i < ctx.n);
#else
#error "unsupported D_EXP size"
#endif
@@ -1508,13 +1915,13 @@ make_hash2(Eterm term)
}
case _TAG_IMMED1_SMALL:
{
- Sint x = signed_val(term);
+ Sint small = signed_val(term);
+ if (SMALL_BITS > 28 && !IS_SSMALL28(small)) {
+ NOT_SSMALL28_HASH(small);
+ } else {
+ SINT32_HASH(small, HCONST);
+ }
- if (SMALL_BITS > 28 && !IS_SSMALL28(x)) {
- term = small_to_big(x, tmp_big);
- break;
- }
- SINT32_HASH(x, HCONST);
goto hash2_common;
}
}
@@ -1529,7 +1936,10 @@ make_hash2(Eterm term)
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
- UnUseTmpHeapNoproc(2);
+ if (can_trap) {
+ BUMP_REDS(p, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
+ ASSERT(!(p->flags & F_DISABLE_GC));
+ }
return hash;
}
@@ -1540,18 +1950,37 @@ make_hash2(Eterm term)
hash = (Uint32) ESTACK_POP(s);
UINT32_HASH(hash_xor_pairs, HCONST_19);
hash_xor_pairs = (Uint32) ESTACK_POP(s);
+ TRAP_LOCATION_NO_CTX(hash2_common_1);
goto hash2_common;
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
hash = 0;
+ TRAP_LOCATION_NO_CTX(hash2_common_2);
goto hash2_common;
default:
break;
}
+
}
+ TRAP_LOCATION_NO_CTX(hash2_common_3);
}
}
+#undef TRAP_LOCATION_NO_RED
+#undef TRAP_LOCATION
+#undef TRAP_LOCATION_NO_CTX
+}
+
+Uint32
+make_hash2(Eterm term)
+{
+ return make_hash2_helper(term, 0, NULL, NULL);
+}
+
+Uint32
+trapping_make_hash2(Eterm term, Eterm* state_mref_write_back, Process* p)
+{
+ return make_hash2_helper(term, 1, state_mref_write_back, p);
}
/* Term hash function for internal use.
@@ -1731,7 +2160,7 @@ make_internal_hash(Eterm term, Uint32 salt)
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
Uint num_free = funp->num_free;
UINT32_HASH_2(num_free, funp->fe->module, HCONST_20);
- UINT32_HASH_2(funp->fe->old_index, funp->fe->old_uniq, HCONST_21);
+ UINT32_HASH_2(funp->fe->index, funp->fe->old_uniq, HCONST_21);
if (num_free == 0) {
goto pop_next;
} else {
@@ -2381,7 +2810,7 @@ tailrecur_ne:
f1 = (ErlFunThing *) fun_val(a);
f2 = (ErlFunThing *) fun_val(b);
if (f1->fe->module != f2->fe->module ||
- f1->fe->old_index != f2->fe->old_index ||
+ f1->fe->index != f2->fe->index ||
f1->fe->old_uniq != f2->fe->old_uniq ||
f1->num_free != f2->num_free) {
goto not_equal;
@@ -2544,10 +2973,14 @@ tailrecur_ne:
bb = hashmap_val(b) + 1;
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
case HAMT_SUBTAG_HEAD_ARRAY:
+ if (aa[0] != bb[0])
+ goto not_equal;
aa++; bb++;
sz = 16;
break;
case HAMT_SUBTAG_HEAD_BITMAP:
+ if (aa[0] != bb[0])
+ goto not_equal;
aa++; bb++;
case HAMT_SUBTAG_NODE_BITMAP:
sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
@@ -2701,8 +3134,7 @@ Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only)
if((AN)->sysname != (BN)->sysname) \
RETURN_NEQ(erts_cmp_atoms((AN)->sysname, (BN)->sysname)); \
ASSERT((AN)->creation != (BN)->creation); \
- if ((AN)->creation != 0 && (BN)->creation != 0) \
- RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
+ RETURN_NEQ(((AN)->creation < (BN)->creation) ? -1 : 1); \
} \
} while (0)
@@ -2976,7 +3408,7 @@ tailrecur_ne:
if (diff != 0) {
RETURN_NEQ(diff);
}
- diff = f1->fe->old_index - f2->fe->old_index;
+ diff = f1->fe->index - f2->fe->index;
if (diff != 0) {
RETURN_NEQ(diff);
}
@@ -4454,201 +4886,6 @@ erts_get_emu_args(Process *c_p)
return res;
}
-
-Eterm
-erts_get_ethread_info(Process *c_p)
-{
- Uint sz, *szp;
- Eterm res, *hp, **hpp, *end_hp = NULL;
-
- sz = 0;
- szp = &sz;
- hpp = NULL;
-
- while (1) {
- Eterm tup, list, name;
-#if defined(ETHR_NATIVE_ATOMIC32_IMPL) \
- || defined(ETHR_NATIVE_ATOMIC64_IMPL) \
- || defined(ETHR_NATIVE_DW_ATOMIC_IMPL)
- char buf[1024];
- int i;
- char **str;
-#endif
-
- res = NIL;
-
-#ifdef ETHR_X86_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "sse2"),
-#ifdef ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
- erts_bld_string(hpp, szp,
- (ETHR_X86_RUNTIME_CONF_HAVE_SSE2__
- ? "yes" : "no"))
-#else
- erts_bld_string(hpp, szp, "yes")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp,
- "x86"
-#ifdef ARCH_64
- "_64"
-#endif
- " OOO"),
- erts_bld_string(hpp, szp,
-#ifdef ETHR_X86_OUT_OF_ORDER
- "yes"
-#else
- "no"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-#endif
-
-#ifdef ETHR_SPARC_V9_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Sparc V9"),
- erts_bld_string(hpp, szp,
-#if defined(ETHR_SPARC_TSO)
- "TSO"
-#elif defined(ETHR_SPARC_PSO)
- "PSO"
-#elif defined(ETHR_SPARC_RMO)
- "RMO"
-#else
- "undefined"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-
-#endif
-
-#ifdef ETHR_PPC_MEMBAR_H__
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "lwsync"),
- erts_bld_string(hpp, szp,
-#if defined(ETHR_PPC_HAVE_LWSYNC)
- "yes"
-#elif defined(ETHR_PPC_HAVE_NO_LWSYNC)
- "no"
-#elif defined(ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__)
- ETHR_PPC_RUNTIME_CONF_HAVE_LWSYNC__ ? "yes" : "no"
-#else
- "undefined"
-#endif
- ));
-
- res = erts_bld_cons(hpp, szp, tup, res);
-
-#endif
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Native rw-spinlocks"),
-#ifdef ETHR_NATIVE_RWSPINLOCK_IMPL
- erts_bld_string(hpp, szp, ETHR_NATIVE_RWSPINLOCK_IMPL)
-#else
- erts_bld_string(hpp, szp, "no")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
- tup = erts_bld_tuple(hpp, szp, 2,
- erts_bld_string(hpp, szp, "Native spinlocks"),
-#ifdef ETHR_NATIVE_SPINLOCK_IMPL
- erts_bld_string(hpp, szp, ETHR_NATIVE_SPINLOCK_IMPL)
-#else
- erts_bld_string(hpp, szp, "no")
-#endif
- );
- res = erts_bld_cons(hpp, szp, tup, res);
-
-
- list = NIL;
-#ifdef ETHR_NATIVE_DW_ATOMIC_IMPL
- if (ethr_have_native_dw_atomic()) {
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_DW_ATOMIC_IMPL);
- str = ethr_native_dw_atomic_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_dw_atomic_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
- str = ethr_native_su_dw_atomic_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_su_dw_atomic_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
- }
- else
-#endif
- name = erts_bld_string(hpp, szp, "no");
-
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "Double word native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- list = NIL;
-#ifdef ETHR_NATIVE_ATOMIC64_IMPL
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC64_IMPL);
- str = ethr_native_atomic64_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_atomic64_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
-#else
- name = erts_bld_string(hpp, szp, "no");
-#endif
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "64-bit native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- list = NIL;
-#ifdef ETHR_NATIVE_ATOMIC32_IMPL
- name = erts_bld_string(hpp, szp, ETHR_NATIVE_ATOMIC32_IMPL);
- str = ethr_native_atomic32_ops();
- for (i = 0; str[i]; i++) {
- erts_snprintf(buf, sizeof(buf), "ethr_native_atomic32_%s()", str[i]);
- list = erts_bld_cons(hpp, szp,
- erts_bld_string(hpp, szp, buf),
- list);
- }
-#else
- name = erts_bld_string(hpp, szp, "no");
-#endif
- tup = erts_bld_tuple(hpp, szp, 3,
- erts_bld_string(hpp, szp, "32-bit native atomics"),
- name,
- list);
- res = erts_bld_cons(hpp, szp, tup, res);
-
- if (hpp) {
- HRelease(c_p, end_hp, *hpp)
- return res;
- }
-
- hp = HAlloc(c_p, sz);
- end_hp = hp + sz;
- hpp = &hp;
- szp = NULL;
- }
-}
-
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 2ef452fa01..c3d2df6008 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -2574,16 +2574,18 @@ http_request_inetdrv(void* arg, const http_atom_t* meth, const char* meth_ptr,
}
static int
-http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
- int name_len, const char* value_ptr, int value_len)
+http_header_inetdrv(void* arg, const http_atom_t* name,
+ const char* name_ptr, int name_len,
+ const char* oname_ptr, int oname_len,
+ const char* value_ptr, int value_len)
{
tcp_descriptor* desc = (tcp_descriptor*) arg;
int i = 0;
- ErlDrvTermData spec[26];
+ ErlDrvTermData spec[27];
ErlDrvTermData caller = ERL_DRV_NIL;
if (desc->inet.active == INET_PASSIVE) {
- /* {inet_async,S,Ref,{ok,{http_header,Bit,Name,IValue,Value}} */
+ /* {inet_async,S,Ref,{ok,{http_header,Bit,Name,Oname,Value}} */
int req;
int aid;
@@ -2596,7 +2598,7 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
i = LOAD_ATOM(spec, i, am_ok);
}
else {
- /* {http, S, {http_header,Bit,Name,IValue,Value}} */
+ /* {http, S, {http_header,Bit,Name,Oname,Value}} */
i = LOAD_ATOM(spec, i, am_http);
i = LOAD_PORT(spec, i, desc->inet.dport);
}
@@ -2610,19 +2612,19 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
i = LOAD_INT(spec, i, 0);
i = http_load_string(desc, spec, i, name_ptr, name_len);
}
- i = LOAD_ATOM(spec, i, am_undefined);
+ i = http_load_string(desc, spec, i, oname_ptr, oname_len);
i = http_load_string(desc, spec, i, value_ptr, value_len);
i = LOAD_TUPLE(spec, i, 5);
if (desc->inet.active == INET_PASSIVE) {
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
- ASSERT(i <= 26);
+ ASSERT(i <= 27);
return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
i = LOAD_TUPLE(spec, i, 3);
- ASSERT(i <= 26);
+ ASSERT(i <= 27);
return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
index 0bcccfe405..de5701d2f3 100644
--- a/erts/emulator/drivers/win32/win_con.c
+++ b/erts/emulator/drivers/win32/win_con.c
@@ -157,7 +157,7 @@ static RECT winPos;
static BOOL toolbarVisible;
static BOOL destroyed = FALSE;
-static int lines_to_save = 1000; /* Maximum number of screen lines to save. */
+static int lines_to_save = 10000; /* Maximum number of screen lines to save. */
#define TITLE_BUF_SZ 256
@@ -205,6 +205,18 @@ static void window_title(struct title_buf *);
static void free_window_title(struct title_buf *);
static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
+#ifdef HARDDEBUG
+/* For really hard GUI startup debugging, place DEBUGBOX() macros in code
+ and get modal message boxes with the line number. */
+static void debug_box(int line) {
+ TCHAR buff[1024];
+ swprintf(buff,1024,TEXT("DBG:%d"),line);
+ MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
+}
+
+#define DEBUGBOX() debug_box(__LINE__)
+#endif
+
#define CON_VPRINTF_BUF_INC_SIZE 1024
static erts_dsprintf_buf_t *
@@ -430,6 +442,13 @@ ConThreadInit(LPVOID param)
struct title_buf title;
/*DebugBreak();*/
+#ifdef HARDDEBUG
+ if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) {
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ }
+#endif
+
hInstance = GetModuleHandle(NULL);
StartupInfo.dwFlags = 0;
GetStartupInfo(&StartupInfo);
@@ -549,7 +568,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
case WM_CREATE:
/* client window creation */
window_title(&title);
- hClientWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClientClass, title.name,
+ hClientWnd = CreateWindowEx(0, szClientClass, title.name,
WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
@@ -610,7 +629,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
lpttt->hinst = hInstance;
/* check for combobox handle */
if (lpttt->uFlags&TTF_IDISHWND) {
- if ((lpttt->hdr.idFrom == (UINT) hComboWnd)) {
+ if ((lpttt->hdr.idFrom == (UINT_PTR) hComboWnd)) {
lstrcpy(lpttt->lpszText,TEXT("Command History"));
break;
}
@@ -1313,13 +1332,21 @@ LoadUserPreferences(void)
DWORD size;
DWORD res;
DWORD type;
-
+ HFONT hfont;
/* default prefs */
- GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
+ hfont = CreateFont(0,0, 0,0, 0, FALSE,FALSE,FALSE,
+ ANSI_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
+ CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Consolas"));
+ if(hfont) {
+ GetObject(hfont, sizeof(LOGFONT), (PSTR)&logfont);
+ DeleteObject(hfont);
+ } else {
+ GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
+ }
fgColor = GetSysColor(COLOR_WINDOWTEXT);
bkgColor = GetSysColor(COLOR_WINDOW);
winPos.left = -1;
- toolbarVisible = TRUE;
+ toolbarVisible = FALSE;
if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
@@ -1774,7 +1801,7 @@ void ConChooseColor(HWND hwnd)
SetBkColor(hdc,bkgColor);
ReleaseDC(hwnd,hdc);
hbrush = CreateSolidBrush(bkgColor);
- DeleteObject((HBRUSH)SetClassLong(hClientWnd,GCL_HBRBACKGROUND,(LONG)hbrush));
+ DeleteObject((HBRUSH)SetClassLongPtr(hClientWnd,GCL_HBRBACKGROUND,(LONG_PTR)hbrush));
InvalidateRect(hwnd,NULL,TRUE);
}
}
@@ -2012,7 +2039,7 @@ ConDrawText(HWND hwnd)
TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR));
memcpy(bu,buf,num_chars * sizeof(TCHAR));
bu[num_chars]='\0';
- fprintf(stderr,TEXT("ConDrawText\"%s\"\n"),bu);
+ fprintf(stderr,"ConDrawText\"%S\"\n",bu);
FREE(bu);
fflush(stderr);
}
@@ -2211,17 +2238,6 @@ static TBADDBITMAP tbbitmap =
HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
};
-#ifdef HARDDEBUG
-/* For really hard GUI startup debugging, place DEBUGBOX() macros in code
- and get modal message boxes with the line number. */
-static void debug_box(int line) {
- TCHAR buff[1024];
- swprintf(buff,1024,TEXT("DBG:%d"),line);
- MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
-}
-
-#define DEBUGBOX() debug_box(__LINE__)
-#endif
static HWND
InitToolBar(HWND hwndParent)
@@ -2243,7 +2259,7 @@ InitToolBar(HWND hwndParent)
SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE,
(WPARAM) sizeof(TBBUTTON),0);
tbbitmap.hInst = NULL;
- tbbitmap.nID = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
+ tbbitmap.nID = (UINT_PTR) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4,
(LPARAM) &tbbitmap);
@@ -2269,7 +2285,7 @@ InitToolBar(HWND hwndParent)
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS;
ti.hwnd = hwndTB;;
- ti.uId = (UINT)hComboWnd;
+ ti.uId = (UINT_PTR)hComboWnd;
ti.lpszText = LPSTR_TEXTCALLBACK;
hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0);
SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti);
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 7e04f7d9c0..13acf2e6b3 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -193,5 +193,6 @@ BIF_RETTYPE hipe_bifs_llvm_fix_pinned_regs_0(BIF_ALIST_0)
BIF_RETTYPE hipe_bifs_build_stacktrace_1(BIF_ALIST_1)
{
+ BIF_P->ftrace = NIL;
BIF_RET(build_stacktrace(BIF_P, BIF_ARG_1));
}
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 7468860c37..77b5b2c275 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -290,6 +290,8 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
*/
gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1)
gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2)
+gc_bif_interface_1(nbif_term_to_iovec_1, hipe_wrapper_term_to_iovec_1)
+gc_bif_interface_2(nbif_term_to_iovec_2, hipe_wrapper_term_to_iovec_2)
gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1)
gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2)
gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1)
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 138e4f7da3..2e34cfac59 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -232,7 +232,6 @@ void hipe_print_pcb(Process *p)
U("intial.fun ", u.initial.function);
U("intial.ari ", u.initial.arity);
U("current ", current);
- P("cp ", cp);
P("i ", i);
U("catches ", catches);
U("arity ", arity);
diff --git a/erts/emulator/hipe/hipe_instrs.tab b/erts/emulator/hipe/hipe_instrs.tab
index a01baebddf..8aa8544b2a 100644
--- a/erts/emulator/hipe/hipe_instrs.tab
+++ b/erts/emulator/hipe/hipe_instrs.tab
@@ -86,15 +86,14 @@ hipe_trap.post() {
switch( c_p->def_arg_reg[3] ) {
case HIPE_MODE_SWITCH_RES_RETURN:
ASSERT(is_value(reg[0]));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
case HIPE_MODE_SWITCH_RES_CALL_EXPORTED:
c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()];
/*fall through*/
case HIPE_MODE_SWITCH_RES_CALL_BEAM:
SET_I(c_p->i);
- Dispatch();
+ $DISPATCH();
case HIPE_MODE_SWITCH_RES_CALL_CLOSURE:
/* This can be used to call any function value, but currently
it's only used to call closures referring to unloaded
@@ -105,13 +104,11 @@ hipe_trap.post() {
next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE);
HEAVY_SWAPIN;
if (next != NULL) {
- SET_I(next);
- Dispatchfun();
+ $DISPATCH_FUN(next);
}
goto find_func_info;
}
case HIPE_MODE_SWITCH_RES_THROW:
- c_p->cp = NULL;
I = handle_error(c_p, I, reg, NULL);
goto post_error_handling;
default:
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 1cc25a3cf0..6795c526fa 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -202,15 +202,13 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
p->stop -= 2;
p->stop[1] = hipe_beam_catch_throw;
}
- p->stop[0] = make_cp(p->cp);
+ p->stop[0] = (BeamInstr) hipe_beam_pc_return;
++p->catches;
- p->cp = hipe_beam_pc_return;
}
static __inline__ void hipe_pop_beam_trap_frame(Process *p)
{
ASSERT(p->stop[1] == hipe_beam_catch_throw);
- p->cp = cp_val(p->stop[0]);
--p->catches;
p->stop += 2;
}
@@ -263,7 +261,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
unsigned arity = cmd >> 8;
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
@@ -292,7 +290,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
/* just like a normal call from now on */
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
diff --git a/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md b/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md
new file mode 100644
index 0000000000..e68a57a7ab
--- /dev/null
+++ b/erts/emulator/internal_doc/AutomaticYieldingOfCCode.md
@@ -0,0 +1,62 @@
+Automatic Yielding of C Code
+============================
+
+Introduction
+------------
+
+Erlang [NIFs](http://erlang.org/doc/tutorial/nif.html) and
+[BIFs](http://erlang.org/pipermail/erlang-questions/2009-October/046899.html)
+should not run for a too long time without yielding (often referred to
+as trapping in the source code of ERTS). The Erlang/OTP system gets
+unresponsive, and some task may get prioritized unfairly if NIFs and
+BIFs occupy scheduler threads for a too long time. Therefore, the most
+commonly used NIFs and BIFs that may run for a long time can yield.
+
+Problems
+--------
+
+Erlang NIFs and BIFs are typically implemented in the C programming
+language. The C programming language does not have built-in support
+for automatic yielding in the middle of a routine (referred to as
+[coroutine support](https://en.wikipedia.org/wiki/Coroutine) in other
+programming languages). Therefore, most NIFs and BIFs implement
+yielding manually. Manual implementation of yielding has the advantage
+of giving the programmer control over what should be saved and when
+yielding should happen. Unfortunately, manual implementation of
+yielding also leads to code with a lot of boilerplate that is more
+difficult to read than corresponding code that does not
+yield. Furthermore, manual implementation of yielding can be
+time-consuming and error-prone, especially if the NIF or BIF is
+complicated.
+
+Solution
+--------
+
+A source-to-source transformer, called Yielding C Fun (YCF), has been
+created to make it easier to implement yielding NIFs and BIFs. YCF is
+a tool that takes a set of function names and a C source code file and
+transforms the functions with the given names in the source code file
+into yieldable versions that can be used as coroutines. YCF has been
+created with yielding NIFs and BIFs in mind and has several features
+that can be handy when implementing yielding NIFs and BIFs. The reader
+is recommended to look at YCF's documentation for a detailed
+description of YCF.
+
+Yielding C Fun's Source Code and Documentation
+----------------------------------------------
+
+The source code of YCF is included in the folder
+`"$ERL_TOP"/erts/lib_src/yielding_c_fun/` inside the source tree of
+the Erlang/OTP system. The documentation of YCF can be found in
+`"$ERL_TOP"/erts/lib_src/yielding_c_fun/README.md`. A rendered version
+of YCF documentation can be found
+[here](https://github.com/erlang/otp/erts/lib_src/yielding_c_fun/README.md).
+
+Yielding C Fun in the Erlang Run-time System
+-------------------------------------------
+
+YCF is used to implement yielding in the BIFs for the `ets:insert/2`
+and `ets:insert_new/2` functions when these two functions get a list
+as their second parameter. The implementation of these two functions
+is a good starting point if one wants to use YCF to implement
+something else inside ERTS.
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
index 2880099b70..1ec94d4ff9 100644
--- a/erts/emulator/internal_doc/beam_makeops.md
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -1087,12 +1087,14 @@ use as a temporary X register.
* `y` - Y register. The default value is 0.
-* `l` - Foating point register number. The default value is 0.
+* `l` - Floating point register number. The default value is 0.
* `i` - Tagged literal integer. The default value is 0.
* `a` - Tagged atom. The default value is the empty atom (`am_Empty`).
+* `p` - Zero failure label.
+
* `n` - NIL (`[]`, the empty list).
#### Function call on the right side ####
@@ -1457,26 +1459,26 @@ all instructions. It expands to the address of the next instruction.
Here is an example:
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
-When calling a function, the return address is first stored in `c_p->cp`
-(using the `SET_CP()` macro defined in `beam_emu.c`), and then control is
+When calling a function, the return address is first stored in `E[0]`
+(using the `$SAVE_CONTINUATION_POINTER()` macro), and then control is
transferred to the callee. Here is the generated code:
OpCase(i_call_f):
{
- SET_CP(c_p, I+1);
- ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
- I += fb(BeamExtraData(I[0])) + 0;;
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();;
+ ASSERT(VALID_INSTR(*(I+2)));
+ *E = (BeamInstr) (I+2);;
+
+ /* ... dispatch code intentionally left out ... */
}
-We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+1`.
+We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+2`.
That makes sense since the size of the `i_call_f/1` instruction is
-one word.
+two words.
##### The IP_ADJUSTMENT pre-bound variable #####
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
index 3df04e42e2..5c5e9a2d30 100644
--- a/erts/emulator/nifs/common/prim_file_nif.c
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -162,6 +162,7 @@ WRAP_FILE_HANDLE_EXPORT(allocate_nif)
WRAP_FILE_HANDLE_EXPORT(advise_nif)
WRAP_FILE_HANDLE_EXPORT(get_handle_nif)
WRAP_FILE_HANDLE_EXPORT(ipread_s32bu_p32bu_nif)
+WRAP_FILE_HANDLE_EXPORT(read_handle_info_nif)
static ErlNifFunc nif_funcs[] = {
/* File handle ops */
@@ -176,6 +177,7 @@ static ErlNifFunc nif_funcs[] = {
{"truncate_nif", 1, truncate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"allocate_nif", 3, allocate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"advise_nif", 4, advise_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_handle_info_nif", 1, read_handle_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
/* Filesystem ops */
{"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
@@ -231,6 +233,7 @@ static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM prim_file_pid)
am_append = enif_make_atom(env, "append");
am_sync = enif_make_atom(env, "sync");
am_skip_type_check = enif_make_atom(env, "skip_type_check");
+ am_directory = enif_make_atom(env, "directory");
am_read_write = enif_make_atom(env, "read_write");
am_none = enif_make_atom(env, "none");
@@ -447,6 +450,8 @@ static enum efile_modes_t efile_translate_modelist(ErlNifEnv *env, ERL_NIF_TERM
modes |= EFILE_MODE_SYNC;
} else if(enif_is_identical(head, am_skip_type_check)) {
modes |= EFILE_MODE_SKIP_TYPE_CHECK;
+ } else if (enif_is_identical(head, am_directory)) {
+ modes |= EFILE_MODE_DIRECTORY;
} else {
/* Modes like 'raw', 'ram', 'delayed_writes' etc are handled
* further up the chain. */
@@ -893,6 +898,26 @@ static ERL_NIF_TERM get_handle_nif_impl(efile_data_t *d, ErlNifEnv *env, int arg
return efile_get_handle(env, d);
}
+static ERL_NIF_TERM build_file_info(ErlNifEnv *env, efile_fileinfo_t *info) {
+ /* #file_info as declared in file.hrl */
+ return enif_make_tuple(env, 14,
+ am_file_info,
+ enif_make_uint64(env, info->size),
+ efile_filetype_to_atom(info->type),
+ efile_access_to_atom(info->access),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->a_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->m_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->c_time)),
+ enif_make_uint(env, info->mode),
+ enif_make_uint(env, info->links),
+ enif_make_uint(env, info->major_device),
+ enif_make_uint(env, info->minor_device),
+ enif_make_uint(env, info->inode),
+ enif_make_uint(env, info->uid),
+ enif_make_uint(env, info->gid)
+ );
+}
+
static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
posix_errno_t posix_errno;
@@ -911,23 +936,20 @@ static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a
return posix_error_to_tuple(env, posix_errno);
}
- /* #file_info as declared in file.hrl */
- return enif_make_tuple(env, 14,
- am_file_info,
- enif_make_uint64(env, info.size),
- efile_filetype_to_atom(info.type),
- efile_access_to_atom(info.access),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.a_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.m_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.c_time)),
- enif_make_uint(env, info.mode),
- enif_make_uint(env, info.links),
- enif_make_uint(env, info.major_device),
- enif_make_uint(env, info.minor_device),
- enif_make_uint(env, info.inode),
- enif_make_uint(env, info.uid),
- enif_make_uint(env, info.gid)
- );
+ return build_file_info(env, &info);
+}
+
+static ERL_NIF_TERM read_handle_info_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+ efile_fileinfo_t info = {0};
+
+ ASSERT(argc == 0);
+
+ if((posix_errno = efile_read_handle_info(d, &info))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return build_file_info(env, &info);
}
static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h
index b2e30c59dd..1cf5d52192 100644
--- a/erts/emulator/nifs/common/prim_file_nif.h
+++ b/erts/emulator/nifs/common/prim_file_nif.h
@@ -30,6 +30,8 @@ enum efile_modes_t {
EFILE_MODE_SKIP_TYPE_CHECK = (1 << 5), /* Special for device files on Unix. */
EFILE_MODE_NO_TRUNCATE = (1 << 6), /* Special for reopening on VxWorks. */
+ EFILE_MODE_DIRECTORY = (1 << 7),
+
EFILE_MODE_READ_WRITE = EFILE_MODE_READ | EFILE_MODE_WRITE
};
@@ -110,7 +112,7 @@ typedef struct {
typedef ErlNifBinary efile_path_t;
-/* @brief Translates the given "raw name" into the format expected by the APIs
+/** @brief Translates the given "raw name" into the format expected by the APIs
* used by the underlying implementation. The result is transient and does not
* need to be released.
*
@@ -121,30 +123,30 @@ typedef ErlNifBinary efile_path_t;
* prim_file:internal_native2name for compatibility reasons. */
posix_errno_t efile_marshal_path(ErlNifEnv *env, ERL_NIF_TERM path, efile_path_t *result);
-/* @brief Returns the underlying handle as an implementation-defined term.
+/** @brief Returns the underlying handle as an implementation-defined term.
*
* This is an internal function intended to support tests and tricky
* operations like sendfile(2). */
ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d);
-/* @brief Read until EOF or the given iovec has been filled.
+/** @brief Read until EOF or the given iovec has been filled.
*
* @return -1 on failure, or the number of bytes read on success. The return
* value will be 0 if no bytes could be read before EOF or the end of the
* iovec. */
Sint64 efile_readv(efile_data_t *d, SysIOVec *iov, int iovlen);
-/* @brief Write the entirety of the given iovec.
+/** @brief Write the entirety of the given iovec.
*
* @return -1 on failure, or the number of bytes written on success. "Partial"
* failures will be reported with -1 and not the number of bytes we managed to
* write to disk before the failure. */
Sint64 efile_writev(efile_data_t *d, SysIOVec *iov, int iovlen);
-/* @brief As \c efile_readv, but starting from a file offset. */
+/** @brief As \c efile_readv, but starting from a file offset. */
Sint64 efile_preadv(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen);
-/* @brief As \c efile_writev, but starting from a file offset. */
+/** @brief As \c efile_writev, but starting from a file offset. */
Sint64 efile_pwritev(efile_data_t *d, Sint64 offset, SysIOVec *iov, int iovlen);
int efile_seek(efile_data_t *d, enum efile_seek_t seek, Sint64 offset, Sint64 *new_position);
@@ -168,6 +170,7 @@ int efile_close(efile_data_t *d, posix_errno_t *error);
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
posix_errno_t efile_read_info(const efile_path_t *path, int follow_link, efile_fileinfo_t *result);
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result);
/** @brief Sets the file times to the given values. Refer to efile_fileinfo_t
* for a description of each. */
diff --git a/erts/emulator/nifs/common/prim_net_nif.c b/erts/emulator/nifs/common/prim_net_nif.c
index 1fbd3e2366..28ece878c2 100644
--- a/erts/emulator/nifs/common/prim_net_nif.c
+++ b/erts/emulator/nifs/common/prim_net_nif.c
@@ -65,6 +65,14 @@
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
+#elif defined(__PASE__)
+/* PASE has this, but under a different name because AIX doesn't have it. */
+#include <as400_protos.h>
+/*
+ * We don't redefine the function names because they're used in other
+ * contexts, but the struct is safe to rename.
+ */
+#define ifaddrs ifaddrs_pase
#endif
#ifdef HAVE_NETPACKET_PACKET_H
@@ -1030,7 +1038,7 @@ ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env,
{
#if defined(__WIN32__)
return enif_raise_exception(env, MKA(env, "notsup"));
-#elif defined(HAVE_GETIFADDRS)
+#elif defined(HAVE_GETIFADDRS) || defined(__PASE__)
ERL_NIF_TERM extra;
char* netns;
ERL_NIF_TERM result;
@@ -1066,7 +1074,7 @@ ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env,
}
-#ifdef HAVE_GETIFADDRS
+#if defined(HAVE_GETIFADDRS) || defined(__PASE__)
#ifdef HAVE_SETNS
/* enet_getifaddrs_netns - extract the netns field from the 'extra' map
*
@@ -1146,9 +1154,17 @@ ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env, char* netns)
return esock_make_error_errno(env, save_errno);
#endif
+#ifdef __PASE__
+ if (0 == Qp2getifaddrs(&ifap)) {
+#else
if (0 == getifaddrs(&ifap)) {
+#endif
result = enet_getifaddrs_process(env, ifap);
+#ifdef __PASE__
+ Qp2freeifaddrs(ifap);
+#else
freeifaddrs(ifap);
+#endif
} else {
save_errno = get_errno();
@@ -1676,7 +1692,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#if defined(__WIN32__)
+#if defined(__WIN32__) || (defined(__ANDROID__) && (__ANDROID_API__ < 24))
return enif_raise_exception(env, MKA(env, "notsup"));
#else
ERL_NIF_TERM result;
@@ -1697,7 +1713,10 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
-#if !defined(__WIN32__)
+/* if_nameindex and if_freenameindex were added in Android 7.0 Nougat. With
+the Android NDK Unified Headers, check that the build is targeting at least
+the corresponding API level 24. */
+#if !defined(__WIN32__) && !(defined(__ANDROID__) && (__ANDROID_API__ < 24))
static
ERL_NIF_TERM enet_if_names(ErlNifEnv* env)
{
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index ab95748458..a775889f26 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -690,9 +690,14 @@ typedef union {
#define ESOCK_SUPPORTS_LOCAL 0x0004
#define ESOCK_SUPPORTS_SEND_FLAGS 0x0005
#define ESOCK_SUPPORTS_RECV_FLAGS 0x0006
+#define ESOCK_SUPPORTS_NETNS 0x0007
-#define ESOCK_WHICH_PROTO_ERROR -1
-#define ESOCK_WHICH_PROTO_UNSUP -2
+#define ESOCK_WHICH_DOMAIN_ERROR -1
+#define ESOCK_WHICH_DOMAIN_UNSUP -2
+#define ESOCK_WHICH_TYPE_ERROR -1
+#define ESOCK_WHICH_TYPE_UNSUP -2
+#define ESOCK_WHICH_PROTO_ERROR -1
+#define ESOCK_WHICH_PROTO_UNSUP -2
/* =================================================================== *
@@ -702,9 +707,10 @@ typedef union {
* =================================================================== */
/* Global socket debug */
-#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto )
+#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto )
+#define SGDBG2( __DBG__ , proto ) ESOCK_DBG_PRINTF( __DBG__ || data.dbg , proto )
/* Socket specific debug */
-#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
+#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
#define ESOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC) \
{ \
@@ -902,6 +908,8 @@ typedef struct {
/* +++ The actual socket +++ */
SOCKET sock;
HANDLE event;
+ SOCKET origFD; // A 'socket' created from this FD
+ BOOLEAN_T closeOnClose; // Have we dup'ed or not
/* +++ Stuff "about" the socket +++ */
int domain;
@@ -1134,6 +1142,8 @@ static ERL_NIF_TERM esock_socket_info_protocol(ErlNifEnv* env,
ESockDescriptor* descP);
static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
+ ESockDescriptor* descP);
#define ESOCK_SOCKET_INFO_REQ_FUNCS \
ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \
ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \
@@ -1162,14 +1172,33 @@ static ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_local(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_send_flags(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_recv_flags(ErlNifEnv* env);
-static ERL_NIF_TERM esock_open(ErlNifEnv* env,
- int domain,
- int type,
- int protocol,
- char* netns);
+static ERL_NIF_TERM esock_open2(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_todup(ErlNifEnv* env,
+ ERL_NIF_TERM eextra);
+static BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* domain);
+static BOOLEAN_T esock_open2_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopt,
+ int* type);
+static BOOLEAN_T esock_open2_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* protocol);
+static ERL_NIF_TERM esock_open4(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts);
+static BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain);
+static BOOLEAN_T esock_open_which_type(SOCKET sock, int* type);
static BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
static ERL_NIF_TERM esock_bind(ErlNifEnv* env,
@@ -2618,7 +2647,9 @@ static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
BOOLEAN_T* isOTP,
int* level);
#ifdef HAVE_SETNS
-static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns);
+static BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env,
+ ERL_NIF_TERM opts,
+ char** netns);
static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err);
#endif
@@ -3123,6 +3154,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(cookie_life); \
LOCAL_ATOM_DECL(counter_wrap); \
LOCAL_ATOM_DECL(counters); \
+ LOCAL_ATOM_DECL(ctype); \
LOCAL_ATOM_DECL(data_in); \
LOCAL_ATOM_DECL(del); \
LOCAL_ATOM_DECL(dest_unreach); \
@@ -3147,6 +3179,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(max_instreams); \
LOCAL_ATOM_DECL(max_rxt); \
LOCAL_ATOM_DECL(min); \
+ LOCAL_ATOM_DECL(missing); \
LOCAL_ATOM_DECL(mode); \
LOCAL_ATOM_DECL(multiaddr); \
LOCAL_ATOM_DECL(net_unknown); \
@@ -3277,6 +3310,7 @@ static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan)
*
* The "proper" socket functions:
* ------------------------------
+ * nif_open(FD, Extra)
* nif_open(Domain, Type, Protocol, Extra)
* nif_bind(Sock, LocalAddr)
* nif_connect(Sock, SockAddr)
@@ -3436,6 +3470,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
ERL_NIF_TERM type = esock_socket_info_type(env, descP);
ERL_NIF_TERM protocol = esock_socket_info_protocol(env, descP);
ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid);
+ ERL_NIF_TERM ctype = esock_socket_info_ctype(env, descP);
ERL_NIF_TERM readable = BOOL2ATOM(descP->isReadable);
ERL_NIF_TERM writable = BOOL2ATOM(descP->isWritable);
// ERL_NIF_TERM connected = BOOL2ATOM(descP->isConnected);
@@ -3447,6 +3482,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
esock_atom_type,
esock_atom_protocol,
esock_atom_ctrl,
+ atom_ctype,
atom_readable,
atom_writable,
atom_counters,
@@ -3457,6 +3493,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
type,
protocol,
ctrlPid,
+ ctype,
readable,
writable,
counters,
@@ -3541,6 +3578,45 @@ ERL_NIF_TERM esock_socket_info_protocol(ErlNifEnv* env,
/*
+ * Encode the socket "create type"
+ * That is "show" how this socket was created:
+ *
+ * normal | fromfd | {fromfd, integer()}
+ */
+static
+ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ERL_NIF_TERM ctype;
+
+ SSDBG( descP, ("SOCKET", "esock_socket_info_ctype -> entry with"
+ "\r\n origFD: %d"
+ "\r\n closeOnClose: %s"
+ "\r\n", descP->origFD, B2S(descP->closeOnClose)) );
+
+ if (descP->origFD > 0) {
+ /* Created from other FD */
+ if (descP->closeOnClose) {
+ /* We *have* dup'ed: {fromfd, integer()} */
+ ctype = MKT2(env, MKA(env, "fromfd"), MKI(env, descP->origFD));
+ } else {
+ /* We have *not* dup'ed: fromfd */
+ ctype = MKA(env, "fromfd");
+ }
+ } else {
+ /* Normal socket */
+ ctype = MKA(env, "normal");
+ }
+
+ SSDBG( descP, ("SOCKET", "esock_socket_info_ctype -> done:"
+ "\r\n ctype: %T"
+ "\r\n", ctype) );
+
+ return ctype;
+}
+
+
+/*
* Collect all counters for a socket.
*/
static
@@ -3789,6 +3865,9 @@ ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
* sctp boolean()
* ipv6 boolean()
* local boolean()
+ * netns boolean()
+ * send_flags [{SendFlag, boolean()}]
+ * recv_flags [{RecvFlag, boolean()}]
*/
static
@@ -3846,6 +3925,10 @@ ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key)
result = esock_supports_local(env);
break;
+ case ESOCK_SUPPORTS_NETNS:
+ result = esock_supports_netns(env);
+ break;
+
case ESOCK_SUPPORTS_SEND_FLAGS:
result = esock_supports_send_flags(env);
break;
@@ -5127,6 +5210,24 @@ ERL_NIF_TERM esock_supports_local(ErlNifEnv* env)
#if !defined(__WIN32__)
static
+ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env)
+{
+ ERL_NIF_TERM supports;
+
+#if defined(HAVE_SETNS)
+ supports = esock_atom_true;
+#else
+ supports = esock_atom_false;
+#endif
+
+ return supports;
+}
+#endif
+
+
+
+#if !defined(__WIN32__)
+static
ERL_NIF_TERM esock_supports_send_flags(ErlNifEnv* env)
{
SocketTArray sflags = TARRAY_CREATE(8);
@@ -5265,16 +5366,35 @@ ERL_NIF_TERM esock_supports_recv_flags(ErlNifEnv* env)
*
* Description:
* Create an endpoint for communication.
- *
- * Arguments:
+ * This function "exist" in two variants.
+ * One with two args and onewith four.
+ *
+ * Arguments (2):
+ * FD - File Descriptor (of an already open socket).
+ * Extra - A map with extra options.
+ * The options are:
+ * [M] dup - boolean() - Shall the fd be dup'ed or not.
+ * [O] bound - boolean() - Is the fd already bound.
+ * [O] domain - domain() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * [O] type - type() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * [O] proto - protocol() - We may not be able to retrieve
+ * this on all platforms, and in
+ * *those* cases this *must* be
+ * provided.
+ * Arguments (4):
* Domain - The domain, for example 'inet'
* Type - Type of socket, for example 'stream'
* Protocol - The protocol, for example 'tcp'
* Extra - A map with "obscure" options.
* Currently the only allowed option is netns (network namespace).
* This is *only* allowed on linux!
- * We should also use this for the fd value, in case we should use
- * an already existing (file) descriptor.
+ *
*/
static
ERL_NIF_TERM nif_open(ErlNifEnv* env,
@@ -5284,72 +5404,363 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
#if defined(__WIN32__)
return enif_raise_exception(env, MKA(env, "notsup"));
#else
- int edomain, etype;
- ERL_NIF_TERM eproto; // This is normally an int, but can also be '{raw, int}'
- ERL_NIF_TERM emap;
- int domain, type, proto;
- char* netns;
ERL_NIF_TERM result;
- SGDBG( ("SOCKET", "nif_open -> entry with %d args\r\n", argc) );
-
- /* Extract arguments and perform preliminary validation */
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n argc: %d"
+ "\r\n", argc) );
- if ((argc != 4) ||
- !GET_INT(env, argv[0], &edomain) ||
- !GET_INT(env, argv[1], &etype) ||
- !IS_MAP(env, argv[3])) {
- return enif_make_badarg(env);
- }
- eproto = argv[2];
- emap = argv[3];
+ switch (argc) {
+ case 2:
+ /* The FD-version */
+ {
+ int fd;
+ ERL_NIF_TERM eopts;
- SGDBG( ("SOCKET", "nif_open -> "
- "\r\n edomain: %T"
- "\r\n etype: %T"
- "\r\n eproto: %T"
- "\r\n extra: %T"
- "\r\n", argv[0], argv[1], eproto, emap) );
-
- if (!edomain2domain(edomain, &domain)) {
- SGDBG( ("SOCKET", "nif_open -> invalid domain: %d\r\n", edomain) );
- return esock_make_error(env, esock_atom_einval);
+ if (!GET_INT(env, argv[0], &fd) ||
+ !IS_MAP(env, argv[1])) {
+ return enif_make_badarg(env);
+ }
+ eopts = argv[1];
+
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n FD: %d"
+ "\r\n eopts: %T"
+ "\r\n", fd, eopts) );
+
+ result = esock_open2(env, fd, eopts);
+ }
+ break;
+
+ case 4:
+ /* The normal version */
+ {
+ int edomain, etype;
+ ERL_NIF_TERM eproto; // integer() (normal case) | {raw, integer()}
+ ERL_NIF_TERM eopts;
+ int domain, type, proto;
+
+ /* Extract arguments and perform preliminary validation */
+
+ if (!GET_INT(env, argv[0], &edomain) ||
+ !GET_INT(env, argv[1], &etype) ||
+ !IS_MAP(env, argv[3])) {
+ return enif_make_badarg(env);
+ }
+ eproto = argv[2];
+ eopts = argv[3];
+
+ SGDBG( ("SOCKET", "nif_open -> "
+ "\r\n edomain: %T"
+ "\r\n etype: %T"
+ "\r\n eproto: %T"
+ "\r\n eopts: %T"
+ "\r\n", argv[0], argv[1], eproto, eopts) );
+
+ if (!edomain2domain(edomain, &domain)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid domain: %d\r\n", edomain) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ if (!etype2type(etype, &type)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid type: %d\r\n", etype) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ if (!eproto2proto(env, eproto, &proto)) {
+ SGDBG( ("SOCKET", "nif_open -> invalid protocol: %d\r\n", eproto) );
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ result = esock_open4(env, domain, type, proto, eopts);
+
+ }
+ break;
+
+ default:
+ SGDBG( ("SOCKET", "nif_open -> invalid number of arguments: %d"
+ "\r\n", argc) );
+ result = enif_make_badarg(env);
+ break;
}
- if (!etype2type(etype, &type)) {
- SGDBG( ("SOCKET", "nif_open -> invalid type: %d\r\n", etype) );
- return esock_make_error(env, esock_atom_einval);
+ SGDBG( ("SOCKET", "nif_open -> done with result: "
+ "\r\n %T"
+ "\r\n", result) );
+
+ return result;
+
+#endif // if defined(__WIN32__)
+}
+
+
+/* esock_open - create an endpoint (from an existing fd) for communication
+ *
+ * Assumes the input has been validated.
+ *
+ * Normally we want debugging on (individual) sockets to be controlled
+ * by the sockets own debug flag. But since we don't even have a socket
+ * yet, we must use the global debug flag.
+ */
+#if !defined(__WIN32__)
+static
+ERL_NIF_TERM esock_open2(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eopts)
+{
+ BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
+ ESockDescriptor* descP;
+ ERL_NIF_TERM res, reason;
+ int domain, type, protocol;
+ int save_errno = 0;
+ BOOLEAN_T closeOnClose;
+ SOCKET sock;
+ HANDLE event;
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> entry with"
+ "\r\n fd: %d"
+ "\r\n eopts: %T"
+ "\r\n", fd, eopts) );
+
+ /*
+ * Before we do anything else, we try to retrieve domain, type and protocol
+ * This information is either present in the eextra map or if not we need
+ * to "get" it from the system (getsockopt).
+ * Note that its not possible to get all of these on all platoforms,
+ * and in those cases the user *must* provide us with them (eextra).
+ *
+ * We try the system first (since its more reliable) and if that fails
+ * we check the eextra map. If neither one works, we *give up*!
+ */
+
+ if (!esock_open_which_domain(fd, &domain)) {
+ SGDBG2( dbg,
+ ("SOCKET",
+ "esock_open2 -> failed get domain from system\r\n") );
+ if (!esock_open2_get_domain(env, eopts, &domain)) {
+ reason = MKT2(env, atom_missing, esock_atom_domain);
+ return esock_make_error(env, reason);
+ }
+ }
+
+ if (!esock_open_which_type(fd, &type)) {
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> failed get type from system\r\n") );
+ if (!esock_open2_get_type(env, eopts, &type)) {
+ reason = MKT2(env, atom_missing, esock_atom_type);
+ return esock_make_error(env, reason);
+
+ }
+ }
+
+ if (!esock_open_which_protocol(fd, &protocol)) {
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> failed get protocol from system\r\n") );
+ if (!esock_open2_get_protocol(env, eopts, &protocol)) {
+ reason = MKT2(env, atom_missing, esock_atom_protocol);
+ return esock_make_error(env, reason);
+ }
}
- if (!eproto2proto(env, eproto, &proto)) {
- SGDBG( ("SOCKET", "nif_open -> invalid protocol: %d\r\n", eproto) );
- return esock_make_error(env, esock_atom_einval);
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> "
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n", domain, type, protocol) );
+
+
+ if (esock_open2_todup(env, eopts)) {
+ /* We shall dup the socket */
+ if ((sock = dup(fd)) == INVALID_SOCKET) {
+ save_errno = sock_errno();
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> dup failed: %d\r\n", save_errno) );
+
+ /* reason = {dup, 'errno atom'} */
+ reason = MKT2(env, MKA(env, "dup"), MKA(env, erl_errno_id(save_errno)));
+ return esock_make_error(env, reason);
+ }
+ closeOnClose = TRUE;
+ } else {
+ sock = fd;
+ closeOnClose = FALSE;
}
-#ifdef HAVE_SETNS
- /* We *currently* only support one extra option: netns */
- if (!emap2netns(env, emap, &netns)) {
- SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) );
+ event = sock;
+
+ SET_NONBLOCKING(sock);
+
+ /* Create and initiate the socket "descriptor" */
+ if ((descP = alloc_descriptor(sock, event)) == NULL) {
+ if (closeOnClose) sock_close(sock);
+ // Not sure if this is really the proper error, but...
return enif_make_badarg(env);
}
-#else
- netns = NULL;
-#endif
+ descP->state = ESOCK_STATE_OPEN;
+ descP->domain = domain;
+ descP->type = type;
+ descP->protocol = protocol;
+ descP->closeOnClose = closeOnClose;
+ descP->origFD = fd;
- result = esock_open(env, domain, type, proto, netns);
+ /* Does this apply to other types? Such as RAW?
+ * Also, is this really correct? Should we not wait for bind?
+ */
+ if ((type == SOCK_DGRAM) ||
+ (type == SOCK_SEQPACKET)) {
+ descP->isReadable = TRUE;
+ descP->isWritable = TRUE;
+ } else if (type == SOCK_STREAM) {
+ /* Check if we are already connected, if so change state */
+ unsigned int sz = sizeof(descP->remote);
+ int code = sock_peer(descP->sock,
+ (struct sockaddr*) &descP->remote, &sz);
+ if (!IS_SOCKET_ERROR(code)) {
+ SGDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
+ descP->state = ESOCK_STATE_CONNECTED;
+ descP->isReadable = TRUE;
+ descP->isWritable = TRUE;
+ enif_set_pid_undefined(&descP->connPid);
+ } else {
+ SGDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
+ }
+ }
- SGDBG( ("SOCKET", "nif_open -> done with result: "
- "\r\n %T"
- "\r\n", result) );
+ /* And create the 'socket' resource */
+ res = enif_make_resource(env, descP);
+ enif_release_resource(descP);
- return result;
+ /* Keep track of the creator
+ * This should not be a problem, but just in case
+ * the *open* function is used with the wrong kind
+ * of environment...
+ */
+ if (enif_self(env, &descP->ctrlPid) == NULL)
+ return esock_make_error(env, atom_exself);
-#endif // if defined(__WIN32__)
+ if (MONP("esock_open2 -> ctrl",
+ env, descP,
+ &descP->ctrlPid,
+ &descP->ctrlMon) != 0)
+ return esock_make_error(env, atom_exmon);
+
+
+ inc_socket(domain, type, protocol);
+
+ /* And finally update the registry.
+ * Shall we keep track of the fact that this socket is created elsewhere?
+ */
+ esock_send_reg_add_msg(env, res);
+
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open2 -> done: %T\r\n", res) );
+
+ return esock_make_ok2(env, res);
+}
+
+
+/* The eextra map "may" contain a boolean 'debug' key, if not we assume
+ * the default of FALSE.
+ */
+static
+BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env, ERL_NIF_TERM eextra)
+{
+ return esock_get_bool_from_map(env, eextra, MKA(env, "debug"), FALSE);
}
-/* esock_open - create an endpoint for communication
+/* The eextra contains a boolean 'dup' key. Defaults to TRUE.
+ */
+static
+BOOLEAN_T esock_open2_todup(ErlNifEnv* env, ERL_NIF_TERM eextra)
+{
+ return esock_get_bool_from_map(env, eextra, MKA(env, "dup"), TRUE);
+}
+
+/* The eextra contains an integer 'domain' key.
+ */
+static
+BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* domain)
+{
+ ERL_NIF_TERM key = MKA(env, "domain");
+ int edomain;
+
+ SGDBG( ("SOCKET", "esock_open2_get_domain -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &edomain)) {
+ /* decode */
+ if (edomain2domain(edomain, domain))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *domain = edomain; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+/* The eextra contains an integer 'type' key.
+ */
+static
+BOOLEAN_T esock_open2_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* type)
+{
+ ERL_NIF_TERM key = MKA(env, "type");
+ int etype;
+
+ SGDBG( ("SOCKET", "esock_open2_get_type -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &etype)) {
+ /* decode */
+ if (etype2type(etype, type))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *type = etype; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+/* The eextra contains an integer 'protocol' key.
+ */
+static
+BOOLEAN_T esock_open2_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts, int* protocol)
+{
+ ERL_NIF_TERM key = MKA(env, "protocol");
+ int eproto;
+
+ SGDBG( ("SOCKET", "esock_open2_get_protocol -> entry with"
+ "\r\n eopts: %T"
+ "\r\n", eopts) );
+
+ if (esock_get_int_from_map(env, eopts, key, &eproto)) {
+ /* decode */
+ if (eproto2proto(env, eproto, protocol))
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ *protocol = eproto; // Contains an "error code"
+ return FALSE;
+ }
+}
+
+#endif // if defined(__WIN32__)
+
+
+/* esock_open4 - create an endpoint for communication
*
* Assumes the input has been validated.
*
@@ -5358,38 +5769,56 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
* yet, we must use the global debug flag.
*/
#if !defined(__WIN32__)
-
static
-ERL_NIF_TERM esock_open(ErlNifEnv* env,
- int domain, int type, int protocol,
- char* netns)
+ERL_NIF_TERM esock_open4(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts)
{
+ BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
ESockDescriptor* descP;
ERL_NIF_TERM res;
int proto = protocol, save_errno = 0;
SOCKET sock;
HANDLE event;
+ char* netns;
#ifdef HAVE_SETNS
int current_ns = 0;
#endif
- SGDBG( ("SOCKET", "esock_open -> entry with"
- "\r\n domain: %d"
- "\r\n type: %d"
- "\r\n protocol: %d"
- "\r\n netns: %s"
- "\r\n", domain, type, protocol, ((netns == NULL) ? "NULL" : netns)) );
+ SGDBG2( dbg,
+ ("SOCKET", "esock_open4 -> entry with"
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n eopts: %T"
+ "\r\n", domain, type, protocol, eopts) );
+
+
+#ifdef HAVE_SETNS
+ if (esock_open4_get_netns(env, eopts, &netns)) {
+ SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) );
+ }
+#else
+ netns = NULL;
+#endif
+
#ifdef HAVE_SETNS
if ((netns != NULL) &&
- !change_network_namespace(netns, &current_ns, &save_errno))
+ !change_network_namespace(netns, &current_ns, &save_errno)) {
+ FREE(netns);
return esock_make_error_errno(env, save_errno);
+ }
#endif
- if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET)
+ if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET) {
+ if (netns != NULL) FREE(netns);
return esock_make_error_errno(env, sock_errno());
+ }
- SGDBG( ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
+ SGDBG2( dbg, ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
/* NOTE that if the protocol = 0 (default) and the domain is not
@@ -5406,10 +5835,12 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
save_errno = sock_errno();
while ((sock_close(sock) == INVALID_SOCKET) &&
(sock_errno() == EINTR));
+ if (netns != NULL) FREE(netns);
return esock_make_error_errno(env, save_errno);
} else {
while ((sock_close(sock) == INVALID_SOCKET) &&
(sock_errno() == EINTR));
+ if (netns != NULL) FREE(netns);
return esock_make_error(env, esock_atom_eafnosupport);
}
}
@@ -5417,11 +5848,13 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
#ifdef HAVE_SETNS
if ((netns != NULL) &&
- !restore_network_namespace(current_ns, sock, &save_errno))
+ !restore_network_namespace(current_ns, sock, &save_errno)) {
+ FREE(netns);
return esock_make_error_errno(env, save_errno);
+ }
+
+ if (netns != NULL) FREE(netns);
- if (netns != NULL)
- FREE(netns);
#endif
@@ -5431,7 +5864,7 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
return esock_make_error_errno(env, save_errno);
}
- SGDBG( ("SOCKET", "esock_open -> event success: %d\r\n", event) );
+ SGDBG2( dbg, ("SOCKET", "esock_open4 -> event success: %d\r\n", event) );
SET_NONBLOCKING(sock);
@@ -5490,6 +5923,54 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env,
static
+BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain)
+{
+#if defined(SO_DOMAIN)
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(sock, SOL_SOCKET, SO_DOMAIN, &val, &valSz);
+
+ if (res != 0) {
+ *domain = ESOCK_WHICH_DOMAIN_ERROR;
+ return FALSE;
+ } else {
+ *domain = val;
+ return TRUE;
+ }
+#else
+ *domain = ESOCK_WHICH_DOMAIN_UNSUP;
+ return FALSE;
+#endif
+}
+
+
+static
+BOOLEAN_T esock_open_which_type(SOCKET sock, int* type)
+{
+#if defined(SO_TYPE)
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(sock, SOL_SOCKET, SO_TYPE, &val, &valSz);
+
+ if (res != 0) {
+ *type = ESOCK_WHICH_TYPE_ERROR;
+ return FALSE;
+ } else {
+ *type = val;
+ return TRUE;
+ }
+#else
+ *type = ESOCK_WHICH_TYPE_UNSUP;
+ return FALSE;
+#endif
+}
+
+
+static
BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto)
{
#if defined(SO_PROTOCOL)
@@ -8108,7 +8589,7 @@ ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
*/
SET_BLOCKING(descP->sock);
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
if (save_errno != ERRNO_BLOCK) {
@@ -10411,8 +10892,12 @@ ERL_NIF_TERM esock_setopt_lvl_ipv6_addrform(ErlNifEnv* env,
domain) );
res = socket_setopt(descP->sock,
- SOL_IPV6, IPV6_ADDRFORM,
- &domain, sizeof(domain));
+#if defined(SOL_IPV6)
+ SOL_IPV6,
+#else
+ IPPROTO_IPV6,
+#endif
+ IPV6_ADDRFORM, &domain, sizeof(domain));
if (res != 0)
result = esock_make_error_errno(env, sock_errno());
@@ -10442,7 +10927,14 @@ ERL_NIF_TERM esock_setopt_lvl_ipv6_authhdr(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM eVal)
{
- return esock_setopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR, eVal);
+#if defined(SOL_IPV6)
+ int level = SOL_IPV6;
+#else
+ int level = IPPROTO_IPV6;
+#endif
+
+
+ return esock_setopt_bool_opt(env, descP, level, IPV6_AUTHHDR, eVal);
}
#endif
@@ -19040,6 +19532,8 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event)
descP->sock = sock;
descP->event = event;
+ descP->origFD = -1;
+ descP->closeOnClose = TRUE;
enif_set_pid_undefined(&descP->ctrlPid);
MON_INIT(&descP->ctrlMon);
@@ -19304,58 +19798,12 @@ BOOLEAN_T eproto2proto(ErlNifEnv* env,
#ifdef HAVE_SETNS
- /* emap2netns - extract the netns field from the extra map
- *
- * Note that currently we only support one extra option, the netns.
+/* esock_open4_get_netns - extract the netns field from the opts map
*/
static
-BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns)
+BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env, ERL_NIF_TERM opts, char** netns)
{
- size_t sz;
- ERL_NIF_TERM key;
- ERL_NIF_TERM value;
- unsigned int len;
- char* buf;
- int written;
-
- /* Note that its acceptable that the extra map is empty */
- if (!enif_get_map_size(env, map, &sz) ||
- (sz != 1)) {
- *netns = NULL;
- return TRUE;
- }
-
- /* The currently only supported extra option is: netns */
- key = enif_make_atom(env, "netns");
- if (!GET_MAP_VAL(env, map, key, &value)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- /* So far so good. The value should be a string, check. */
- if (!enif_is_list(env, value)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- if (!enif_get_list_length(env, value, &len)) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- if ((buf = MALLOC(len+1)) == NULL) {
- *netns = NULL; // Just in case...
- return FALSE;
- }
-
- written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
- if (written == (len+1)) {
- *netns = buf;
- return TRUE;
- } else {
- *netns = NULL; // Just in case...
- return FALSE;
- }
+ return esock_get_string_from_map(env, opts, MKA(env, "netns"), netns);
}
#endif
@@ -20995,7 +21443,7 @@ void esock_down(ErlNifEnv* env,
* If the owner wish to ensure the buffer are written,
* it should have closed the socket explicitly...
*/
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
esock_warning_msg("Failed closing socket for terminating "
@@ -21034,7 +21482,7 @@ void esock_down(ErlNifEnv* env,
* for later (when the stop function actually gets called...
*/
- if (sock_close(descP->sock) != 0) {
+ if (descP->closeOnClose && (sock_close(descP->sock) != 0)) {
int save_errno = sock_errno();
esock_warning_msg("Failed closing socket for terminating "
@@ -21249,8 +21697,7 @@ ErlNifFunc esock_funcs[] =
{"nif_command", 1, nif_command, 0},
// The proper "socket" interface
- // nif_open/1 is (supposed to be) used when we already have a file descriptor
- // {"nif_open", 1, nif_open, 0},
+ {"nif_open", 2, nif_open, 0},
{"nif_open", 4, nif_open, 0},
{"nif_bind", 2, nif_bind, 0},
{"nif_connect", 2, nif_connect, 0},
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index a30c2e36ae..c12899b134 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2018-2019. All Rights Reserved.
+ * Copyright Ericsson AB 2018-2020. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -111,6 +111,119 @@ static BOOLEAN_T esock_extract_from_map(ErlNifEnv* env,
ERL_NIF_TERM key,
ERL_NIF_TERM* val);
+
+
+/* *** esock_get_bool_from_map ***
+ *
+ * Simple utility function used to extract a boolean value from a map.
+ * If it fails to extract the value (for whatever reason) the default
+ * value is returned.
+ */
+
+extern
+BOOLEAN_T esock_get_bool_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ BOOLEAN_T def)
+{
+ ERL_NIF_TERM val;
+
+ if (!GET_MAP_VAL(env, map, key, &val)) {
+ return def;
+ } else {
+ if (COMPARE(val, esock_atom_true) == 0)
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+
+/* *** esock_get_bool_from_map ***
+ *
+ * Simple utility function used to extract a integer value from a map.
+ */
+
+extern
+BOOLEAN_T esock_get_int_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ int* val)
+{
+ ERL_NIF_TERM eval;
+
+ if (!GET_MAP_VAL(env, map, key, &eval)) {
+ *val = -1;
+ return FALSE;
+ }
+
+ if (!IS_NUM(env, eval)) {
+ *val = -2;
+ return FALSE;
+ }
+
+ if (!GET_INT(env, eval, val)) {
+ *val = -3;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+/* *** esock_get_string_from_map ***
+ *
+ * Simple utility function used to extract a (latin1) string value from a map.
+ * This function will allocate a buffer to put the string!
+ */
+
+extern
+BOOLEAN_T esock_get_string_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ char** str)
+{
+ ERL_NIF_TERM value;
+ unsigned int len;
+ char* buf;
+ int written;
+
+ /* The currently only supported extra option is: netns */
+ if (!GET_MAP_VAL(env, map, key, &value)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ /* So far so good. The value should be a string, check. */
+ if (!enif_is_list(env, value)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ if (!enif_get_list_length(env, value, &len)) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ if ((buf = MALLOC(len+1)) == NULL) {
+ *str = NULL;
+ return FALSE;
+ }
+
+ written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
+ if (written == (len+1)) {
+ *str = buf;
+ return TRUE;
+ } else {
+ *str = NULL;
+ return FALSE;
+ }
+}
+
+
+
+
/* +++ esock_encode_iov +++
*
* Encode an IO Vector. In erlang we represented this as a list of binaries.
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index 398fb40a5a..0a1eb92339 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2018-2019. All Rights Reserved.
+ * Copyright Ericsson AB 2018-2020. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
* %CopyrightEnd%
*
* ----------------------------------------------------------------------
- * Purpose : Utility "stuff" for socket and net.
+ * Purpose : Utility "stuff" for socket and prim_net (nifs).
* ----------------------------------------------------------------------
*
*/
@@ -43,6 +43,22 @@
#define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0)))
extern
+BOOLEAN_T esock_get_bool_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ BOOLEAN_T def);
+extern
+BOOLEAN_T esock_get_int_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ int* val);
+extern
+BOOLEAN_T esock_get_string_from_map(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ ERL_NIF_TERM key,
+ char** str);
+
+extern
char* esock_encode_iov(ErlNifEnv* env,
int read,
struct iovec* iov,
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index fbf312a3b8..e6ddaa3479 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -107,7 +107,7 @@ ERL_NIF_TERM efile_get_handle(ErlNifEnv *env, efile_data_t *d) {
return result;
}
-static int open_file_type_check(const efile_path_t *path, int fd) {
+static int open_file_is_dir(const efile_path_t *path, int fd) {
struct stat file_info;
int error;
@@ -119,27 +119,14 @@ static int open_file_type_check(const efile_path_t *path, int fd) {
(void)path;
#endif
- if(error < 0) {
- /* If we failed to stat assume success and let the next call handle the
- * error. The old driver checked whether the file was to be used
- * immediately in a read within the call, but the new implementation
- * never does that. */
- return 1;
- }
-
- /* Allow everything that isn't a directory, and error out on the next call
- * if it's unsupported. */
- if(S_ISDIR(file_info.st_mode)) {
- return 0;
- }
-
- return 1;
+ /* Assume not a directory on error. */
+ return error == 0 && S_ISDIR(file_info.st_mode);
}
posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
ErlNifResourceType *nif_type, efile_data_t **d) {
- int flags, fd;
+ int mode, flags, fd;
flags = 0;
@@ -174,18 +161,38 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
#endif
}
+ if(modes & EFILE_MODE_DIRECTORY) {
+ mode = DIR_MODE;
+#ifdef O_DIRECTORY
+ flags |= O_DIRECTORY;
+#endif
+ } else {
+ mode = FILE_MODE;
+ }
+
do {
- fd = open((const char*)path->data, flags, FILE_MODE);
+ fd = open((const char*)path->data, flags, mode);
} while(fd == -1 && errno == EINTR);
if(fd != -1) {
efile_unix_t *u;
- if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) && !open_file_type_check(path, fd)) {
+#ifndef O_DIRECTORY
+ /* On platforms without O_DIRECTORY support, ensure that using the
+ * directory flag to open a file fails. */
+ if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) &&
+ (modes & EFILE_MODE_DIRECTORY) && !open_file_is_dir(path, fd)) {
close(fd);
+ return ENOTDIR;
+ }
+#endif
- /* This is blatantly incorrect, but we're documented as returning
- * this for everything that isn't a file. */
+ /* open() works on directories without the O_DIRECTORY flag but for
+ * consistency across platforms we require that the user has requested
+ * directory mode. */
+ if(!(modes & EFILE_MODE_SKIP_TYPE_CHECK) &&
+ !(modes & EFILE_MODE_DIRECTORY) && open_file_is_dir(path, fd)) {
+ close(fd);
return EISDIR;
}
@@ -555,7 +562,7 @@ int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) {
#if defined(HAVE_FALLOCATE)
/* Linux-specific */
do {
- ret = fallocate(u->fd, FALLOC_FL_KEEP_SIZE, offset, length);
+ ret = fallocate(u->fd, 0, offset, length);
} while(ret < 0 && errno == EINTR);
#elif defined(F_PREALLOCATE)
/* Mac-specific */
@@ -656,6 +663,33 @@ int efile_truncate(efile_data_t *d) {
return 1;
}
+static void build_file_info(struct stat *data, efile_fileinfo_t *result) {
+ if(S_ISCHR(data->st_mode) || S_ISBLK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DEVICE;
+ } else if(S_ISDIR(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ } else if(S_ISREG(data->st_mode)) {
+ result->type = EFILE_FILETYPE_REGULAR;
+ } else if(S_ISLNK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ } else {
+ result->type = EFILE_FILETYPE_OTHER;
+ }
+
+ result->a_time = (Sint64)data->st_atime;
+ result->m_time = (Sint64)data->st_mtime;
+ result->c_time = (Sint64)data->st_ctime;
+ result->size = data->st_size;
+
+ result->major_device = data->st_dev;
+ result->minor_device = data->st_rdev;
+ result->links = data->st_nlink;
+ result->inode = data->st_ino;
+ result->mode = data->st_mode;
+ result->uid = data->st_uid;
+ result->gid = data->st_gid;
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
struct stat data;
@@ -669,30 +703,7 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
}
}
- if(S_ISCHR(data.st_mode) || S_ISBLK(data.st_mode)) {
- result->type = EFILE_FILETYPE_DEVICE;
- } else if(S_ISDIR(data.st_mode)) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- } else if(S_ISREG(data.st_mode)) {
- result->type = EFILE_FILETYPE_REGULAR;
- } else if(S_ISLNK(data.st_mode)) {
- result->type = EFILE_FILETYPE_SYMLINK;
- } else {
- result->type = EFILE_FILETYPE_OTHER;
- }
-
- result->a_time = (Sint64)data.st_atime;
- result->m_time = (Sint64)data.st_mtime;
- result->c_time = (Sint64)data.st_ctime;
- result->size = data.st_size;
-
- result->major_device = data.st_dev;
- result->minor_device = data.st_rdev;
- result->links = data.st_nlink;
- result->inode = data.st_ino;
- result->mode = data.st_mode;
- result->uid = data.st_uid;
- result->gid = data.st_gid;
+ build_file_info(&data, result);
#ifndef NO_ACCESS
result->access = EFILE_ACCESS_NONE;
@@ -711,6 +722,56 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return 0;
}
+static int check_access(struct stat *st) {
+ int ret = EFILE_ACCESS_NONE;
+
+ if(st->st_uid == getuid()) {
+ if(st->st_mode & S_IRUSR) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWUSR) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_gid == getgid()) {
+ if(st->st_mode & S_IRGRP) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWGRP) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_mode & S_IROTH) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWOTH) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+}
+
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ struct stat data;
+ efile_unix_t *u = (efile_unix_t*)d;
+
+#ifdef HAVE_FSTAT
+ if(fstat(u->fd, &data) < 0) {
+ return errno;
+ }
+
+ build_file_info(&data, result);
+ result->access = check_access(&data);
+
+ return 0;
+#else
+ return ENOTSUP;
+#endif
+}
+
posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions) {
const mode_t MUTABLE_MODES = (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO);
mode_t new_modes = permissions & MUTABLE_MODES;
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
index e7d3924240..fca7385809 100644
--- a/erts/emulator/nifs/win32/win_prim_file.c
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -269,7 +269,18 @@ static int normalize_path_result(ErlNifBinary *path) {
return enif_realloc_binary(path, length * sizeof(WCHAR));
}
-/* @brief Checks whether all the given attributes are set on the object at the
+/** @brief Checks whether all the given attributes are set on the object at the
+ * given handle. Note that it assumes false on errors. */
+static int handle_has_file_attributes(HANDLE handle, DWORD mask) {
+ BY_HANDLE_FILE_INFORMATION native_file_info;
+ if(!GetFileInformationByHandle(handle, &native_file_info)) {
+ return 0;
+ }
+
+ return !!((native_file_info.dwFileAttributes & mask) == mask);
+}
+
+/** @brief Checks whether all the given attributes are set on the object at the
* given path. Note that it assumes false on errors. */
static int has_file_attributes(const efile_path_t *path, DWORD mask) {
DWORD attributes = GetFileAttributesW((WCHAR*)path->data);
@@ -313,7 +324,7 @@ static int get_drive_number(const efile_path_t *path) {
return -1;
}
-/* @brief Checks whether two *paths* are on the same mount point; they don't
+/** @brief Checks whether two *paths* are on the same mount point; they don't
* have to refer to existing or accessible files/directories. */
static int has_same_mount_point(const efile_path_t *path_a, const efile_path_t *path_b) {
WCHAR *mount_a, *mount_b;
@@ -412,10 +423,15 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
ASSERT_PATH_FORMAT(path);
+ attributes = 0;
access_flags = 0;
open_mode = 0;
- if(modes & EFILE_MODE_READ && !(modes & EFILE_MODE_WRITE)) {
+ if(modes & EFILE_MODE_DIRECTORY) {
+ attributes = FILE_FLAG_BACKUP_SEMANTICS;
+ access_flags = GENERIC_READ;
+ open_mode = OPEN_EXISTING;
+ } else if(modes & EFILE_MODE_READ && !(modes & EFILE_MODE_WRITE)) {
access_flags = GENERIC_READ;
open_mode = OPEN_EXISTING;
} else if(modes & EFILE_MODE_WRITE && !(modes & EFILE_MODE_READ)) {
@@ -438,9 +454,9 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
}
if(modes & EFILE_MODE_SYNC) {
- attributes = FILE_FLAG_WRITE_THROUGH;
+ attributes |= FILE_FLAG_WRITE_THROUGH;
} else {
- attributes = FILE_ATTRIBUTE_NORMAL;
+ attributes |= FILE_ATTRIBUTE_NORMAL;
}
handle = CreateFileW((WCHAR*)path->data, access_flags,
@@ -449,6 +465,12 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
if(handle != INVALID_HANDLE_VALUE) {
efile_win_t *w;
+ /* Directory mode specified, but path is not a directory. */
+ if((modes & EFILE_MODE_DIRECTORY) && !handle_has_file_attributes(handle, FILE_ATTRIBUTE_DIRECTORY)) {
+ CloseHandle(handle);
+ return ENOTDIR;
+ }
+
w = (efile_win_t*)enif_alloc_resource(nif_type, sizeof(efile_win_t));
w->handle = handle;
@@ -461,7 +483,7 @@ posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes,
/* Rewrite all failures on directories to EISDIR to match the old
* driver. */
- if(has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
+ if(!(modes & EFILE_MODE_DIRECTORY) && has_file_attributes(path, FILE_ATTRIBUTE_DIRECTORY)) {
return EISDIR;
}
@@ -751,6 +773,71 @@ static int is_name_surrogate(const efile_path_t *path) {
return result;
}
+static void build_file_info(BY_HANDLE_FILE_INFORMATION *native_file_info, const efile_path_t *path, int is_link, efile_fileinfo_t *result) {
+ DWORD attributes;
+
+ attributes = native_file_info->dwFileAttributes;
+
+ if(is_link) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ /* This should be _S_IFLNK, but the old driver always set
+ * non-directories to _S_IFREG. */
+ result->mode |= _S_IFREG;
+ } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ result->mode |= _S_IFDIR | _S_IEXEC;
+ } else {
+ if(is_executable_file(path)) {
+ result->mode |= _S_IEXEC;
+ }
+
+ result->type = EFILE_FILETYPE_REGULAR;
+ result->mode |= _S_IFREG;
+ }
+
+ if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
+ result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
+ result->mode |= _S_IREAD | _S_IWRITE;
+ } else {
+ result->access = EFILE_ACCESS_READ;
+ result->mode |= _S_IREAD;
+ }
+
+ /* Propagate user mode-bits to group/other fields */
+ result->mode |= (result->mode & 0700) >> 3;
+ result->mode |= (result->mode & 0700) >> 6;
+
+ result->size =
+ ((Uint64)native_file_info->nFileSizeHigh << 32ull) |
+ (Uint64)native_file_info->nFileSizeLow;
+ result->links = MAX(1, native_file_info->nNumberOfLinks);
+
+ result->major_device = get_drive_number(path);
+ result->minor_device = 0;
+ result->inode = 0;
+ result->uid = 0;
+ result->gid = 0;
+}
+
+static void build_file_info_times(BY_HANDLE_FILE_INFORMATION *native_file_info, efile_fileinfo_t *result) {
+ FILETIME_TO_EPOCH(result->m_time, native_file_info->ftLastWriteTime);
+ FILETIME_TO_EPOCH(result->a_time, native_file_info->ftLastAccessTime);
+ FILETIME_TO_EPOCH(result->c_time, native_file_info->ftCreationTime);
+
+ if(result->m_time == -EPOCH_DIFFERENCE) {
+ /* Default to 1970 just like the old driver. */
+ result->m_time = 0;
+ }
+
+ if(result->a_time == -EPOCH_DIFFERENCE) {
+ result->a_time = result->m_time;
+ }
+
+ if(result->c_time == -EPOCH_DIFFERENCE) {
+ result->c_time = result->m_time;
+ }
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
BY_HANDLE_FILE_INFORMATION native_file_info;
DWORD attributes;
@@ -770,23 +857,41 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return windows_to_posix_errno(last_error);
}
- attributes = FILE_ATTRIBUTE_DIRECTORY;
+ native_file_info.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
} else if(is_path_root(path)) {
/* Local (or mounted) roots can be queried with GetFileAttributesW but
* lack support for GetFileInformationByHandle, so we'll skip that
* part. */
+ native_file_info.dwFileAttributes = attributes;
} else {
HANDLE handle;
+ DWORD last_error;
+ DWORD flags;
if(attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
is_link = is_name_surrogate(path);
}
+ flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if(!follow_links && is_link) {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+
+ handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, flags, NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+
if(follow_links && is_link) {
posix_errno_t posix_errno;
efile_path_t resolved_path;
- posix_errno = internal_read_link(path, &resolved_path);
+ posix_errno = internal_read_link(handle, &resolved_path);
+
+ CloseHandle(handle);
if(posix_errno != 0) {
return posix_errno;
@@ -795,74 +900,43 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return efile_read_info(&resolved_path, 0, result);
}
- handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- /* The old driver never cared whether this succeeded. */
- if(handle != INVALID_HANDLE_VALUE) {
- GetFileInformationByHandle(handle, &native_file_info);
- CloseHandle(handle);
- }
+ GetFileInformationByHandle(handle, &native_file_info);
+ CloseHandle(handle);
- FILETIME_TO_EPOCH(result->m_time, native_file_info.ftLastWriteTime);
- FILETIME_TO_EPOCH(result->a_time, native_file_info.ftLastAccessTime);
- FILETIME_TO_EPOCH(result->c_time, native_file_info.ftCreationTime);
+ build_file_info_times(&native_file_info, result);
+ }
- if(result->m_time == -EPOCH_DIFFERENCE) {
- /* Default to 1970 just like the old driver. */
- result->m_time = 0;
- }
+ build_file_info(&native_file_info, path, is_link, result);
- if(result->a_time == -EPOCH_DIFFERENCE) {
- result->a_time = result->m_time;
- }
+ return 0;
+}
- if(result->c_time == -EPOCH_DIFFERENCE) {
- result->c_time = result->m_time;
- }
- }
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ efile_win_t *w = (efile_win_t*)d;
+ HANDLE handle;
+ BY_HANDLE_FILE_INFORMATION native_file_info;
+ posix_errno_t posix_errno;
+ efile_path_t path;
+ int length;
- if(is_link) {
- result->type = EFILE_FILETYPE_SYMLINK;
- /* This should be _S_IFLNK, but the old driver always set
- * non-directories to _S_IFREG. */
- result->mode |= _S_IFREG;
- } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- result->mode |= _S_IFDIR | _S_IEXEC;
- } else {
- if(is_executable_file(path)) {
- result->mode |= _S_IEXEC;
- }
+ sys_memset(&native_file_info, 0, sizeof(native_file_info));
- result->type = EFILE_FILETYPE_REGULAR;
- result->mode |= _S_IFREG;
+ posix_errno = internal_read_link(w->handle, &path);
+ if(posix_errno != 0) {
+ return posix_errno;
}
- if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
- result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
- result->mode |= _S_IREAD | _S_IWRITE;
+ if(GetFileInformationByHandle(w->handle, &native_file_info)) {
+ build_file_info_times(&native_file_info, result);
+ } else if(is_path_root(&path)) {
+ /* GetFileInformationByHandle is not supported on path roots, so
+ * fall back to efile_read_info. */
+ return efile_read_info(&path, 0, result);
} else {
- result->access = EFILE_ACCESS_READ;
- result->mode |= _S_IREAD;
+ return windows_to_posix_errno(GetLastError());
}
- /* Propagate user mode-bits to group/other fields */
- result->mode |= (result->mode & 0700) >> 3;
- result->mode |= (result->mode & 0700) >> 6;
-
- result->size =
- ((Uint64)native_file_info.nFileSizeHigh << 32ull) |
- (Uint64)native_file_info.nFileSizeLow;
-
- result->links = MAX(1, native_file_info.nNumberOfLinks);
-
- result->major_device = get_drive_number(path);
- result->minor_device = 0;
- result->inode = 0;
- result->uid = 0;
- result->gid = 0;
+ build_file_info(&native_file_info, &path, 0, result);
return 0;
}
@@ -941,31 +1015,20 @@ posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_t
return windows_to_posix_errno(last_error);
}
-static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *result) {
+static posix_errno_t internal_read_link(HANDLE link_handle, efile_path_t *result) {
DWORD required_length, actual_length;
- HANDLE link_handle;
DWORD last_error;
- link_handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- last_error = GetLastError();
-
- if(link_handle == INVALID_HANDLE_VALUE) {
- return windows_to_posix_errno(last_error);
- }
-
required_length = GetFinalPathNameByHandleW(link_handle, NULL, 0, 0);
last_error = GetLastError();
if(required_length <= 0) {
- CloseHandle(link_handle);
return windows_to_posix_errno(last_error);
}
/* Unlike many other path functions (eg. GetFullPathNameW), this one
* includes the NUL terminator in its required length. */
if(!enif_alloc_binary(required_length * sizeof(WCHAR), result)) {
- CloseHandle(link_handle);
return ENOMEM;
}
@@ -973,8 +1036,6 @@ static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *
(WCHAR*)result->data, required_length, 0);
last_error = GetLastError();
- CloseHandle(link_handle);
-
if(actual_length == 0 || actual_length >= required_length) {
enif_release_binary(result);
return windows_to_posix_errno(last_error);
@@ -992,6 +1053,8 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
posix_errno_t posix_errno;
ErlNifBinary result_bin;
DWORD attributes;
+ HANDLE handle;
+ DWORD last_error;
ASSERT_PATH_FORMAT(path);
@@ -1007,7 +1070,17 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
return EINVAL;
}
- posix_errno = internal_read_link(path, &result_bin);
+ handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+ posix_errno = internal_read_link(handle, &result_bin);
+
+ CloseHandle(handle);
if(posix_errno == 0) {
if(!normalize_path_result(&result_bin)) {
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 308d829c09..35b6d99ba2 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1082,7 +1082,7 @@ enif_select_x(ErlNifEnv* env,
pid ? pid->pid : THE_NON_VALUE, THE_NON_VALUE);
if (mode & ERL_NIF_SELECT_STOP) {
- ASSERT(resource->type->stop);
+ ASSERT(resource->type->fn.stop);
if (IS_FD_UNKNOWN(state)) {
/* fast track to stop callback */
call_stop = CALL_STOP;
diff --git a/erts/emulator/sys/common/erl_osenv.h b/erts/emulator/sys/common/erl_osenv.h
index 4777f2148a..d0902e0ba1 100644
--- a/erts/emulator/sys/common/erl_osenv.h
+++ b/erts/emulator/sys/common/erl_osenv.h
@@ -35,16 +35,10 @@
# include "config.h"
#endif
-typedef struct __erts_osenv_data_t erts_osenv_data_t;
-
-typedef struct __erts_osenv_t {
- struct __env_rbtnode_t *tree;
- int variable_count;
- int content_size;
-} erts_osenv_t;
-
#include "sys.h"
+typedef struct __erts_osenv_data_t erts_osenv_data_t;
+
struct __erts_osenv_data_t {
Sint length;
void *data;
@@ -53,7 +47,7 @@ struct __erts_osenv_data_t {
void erts_osenv_init(erts_osenv_t *env);
void erts_osenv_clear(erts_osenv_t *env);
-/* @brief Merges \c with into \c env
+/** @brief Merges \c with into \c env
*
* @param overwrite Whether to overwrite existing entries or keep them as they
* are. */
@@ -61,25 +55,25 @@ void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/* @brief Copies env[key] into \c value
+/** @brief Copies env[key] into \c value
*
* @return 1 on success, 0 if the key couldn't be found, and -1 if the input
* was invalid. */
int erts_osenv_get_term(const erts_osenv_t *env, struct process *process,
Eterm key, Eterm *value);
-/* @brief Copies \c value into \c env[key]
+/** @brief Copies \c value into \c env[key]
*
* @return 1 on success, -1 if the input was invalid. */
int erts_osenv_put_term(erts_osenv_t *env, Eterm key, Eterm value);
-/* @brief Removes \c env[key]
+/** @brief Removes \c env[key]
*
* @return 1 on success, 0 if the key couldn't be found, and -1 if the input
* was invalid. */
int erts_osenv_unset_term(erts_osenv_t *env, Eterm key);
-/* @brief Copies env[key] into \c value
+/** @brief Copies env[key] into \c value
*
* @param value [in,out] The buffer to copy the value into, may be NULL if you
* only wish to query presence.
@@ -89,13 +83,13 @@ int erts_osenv_unset_term(erts_osenv_t *env, Eterm key);
int erts_osenv_get_native(const erts_osenv_t *env, const erts_osenv_data_t *key,
erts_osenv_data_t *value);
-/* @brief Copies \c value into \c env[key]
+/** @brief Copies \c value into \c env[key]
*
* @return 1 on success, -1 on failure. */
int erts_osenv_put_native(erts_osenv_t *env, const erts_osenv_data_t *key,
const erts_osenv_data_t *value);
-/* @brief Removes \c key from the env.
+/** @brief Removes \c key from the env.
*
* @return 1 on success, 0 if the key couldn't be found. */
int erts_osenv_unset_native(erts_osenv_t *env, const erts_osenv_data_t *key);
@@ -109,8 +103,8 @@ typedef void (*erts_osenv_foreach_native_cb_t)(void *state,
const erts_osenv_data_t *key,
const erts_osenv_data_t *value);
-/* @brief Walks through all environment variables, calling \c callback for each
- * one. It's unsafe to modify \c env within the callback. */
+/** @brief Walks through all environment variables, calling \c callback for
+ * each one. It's unsafe to modify \c env within the callback. */
void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
void *state, erts_osenv_foreach_term_cb_t callback);
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index d40dabc529..096c14816e 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2006-2018. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2006-2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -161,7 +161,10 @@ typedef enum {
#include <sys/epoll.h>
#if ERTS_POLL_USE_EPOLL
-#ifdef HAVE_SYS_TIMERFD_H
+/* sys/timerfd.h was added in Android 4.4 KitKat. With the Android NDK
+Unified Headers, check that the build is targeting at least the
+corresponding API level 19. */
+#if defined(HAVE_SYS_TIMERFD_H) && !(defined(__ANDROID__) && (__ANDROID_API__ < 19))
#include <sys/timerfd.h>
#undef ERTS_POLL_USE_TIMERFD
#define ERTS_POLL_USE_TIMERFD 1
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 129861ebd5..103eb40288 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -63,10 +63,13 @@
#include "erl_driver.h"
#include "sys_uds.h"
-#include "hash.h"
#include "erl_term.h"
#include "erl_child_setup.h"
+#undef ERTS_GLB_INLINE_INCL_FUNC_DEF
+#define ERTS_GLB_INLINE_INCL_FUNC_DEF 1
+#include "hash.h"
+
#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
#if defined(__ANDROID__)
@@ -75,6 +78,10 @@
#define SHELL "/bin/sh"
#endif /* __ANDROID__ */
+#if !defined(MSG_DONTWAIT) && defined(MSG_NONBLOCK)
+#define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
//#define HARD_DEBUG
#ifdef HARD_DEBUG
#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__)
@@ -411,6 +418,7 @@ main(int argc, char *argv[])
int uds_fd = 3, max_fd = 3;
#ifndef HAVE_CLOSEFROM
int i;
+ DIR *dir;
#endif
struct sigaction sa;
@@ -426,11 +434,29 @@ main(int argc, char *argv[])
#if defined(HAVE_CLOSEFROM)
closefrom(4);
#else
- for (i = 4; i < max_files; i++)
+ dir = opendir("/dev/fd");
+ if (dir == NULL) { /* /dev/fd not available */
+ for (i = 4; i < max_files; i++)
+#if defined(__ANDROID__)
+ if (i != system_properties_fd())
+#endif
+ (void) close(i);
+ } else {
+ /* Iterate over fds obtained from /dev/fd */
+ struct dirent *entry;
+ int dir_fd = dirfd(dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ i = atoi(entry->d_name);
#if defined(__ANDROID__)
- if (i != system_properties_fd())
+ if (i != system_properties_fd())
#endif
- (void) close(i);
+ if (i >= 4 && i != dir_fd)
+ (void) close(i);
+ }
+
+ closedir(dir);
+ }
#endif
if (pipe(sigchld_pipe) < 0) {
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6475a70fbf..a85426092e 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -692,6 +692,10 @@ void
erts_sys_unix_later_init(void)
{
sys_signal(SIGTERM, generic_signal_handler);
+
+ /* Ignore SIGCHLD to ensure orphaned processes don't turn into zombies on
+ * death when we're pid 1. */
+ sys_signal(SIGCHLD, SIG_IGN);
}
int sys_max_files(void)
@@ -743,10 +747,17 @@ void os_version(int *pMajor, int *pMinor, int *pBuild) {
* X.Y or X.Y.Z. */
(void) uname(&uts);
+#ifdef _AIX
+ /* AIX stores the major in version and minor in release */
+ *pMajor = atoi(uts.version);
+ *pMinor = atoi(uts.release);
+ *pBuild = 0; /* XXX: get oslevel for AIX or TR on i */
+#else
release = uts.release;
*pMajor = get_number(&release); /* Pointer to major version. */
*pMinor = get_number(&release); /* Pointer to minor version. */
*pBuild = get_number(&release); /* Pointer to build number. */
+#endif
}
void erts_do_break_handling(void)
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 6883519dc3..b754f90004 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -55,6 +55,7 @@
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_threads.h"
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
index c9f73622ba..c3094d20d4 100644
--- a/erts/emulator/sys/unix/sys_uds.c
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -23,7 +23,7 @@
#endif
#if defined(__sun__) && !defined(_XOPEN_SOURCE)
-#define _XOPEN_SOURCE 500
+#define _XOPEN_SOURCE 600
#endif
#include <limits.h>
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 53411a4cc2..6c3fe7ab0a 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -27,6 +27,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_alloc.h"
#include "erl_sys_driver.h"
#include "global.h"
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index c78161b344..36223c2c14 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -23,6 +23,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_sys_driver.h"
#include "erl_alloc.h"
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 478555a902..fb56d72691 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -87,6 +87,7 @@ MODULES= \
gc_SUITE \
guard_SUITE \
hash_SUITE \
+ hash_property_test_SUITE \
hibernate_SUITE \
hipe_SUITE \
iovec_SUITE \
@@ -148,6 +149,7 @@ MODULES= \
dgawd_handler \
random_iolist \
erts_test_utils \
+ erts_test_destructor \
crypto_reference \
literal_area_collector_test
@@ -247,7 +249,7 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
- tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 4e0243c1cd..24677247a7 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -302,45 +302,52 @@ wait_for_memory_deallocations() ->
end.
print_stats(migration) ->
- IFun = fun({instance,Inr,Istats}, {Bacc,Cacc,Pacc}) ->
- {mbcs,MBCS} = lists:keyfind(mbcs, 1, Istats),
- Btup = lists:keyfind(blocks, 1, MBCS),
- Ctup = lists:keyfind(carriers, 1, MBCS),
-
- Ptup = case lists:keyfind(mbcs_pool, 1, Istats) of
- {mbcs_pool,POOL} ->
- {blocks, Bpool} = lists:keyfind(blocks, 1, POOL),
- {carriers, Cpool} = lists:keyfind(carriers, 1, POOL),
- {pool, Bpool, Cpool};
- false ->
- {pool, 0, 0}
- end,
- io:format("{instance,~p,~p,~p,~p}}\n",
- [Inr, Btup, Ctup, Ptup]),
- {tuple_add(Bacc,Btup),tuple_add(Cacc,Ctup),
- tuple_add(Pacc,Ptup)};
- (_, Acc) -> Acc
+ IFun = fun({instance,_,Stats}, {Regular0, Pooled0}) ->
+ {mbcs,MBCS} = lists:keyfind(mbcs, 1, Stats),
+ {sbcs,SBCS} = lists:keyfind(sbcs, 1, Stats),
+
+ Regular = MBCS ++ SBCS ++ Regular0,
+ case lists:keyfind(mbcs_pool, 1, Stats) of
+ {mbcs_pool,Pool} -> {Regular, Pool ++ Pooled0};
+ false -> {Regular, Pooled0}
+ end;
+ (_, Acc) ->
+ Acc
end,
- {Btot,Ctot,Ptot} = lists:foldl(IFun,
- {{blocks,0,0,0},{carriers,0,0,0},{pool,0,0}},
- erlang:system_info({allocator,test_alloc})),
-
- {pool, PBtot, PCtot} = Ptot,
- io:format("Number of blocks : ~p\n", [Btot]),
- io:format("Number of carriers: ~p\n", [Ctot]),
- io:format("Number of pooled blocks : ~p\n", [PBtot]),
- io:format("Number of pooled carriers: ~p\n", [PCtot]);
-print_stats(_) -> ok.
-
-tuple_add(T1, T2) ->
- list_to_tuple(lists:zipwith(fun(E1,E2) when is_number(E1), is_number(E2) ->
- E1 + E2;
- (A,A) ->
- A
- end,
- tuple_to_list(T1), tuple_to_list(T2))).
+ Stats = erlang:system_info({allocator,test_alloc}),
+ {Regular, Pooled} = lists:foldl(IFun, {[], []}, Stats),
+ {RegBlocks, RegCarriers} = summarize_alloc_stats(Regular, {0, 0}),
+ {PooledBlocks, PooledCarriers} = summarize_alloc_stats(Pooled, {0, 0}),
+
+ io:format("Number of blocks : ~p\n", [RegBlocks]),
+ io:format("Number of carriers: ~p\n", [RegCarriers]),
+ io:format("Number of pooled blocks : ~p\n", [PooledBlocks]),
+ io:format("Number of pooled carriers: ~p\n", [PooledCarriers]);
+print_stats(_) ->
+ ok.
+
+summarize_alloc_stats([{blocks,L} | Rest], {Blocks0, Carriers}) ->
+ Blocks = count_blocks([S || {_Type, S} <- L], Blocks0),
+ summarize_alloc_stats(Rest, {Blocks, Carriers});
+summarize_alloc_stats([{carriers, Count, _, _} | Rest], {Blocks, Carriers0}) ->
+ summarize_alloc_stats(Rest, {Blocks, Carriers0 + Count});
+summarize_alloc_stats([{carriers, Count} | Rest], {Blocks, Carriers0}) ->
+ summarize_alloc_stats(Rest, {Blocks, Carriers0 + Count});
+summarize_alloc_stats([_ | Rest], Acc) ->
+ summarize_alloc_stats(Rest, Acc);
+summarize_alloc_stats([], Acc) ->
+ Acc.
+
+count_blocks([{count, Count, _, _} | Rest], Acc) ->
+ count_blocks(Rest, Acc + Count);
+count_blocks([{count, Count} | Rest], Acc) ->
+ count_blocks(Rest, Acc + Count);
+count_blocks([_ | Rest], Acc) ->
+ count_blocks(Rest, Acc);
+count_blocks([], Acc) ->
+ Acc.
one_shot(CaseName) ->
State = CaseName:start({1, 0, erlang:system_info(build_type)}),
diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl
index d3b3b96b14..b1f1e115ac 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.erl
@@ -132,7 +132,8 @@ packed_registers(Config) when is_list(Config) ->
"_ = id(2),\n"
"id([_@Vars,_@NewVars,_@MoreNewVars]).\n"
"id(I) -> I.\n"]),
- merl:compile_and_load(Code),
+
+ merl:compile_and_load(Code, []),
%% Optionally print the generated code.
PrintCode = false, %Change to true to print code.
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index c5abd04e07..1904ffdf93 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -29,8 +29,8 @@
shadow_comments/1,list_to_utf8_atom/1,
specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
- binary_to_atom/1,binary_to_existing_atom/1,
- atom_to_binary/1,min_max/1, erlang_halt/1,
+ t_binary_to_atom/1,t_binary_to_existing_atom/1,
+ t_atom_to_binary/1,min_max/1, erlang_halt/1,
erl_crash_dump_bytes/1,
is_builtin/1, error_stacktrace/1,
error_stacktrace_during_call_trace/1,
@@ -50,7 +50,7 @@ all() ->
specs, improper_bif_stubs, auto_imports,
t_list_to_existing_atom, os_env, otp_7526,
display, display_string, list_to_utf8_atom,
- atom_to_binary, binary_to_atom, binary_to_existing_atom,
+ t_atom_to_binary, t_binary_to_atom, t_binary_to_existing_atom,
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
error_stacktrace, error_stacktrace_during_call_trace,
group_leader_prio, group_leader_prio_dirty,
@@ -496,7 +496,7 @@ test_7526(N) ->
-define(BADARG(E), {'EXIT',{badarg,_}} = (catch E)).
-define(SYS_LIMIT(E), {'EXIT',{system_limit,_}} = (catch E)).
-binary_to_atom(Config) when is_list(Config) ->
+t_binary_to_atom(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -524,8 +524,10 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(<<C/utf8>>, utf8)
end],
- <<"こんにちは"/utf8>> =
- atom_to_binary(test_binary_to_atom(<<"こんにちは"/utf8>>, utf8), utf8),
+ ExoticBin = <<"こんにちは"/utf8>>,
+ ExoticAtom = test_binary_to_atom(ExoticBin, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom),
%% badarg failures.
fail_binary_to_atom(atom),
@@ -543,6 +545,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% Bad UTF8 sequences.
?BADARG(binary_to_atom(id(<<255>>), utf8)),
+ ?BADARG(binary_to_atom(id(<<255>>))),
?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
<<B:1/binary, _/binary>> = id(<<194, 163>>), %Truncated character ERL-474
@@ -550,6 +553,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% system_limit failures.
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
+ ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>))),
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
@@ -562,6 +566,14 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(Bin0, Encoding) ->
Res = binary_to_atom(Bin0, Encoding),
Res = binary_to_existing_atom(Bin0, Encoding),
+ if
+ Encoding =:= utf8;
+ Encoding =:= unicode ->
+ Res = binary_to_atom(Bin0),
+ Res = binary_to_existing_atom(Bin0);
+ true ->
+ ok
+ end,
Bin1 = id(<<7:3,Bin0/binary,32:5>>),
Sz = byte_size(Bin0),
<<_:3,UnalignedBin:Sz/binary,_:5>> = Bin1,
@@ -581,6 +593,12 @@ fail_binary_to_atom(Bin) ->
ok
end,
try
+ binary_to_atom(Bin)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
binary_to_existing_atom(Bin, latin1)
catch
error:badarg ->
@@ -591,10 +609,16 @@ fail_binary_to_atom(Bin) ->
catch
error:badarg ->
ok
+ end,
+ try
+ binary_to_existing_atom(Bin)
+ catch
+ error:badarg ->
+ ok
end.
-binary_to_existing_atom(Config) when is_list(Config) ->
+t_binary_to_existing_atom(Config) when is_list(Config) ->
UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>,
try
binary_to_existing_atom(UnlikelyBin, latin1),
@@ -609,6 +633,12 @@ binary_to_existing_atom(Config) when is_list(Config) ->
catch
error:badarg -> ok
end,
+ try
+ binary_to_existing_atom(UnlikelyBin),
+ ct:fail(atom_exists)
+ catch
+ error:badarg -> ok
+ end,
UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
@@ -625,7 +655,7 @@ binary_to_existing_atom(Config) when is_list(Config) ->
ok.
-atom_to_binary(Config) when is_list(Config) ->
+t_atom_to_binary(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -641,12 +671,15 @@ atom_to_binary(Config) when is_list(Config) ->
LongBin = atom_to_binary(LongAtom, latin1),
%% utf8.
+ <<>> = atom_to_binary(''),
<<>> = atom_to_binary('', utf8),
<<>> = atom_to_binary('', unicode),
<<127>> = atom_to_binary('\177', utf8),
<<"abcdef">> = atom_to_binary(abcdef, utf8),
HalfLongBin = atom_to_binary(HalfLongAtom, utf8),
+ HalfLongBin = atom_to_binary(HalfLongAtom),
LongAtomBin = atom_to_binary(LongAtom, utf8),
+ LongAtomBin = atom_to_binary(LongAtom),
verify_long_atom_bin(LongAtomBin, 0),
%% Failing cases.
@@ -678,8 +711,15 @@ fail_atom_to_binary(Term) ->
catch
error:badarg ->
ok
+ end,
+ try
+ atom_to_binary(Term)
+ catch
+ error:badarg ->
+ ok
end.
+
min_max(Config) when is_list(Config) ->
a = erlang:min(id(a), a),
a = erlang:min(id(a), b),
@@ -860,6 +900,7 @@ error_stacktrace_test() ->
Types = [apply_const_last, apply_const, apply_last,
apply, double_apply_const_last, double_apply_const,
double_apply_last, double_apply, multi_apply_const_last,
+ apply_const_only, apply_only,
multi_apply_const, multi_apply_last, multi_apply,
call_const_last, call_last, call_const, call],
lists:foreach(fun (Type) ->
@@ -896,12 +937,18 @@ error_stacktrace_test() ->
Types),
ok.
-stk([], Type, Func) ->
- tail(Type, Func, jump),
- ok;
stk([_|L], Type, Func) ->
stk(L, Type, Func),
- ok.
+ %% Force the compiler to keep this body-recursive. We want the stack trace
+ %% to have one entry here and another in the base case to test that
+ %% multiple frames in the same function aren't removed unless they're
+ %% identical.
+ id(ok);
+stk([], Type, Func) ->
+ put(erlang, erlang),
+ put(tail, []),
+ tail(Type, Func, jump),
+ id(ok).
tail(Type, Func, jump) ->
tail(Type, Func, do);
@@ -910,6 +957,12 @@ tail(Type, error_1, do) ->
tail(Type, error_2, do) ->
do_error_2(Type).
+do_error_2(apply_const_only) ->
+ apply(erlang, error, [oops, [apply_const_only]]);
+do_error_2(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops, [apply_only|Tail]]);
do_error_2(apply_const_last) ->
erlang:apply(erlang, error, [oops, [apply_const_last]]);
do_error_2(apply_const) ->
@@ -951,6 +1004,12 @@ do_error_2(call) ->
erlang:error(id(oops), id([call])).
+do_error_1(apply_const_only) ->
+ apply(erlang, error, [oops]);
+do_error_1(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops|Tail]);
do_error_1(apply_const_last) ->
erlang:apply(erlang, error, [oops]);
do_error_1(apply_const) ->
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index cc4cbaad0e..6227148614 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -73,7 +73,8 @@
robustness/1,otp_8117/1,
otp_8180/1, trapping/1, large/1,
error_after_yield/1, cmp_old_impl/1,
- t2b_system_limit/1]).
+ t2b_system_limit/1,
+ term_to_iovec/1]).
%% Internal exports.
-export([sleeper/0,trapping_loop/4]).
@@ -92,8 +93,8 @@ all() ->
b2t_used_big,
bad_binary_to_term_2, safe_binary_to_term2,
bad_binary_to_term, bad_terms, t_hash, bad_size,
- sub_bin_copy,
- bad_term_to_binary, t2b_system_limit, more_bad_terms,
+ sub_bin_copy, bad_term_to_binary, t2b_system_limit,
+ term_to_iovec, more_bad_terms,
otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
@@ -456,6 +457,7 @@ bad_term_to_binary(Config) when is_list(Config) ->
T = id({a,b,c}),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])),
+ {'EXIT',{badarg,_}} = (catch term_to_binary(T, [iovec])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])),
{'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])),
@@ -473,7 +475,8 @@ t2b_system_limit(Config) when is_list(Config) ->
memsup:get_system_memory_data()) of
Memory when is_integer(Memory),
Memory > 6*1024*1024*1024 ->
- test_t2b_system_limit(),
+ test_t2b_system_limit(term_to_binary, fun erlang:term_to_binary/1, fun erlang:term_to_binary/2),
+ test_t2b_system_limit(term_to_iovec, fun erlang:term_to_iovec/1, fun erlang:term_to_iovec/2),
garbage_collect(),
ok;
_ ->
@@ -483,37 +486,81 @@ t2b_system_limit(Config) when is_list(Config) ->
{skipped, "Only interesting on 64-bit builds"}
end.
-test_t2b_system_limit() ->
+test_t2b_system_limit(Name, F1, F2) ->
io:format("Creating HugeBin~n", []),
Bits = ((1 bsl 32)+1)*8,
HugeBin = <<0:Bits>>,
- io:format("Testing term_to_binary(HugeBin)~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeBin)~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeBin],
- _} |_]}} = (catch term_to_binary(HugeBin)),
+ _} |_]}} = (catch F1(HugeBin)),
- io:format("Testing term_to_binary(HugeBin, [compressed])~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeBin, [compressed])~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeBin, [compressed]],
- _} |_]}} = (catch term_to_binary(HugeBin, [compressed])),
+ _} |_]}} = (catch F2(HugeBin, [compressed])),
%% Check that it works also after we have trapped...
io:format("Creating HugeListBin~n", []),
HugeListBin = [lists:duplicate(2000000,2000000), HugeBin],
- io:format("Testing term_to_binary(HugeListBin)~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeListBin)~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeListBin],
- _} |_]}} = (catch term_to_binary(HugeListBin)),
+ _} |_]}} = (catch F1(HugeListBin)),
- io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []),
- {'EXIT',{system_limit,[{erlang,term_to_binary,
+ io:format("Testing ~p(HugeListBin, [compressed])~n", [Name]),
+ {'EXIT',{system_limit,[{erlang,Name,
[HugeListBin, [compressed]],
- _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])),
+ _} |_]}} = (catch F2(HugeListBin, [compressed])),
ok.
+term_to_iovec(Config) when is_list(Config) ->
+ Bin = list_to_binary(lists:duplicate(1000,100)),
+ Bin2 = list_to_binary(lists:duplicate(65,100)),
+ check_term_to_iovec({[Bin, atom, Bin, 1244, make_ref(), Bin]}),
+ check_term_to_iovec(Bin),
+ check_term_to_iovec([Bin,Bin,Bin,Bin]),
+ check_term_to_iovec(blipp),
+ check_term_to_iovec(lists:duplicate(1000,100)),
+ check_term_to_iovec([[Bin2]]),
+ check_term_to_iovec([erlang:ports(), Bin, erlang:processes()]),
+ ok.
+
+check_term_to_iovec(Term) ->
+ IoVec1 = erlang:term_to_iovec(Term),
+ ok = check_is_iovec(IoVec1),
+ IoVec2 = erlang:term_to_iovec(Term, []),
+ ok = check_is_iovec(IoVec2),
+ B = erlang:term_to_binary(Term),
+ IoVec1Bin = erlang:iolist_to_binary(IoVec1),
+ IoVec2Bin = erlang:iolist_to_binary(IoVec2),
+ try
+ B = IoVec1Bin
+ catch
+ _:_ ->
+ io:format("Binary: ~p~n", [B]),
+ io:format("I/O vec1 binary: ~p~n", [IoVec1Bin]),
+ io:format("I/O vec1: ~p~n", [IoVec1]),
+ ct:fail(not_same_result)
+ end,
+ try
+ B = IoVec2Bin
+ catch
+ _:_ ->
+ io:format("Binary: ~p~n", [B]),
+ io:format("I/O vec2 binary: ~p~n", [IoVec2Bin]),
+ io:format("I/O vec2: ~p~n", [IoVec2]),
+ ct:fail(not_same_result)
+ end.
+
+check_is_iovec([]) ->
+ ok;
+check_is_iovec([B|Bs]) when is_binary(B) ->
+ check_is_iovec(Bs).
+
%% Tests binary_to_term/1 and term_to_binary/1.
terms(Config) when is_list(Config) ->
@@ -1862,9 +1909,16 @@ 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),
+ ResN = rpc:call(Node, M, F, A),
Res = apply(M, F, A),
- ok.
+ case ResN =:= Res of
+ true ->
+ ok;
+ false ->
+ io:format("~p: ~p~n~p: ~p~n",
+ [Node, ResN, node(), Res]),
+ ct:fail(different_results)
+ end.
make_sub_binary(Bin) when is_binary(Bin) ->
{_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 742592f88e..477b0f5bb3 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -832,21 +832,27 @@ deep_exception() ->
R1 -> ct:fail({returned,abbr(R1)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, deep_5, [1,2], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -896,21 +902,27 @@ deep_exception() ->
R2 -> ct:fail({returned,abbr(R2)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -975,21 +987,27 @@ deep_exception() ->
R3 -> ct:fail({returned,abbr(R3)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply,
[fun () -> ?MODULE:deep_5(1,2) end, []], 7,
[{trace,Self,call,{erlang,error,[undef]}},
@@ -1249,6 +1267,24 @@ expect(Message) ->
ct:fail(no_trace_message)
end.
+expect(Validator, State0) when is_function(Validator) ->
+ receive
+ M ->
+ case Validator(M, State0) of
+ expected ->
+ ok = io:format("Expected and got ~p", [abbr(M)]);
+ {next, State} ->
+ ok = io:format("Expected and got ~p", [abbr(M)]),
+ expect(Validator, State);
+ {unexpected,Message} ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [abbr(Validator('_'))]),
+ ct:fail(no_trace_message)
+ end.
+
trace_info(What, Key) ->
get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
Res = receive
diff --git a/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
index 699f0c1161..b08cd5e654 100644
--- a/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
+++ b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl
@@ -8,8 +8,7 @@
do(Priv, Data, Type, Opts) ->
try do_it(Priv, Data, Type, Opts)
catch
- C:E ->
- ST = erlang:get_stacktrace(),
+ C:E:ST ->
io:format("Caught exception from line ~p:\n~p\n",
[get(the_line), ST]),
io:format("Message queue: ~p\n", [process_info(self(), messages)]),
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index ef13b515fb..099dfabcb6 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_SUITE.erl
@@ -301,8 +301,8 @@ http(Config) when is_list(Config) ->
StrA = list_to_atom(Str),
StrB = list_to_binary(Str),
Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>,
- {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin),
- {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin),
+ {ok, {http_header,N,StrA,Str,Val}, Rest} = decode_pkt(httph,Bin),
+ {ok, {http_header,N,StrA,StrB,ValB}, Rest} = decode_pkt(httph_bin,Bin),
N + 1
end,
lists:foldl(HdrF, 1, http_hdr_strings()),
@@ -330,6 +330,15 @@ http(Config) when is_list(Config) ->
%% Response with empty phrase
{ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []),
{ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []),
+
+
+ %% Test error cases
+ {ok,{http_error,"Host\t: localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<"Host\t: localhost:8000\r\na">>, []),
+ {ok,{http_error,"Host : localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<"Host : localhost:8000\r\na">>, []),
+ {ok,{http_error," : localhost:8000\r\n"},<<"a">>} =
+ decode_pkt(httph, <<" : localhost:8000\r\na">>, []),
ok.
http_with_bin(http) ->
@@ -364,31 +373,40 @@ http_request(Msg) ->
{http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}},
{http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}},
{"Connection: close\r\n",
- {http_header,2,'Connection',undefined, "close"},
- {http_header,2,'Connection',undefined,<<"close">>}},
- {"Host\t : localhost:8000\r\n", % white space before :
- {http_header,14,'Host',undefined, "localhost:8000"},
- {http_header,14,'Host',undefined,<<"localhost:8000">>}},
+ {http_header,2,'Connection', "Connection" , "close"},
+ {http_header,2,'Connection',<<"Connection">>,<<"close">>}},
{"User-Agent: perl post\r\n",
- {http_header,24,'User-Agent',undefined, "perl post"},
- {http_header,24,'User-Agent',undefined,<<"perl post">>}},
+ {http_header,24,'User-Agent', "User-Agent" , "perl post"},
+ {http_header,24,'User-Agent',<<"User-Agent">>,<<"perl post">>}},
{"Content-Length: 4\r\n",
- {http_header,38,'Content-Length',undefined, "4"},
- {http_header,38,'Content-Length',undefined,<<"4">>}},
+ {http_header,38,'Content-Length', "Content-Length" , "4"},
+ {http_header,38,'Content-Length',<<"Content-Length">>,<<"4">>}},
{"Content-Type: text/xml; charset=utf-8\r\n",
- {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"},
- {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}},
+ {http_header,42,'Content-Type', "Content-Type" , "text/xml; charset=utf-8"},
+ {http_header,42,'Content-Type',<<"Content-Type">>,<<"text/xml; charset=utf-8">>}},
{"Other-Field: with some text\r\n",
- {http_header,0, "Other-Field" ,undefined, "with some text"},
- {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
+ {http_header,0, "Other-Field" , "Other-Field" , "with some text"},
+ {http_header,0,<<"Other-Field">>,<<"Other-Field">>,<<"with some text">>}},
+ {"Content--Type: text/xml; charset=utf-8\r\n",
+ {http_header,0, "Content--type" , "Content--Type" , "text/xml; charset=utf-8"},
+ {http_header,0,<<"Content--type">>,<<"Content--Type">>,<<"text/xml; charset=utf-8">>}},
+ {"Content---Type: text/xml; charset=utf-8\r\n",
+ {http_header,0, "Content---Type" , "Content---Type" , "text/xml; charset=utf-8"},
+ {http_header,0,<<"Content---Type">>,<<"Content---Type">>,<<"text/xml; charset=utf-8">>}},
+ {"CONTENT-type: text/xml; charset=utf-8\r\n",
+ {http_header,42,'Content-Type', "CONTENT-type" , "text/xml; charset=utf-8"},
+ {http_header,42,'Content-Type',<<"CONTENT-type">>,<<"text/xml; charset=utf-8">>}},
+ {"OTHER-field: with some text\r\n",
+ {http_header,0, "Other-Field" , "OTHER-field" , "with some text"},
+ {http_header,0,<<"Other-Field">>,<<"OTHER-field">>,<<"with some text">>}},
{"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
- {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
- {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
+ {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" , "Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY" , "with some text"},
+ {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,<<"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY">>,<<"with some text">>}},
{"Multi-Line: Once upon a time in a land far far away,\r\n"
" there lived a princess imprisoned in the highest tower\r\n"
" of the most haunted castle.\r\n",
- {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
- {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
+ {http_header,0, "Multi-Line" , "Multi-Line" , "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."},
+ {http_header,0,<<"Multi-Line">>,<<"Multi-Line">>,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}},
{"\r\n",
http_eoh,
http_eoh}],
@@ -404,17 +422,17 @@ http_response(Msg) ->
{http_response, {1,0}, 404, "Object Not Found"},
{http_response, {1,0}, 404, <<"Object Not Found">>}},
{"Server: inets/4.7.16\r\n",
- {http_header, 30, 'Server', undefined, "inets/4.7.16"},
- {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}},
+ {http_header, 30, 'Server', "Server" , "inets/4.7.16"},
+ {http_header, 30, 'Server', <<"Server">>, <<"inets/4.7.16">>}},
{"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n",
- {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"},
- {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
+ {http_header, 3, 'Date', "Date" , "Fri, 04 Jul 2008 17:16:22 GMT"},
+ {http_header, 3, 'Date', <<"Date">>, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}},
{"Content-Type: text/html\r\n",
- {http_header, 42, 'Content-Type', undefined, "text/html"},
- {http_header, 42, 'Content-Type', undefined, <<"text/html">>}},
+ {http_header, 42, 'Content-Type', "Content-Type" , "text/html"},
+ {http_header, 42, 'Content-Type', <<"Content-Type">>, <<"text/html">>}},
{"Content-Length: 207\r\n",
- {http_header, 38, 'Content-Length', undefined, "207"},
- {http_header, 38, 'Content-Length', undefined, <<"207">>}},
+ {http_header, 38, 'Content-Length', "Content-Length" , "207"},
+ {http_header, 38, 'Content-Length', <<"Content-Length">>, <<"207">>}},
{"\r\n",
http_eoh,
http_eoh}],
@@ -542,7 +560,7 @@ otp_8536_do(N) ->
Bin = <<Hdr/binary, ": ", Data/binary, "\r\n\r\n">>,
io:format("Bin='~p'\n",[Bin]),
- {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
+ {ok,{http_header,0,Hdr2,Hdr2,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []),
%% Do something to trash the C-stack, how about another decode_packet:
decode_pkt(httph_bin,<<Letters/binary, ": ", Data/binary, "\r\n\r\n">>, []),
diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl
index 4f5ad0295a..2ded862b8a 100644
--- a/erts/emulator/test/dirty_bif_SUITE.erl
+++ b/erts/emulator/test/dirty_bif_SUITE.erl
@@ -397,7 +397,9 @@ dirty_process_trace(Config) when is_list(Config) ->
access_dirty_process(
Config,
fun() ->
- erlang:trace_pattern({erts_debug,dirty_io,2},
+ %% BIFs can only be traced when their modules are loaded.
+ code:ensure_loaded(erts_debug),
+ 1 = erlang:trace_pattern({erts_debug,dirty_io,2},
[{'_',[],[{return_trace}]}],
[local,meta]),
ok
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 9dcdd60060..da246d8157 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -68,7 +68,9 @@
message_latency_large_link_exit/1,
message_latency_large_monitor_exit/1,
message_latency_large_exit2/1,
- system_limit/1]).
+ system_limit/1,
+ hopefull_data_encoding/1,
+ mk_hopefull_data/0]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -97,7 +99,8 @@ all() ->
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
{group, message_latency},
{group, bad_dist}, {group, bad_dist_ext},
- start_epmd_false, epmd_module, system_limit].
+ start_epmd_false, epmd_module, system_limit,
+ hopefull_data_encoding].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -2567,6 +2570,134 @@ address_please(_Name, _Address, _AddressFamily) ->
IP = {127,0,0,1},
{ok, IP}.
+hopefull_data_encoding(Config) when is_list(Config) ->
+ test_hopefull_data_encoding(Config, true),
+ test_hopefull_data_encoding(Config, false).
+
+test_hopefull_data_encoding(Config, Fallback) when is_list(Config) ->
+ {ok, ProxyNode} = start_node(hopefull_data_normal),
+ {ok, BouncerNode} = start_node(hopefull_data_bouncer, "-hidden"),
+ case Fallback of
+ false ->
+ ok;
+ true ->
+ rpc:call(BouncerNode, erts_debug, set_internal_state,
+ [available_internal_state, true]),
+ false = rpc:call(BouncerNode, erts_debug, set_internal_state,
+ [remove_hopefull_dflags, true])
+ end,
+ HData = mk_hopefull_data(),
+ Tester = self(),
+ R1 = make_ref(),
+ R2 = make_ref(),
+ R3 = make_ref(),
+ Bouncer = spawn_link(BouncerNode, fun () -> bounce_loop() end),
+ Proxy = spawn_link(ProxyNode,
+ fun () ->
+ register(bouncer, self()),
+ %% Verify same result between this node and tester
+ Tester ! [R1, HData],
+ %% Test when connection has not been setup yet
+ Bouncer ! {Tester, [R2, HData]},
+ Sync = make_ref(),
+ Bouncer ! {self(), Sync},
+ receive Sync -> ok end,
+ %% Test when connection is already up
+ Bouncer ! {Tester, [R3, HData]},
+ receive after infinity -> ok end
+ end),
+ receive
+ [R1, HData1] ->
+ Hdata = HData1
+ end,
+ receive
+ [R2, HData2] ->
+ case Fallback of
+ false ->
+ HData = HData2;
+ true ->
+ check_hopefull_fallback_data(Hdata, HData2)
+ end
+ end,
+ receive
+ [R3, HData3] ->
+ case Fallback of
+ false ->
+ HData = HData3;
+ true ->
+ check_hopefull_fallback_data(Hdata, HData3)
+ end
+ end,
+ unlink(Proxy),
+ exit(Proxy, bye),
+ unlink(Bouncer),
+ exit(Bouncer, bye),
+ stop_node(ProxyNode),
+ stop_node(BouncerNode),
+ ok.
+
+bounce_loop() ->
+ receive
+ {SendTo, Data} ->
+ SendTo ! Data
+ end,
+ bounce_loop().
+
+mk_hopefull_data() ->
+ HugeBs = list_to_bitstring([lists:duplicate(12*1024*1024, 85), <<6:6>>]),
+ <<_:1/bitstring,HugeBs2/bitstring>> = HugeBs,
+ lists:flatten([mk_hopefull_data(list_to_binary(lists:seq(1,255))),
+ 1234567890, HugeBs, fun gurka:banan/3, fun erlang:node/1,
+ self(), fun erlang:self/0,
+ mk_hopefull_data(list_to_binary(lists:seq(1,32))), an_atom,
+ fun lists:reverse/1, make_ref(), HugeBs2,
+ fun blipp:blapp/7]).
+
+mk_hopefull_data(BS) ->
+ BSsz = bit_size(BS),
+ [lists:map(fun (Offset) ->
+ <<_:Offset/bitstring, NewBs/bitstring>> = BS,
+ NewBs
+ end, lists:seq(1, 16)),
+ lists:map(fun (Offset) ->
+ <<NewBs:Offset/bitstring, _/bitstring>> = BS,
+ NewBs
+ end, lists:seq(BSsz-16, BSsz-1)),
+ lists:map(fun (Offset) ->
+ PreOffset = Offset rem 16,
+ <<_:PreOffset/bitstring, NewBs:Offset/bitstring, _/bitstring>> = BS,
+ NewBs
+ end, lists:seq(BSsz-32, BSsz-17))].
+
+
+check_hopefull_fallback_data([], []) ->
+ ok;
+check_hopefull_fallback_data([X|Xs],[Y|Ys]) ->
+ chk_hopefull_fallback(X, Y),
+ check_hopefull_fallback_data(Xs,Ys).
+
+chk_hopefull_fallback(Binary, FallbackBinary) when is_binary(Binary) ->
+ Binary = FallbackBinary;
+chk_hopefull_fallback(BitStr, {Bin, BitSize}) when is_bitstring(BitStr) ->
+ true = is_binary(Bin),
+ true = is_integer(BitSize),
+ true = BitSize > 0,
+ true = BitSize < 8,
+ Hsz = size(Bin) - 1,
+ <<Head:Hsz/binary, I/integer>> = Bin,
+ IBits = I bsr (8 - BitSize),
+ FallbackBitStr = list_to_bitstring([Head,<<IBits:BitSize>>]),
+ BitStr = FallbackBitStr,
+ ok;
+chk_hopefull_fallback(Func, {ModName, FuncName}) when is_function(Func) ->
+ {M, F, _} = erlang:fun_info_mfa(Func),
+ M = ModName,
+ F = FuncName,
+ ok;
+chk_hopefull_fallback(Other, SameOther) ->
+ Other = SameOther,
+ ok.
+
%%% Utilities
timestamp() ->
@@ -3034,3 +3165,5 @@ free_memory() ->
error : undef ->
ct:fail({"os_mon not built"})
end.
+
+
diff --git a/erts/emulator/test/emulator.spec b/erts/emulator/test/emulator.spec
index 7a6dd83020..087bd8880d 100644
--- a/erts/emulator/test/emulator.spec
+++ b/erts/emulator/test/emulator.spec
@@ -1,2 +1,3 @@
{enable_builtin_hooks, false}.
{suites,"../emulator_test",all}.
+{skip_groups,"../emulator_test",hash_SUITE,[phash2_benchmark],"Benchmark only"}.
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
index 03638bfa23..8b1bb71a40 100644
--- a/erts/emulator/test/emulator_bench.spec
+++ b/erts/emulator/test/emulator_bench.spec
@@ -1,3 +1,4 @@
{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
{groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}.
{groups,"../emulator_test",erts_debug_SUITE,[interpreter_size_bench]}.
+{groups,"../emulator_test",hash_SUITE,[phash2_benchmark]}.
diff --git a/erts/emulator/test/erts_test_destructor.erl b/erts/emulator/test/erts_test_destructor.erl
new file mode 100644
index 0000000000..311bb0aaf9
--- /dev/null
+++ b/erts/emulator/test/erts_test_destructor.erl
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% A NIF resource that sends a message in the destructor.
+
+-module(erts_test_destructor).
+
+-export([init/1, send/2]).
+
+init(Config) ->
+ case is_loaded() of
+ false ->
+ Path = proplists:get_value(data_dir, Config),
+ erlang:load_nif(filename:join(Path,?MODULE), []);
+ true ->
+ ok
+ end.
+
+is_loaded() ->
+ false.
+
+%% Create a resource which sends Msg to Pid when destructed.
+send(_Pid, _Msg) ->
+ erlang:nif_error("NIF not loaded").
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 154bce3c35..e94a8d701b 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -303,57 +303,42 @@ maxbig_gc() ->
Maxbig.
stacktrace(Conf) when is_list(Conf) ->
- Tag = make_ref(),
- {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
- {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
V = [make_ref()|self()],
- {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
- stacktrace_1({'abs',V}, error, {value,V}),
- St1 = erase(stacktrace1),
- St1 = erase(stacktrace2),
- St1 = erlang:get_stacktrace(),
- {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]=St2} =
- stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
- [{erlang,'div',[1,0],_},{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
- St2 = erase(stacktrace2),
- St2 = erlang:get_stacktrace(),
- {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
- stacktrace_1({value,V}, error, {value,V}),
- St3 = erase(stacktrace1),
- St3 = erase(stacktrace2),
- St3 = erlang:get_stacktrace(),
- {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
- stacktrace_1({value,V}, error, {throw,V}),
- [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
- St4 = erase(stacktrace2),
- St4 = erlang:get_stacktrace(),
+ {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]}} =
+ stacktrace_1({'abs',V}, error, {value,V}),
+ {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]} =
+ stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
+ {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]} =
+ stacktrace_1({value,V}, error, {value,V}),
+ {caught2,{throw,V},[{?MODULE,foo,1,_}|_]} =
+ stacktrace_1({value,V}, error, {throw,V}),
try
stacktrace_2()
catch
- error:{badmatch,_} ->
+ error:{badmatch,_}:Stk ->
[{?MODULE,stacktrace_2,0,_},
- {?MODULE,stacktrace,1,_}|_] =
- erlang:get_stacktrace(),
+ {?MODULE,stacktrace,1,_}|_] = Stk,
ok
end.
stacktrace_1(X, C1, Y) ->
- erase(stacktrace1),
- erase(stacktrace2),
try try foo(X) of
C1 -> value1
catch
- C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
+ C1:D1:Stk1 ->
+ [] = erlang:get_stacktrace(),
+ {caught1,D1,Stk1}
after
- put(stacktrace1, erlang:get_stacktrace()),
foo(Y)
end of
V2 -> {value2,V2}
catch
- C2:D2 -> {caught2,{C2,D2},erlang:get_stacktrace()}
+ C2:D2:Stk2 ->
+ [] = erlang:get_stacktrace(),
+ {caught2,{C2,D2},Stk2}
after
- put(stacktrace2, erlang:get_stacktrace())
+ ok
end.
stacktrace_2() ->
@@ -364,76 +349,71 @@ stacktrace_2() ->
nested_stacktrace(Conf) when is_list(Conf) ->
V = [{make_ref()}|[self()]],
value1 =
- nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
- {void,void,void}),
+ nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
+ {void,void,void}),
{caught1,
[{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_],
- value2,
- [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{value,{V,x2}},void,{V,x2}}),
+ value2} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{value,{V,x2}},void,{V,x2}}),
{caught1,
[{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_],
- {caught2,[{erlang,abs,[V],_}|_]},
- [{erlang,abs,[V],_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{'abs',V},error,badarg}),
+ {caught2,[{erlang,abs,[V],_}|_]}} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{'abs',V},error,badarg}),
ok.
nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
try foo(X1) of
V1 -> value1
catch
- C1:V1 ->
- S1 = erlang:get_stacktrace(),
- T2 =
- try foo(X2) of
- V2 -> value2
- catch
- C2:V2 -> {caught2,erlang:get_stacktrace()}
+ C1:V1:S1 ->
+ T2 = try foo(X2) of
+ V2 -> value2
+ catch
+ C2:V2:S2 -> {caught2,S2}
end,
- {caught1,S1,T2,erlang:get_stacktrace()}
+ {caught1,S1,T2}
end.
raise(Conf) when is_list(Conf) ->
erase(raise),
- A =
- try
- try foo({'div',{1,0}})
+ A =
+ try
+ try foo({'div',{1,0}})
+ catch
+ error:badarith:A0 ->
+ put(raise, A0),
+ erlang:raise(error, badarith, A0)
+ end
catch
- error:badarith:A0 ->
- put(raise, A0 = erlang:get_stacktrace()),
- erlang:raise(error, badarith, A0)
- end
- catch
- error:badarith:A1 ->
- A1 = erlang:get_stacktrace(),
- A1 = get(raise)
- end,
- A = erlang:get_stacktrace(),
+ error:badarith:A1 ->
+ A1 = get(raise)
+ end,
A = get(raise),
[{erlang,'div',[1, 0], _},{?MODULE,my_div,2,_}|_] = A,
%%
N = 8, % Must be even
N = erlang:system_flag(backtrace_depth, N),
- B = odd_even(N, []),
- try even(N)
- catch error:function_clause -> ok
+ try
+ even(N)
+ catch
+ error:function_clause -> ok
end,
- B = erlang:get_stacktrace(),
%%
- C0 = odd_even(N+1, []),
- C = lists:sublist(C0, N),
- try odd(N+1)
- catch error:function_clause -> ok
+ C = odd_even(N+1, []),
+ try
+ odd(N+1)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
- try erlang:raise(error, function_clause, C0)
- catch error:function_clause -> ok
+ try
+ erlang:raise(error, function_clause, C)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
ok.
odd_even(N, R) when is_integer(N), N > 1 ->
@@ -601,11 +581,11 @@ do_exception_with_heap_frag(Bin, [Sz|Sizes]) ->
try
binary_to_term(Bin)
catch
- _:_ ->
+ _:_:Stk ->
%% term_to_binary/1 is an easy way to traverse the
%% entire stacktrace term to make sure that every part
%% of it is OK.
- term_to_binary(erlang:get_stacktrace())
+ term_to_binary(Stk)
end,
id(Filler)
end),
@@ -811,8 +791,8 @@ close_calls(Where) -> %Line 2
call2(), %Line 6
call3(), %Line 7
no_crash %Line 8
- catch error:crash ->
- erlang:get_stacktrace() %Line 10
+ catch error:crash:Stk ->
+ Stk %Line 10
end. %Line 11
call1() -> %Line 13
diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl
index 3cbb3c7d5f..dd71c3da58 100644
--- a/erts/emulator/test/hash_SUITE.erl
+++ b/erts/emulator/test/hash_SUITE.erl
@@ -33,7 +33,25 @@
-module(hash_SUITE).
-export([basic_test/0,cmp_test/1,range_test/0,spread_test/1,
phash2_test/0, otp_5292_test/0,
- otp_7127_test/0]).
+ otp_7127_test/0,
+ run_phash2_benchmarks/0,
+ test_phash2_binary_aligned_and_unaligned_equal/1,
+ test_phash2_4GB_plus_bin/1,
+ test_phash2_10MB_plus_bin/1,
+ test_phash2_large_map/1,
+ test_phash2_shallow_long_list/1,
+ test_phash2_deep_list/1,
+ test_phash2_deep_tuple/1,
+ test_phash2_deep_tiny/1,
+ test_phash2_with_42/1,
+ test_phash2_with_short_tuple/1,
+ test_phash2_with_short_list/1,
+ test_phash2_with_tiny_bin/1,
+ test_phash2_with_tiny_unaligned_sub_binary/1,
+ test_phash2_with_small_unaligned_sub_binary/1,
+ test_phash2_with_large_bin/1,
+ test_phash2_with_large_unaligned_sub_binary/1,
+ test_phash2_with_super_large_unaligned_sub_binary/1]).
%%
%% Define to run outside of test server
@@ -43,13 +61,15 @@
%%
%% Define for debug output
%%
-%-define(debug,1).
+-define(debug,1).
-ifdef(STANDALONE).
-define(config(A,B),config(A,B)).
+-record(event, {name, data}).
-export([config/2]).
-else.
-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
-endif.
-ifdef(debug).
@@ -67,12 +87,15 @@
-ifdef(STANDALONE).
config(priv_dir,_) ->
".".
+notify(X) ->
+ erlang:display(X).
-else.
%% When run in test server.
--export([all/0, suite/0,
+-export([groups/0, all/0, suite/0,
test_basic/1,test_cmp/1,test_range/1,test_spread/1,
test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1,
- test_hash_zero/1]).
+ test_hash_zero/1, init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -81,7 +104,71 @@ suite() ->
all() ->
[test_basic, test_cmp, test_range, test_spread,
test_phash2, otp_5292, bit_level_binaries, otp_7127,
- test_hash_zero].
+ test_hash_zero, test_phash2_binary_aligned_and_unaligned_equal,
+ test_phash2_4GB_plus_bin,
+ test_phash2_10MB_plus_bin,
+ {group, phash2_benchmark_tests},
+ {group, phash2_benchmark}].
+
+get_phash2_benchmarks() ->
+ [
+ test_phash2_large_map,
+ test_phash2_shallow_long_list,
+ test_phash2_deep_list,
+ test_phash2_deep_tuple,
+ test_phash2_deep_tiny,
+ test_phash2_with_42,
+ test_phash2_with_short_tuple,
+ test_phash2_with_short_list,
+ test_phash2_with_tiny_bin,
+ test_phash2_with_tiny_unaligned_sub_binary,
+ test_phash2_with_small_unaligned_sub_binary,
+ test_phash2_with_large_bin,
+ test_phash2_with_large_unaligned_sub_binary,
+ test_phash2_with_super_large_unaligned_sub_binary
+ ].
+
+groups() ->
+ [
+ {
+ phash2_benchmark_tests,
+ [],
+ get_phash2_benchmarks()
+ },
+ {
+ phash2_benchmark,
+ [],
+ get_phash2_benchmarks()
+ }
+ ].
+
+
+init_per_suite(Config) ->
+ io:format("START APPS~n"),
+ A0 = case application:start(sasl) of
+ ok -> [sasl];
+ _ -> []
+ end,
+ A = case application:start(os_mon) of
+ ok -> [os_mon|A0];
+ _ -> A0
+ end,
+ io:format("APPS STARTED~n"),
+ [{started_apps, A}|Config].
+
+end_per_suite(Config) ->
+ As = proplists:get_value(started_apps, Config),
+ lists:foreach(fun (A) -> application:stop(A) end, As),
+ Config.
+
+init_per_group(phash2_benchmark_tests, Config) ->
+ [phash2_benchmark_tests |Config];
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
%% Tests basic functionality of erlang:phash and that the
%% hashes has not changed (neither hash nor phash)
@@ -119,6 +206,9 @@ otp_7127(Config) when is_list(Config) ->
test_hash_zero(Config) when is_list(Config) ->
hash_zero_test().
+
+notify(X) ->
+ ct_event:notify(X).
-endif.
@@ -133,26 +223,17 @@ basic_test() ->
16#77777777777777],16#FFFFFFFF),
ExternalReference = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,
110,111,104,111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0>>,
- 1113403635 = erlang:phash(binary_to_term(ExternalReference),
- 16#FFFFFFFF),
- ExternalFun = <<131,117,0,0,0,3,103,100,0,13,110,111,110,111,100,101,64,
- 110,111,104,111,115,116,0,0,0,38,0,0,0,0,0,100,0,8,101,
- 114,108,95,101,118,97,108,97,20,98,5,182,139,98,108,0,0,
- 0,3,104,2,100,0,1,66,109,0,0,0,33,131,114,0,3,100,0,13,
- 110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,
- 0,0,122,0,0,0,0,0,0,0,0,104,2,100,0,1,76,107,0,33,131,
- 114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
- 111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0,104,2,100,0,1,82,
- 114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
- 111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0,106,108,0,0,0,1,
- 104,5,100,0,6,99,108,97,117,115,101,97,1,106,106,108,0,0,
- 0,1,104,3,100,0,7,105,110,116,101,103,101,114,97,1,97,1,
- 106,106,104,3,100,0,4,101,118,97,108,104,2,100,0,5,115,
- 104,101,108,108,100,0,10,108,111,99,97,108,95,102,117,
- 110,99,108,0,0,0,1,103,100,0,13,110,111,110,111,100,101,
- 64,110,111,104,111,115,116,0,0,0,22,0,0,0,0,0,106>>,
- 170987488 = erlang:phash(binary_to_term(ExternalFun),
- 16#FFFFFFFF),
+ ExternalReference = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,
+ 110,111,104,111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0>>,
+ 1113403635 = phash_from_external(ExternalReference),
+
+ ExternalFun = <<131,112,0,0,0,70,1,212,190,220,28,179,144,194,131,
+ 19,215,105,97,77,251,125,93,0,0,0,0,0,0,0,2,100,0,1,
+ 116,97,0,98,6,165,246,224,103,100,0,13,110,111,
+ 110,111,100,101,64,110,111,104,111,115,116,0,0,0,91,
+ 0,0,0,0,0,97,2,97,1>>,
+ 25769064 = phash_from_external(ExternalFun),
+
case (catch erlang:phash(1,0)) of
{'EXIT',{badarg, _}} ->
ok;
@@ -160,6 +241,8 @@ basic_test() ->
exit(phash_accepted_zero_as_range)
end.
+phash_from_external(Ext) ->
+ erlang:phash(binary_to_term(Ext), 16#FFFFFFFF).
range_test() ->
F = fun(From,From,_FF) ->
@@ -354,6 +437,7 @@ phash2_test() ->
%% bit-level binaries
{<<0:7>>, 1055790816},
+ {(fun()-> B = <<255,7:3>>, <<_:4,D/bitstring>> = B, D end)(), 911751529},
{<<"abc",13:4>>, 670412287},
{<<5:3,"12345678901234567890">>, 289973273},
@@ -424,6 +508,159 @@ phash2_test() ->
[] = [{E,H,H2} || {E,H} <- L, (H2 = erlang:phash2(E, Max)) =/= H],
ok.
+test_phash2_binary_aligned_and_unaligned_equal(Config) when is_list(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ test_aligned_and_unaligned_equal_up_to(256*12+255),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+test_aligned_and_unaligned_equal_up_to(BinSize) ->
+ Results =
+ lists:map(fun(Size) ->
+ test_aligned_and_unaligned_equal(Size)
+ end, lists:seq(1, BinSize)),
+ %% DataDir = filename:join(filename:dirname(code:which(?MODULE)), "hash_SUITE_data"),
+ %% ExpResFile = filename:join(DataDir, "phash2_bin_expected_results.txt"),
+ %% {ok, [ExpRes]} = file:consult(ExpResFile),
+ %% %% ok = file:write_file(ExpResFile, io_lib:format("~w.~n", [Results])),
+ %% Results = ExpRes,
+ 110469206 = erlang:phash2(Results).
+
+test_aligned_and_unaligned_equal(BinSize) ->
+ Bin = make_random_bin(BinSize),
+ LastByte = last_byte(Bin),
+ LastInBitstring = LastByte rem 11,
+ Bitstring = << Bin/binary, <<LastInBitstring:5>>/bitstring >>,
+ UnalignedBin = make_unaligned_sub_bitstring(Bin),
+ UnalignedBitstring = make_unaligned_sub_bitstring(Bitstring),
+ case erts_debug:get_internal_state(available_internal_state) of
+ false -> erts_debug:set_internal_state(available_internal_state, true);
+ _ -> ok
+ end,
+ erts_debug:set_internal_state(reds_left, 3),
+ BinHash = erlang:phash2(Bin),
+ BinHash = erlang:phash2(Bin),
+ erts_debug:set_internal_state(reds_left, 3),
+ UnalignedBinHash = erlang:phash2(UnalignedBin),
+ UnalignedBinHash = erlang:phash2(UnalignedBin),
+ BinHash = UnalignedBinHash,
+ erts_debug:set_internal_state(reds_left, 3),
+ BitstringHash = erlang:phash2(Bitstring),
+ BitstringHash = erlang:phash2(Bitstring),
+ erts_debug:set_internal_state(reds_left, 3),
+ UnalignedBitstringHash = erlang:phash2(UnalignedBitstring),
+ UnalignedBitstringHash = erlang:phash2(UnalignedBitstring),
+ BitstringHash = UnalignedBitstringHash,
+ {BinHash, BitstringHash}.
+
+last_byte(Bin) ->
+ NotLastByteSize = (erlang:bit_size(Bin)) - 8,
+ <<_:NotLastByteSize/bitstring, LastByte:8>> = Bin,
+ LastByte.
+
+test_phash2_4GB_plus_bin(Config) when is_list(Config) ->
+ run_when_enough_resources(
+ fun() ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ %% Created Bin4GB here so it only needs to be created once
+ erts_debug:set_internal_state(force_gc, self()),
+ Bin4GB = get_4GB_bin(),
+ test_phash2_plus_bin_helper1(Bin4GB, <<>>, <<>>, 13708901),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin4GB, <<>>, <<3:5>>, 66617678),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin4GB, <<13>>, <<>>, 31308392),
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(available_internal_state, false)
+ end).
+
+
+test_phash2_10MB_plus_bin(Config) when is_list(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:set_internal_state(force_gc, self()),
+ Bin10MB = get_10MB_bin(),
+ test_phash2_plus_bin_helper1(Bin10MB, <<>>, <<>>, 22776267),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin10MB, <<>>, <<3:5>>, 124488972),
+ erts_debug:set_internal_state(force_gc, self()),
+ test_phash2_plus_bin_helper1(Bin10MB, <<13>>, <<>>, 72958346),
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+get_10MB_bin() ->
+ TmpBin = make_random_bin(10239),
+ Bin = erlang:iolist_to_binary([0, TmpBin]),
+ IOList10MB = duplicate_iolist(Bin, 10),
+ Bin10MB = erlang:iolist_to_binary(IOList10MB),
+ 10485760 = size(Bin10MB),
+ Bin10MB.
+
+get_4GB_bin() ->
+ TmpBin = make_random_bin(65535),
+ Bin = erlang:iolist_to_binary([0, TmpBin]),
+ IOList4GB = duplicate_iolist(Bin, 16),
+ Bin4GB = erlang:iolist_to_binary(IOList4GB),
+ 4294967296 = size(Bin4GB),
+ Bin4GB.
+
+duplicate_iolist(IOList, 0) ->
+ IOList;
+duplicate_iolist(IOList, NrOfTimes) ->
+ duplicate_iolist([IOList, IOList], NrOfTimes - 1).
+
+test_phash2_plus_bin_helper1(Bin4GB, ExtraBytes, ExtraBits, ExpectedHash) ->
+ test_phash2_plus_bin_helper2(Bin4GB, fun id/1, ExtraBytes, ExtraBits, ExpectedHash),
+ test_phash2_plus_bin_helper2(Bin4GB, fun make_unaligned_sub_bitstring/1, ExtraBytes, ExtraBits, ExpectedHash).
+
+test_phash2_plus_bin_helper2(Bin, TransformerFun, ExtraBytes, ExtraBits, ExpectedHash) ->
+ ExtraBitstring = << ExtraBytes/binary, ExtraBits/bitstring >>,
+ LargerBitstring = << ExtraBytes/binary,
+ ExtraBits/bitstring,
+ Bin/bitstring >>,
+ LargerTransformedBitstring = TransformerFun(LargerBitstring),
+ ExtraBitstringHash = erlang:phash2(ExtraBitstring),
+ ExpectedHash =
+ case size(LargerTransformedBitstring) < 4294967296 of
+ true ->
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(reds_left, 1),
+ Hash = erlang:phash2(LargerTransformedBitstring),
+ Hash = erlang:phash2(LargerTransformedBitstring),
+ Hash;
+ false ->
+ erts_debug:set_internal_state(force_gc, self()),
+ erts_debug:set_internal_state(reds_left, 1),
+ ExtraBitstringHash = erlang:phash2(LargerTransformedBitstring),
+ ExtraBitstringHash = erlang:phash2(LargerTransformedBitstring),
+ ExtraBitstringHash
+ end.
+
+run_when_enough_resources(Fun) ->
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem >= 31 ->
+ Fun();
+ {Mem, WordSize} ->
+ {skipped,
+ io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)",
+ [Mem, WordSize])}
+ end.
+
+%% Total memory in GB
+total_memory() ->
+ try
+ MemoryData = memsup:get_system_memory_data(),
+ case lists:keysearch(total_memory, 1, MemoryData) of
+ {value, {total_memory, TM}} ->
+ TM div (1024*1024*1024);
+ false ->
+ {value, {system_total_memory, STM}} =
+ lists:keysearch(system_total_memory, 1, MemoryData),
+ STM div (1024*1024*1024)
+ end
+ catch
+ _ : _ ->
+ undefined
+ end.
+
-ifdef(FALSE).
f1() ->
abc.
@@ -436,14 +673,23 @@ f3(X, Y) ->
-endif.
otp_5292_test() ->
- PH = fun(E) -> [erlang:phash(E, 1 bsl 32),
- erlang:phash(-E, 1 bsl 32),
- erlang:phash2(E, 1 bsl 32),
- erlang:phash2(-E, 1 bsl 32)]
- end,
+ PH = fun(E) ->
+ EInList = [1, 2, 3, E],
+ EInList2 = [E, 1, 2, 3],
+ NegEInList = [1, 2, 3, -E],
+ NegEInList2 = [-E, 1, 2, 3],
+ [erlang:phash(E, 1 bsl 32),
+ erlang:phash(-E, 1 bsl 32),
+ erlang:phash2(E, 1 bsl 32),
+ erlang:phash2(-E, 1 bsl 32),
+ erlang:phash2(EInList, 1 bsl 32),
+ erlang:phash2(EInList2, 1 bsl 32),
+ erlang:phash2(NegEInList, 1 bsl 32),
+ erlang:phash2(NegEInList2, 1 bsl 32)]
+ end,
S2 = md5([md5(hash_int(S, E, PH)) || {Start, N, Sz} <- d(),
{S, E} <- int(Start, N, Sz)]),
- <<124,81,198,121,174,233,19,137,10,83,33,80,226,111,238,99>> = S2,
+ <<234,63,192,76,253,57,250,32,44,11,73,1,161,102,14,238>> = S2,
ok.
d() ->
@@ -684,3 +930,313 @@ unaligned_sub_bitstr(Bin0) when is_bitstring(Bin0) ->
id(I) -> I.
+
+%% Benchmarks for phash2
+
+run_phash2_benchmarks() ->
+ Benchmarks = [
+ test_phash2_large_map,
+ test_phash2_shallow_long_list,
+ test_phash2_deep_list,
+ test_phash2_deep_tuple,
+ test_phash2_deep_tiny,
+ test_phash2_with_42,
+ test_phash2_with_short_tuple,
+ test_phash2_with_short_list,
+ test_phash2_with_tiny_bin,
+ test_phash2_with_tiny_unaligned_sub_binary,
+ test_phash2_with_small_unaligned_sub_binary,
+ test_phash2_with_large_bin,
+ test_phash2_with_large_unaligned_sub_binary,
+ test_phash2_with_super_large_unaligned_sub_binary
+ ],
+ [print_comment(B) || B <- Benchmarks].
+
+
+print_comment(FunctionName) ->
+ io:format("~p~n", [FunctionName]),
+ io:format("~s~n", [element(2, erlang:apply(?MODULE, FunctionName, [[]]))]).
+
+nr_of_iters(BenchmarkNumberOfIterations, Config) ->
+ case lists:member(phash2_benchmark_tests, Config) of
+ true -> 1;
+ false -> BenchmarkNumberOfIterations
+ end.
+
+
+test_phash2_large_map(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {1000000, 121857429};
+ _ ->
+ {1000, 66609305}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(45, Config),
+ get_map(Size),
+ ExpectedHash).
+
+test_phash2_shallow_long_list(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {1000000, 78700388};
+ _ ->
+ {1000, 54749638}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ lists:duplicate(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_list(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {500000, 17986444};
+ _ ->
+ {1000, 81794308}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ make_deep_list(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_tuple(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {500000, 116594715};
+ _ ->
+ {500, 109057352}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(1, Config),
+ make_deep_tuple(Size, get_complex_tuple()),
+ ExpectedHash).
+
+test_phash2_deep_tiny(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(1000000, Config),
+ make_deep_list(19, 42),
+ 111589624).
+
+test_phash2_with_42(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(20000000, Config),
+ 42,
+ 30328728).
+
+test_phash2_with_short_tuple(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ {a,b,<<"hej">>, "hej"},
+ 50727199).
+
+test_phash2_with_short_list(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ [a,b,"hej", "hello"],
+ 117108642).
+
+test_phash2_with_tiny_bin(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(20000000, Config),
+ make_random_bin(10),
+ 129616602).
+
+test_phash2_with_tiny_unaligned_sub_binary(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(10000000, Config),
+ make_unaligned_sub_binary(make_random_bin(11)),
+ 59364725).
+
+test_phash2_with_small_unaligned_sub_binary(Config) when is_list(Config) ->
+ run_phash2_test_and_benchmark(nr_of_iters(400000, Config),
+ make_unaligned_sub_binary(make_random_bin(1001)),
+ 130388119).
+
+test_phash2_with_large_bin(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {10000000, 48249379};
+ _ ->
+ {1042, 14679520}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(150, Config),
+ make_random_bin(Size),
+ ExpectedHash).
+
+test_phash2_with_large_unaligned_sub_binary(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {10000001, 122836437};
+ _ ->
+ {10042, 127144287}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(50, Config),
+ make_unaligned_sub_binary(make_random_bin(Size)),
+ ExpectedHash).
+
+test_phash2_with_super_large_unaligned_sub_binary(Config) when is_list(Config) ->
+ {Size, ExpectedHash} =
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem > 2 ->
+ {20000001, 112086727};
+ _ ->
+ {20042, 91996619}
+ end,
+ run_phash2_test_and_benchmark(nr_of_iters(20, Config),
+ make_unaligned_sub_binary(make_random_bin(Size)),
+ ExpectedHash).
+
+make_deep_list(1, Item) ->
+ {Item, Item};
+make_deep_list(Depth, Item) ->
+ [{Item, Item}, make_deep_list(Depth - 1, Item)].
+
+make_deep_tuple(1, Item) ->
+ [Item, Item];
+make_deep_tuple(Depth, Item) ->
+ {[Item, Item], make_deep_tuple(Depth - 1, Item)}.
+
+% Helper functions for benchmarking
+
+loop(0, _) -> ok;
+loop(Iterations, Fun) ->
+ Fun(),
+ loop(Iterations - 1, Fun).
+
+run_phash2_test_and_benchmark(Iterations, Term, ExpectedHash) ->
+ Parent = self(),
+ Test =
+ fun() ->
+ Hash = erlang:phash2(Term),
+ case ExpectedHash =:= Hash of
+ false ->
+ Parent ! {got_bad_hash, Hash},
+ ExpectedHash = Hash;
+ _ -> ok
+ end
+ end,
+ Benchmark =
+ fun() ->
+ garbage_collect(),
+ {Time, _} =timer:tc(fun() -> loop(Iterations, Test) end),
+ Parent ! Time
+ end,
+ spawn(Benchmark),
+ receive
+ {got_bad_hash, Hash} ->
+ ExpectedHash = Hash;
+ Time ->
+ TimeInS = case (Time/1000000) of
+ 0.0 -> 0.0000000001;
+ T -> T
+ end,
+ IterationsPerSecond = Iterations / TimeInS,
+ notify(#event{ name = benchmark_data, data = [{value, IterationsPerSecond}]}),
+ {comment, io_lib:format("Iterations per second: ~p, Iterations ~p, Benchmark time: ~p seconds)",
+ [IterationsPerSecond, Iterations, Time/1000000])}
+ end.
+
+get_complex_tuple() ->
+ BPort = <<131,102,100,0,13,110,111,110,111,100,101,64,110,111,104,
+ 111,115,116,0,0,0,1,0>>,
+ Port = binary_to_term(BPort),
+
+ BXPort = <<131,102,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 0,0,0,24,3>>,
+ XPort = binary_to_term(BXPort),
+
+ BRef = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,110,111,104,
+ 111,115,116,0,0,0,1,255,0,0,0,0,0,0,0,0>>,
+ Ref = binary_to_term(BRef),
+
+ BXRef = <<131,114,0,3,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 2,0,0,0,155,0,0,0,0,0,0,0,0>>,
+ XRef = binary_to_term(BXRef),
+
+ BXPid = <<131,103,100,0,11,97,112,97,64,108,101,103,111,108,97,115,
+ 0,0,0,36,0,0,0,0,1>>,
+ XPid = binary_to_term(BXPid),
+
+
+ %% X = f1(), Y = f2(), Z = f3(X, Y),
+
+ %% F1 = fun f1/0, % -> abc
+ B1 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,1,0,0,0,0,100,0,1,116,97,1,98,2,195,126,
+ 58,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F1 = binary_to_term(B1),
+
+ %% F2 = fun f2/0, % -> abd
+ B2 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,2,0,0,0,0,100,0,1,116,97,2,98,3,130,152,
+ 185,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F2 = binary_to_term(B2),
+
+ %% F3 = fun f3/2, % -> {abc, abd}
+ B3 = <<131,112,0,0,0,66,2,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,3,0,0,0,0,100,0,1,116,97,3,98,7,168,160,
+ 93,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F3 = binary_to_term(B3),
+
+ %% F4 = fun () -> 123456789012345678901234567 end,
+ B4 = <<131,112,0,0,0,66,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,4,0,0,0,0,100,0,1,116,97,4,98,2,230,21,
+ 171,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0>>,
+ F4 = binary_to_term(B4),
+
+ %% F5 = fun() -> {X,Y,Z} end,
+ B5 = <<131,112,0,0,0,92,0,215,206,77,69,249,50,170,17,129,47,21,98,
+ 13,196,76,242,0,0,0,5,0,0,0,3,100,0,1,116,97,5,98,0,99,101,
+ 130,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,
+ 115,116,0,0,0,112,0,0,0,0,0,100,0,3,97,98,99,100,0,3,97,98,
+ 100,104,2,100,0,3,97,98,99,100,0,3,97,98,100>>,
+ F5 = binary_to_term(B5),
+ {{1,{2}},an_atom, 1, 3434.923942394,<<"this is a binary">>,
+ make_unaligned_sub_binary(<<"this is also a binary">>),c,d,e,f,g,h,i,j,k,l,[f],
+ 999999999999999999666666662123123123123324234999999999999999, 234234234,
+ BPort, Port, BXPort, XPort, BRef, Ref, BXRef, XRef, BXPid, XPid, F1, F2, F3, F4, F5,
+ #{a => 1, b => 2, c => 3, d => 4, e => 5, f => 6, g => 7, h => 8, i => 9,
+ j => 1, k => 1, l => 123123123123213, m => [1,2,3,4,5,6,7,8], o => 5, p => 6,
+ q => 7, r => 8, s => 9}}.
+
+get_map_helper(MapSoFar, 0) ->
+ MapSoFar;
+get_map_helper(MapSoFar, NumOfItemsToAdd) ->
+ NewMapSoFar = maps:put(NumOfItemsToAdd, NumOfItemsToAdd, MapSoFar),
+ get_map_helper(NewMapSoFar, NumOfItemsToAdd -1).
+
+get_map(Size) ->
+ get_map_helper(#{}, Size).
+
+
+%% Copied from binary_SUITE
+make_unaligned_sub_binary(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin.
+
+make_unaligned_sub_bitstring(Bin0) ->
+ Bin1 = <<0:3,Bin0/bitstring,31:5>>,
+ Sz = erlang:bit_size(Bin0),
+ <<0:3,Bin:Sz/bitstring,31:5>> = id(Bin1),
+ Bin.
+
+make_random_bin(Size) ->
+ make_random_bin(Size, []).
+
+make_random_bin(0, Acc) ->
+ iolist_to_binary(Acc);
+make_random_bin(Size, []) ->
+ make_random_bin(Size - 1, [simple_rand() rem 256]);
+make_random_bin(Size, [N | Tail]) ->
+ make_random_bin(Size - 1, [simple_rand(N) rem 256, N |Tail]).
+
+simple_rand() ->
+ 123456789.
+simple_rand(Seed) ->
+ A = 1103515245,
+ C = 12345,
+ M = (1 bsl 31),
+ (A * Seed + C) rem M.
diff --git a/erts/emulator/test/hash_property_test_SUITE.erl b/erts/emulator/test/hash_property_test_SUITE.erl
new file mode 100644
index 0000000000..b4c7810a52
--- /dev/null
+++ b/erts/emulator/test/hash_property_test_SUITE.erl
@@ -0,0 +1,103 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(hash_property_test_SUITE).
+
+-export([suite/0,all/0,groups/0,init_per_suite/1,
+ end_per_suite/1,init_per_group/2,end_per_group/2]).
+
+-export([test_phash2_no_diff/1,
+ test_phash2_no_diff_long/1,
+ test_phash2_no_diff_between_versions/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() -> [{group, proper}].
+
+groups() ->
+ [{proper, [], [test_phash2_no_diff,
+ test_phash2_no_diff_long,
+ test_phash2_no_diff_between_versions]}].
+
+
+%%% First prepare Config and compile the property tests for the found tool:
+init_per_suite(Config) ->
+ ct_property_test:init_per_suite(Config).
+
+end_per_suite(Config) ->
+ Config.
+
+%%% Only proper is supported
+init_per_group(proper, Config) ->
+ case proplists:get_value(property_test_tool,Config) of
+ proper -> Config;
+ X -> {skip, lists:concat([X," is not supported"])}
+ end;
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
+test_phash2_no_diff(Config) when is_list(Config) ->
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_with_same_input(),
+ Config).
+
+test_phash2_no_diff_long(Config) when is_list(Config) ->
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_with_same_long_input(),
+ Config).
+
+test_phash2_no_diff_between_versions(Config) when is_list(Config) ->
+ R = "21",
+ case test_server:is_release_available(R) of
+ true ->
+ Rel = {release,R},
+ case test_server:start_node(rel21,peer,[{erl,[Rel]}]) of
+ {error, Reason} -> {skip, io_lib:format("Could not start node: ~p~n", [Reason])};
+ {ok, Node} ->
+ try
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_in_different_versions(Node),
+ Config),
+ true = ct_property_test:quickcheck(
+ phash2_properties:prop_phash2_same_in_different_versions_with_long_input(Node),
+ Config)
+ after
+ test_server:stop_node(Node)
+ end
+ end;
+ false ->
+ {skip, io_lib:format("Release ~s not available~n", [R])}
+ end.
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index a20f306e04..d65d0ff2fd 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -46,12 +46,17 @@ all() ->
basic(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> basic_hibernator(Info) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
ok.
+expected_heap_size(Term) ->
+ %% When hibernating, an extra word will be allocated on the stack
+ %% for a continuation pointer.
+ erts_debug:size(Term) + 1.
+
hibernate_wake_up(0, _, _) -> ok;
hibernate_wake_up(N, ExpectedHeapSz, Child) ->
{heap_size,Before} = process_info(Child, heap_size),
@@ -142,7 +147,7 @@ whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) ->
dynamic_call(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
index e62d4260f6..9741872fd8 100644
--- a/erts/emulator/test/hipe_SUITE.erl
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -131,8 +131,8 @@ t_trycatch(Config) ->
t_trycatch_1([S|Ss]) ->
io:format("~p", [S]),
compile_and_load(S),
- call_trycatch(try_catch),
- call_trycatch(plain_catch),
+ call_trycatch(),
+ call_catch(),
io:nl(),
t_trycatch_1(Ss);
t_trycatch_1([]) ->
@@ -144,38 +144,49 @@ trycatch_combine([N|Ns]) ->
trycatch_combine([]) ->
[[]].
-call_trycatch(Func) ->
- case do_call_trycatch(error, Func, {error,whatever}) of
+call_trycatch() ->
+ case trycatch_1:one_try_catch({error,whatever}) of
{error,whatever,[{trycatch_3,three,1,_}|_]} ->
ok
end,
- case do_call_trycatch(error, Func, fc) of
+ case trycatch_1:one_try_catch(fc) of
{error,function_clause,[{trycatch_3,three,[fc],_}|_]} ->
ok;
{error,function_clause,[{trycatch_3,three,1,_}|_]} ->
+ true = trycatch_3:module_info(native),
ok
end,
- case do_call_trycatch(throw, Func, {throw,{a,b}}) of
+ case trycatch_1:one_try_catch({throw,{a,b}}) of
{throw,{a,b},[{trycatch_3,three,1,_}|_]} ->
ok
end,
- case do_call_trycatch(exit, Func, {exit,{a,b,c}}) of
+ case trycatch_1:one_try_catch({exit,{a,b,c}}) of
{exit,{a,b,c},[{trycatch_3,three,1,_}|_]} ->
ok
end,
ok.
-do_call_trycatch(_Class, try_catch, Argument) ->
- trycatch_1:one_try_catch(Argument);
-do_call_trycatch(error, plain_catch, Argument) ->
- {{'EXIT',{Reason,Stk}},Stk} = trycatch_1:one_plain_catch(Argument),
- {error,Reason,Stk};
-do_call_trycatch(throw, plain_catch, Argument) ->
- {Reason,Stk} = trycatch_1:one_plain_catch(Argument),
- {throw,Reason,Stk};
-do_call_trycatch(exit, plain_catch, Argument) ->
- {{'EXIT',Reason},Stk} = trycatch_1:one_plain_catch(Argument),
- {exit,Reason,Stk}.
+call_catch() ->
+ case trycatch_1:one_plain_catch({error,whatever}) of
+ {'EXIT',{whatever,[{trycatch_3,three,1,_}|_]}} ->
+ ok
+ end,
+
+ case trycatch_1:one_plain_catch(fc) of
+ {'EXIT',{function_clause,[{trycatch_3,three,[fc],_}|_]}} ->
+ ok;
+ {'EXIT',{function_clause,[{trycatch_3,three,1,_}|_]}} ->
+ true = trycatch_3:module_info(native)
+ end,
+ case trycatch_1:one_plain_catch({throw,{a,b}}) of
+ {a,b} ->
+ ok
+ end,
+ case trycatch_1:one_plain_catch({exit,{a,b,c}}) of
+ {'EXIT',{a,b,c}} ->
+ ok
+ end,
+ ok.
compile_and_load(Sources) ->
_ = [begin
diff --git a/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl b/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
index 702b14b5b9..f7d0e3bd1e 100644
--- a/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
+++ b/erts/emulator/test/hipe_SUITE_data/trycatch_1.erl
@@ -5,10 +5,9 @@ one_try_catch(Term) ->
try
trycatch_2:two(Term)
catch
- C:R ->
- Stk = erlang:get_stacktrace(),
+ C:R:Stk ->
{C,R,Stk}
end.
one_plain_catch(Term) ->
- {catch trycatch_2:two(Term),erlang:get_stacktrace()}.
+ catch trycatch_2:two(Term).
diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl
index 4ddcd0f60b..68773e8611 100644
--- a/erts/emulator/test/list_bif_SUITE.erl
+++ b/erts/emulator/test/list_bif_SUITE.erl
@@ -156,22 +156,18 @@ t_list_to_ext_pidportref(Config) when is_list(Config) ->
Port2 = list_to_port(PortStr),
Ref2 = list_to_ref(RefStr),
- %% The local roundtrips of externals does not work
- %% as 'creation' is missing in the string formats and we don't know
- %% the 'creation' of the connected node.
- false = (Pid =:= Pid2),
- false = (Port =:= Port2),
- false = (Ref =:= Ref2),
-
- %% Local roundtrip kind of "works" for '==' since OTP-22.0 (bf7c722bd3b)
- %% Operator '==' treats 0-creations as wildcards
- %% which breaks term transitivity (A==B and B==C => B==C).
+ %% Local roundtrips of externals work from OTP-23
+ %% as even though 'creation' is missing in the string formats
+ %% we know the 'creation' of the connected node and list_to_* use that.
+ true = (Pid =:= Pid2),
+ true = (Port =:= Port2),
+ true = (Ref =:= Ref2),
true = (Pid == Pid2),
true = (Port == Port2),
true = (Ref == Ref2),
- %% It works when sent back to node with matching name, as 0-creations
- %% will be converted to the local node creation.
+ %% And it works when sent back to the same node instance,
+ %% which was connected when list_to_* were called.
true = rpc:call(Node, erlang, '=:=', [Pid, Pid2]),
true = rpc:call(Node, erlang, '==', [Pid, Pid2]),
true = rpc:call(Node, erlang, '=:=', [Port, Port2]),
@@ -179,9 +175,57 @@ t_list_to_ext_pidportref(Config) when is_list(Config) ->
true = rpc:call(Node, erlang, '=:=', [Ref, Ref2]),
true = rpc:call(Node, erlang, '==', [Ref, Ref2]),
+ %% Make sure no ugly comparison with 0-creation as wildcard is done.
+ Pid0 = make_0_creation(Pid),
+ Port0 = make_0_creation(Port),
+ Ref0 = make_0_creation(Ref),
+ false = (Pid =:= Pid0),
+ false = (Port =:= Port0),
+ false = (Ref =:= Ref0),
+ false = (Pid == Pid0),
+ false = (Port == Port0),
+ false = (Ref == Ref0),
+
+ %% Check 0-creations are converted to local node creations
+ %% when sent to matching node name.
+ true = rpc:call(Node, erlang, '=:=', [Pid, Pid0]),
+ true = rpc:call(Node, erlang, '==', [Pid, Pid0]),
+ true = rpc:call(Node, erlang, '=:=', [Port, Port0]),
+ true = rpc:call(Node, erlang, '==', [Port, Port0]),
+ true = rpc:call(Node, erlang, '=:=', [Ref, Ref0]),
+ true = rpc:call(Node, erlang, '==', [Ref, Ref0]),
+
slave:stop(Node),
ok.
+-define(NEW_PID_EXT, 88).
+-define(NEW_PORT_EXT, 89).
+-define(NEWER_REFERENCE_EXT, 90).
+
+%% Copy pid/port/ref but set creation=0
+make_0_creation(X) when is_pid(X); is_port(X); is_reference(X) ->
+ B = term_to_binary(X),
+ Sz = byte_size(B),
+ B2 = case B of
+ <<131, ?NEW_PID_EXT, _/binary>> ->
+ PreSz = Sz - 4,
+ <<_:PreSz/binary, Cr:32>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32>>;
+ <<131, ?NEW_PORT_EXT, _/binary>> ->
+ PreSz = Sz - 4,
+ <<_:PreSz/binary, Cr:32>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32>>;
+ <<131, ?NEWER_REFERENCE_EXT, Len:16, _/binary>> ->
+ PostSz = Len*4,
+ PreSz = Sz - (4 + PostSz),
+ <<_:PreSz/binary, Cr:32, PostFix:PostSz/binary>> = B,
+ true = (Cr =/= 0),
+ <<B:PreSz/binary, 0:32, PostFix/binary>>
+ end,
+ binary_to_term(B2).
+
%% Test list_to_float/1 with correct and incorrect arguments.
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index f19047ba71..18d72cc16a 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -32,8 +32,7 @@
t_driver_ready_input_output/1,
t_driver_timeout/1,
t_driver_caller/1,
- t_driver_flush/1,
- t_scheduler_poll/1]).
+ t_driver_flush/1]).
-export([ets_load/0]).
@@ -43,7 +42,7 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
-all() ->
+all() ->
[t_lttng_list,
t_memory_carrier,
t_carrier_pool,
@@ -52,9 +51,7 @@ all() ->
t_driver_control,
t_driver_timeout,
t_driver_caller,
- t_driver_flush,
- t_scheduler_poll].
-
+ t_driver_flush].
init_per_suite(Config) ->
case erlang:system_info(dynamic_trace) of
@@ -88,8 +85,6 @@ end_per_testcase(Case, _Config) ->
%% org_erlang_otp:carrier_pool_put
%% org_erlang_otp:carrier_destroy
%% org_erlang_otp:carrier_create
-%% org_erlang_otp:aio_pool_put
-%% org_erlang_otp:aio_pool_get
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_call
%% org_erlang_otp:driver_finish
@@ -105,7 +100,6 @@ end_per_testcase(Case, _Config) ->
%% org_erlang_otp:driver_outputv
%% org_erlang_otp:driver_init
%% org_erlang_otp:driver_start
-%% org_erlang_otp:scheduler_poll
%%
%% Testcases
@@ -264,35 +258,6 @@ t_driver_caller(Config) ->
ok = check_tracepoint("org_erlang_otp:driver_init", Res),
ok = check_tracepoint("org_erlang_otp:driver_finish", Res),
ok.
-
-%% org_erlang_otp:scheduler_poll
-t_scheduler_poll(Config) ->
- ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
-
- N = 100,
-
- Me = self(),
- Pid = spawn_link(fun() -> tcp_server(Me, {active, N*2}) end),
- receive {Pid, accept} -> ok end,
-
- %% We want to create a scenario where the fd is moved into a scheduler
- %% pollset, this means we have to send many small packages to the
- %% same socket, but not fast enough for them to all arrive at the
- %% same time.
- {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
- [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
-
- ok = memory_load(),
-
- [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
-
- ok = gen_tcp:close(Sock),
- Pid ! die,
- receive {Pid, done} -> ok end,
-
- Res = lttng_stop_and_view(Config),
- ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
- ok.
%% org_erlang_otp:driver_flush
t_driver_flush(Config) ->
@@ -333,24 +298,6 @@ chk_caller(Port, Callback, ExpectedCaller) ->
ExpectedCaller = Caller
end.
-memory_load() ->
- Me = self(),
- Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- timer:sleep(50),
- Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
- timer:sleep(500),
- ok.
-
-memory_loop(Parent, N, Bin) ->
- memory_loop(Parent, N, Bin, []).
-
-memory_loop(Parent, 0, _Bin, _) ->
- Parent ! {self(), done};
-memory_loop(Parent, N, Bin0, Ls) ->
- Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
- memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
-
ets_load(Config) ->
%% Have to do on a fresh node to guarantee that carriers are created
@@ -429,8 +376,6 @@ txt() ->
"%% org_erlang_otp:carrier_pool_put\n"
"%% org_erlang_otp:carrier_destroy\n"
"%% org_erlang_otp:carrier_create\n"
- "%% org_erlang_otp:aio_pool_put\n"
- "%% org_erlang_otp:aio_pool_get\n"
"%% org_erlang_otp:driver_control\n"
"%% org_erlang_otp:driver_call\n"
"%% org_erlang_otp:driver_finish\n"
@@ -445,8 +390,7 @@ txt() ->
"%% org_erlang_otp:driver_output\n"
"%% org_erlang_otp:driver_outputv\n"
"%% org_erlang_otp:driver_init\n"
- "%% org_erlang_otp:driver_start\n"
- "%% org_erlang_otp:scheduler_poll">>.
+ "%% org_erlang_otp:driver_start">>.
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
@@ -464,12 +408,6 @@ have_carriers(Alloc) ->
_ -> true
end.
-have_async_threads() ->
- Tps = erlang:system_info(thread_pool_size),
- if Tps =:= 0 -> false;
- true -> true
- end.
-
%% lttng
lttng_stop_and_view(Config) ->
Path = proplists:get_value(priv_dir, Config),
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 4b638b9082..dbf6fa58ed 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -1872,15 +1872,18 @@ t_bif_map_get(Config) when is_list(Config) ->
"v3" = maps:get(<<"k2">>, M1),
%% error cases
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:get/2 to map_get.
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badmap,T},_}} =
(catch maps:get(a, T))
end),
- {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badkey,{1,1}},_}} =
(catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
- {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})),
- {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} =
+ {'EXIT',{{badkey,a},_}} = (catch maps:get(a, #{})),
+ {'EXIT',{{badkey,a},_}} =
(catch maps:get(a, #{b=>1, c=>2})),
ok.
@@ -1942,8 +1945,11 @@ t_bif_map_is_key(Config) when is_list(Config) ->
false = maps:is_key(1.0, maps:put(1, "number", M1)),
%% error case
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:is_key/2 to is_map_key.
do_badmap(fun(T) ->
- {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} =
+ {'EXIT',{{badmap,T},_}} =
(catch maps:is_key(a, T))
end),
ok.
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 21de6b1002..686b431876 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -198,7 +198,8 @@ caller_and_return_to(Config) ->
{trace,Tracee,call,{?MODULE,do_the_put,[test]},{?MODULE,do_put,1}},
{trace,Tracee,call,{erlang,integer_to_list,[1]},{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_the_put,1}},
- {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_put,1}},
+ {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_the_put,1}},
+ {trace,Tracee,return_to,{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_put,1}},
%% These last trace messages are a bit strange...
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index 1816dc6798..c736cfa7dd 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -28,8 +28,10 @@ LIBS = @ERTS_LIBS@
all: $(NIF_LIBS)
+WSL=@WSL@
+
mtx_SUITE.c: force_rebuild
- touch mtx_SUITE.c
+ $(WSL) touch mtx_SUITE.c
force_rebuild:
echo "Force rebuild to compensate for emulator type dependencies"
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 6a8f7607cd..009c3dc7ac 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -33,6 +33,7 @@
init_per_testcase/2, end_per_testcase/2,
basic/1, reload_error/1, upgrade/1, heap_frag/1,
t_on_load/1,
+ load_traced_nif/1,
select/1, select_steal/1,
monitor_process_a/1,
monitor_process_b/1,
@@ -93,6 +94,7 @@ all() ->
{group, monitor},
monitor_frenzy,
hipe,
+ load_traced_nif,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
threading, send, send2, send3,
@@ -500,6 +502,47 @@ t_on_load(Config) when is_list(Config) ->
verify_tmpmem(TmpMem),
ok.
+%% Test load of module where a NIF stub is already traced.
+load_traced_nif(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+
+ Tracee = spawn_link(fun Loop() -> receive {lib_version,ExpRet} ->
+ ExpRet = nif_mod:lib_version()
+ end,
+ Loop()
+ end),
+ 1 = erlang:trace_pattern({nif_mod,lib_version,0}, true, [local]),
+ 1 = erlang:trace(Tracee, true, [call]),
+
+ Tracee ! {lib_version, undefined},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+
+ Tracee ! {lib_version, 1},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ %% Wait for NIF loading to finish and write final call_nif instruction
+ timer:sleep(500),
+
+ Tracee ! {lib_version, 1},
+ {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000),
+
+ true = erlang:delete_module(nif_mod),
+ true = erlang:purge_module(nif_mod),
+
+ unlink(Tracee),
+ exit(Tracee, kill),
+
+ verify_tmpmem(TmpMem),
+ ok.
+
+
-define(ERL_NIF_SELECT_READ, (1 bsl 0)).
-define(ERL_NIF_SELECT_WRITE, (1 bsl 1)).
-define(ERL_NIF_SELECT_STOP, (1 bsl 2)).
@@ -2520,26 +2563,7 @@ dummy_call(_) ->
ok.
tmpmem() ->
- case erlang:system_info({allocator,temp_alloc}) of
- false -> undefined;
- MemInfo ->
- MSBCS = lists:foldl(
- fun ({instance, 0, _}, Acc) ->
- Acc; % Ignore instance 0
- ({instance, _, L}, Acc) ->
- {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
- {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
- end,
- [],
- MemInfo),
- lists:foldl(
- fun(L, {Bl0,BlSz0}) ->
- {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L),
- {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
- {Bl0+Bl,BlSz0+BlSz}
- end, {0,0}, MSBCS)
- end.
+ erts_debug:alloc_blocks_size(temp_alloc).
verify_tmpmem(MemInfo) ->
%%wait_for_test_procs(),
diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src
index de06026780..69dd2d6757 100644
--- a/erts/emulator/test/nif_SUITE_data/Makefile.src
+++ b/erts/emulator/test/nif_SUITE_data/Makefile.src
@@ -24,7 +24,8 @@ tsd@dll@: tester.c testcase_driver.h
DRIVER_DIR = ../erl_drv_thread_SUITE_data
+WSL=@WSL@
+
basic.c rwlock.c tsd.c: $(DRIVER_DIR)/$@
- cat head.txt > $@
- cat $(DRIVER_DIR)/$@ | sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' >> $@
- cat tail.txt >> $@
+ $(WSL) sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' $(DRIVER_DIR)/$@ > $@.tmp
+ $(WSL) cat head.txt $@.tmp tail.txt > $@
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index ff47cfe500..f4bb1504b8 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -3634,7 +3634,6 @@ static ErlNifFunc nif_funcs[] =
{"release_resource", 1, release_resource},
{"release_resource_from_thread", 1, release_resource_from_thread},
{"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif},
- {"make_new_resource", 2, make_new_resource},
{"check_is", 11, check_is},
{"check_is_exception", 0, check_is_exception},
{"length_test", 6, length_test},
diff --git a/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h b/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
index 4b2b7550e5..90ed8da0b6 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
+++ b/erts/emulator/test/nif_SUITE_data/nif_api_2_0/erl_nif.h
@@ -164,11 +164,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
#else
# define ERL_NIF_INIT_GLOB
# define ERL_NIF_INIT_BODY
-# if defined(VXWORKS)
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _init(void)
-# else
# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
-# endif
#endif
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 8b1519ae36..d4c74579e2 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -22,6 +22,11 @@
-include_lib("common_test/include/ct.hrl").
+%% This suite alters the return values of functions which breaks certain
+%% assumptions made by the compiler, so we have to turn off module-level type
+%% optimization to be safe.
+-compile(no_module_opt).
+
-export([all/0, suite/0,
error_handler/1,error_handler_apply/1,
error_handler_fixed_apply/1,error_handler_fun/1,
diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl
index c9874e5679..6682af489a 100644
--- a/erts/emulator/test/persistent_term_SUITE.erl
+++ b/erts/emulator/test/persistent_term_SUITE.erl
@@ -23,6 +23,7 @@
-export([all/0,suite/0,init_per_suite/1,end_per_suite/1,
basic/1,purging/1,sharing/1,get_trapping/1,
+ destruction/1,
info/1,info_trapping/1,killed_while_trapping/1,
off_heap_values/1,keys/1,collisions/1,
init_restart/1, put_erase_trapping/1,
@@ -38,11 +39,13 @@ suite() ->
all() ->
[basic,purging,sharing,get_trapping,info,info_trapping,
+ destruction,
killed_while_trapping,off_heap_values,keys,collisions,
init_restart, put_erase_trapping, killed_while_trapping_put,
killed_while_trapping_erase].
init_per_suite(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
%% Put a term in the dict so that we know that the testcases handle
%% stray terms left by stdlib or other test suites.
persistent_term:put(init_per_suite, {?MODULE}),
@@ -50,6 +53,7 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
persistent_term:erase(init_per_suite),
+ erts_debug:set_internal_state(available_internal_state, false),
Config.
basic(_Config) ->
@@ -152,19 +156,25 @@ purging_tester(Parent, Key) ->
receive
{Parent,erased} ->
{'EXIT',{badarg,_}} = (catch persistent_term:get(Key)),
- purging_tester_1(Term);
+ purging_tester_1(Term, 1);
{Parent,replaced} ->
{?MODULE,new} = persistent_term:get(Key),
- purging_tester_1(Term)
+ purging_tester_1(Term, 1)
end.
%% Wait for the term to be copied into this process.
-purging_tester_1(Term) ->
+purging_tester_1(Term, Timeout) ->
purging_check_term(Term),
- receive after 1 -> ok end,
+ receive after Timeout -> ok end,
case erts_debug:size_shared(Term) of
0 ->
- purging_tester_1(Term);
+ case Timeout of
+ 1000 ->
+ flush_later_ops(),
+ purging_tester_1(Term, 1);
+ _ ->
+ purging_tester_1(Term, Timeout*10)
+ end;
Size ->
%% The term has been copied into this process.
purging_check_term(Term),
@@ -174,6 +184,83 @@ purging_tester_1(Term) ->
purging_check_term({term,[<<"abc",0:777/unit:8>>]}) ->
ok.
+%% Make sure terms are really deallocated when overwritten or erased.
+destruction(Config) ->
+ ok = erts_test_destructor:init(Config),
+
+ NKeys = 100,
+ Keys = lists:seq(0,NKeys-1),
+ [begin
+ V = erts_test_destructor:send(self(), K),
+ persistent_term:put({?MODULE,K}, V)
+ end
+ || K <- Keys],
+
+ %% Erase or overwrite all keys in "random" order.
+ lists:foldl(fun(_, K) ->
+ case erlang:phash2(K) band 1 of
+ 0 ->
+ %%io:format("erase key ~p\n", [K]),
+ persistent_term:erase({?MODULE,K});
+ 1 ->
+ %%io:format("replace key ~p\n", [K]),
+ persistent_term:put({?MODULE,K}, value)
+ end,
+ (K + 13) rem NKeys
+ end,
+ 17, Keys),
+
+ destruction_1(Keys).
+
+destruction_1(Keys) ->
+ erlang:garbage_collect(),
+
+ %% Receive all destruction messages
+ MsgLst = destruction_recv(length(Keys), [], 2),
+ ok = case lists:sort(MsgLst) of
+ Keys ->
+ ok;
+ _ ->
+ io:format("GOT ~p\n", [MsgLst]),
+ io:format("MISSING ~p\n", [Keys -- MsgLst]),
+ error
+ end,
+
+ %% Cleanup all remaining
+ [persistent_term:erase({?MODULE,K}) || K <- Keys],
+ ok.
+
+destruction_recv(0, Acc, _) ->
+ Acc;
+destruction_recv(N, Acc, Flush) ->
+ receive M ->
+ destruction_recv(N-1, [M | Acc], Flush)
+ after 1000 ->
+ io:format("TIMEOUT. Missing ~p destruction messages.\n", [N]),
+ case Flush of
+ 0 ->
+ Acc;
+ _ ->
+ io:format("Try flush last literal area cleanup...\n"),
+ flush_later_ops(),
+ destruction_recv(N, Acc, Flush-1)
+ end
+ end.
+
+%% Both persistent_term itself and erts_literal_are_collector use
+%% erts_schedule_thr_prgr_later_cleanup_op() to schedule purge and deallocation
+%% of literals. To avoid waiting forever on sleeping schedulers we flush
+%% all later ops to make these cleanup jobs go through.
+flush_later_ops() ->
+ try
+ erts_debug:set_internal_state(wait, thread_progress)
+ catch
+ error:system_limit ->
+ ok % already ongoing; called by other process
+ end,
+ ok.
+
+
%% Test that sharing is preserved when storing terms.
sharing(_Config) ->
@@ -517,17 +604,12 @@ colliding_keys() ->
%% Verify that the keys still collide (this will fail if the
%% internal hash function has been changed).
- erts_debug:set_internal_state(available_internal_state, true),
- try
- case erlang:system_info(wordsize) of
- 8 ->
- verify_colliding_keys(L);
- 4 ->
- %% Not guaranteed to collide on a 32-bit system.
- ok
- end
- after
- erts_debug:set_internal_state(available_internal_state, false)
+ case erlang:system_info(wordsize) of
+ 8 ->
+ verify_colliding_keys(L);
+ 4 ->
+ %% Not guaranteed to collide on a 32-bit system.
+ ok
end,
L.
@@ -611,19 +693,25 @@ chk({Info, _Initial} = Chk) ->
ok = persistent_term:put(Key, {term,Info}),
Term = persistent_term:get(Key),
true = persistent_term:erase(Key),
- chk_not_stuck(Term),
+ chk_not_stuck(Term, 1),
[persistent_term:erase(K) || {K, _} <- pget(Chk)],
ok.
-chk_not_stuck(Term) ->
+chk_not_stuck(Term, Timeout) ->
%% Hash tables to be deleted are put onto a queue.
%% Make sure that the queue isn't stuck by a table with
%% a non-zero ref count.
case erts_debug:size_shared(Term) of
0 ->
- erlang:yield(),
- chk_not_stuck(Term);
+ receive after Timeout -> ok end,
+ case Timeout of
+ 1000 ->
+ flush_later_ops(),
+ chk_not_stuck(Term, 1);
+ _ ->
+ chk_not_stuck(Term, Timeout*10)
+ end;
_ ->
ok
end.
@@ -633,7 +721,6 @@ pget({_, Initial}) ->
killed_while_trapping_put(_Config) ->
- erts_debug:set_internal_state(available_internal_state, true),
repeat(
fun() ->
NrOfPutsInChild = 10000,
@@ -647,10 +734,9 @@ killed_while_trapping_put(_Config) ->
do_erases(NrOfPutsInChild)
end,
10),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
killed_while_trapping_erase(_Config) ->
- erts_debug:set_internal_state(available_internal_state, true),
repeat(
fun() ->
NrOfErases = 2500,
@@ -664,15 +750,14 @@ killed_while_trapping_erase(_Config) ->
do_erases(NrOfErases)
end,
10),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
put_erase_trapping(_Config) ->
NrOfItems = 5000,
- erts_debug:set_internal_state(available_internal_state, true),
do_puts(NrOfItems, first),
do_puts(NrOfItems, second),
do_erases(NrOfItems),
- erts_debug:set_internal_state(available_internal_state, false).
+ ok.
do_puts(0, _) -> ok;
do_puts(NrOfPuts, ValuePrefix) ->
diff --git a/erts/emulator/test/persistent_term_SUITE_data/Makefile.src b/erts/emulator/test/persistent_term_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..29b7fcb647
--- /dev/null
+++ b/erts/emulator/test/persistent_term_SUITE_data/Makefile.src
@@ -0,0 +1,8 @@
+
+NIF_LIBS = erts_test_destructor@dll@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
+
+$(NIF_LIBS): erts_test_destructor.c
diff --git a/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c b/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c
new file mode 100644
index 0000000000..808334f1c4
--- /dev/null
+++ b/erts/emulator/test/persistent_term_SUITE_data/erts_test_destructor.c
@@ -0,0 +1,83 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <erl_nif.h>
+
+#include <stdio.h>
+
+
+static ErlNifResourceType* resource_type;
+static void resource_dtor(ErlNifEnv* env, void* obj);
+
+typedef struct {
+ ErlNifPid to;
+ ERL_NIF_TERM msg;
+ ErlNifEnv* msg_env;
+} DtorSender;
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ resource_type = enif_open_resource_type(env,NULL,"DtorSender",resource_dtor,
+ ERL_NIF_RT_CREATE, NULL);
+ return 0;
+}
+
+static ERL_NIF_TERM is_loaded_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_atom(env, "true");
+}
+
+static ERL_NIF_TERM send_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ DtorSender *p;
+ ErlNifPid pid;
+ ERL_NIF_TERM res;
+
+ p = enif_alloc_resource(resource_type, sizeof(DtorSender));
+
+ if (!enif_get_local_pid(env, argv[0], &p->to)) {
+ p->msg_env = NULL;
+ enif_release_resource(p);
+ return enif_make_badarg(env);
+ }
+ p->msg_env = enif_alloc_env();
+ p->msg = enif_make_copy(p->msg_env, argv[1]);
+ res = enif_make_resource(env, p);
+ enif_release_resource(p);
+ return res;
+}
+
+static void resource_dtor(ErlNifEnv* env, void* obj)
+{
+ DtorSender *p = (DtorSender*)obj;
+
+ if (p->msg_env) {
+ enif_send(env, &p->to, p->msg_env, p->msg);
+ enif_free(p->msg_env);
+ }
+}
+
+
+static ErlNifFunc nif_funcs[] =
+{
+ {"is_loaded", 0, is_loaded_nif},
+ {"send", 2, send_nif}
+};
+
+ERL_NIF_INIT(erts_test_destructor,nif_funcs,load,NULL,NULL,NULL)
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index eb9b94a316..8a67bf7512 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1052,7 +1052,9 @@ huge_env(Config) when is_list(Config) ->
%% Test to spawn program with command payload buffer
%% just around pipe capacity (9f779819f6bda734c5953468f7798)
pipe_limit_env(Config) when is_list(Config) ->
+ WSL = os:getenv("WSLENV") =/= false,
Cmd = case os:type() of
+ {win32,_} when WSL -> "cmd.exe /q /c wsl true";
{win32,_} -> "cmd /q /c true";
_ -> "true"
end,
@@ -1706,7 +1708,11 @@ spawn_executable(Config) when is_list(Config) ->
ok.
unregister_name(Config) when is_list(Config) ->
- true = register(crash, open_port({spawn, "sleep 100"}, [])),
+ Cmd = case os:getenv("WSLENV") of
+ false -> "sleep 5";
+ _ -> "wsl.exe sleep 5"
+ end,
+ true = register(crash, open_port({spawn, Cmd}, [])),
true = unregister(crash).
test_bat_file(Dir) ->
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 0e1c15e160..a6210a3ca2 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -63,13 +63,24 @@
system_task_failed_enqueue/1,
gc_request_when_gc_disabled/1,
gc_request_blast_when_gc_disabled/1,
- otp_16436/1]).
+ otp_16436/1,
+ spawn_huge_arglist/1,
+ spawn_request_bif/1,
+ spawn_request_monitor_demonitor/1,
+ spawn_request_monitor_child_exit/1,
+ spawn_request_link_child_exit/1,
+ spawn_request_link_parent_exit/1,
+ spawn_request_abandon_bif/1,
+ dist_spawn_monitor/1,
+ spawn_old_node/1,
+ spawn_new_node/1,
+ spawn_request_reply_option/1]).
-export([prio_server/2, prio_client/2, init/1, handle_event/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([hangaround/2, processes_bif_test/0, do_processes/1,
- processes_term_proc_list_test/1]).
+ processes_term_proc_list_test/1, huge_arglist_child/255]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -90,7 +101,19 @@ all() ->
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
- spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237,
+ spawn_opt_heap_size, spawn_opt_max_heap_size,
+ spawn_huge_arglist,
+ spawn_request_bif,
+ spawn_request_monitor_demonitor,
+ spawn_request_monitor_child_exit,
+ spawn_request_link_child_exit,
+ spawn_request_link_parent_exit,
+ spawn_request_abandon_bif,
+ dist_spawn_monitor,
+ spawn_old_node,
+ spawn_new_node,
+ spawn_request_reply_option,
+ otp_6237,
{group, processes_bif},
{group, otp_7738}, garb_other_running,
{group, system_task}].
@@ -2250,7 +2273,7 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger)
when is_map(Option); is_integer(Option) ->
max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger);
max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
- OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end,
+ OomFun = fun () -> oom_fun([]) end,
Pid = spawn_opt(OomFun, Option),
{max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size),
ct:log("Default: ~p~nOption: ~p~nProc: ~p~n",
@@ -2293,6 +2316,13 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
%% Make sure that there are no unexpected messages.
receive_unexpected().
+oom_fun(Acc0) ->
+ %% This is tail-recursive since the compiler is smart enough to figure
+ %% out that a body-recursive variant never returns, and loops forever
+ %% without keeping the list alive.
+ timer:sleep(5),
+ oom_fun([lists:seq(1, 1000) | Acc0]).
+
receive_error_messages(Pid) ->
receive
{error, _, {emulator, _, [Pid|_]}} ->
@@ -2327,6 +2357,959 @@ handle_event(Event, Pid) ->
Pid ! Event,
{ok, Pid}.
+huge_arglist_child(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
+ A20, A21, A22, A23, A24, A25, A26, A27, A28, A29,
+ A30, A31, A32, A33, A34, A35, A36, A37, A38, A39,
+ A40, A41, A42, A43, A44, A45, A46, A47, A48, A49,
+ A50, A51, A52, A53, A54, A55, A56, A57, A58, A59,
+ A60, A61, A62, A63, A64, A65, A66, A67, A68, A69,
+ A70, A71, A72, A73, A74, A75, A76, A77, A78, A79,
+ A80, A81, A82, A83, A84, A85, A86, A87, A88, A89,
+ A90, A91, A92, A93, A94, A95, A96, A97, A98, A99,
+ A100, A101, A102, A103, A104, A105, A106, A107, A108, A109,
+ A110, A111, A112, A113, A114, A115, A116, A117, A118, A119,
+ A120, A121, A122, A123, A124, A125, A126, A127, A128, A129,
+ A130, A131, A132, A133, A134, A135, A136, A137, A138, A139,
+ A140, A141, A142, A143, A144, A145, A146, A147, A148, A149,
+ A150, A151, A152, A153, A154, A155, A156, A157, A158, A159,
+ A160, A161, A162, A163, A164, A165, A166, A167, A168, A169,
+ A170, A171, A172, A173, A174, A175, A176, A177, A178, A179,
+ A180, A181, A182, A183, A184, A185, A186, A187, A188, A189,
+ A190, A191, A192, A193, A194, A195, A196, A197, A198, A199,
+ A200, A201, A202, A203, A204, A205, A206, A207, A208, A209,
+ A210, A211, A212, A213, A214, A215, A216, A217, A218, A219,
+ A220, A221, A222, A223, A224, A225, A226, A227, A228, A229,
+ A230, A231, A232, A233, A234, A235, A236, A237, A238, A239,
+ A240, A241, A242, A243, A244, A245, A246, A247, A248, A249,
+ A250, A251, A252, A253, A254) ->
+ receive go -> ok end,
+ exit([A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
+ A20, A21, A22, A23, A24, A25, A26, A27, A28, A29,
+ A30, A31, A32, A33, A34, A35, A36, A37, A38, A39,
+ A40, A41, A42, A43, A44, A45, A46, A47, A48, A49,
+ A50, A51, A52, A53, A54, A55, A56, A57, A58, A59,
+ A60, A61, A62, A63, A64, A65, A66, A67, A68, A69,
+ A70, A71, A72, A73, A74, A75, A76, A77, A78, A79,
+ A80, A81, A82, A83, A84, A85, A86, A87, A88, A89,
+ A90, A91, A92, A93, A94, A95, A96, A97, A98, A99,
+ A100, A101, A102, A103, A104, A105, A106, A107, A108, A109,
+ A110, A111, A112, A113, A114, A115, A116, A117, A118, A119,
+ A120, A121, A122, A123, A124, A125, A126, A127, A128, A129,
+ A130, A131, A132, A133, A134, A135, A136, A137, A138, A139,
+ A140, A141, A142, A143, A144, A145, A146, A147, A148, A149,
+ A150, A151, A152, A153, A154, A155, A156, A157, A158, A159,
+ A160, A161, A162, A163, A164, A165, A166, A167, A168, A169,
+ A170, A171, A172, A173, A174, A175, A176, A177, A178, A179,
+ A180, A181, A182, A183, A184, A185, A186, A187, A188, A189,
+ A190, A191, A192, A193, A194, A195, A196, A197, A198, A199,
+ A200, A201, A202, A203, A204, A205, A206, A207, A208, A209,
+ A210, A211, A212, A213, A214, A215, A216, A217, A218, A219,
+ A220, A221, A222, A223, A224, A225, A226, A227, A228, A229,
+ A230, A231, A232, A233, A234, A235, A236, A237, A238, A239,
+ A240, A241, A242, A243, A244, A245, A246, A247, A248, A249,
+ A250, A251, A252, A253, A254]).
+
+spawn_huge_arglist(Config) when is_list(Config) ->
+ %% Huge in two different ways; encoded size and
+ %% length...
+ ArgListHead = [make_ref(),
+ lists:duplicate(1000000, $a),
+ <<1:8388608>>,
+ processes(),
+ erlang:ports(),
+ {hej, hopp},
+ <<17:8388608>>,
+ lists:duplicate(3000000, $x),
+ #{ a => 1, b => 2, c => 3, d => 4, e => 5}],
+ ArgList = ArgListHead ++ lists:seq(1, 255 - length(ArgListHead)),
+
+ io:format("size(term_to_binary(ArgList)) = ~p~n",
+ [size(term_to_binary(ArgList))]),
+
+ io:format("Testing spawn with huge argument list on local node...~n", []),
+ spawn_huge_arglist_test(true, node(), ArgList),
+ io:format("Testing spawn with huge argument list on local node with Node...~n", []),
+ spawn_huge_arglist_test(false, node(), ArgList),
+ {ok, Node} = start_node(Config),
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+ io:format("Testing spawn with huge argument list on remote node ~p...~n", [Node]),
+ spawn_huge_arglist_test(false, Node, ArgList),
+ stop_node(Node),
+ ok.
+
+spawn_huge_arglist_test(Local, Node, ArgList) ->
+
+ R1 = case Local of
+ true ->
+ spawn_request(?MODULE, huge_arglist_child, ArgList, [monitor]);
+ false ->
+ spawn_request(Node, ?MODULE, huge_arglist_child, ArgList, [monitor])
+ end,
+ receive
+ {spawn_reply, R1, ok, Pid1} ->
+ Pid1 ! go,
+ receive
+ {'DOWN', R1, process, Pid1, Reason1} ->
+ ArgList = Reason1
+ end
+ end,
+
+ {Pid2, R2} = case Local of
+ true ->
+ spawn_monitor(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn_monitor(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid2),
+ Pid2 ! go,
+ receive
+ {'DOWN', R2, process, Pid2, Reason2} ->
+ ArgList = Reason2
+ end,
+
+ {Pid3, R3} = case Local of
+ true ->
+ spawn_opt(?MODULE, huge_arglist_child, ArgList, [monitor]);
+ false ->
+ spawn_opt(Node, ?MODULE, huge_arglist_child, ArgList, [monitor])
+ end,
+ Node = node(Pid3),
+ Pid3 ! go,
+ receive
+ {'DOWN', R3, process, Pid3, Reason3} ->
+ ArgList = Reason3
+ end,
+
+ OldTA = process_flag(trap_exit, true),
+ Pid4 = case Local of
+ true ->
+ spawn_link(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn_link(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid4),
+ Pid4 ! go,
+ receive
+ {'EXIT', Pid4, Reason4} ->
+ ArgList = Reason4
+ end,
+
+ true = process_flag(trap_exit, OldTA),
+
+ Pid5 = case Local of
+ true ->
+ spawn(?MODULE, huge_arglist_child, ArgList);
+ false ->
+ spawn(Node, ?MODULE, huge_arglist_child, ArgList)
+ end,
+ Node = node(Pid5),
+ R5 = erlang:monitor(process, Pid5),
+ Pid5 ! go,
+ receive
+ {'DOWN', R5, process, Pid5, Reason5} ->
+ ArgList = Reason5
+ end,
+ ok.
+
+spawn_request_bif(Config) when is_list(Config) ->
+ io:format("Testing spawn_request() on local node...~n", []),
+ spawn_request_bif_test(true, node()),
+ io:format("Testing spawn_request() on local node with Node...~n", []),
+ spawn_request_bif_test(false, node()),
+ {ok, Node} = start_node(Config),
+ io:format("Testing spawn_request() on remote node ~p...~n", [Node]),
+ spawn_request_bif_test(false, Node),
+ stop_node(Node),
+ ok.
+
+spawn_request_bif_test(Local, Node) ->
+
+ Me = self(),
+
+ process_flag(trap_exit, true),
+
+ T1 = {test, 1},
+ F1 = fun () -> exit({exit, T1}) end,
+ R1 = if Local ->
+ spawn_request(F1, [{reply_tag, T1}, monitor, link]);
+ true ->
+ spawn_request(Node, F1, [{reply_tag, T1}, monitor, link])
+ end,
+ receive
+ {T1, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, {exit, T1}} ->
+ ok
+ end,
+ receive
+ {'EXIT', P1, {exit, T1}} ->
+ ok
+ end
+ end,
+
+ R1b = if Local ->
+ spawn_request(F1, [monitor, link]);
+ true ->
+ spawn_request(Node, F1, [monitor, link])
+ end,
+ receive
+ {spawn_reply, R1b, ok, P1b} ->
+ receive
+ {'DOWN', R1b, process, P1b, {exit, T1}} ->
+ ok
+ end,
+ receive
+ {'EXIT', P1b, {exit, T1}} ->
+ ok
+ end
+ end,
+
+ Ref1c = make_ref(),
+ F1c = fun () -> Me ! Ref1c end,
+ R1c = if Local ->
+ spawn_request(F1c);
+ true ->
+ spawn_request(Node, F1c)
+ end,
+ receive
+ {spawn_reply, R1c, ok, _P1c} ->
+ receive Ref1c -> ok end
+ end,
+
+ R1e = if Local ->
+ spawn_request(F1, [monitors, links, {reply_tag, T1}]);
+ true ->
+ spawn_request(Node, F1, [monitors, links, {reply_tag, T1}])
+ end,
+ receive
+ {T1, R1e, error, BadOpt1} ->
+ badopt = BadOpt1,
+ ok
+ end,
+ ok = try
+ BadF = fun (X) -> exit({X,T1}) end,
+ if Local ->
+ spawn_request(BadF, [monitor, {reply_tag, T1}, link]);
+ true ->
+ spawn_request(Node, BadF, [monitor, {reply_tag, T1}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, F1, [monitor, link], T1),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+
+ T2 = {test, 2},
+ M2 = erlang,
+ F2 = exit,
+ Reason2 = {exit, T2},
+ Args2 = [Reason2],
+ R2 = if Local ->
+ spawn_request(M2, F2, Args2, [monitor, link, {reply_tag, T2}]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitor, link, {reply_tag, T2}])
+ end,
+ receive
+ {T2, R2, ok, P2} ->
+ receive
+ {'DOWN', R2, process, P2, Reason2} ->
+ ok
+ end,
+ receive
+ {'EXIT', P2, Reason2} ->
+ ok
+ end
+ end,
+
+ R2b = if Local ->
+ spawn_request(M2, F2, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitor, link])
+ end,
+ receive
+ {spawn_reply, R2b, ok, P2b} ->
+ receive
+ {'DOWN', R2b, process, P2b, Reason2} ->
+ ok
+ end,
+ receive
+ {'EXIT', P2b, Reason2} ->
+ ok
+ end
+ end,
+
+ Ref2c = make_ref(),
+ R2c = if Local ->
+ spawn_request(erlang, send, [Me, Ref2c]);
+ true ->
+ spawn_request(Node, erlang, send, [Me, Ref2c])
+ end,
+ receive
+ {spawn_reply, R2c, ok, _P2c} ->
+ receive Ref2c -> ok end
+ end,
+
+ R2e = if Local ->
+ spawn_request(M2, F2, Args2, [monitors, {reply_tag, T2}, links]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitors, {reply_tag, T2}, links])
+ end,
+ receive
+ {T2, R2e, error, BadOpt2} ->
+ badopt = BadOpt2,
+ ok
+ end,
+
+ R2eb = if Local ->
+ spawn_request(M2, F2, Args2, [monitors, links]);
+ true ->
+ spawn_request(Node, M2, F2, Args2, [monitors, links])
+ end,
+ receive
+ {spawn_reply, R2eb, error, BadOpt2b} ->
+ badopt = BadOpt2b,
+ ok
+ end,
+
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, F2, [Args2|oops]);
+ true ->
+ spawn_request(Node, M2, F2, [Args2|oops])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link]);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2, [monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(M2, <<"exit">>, Args2);
+ true ->
+ spawn_request(Node, M2, <<"exit">>, Args2)
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link]);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2, [monitor, link]);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2, [monitor, link])
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ if Local ->
+ spawn_request(<<"erlang">>, F2, Args2);
+ true ->
+ spawn_request(Node, <<"erlang">>, F2, Args2)
+ end,
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2, [{reply_tag, T2}, monitor, link]),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2, [monitor, link]),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok = try
+ spawn_request(<<"node">>, M2, F2, Args2),
+ nok
+ catch
+ error:badarg -> ok
+ end,
+ ok.
+
+
+spawn_request_monitor_demonitor(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ BlockFun = fun () ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ erts_debug:set_internal_state(block, 1000),
+ ok
+ end,
+
+ %% Block receiver node...
+ spawn_request(Node, BlockFun, [{priority,max}, link]),
+ receive after 100 -> ok end,
+
+ erlang:display(spawning),
+ erlang:yield(),
+ R = spawn_request(Node, timer, sleep, [10000], [monitor]),
+ %% Should not be possible to demonitor
+ %% before operation has succeeded...
+ erlang:display(premature_demonitor),
+ {monitors, []} = process_info(self(), monitors),
+ false = erlang:demonitor(R, [info]), %% Should be ignored by VM...
+ erlang:display(wait_success),
+ receive
+ {spawn_reply, R, ok, P} ->
+ erlang:display(demonitor),
+ {monitors, [{process,P}]} = process_info(self(), monitors),
+ true = erlang:demonitor(R, [info]),
+ {monitors, []} = process_info(self(), monitors),
+ exit(P, kill)
+ end,
+ erlang:display(done),
+ stop_node(Node),
+ ok.
+
+spawn_request_monitor_child_exit(Config) when is_list(Config) ->
+ %% Early child exit...
+ Tag = {a, tag},
+ R1 = spawn_request(nonexisting_module, nonexisting_function, [], [monitor, {reply_tag, Tag}]),
+ receive
+ {Tag, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, Reason1} ->
+ {undef, _} = Reason1
+ end
+ end,
+ {ok, Node} = start_node(Config),
+ R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, monitor]),
+ receive
+ {Tag, R2, ok, P2} ->
+ receive
+ {'DOWN', R2, process, P2, Reason2} ->
+ {undef, _} = Reason2
+ end
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_request_link_child_exit(Config) when is_list(Config) ->
+ %% Early child exit...
+ process_flag(trap_exit, true),
+ Tag = {a, tag},
+ R1 = spawn_request(nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, link]),
+ receive
+ {Tag, R1, ok, P1} ->
+ receive
+ {'EXIT', P1, Reason1} ->
+ {undef, _} = Reason1
+ end
+ end,
+ {ok, Node} = start_node(Config),
+ R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [link, {reply_tag, Tag}]),
+ receive
+ {Tag, R2, ok, P2} ->
+ receive
+ {'EXIT', P2, Reason2} ->
+ {undef, _} = Reason2
+ end
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_request_link_parent_exit(Config) when is_list(Config) ->
+ C1 = spawn_request_link_parent_exit_test(node()),
+ {ok, Node} = start_node(Config),
+ C2 = spawn_request_link_parent_exit_test(Node),
+ stop_node(Node),
+ {comment, C1 ++ " " ++ C2}.
+
+spawn_request_link_parent_exit_test(Node) ->
+ %% Early parent exit...
+ Tester = self(),
+
+ verify_nc(node()),
+
+ %% Ensure code loaded on other node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+ ChildFun = fun () ->
+ Child = self(),
+ spawn_opt(fun () ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', Child, Reason} ->
+ Tester ! {parent_exit, Reason}
+ end
+ end, [link,{priority,max}]),
+ receive after infinity -> ok end
+ end,
+ ParentFun = case node() == Node of
+ true ->
+ fun (Wait) ->
+ spawn_request(ChildFun, [link,{priority,max}]),
+ receive after Wait -> ok end,
+ exit(kaboom)
+ end;
+ false ->
+ fun (Wait) ->
+ spawn_request(Node, ChildFun, [link,{priority,max}]),
+ receive after Wait -> ok end,
+ exit(kaboom)
+ end
+ end,
+ lists:foreach(fun (N) ->
+ spawn(fun () -> ParentFun(N rem 10) end)
+ end,
+ lists:seq(1, 1000)),
+ N = gather_parent_exits(kaboom, false),
+ Comment = case node() == Node of
+ true ->
+ C = "Got " ++ integer_to_list(N) ++ " node local kabooms!",
+ erlang:display(C),
+ C;
+ false ->
+ C = "Got " ++ integer_to_list(N) ++ " node remote kabooms!",
+ erlang:display(C),
+ true = N /= 0,
+ C
+ end,
+ Comment.
+
+spawn_request_abandon_bif(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ false = spawn_request_abandon(make_ref()),
+ false = spawn_request_abandon(spawn_request(fun () -> ok end)),
+ false = spawn_request_abandon(rpc:call(Node, erlang, make_ref, [])),
+ try
+ noreturn = spawn_request_abandon(self())
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ noreturn = spawn_request_abandon(4711)
+ catch
+ error:badarg ->
+ ok
+ end,
+
+ verify_nc(node()),
+
+ %% Ensure code loaded on other node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+
+ TotOps = 1000,
+ Tester = self(),
+
+ ChildFun = fun () ->
+ Child = self(),
+ spawn_opt(fun () ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', Child, Reason} ->
+ Tester ! {parent_exit, Reason}
+ end
+ end, [link,{priority,max}]),
+ receive after infinity -> ok end
+ end,
+ ParentFun = fun (Wait, Opts) ->
+ ReqId = spawn_request(Node, ChildFun, Opts),
+ receive after Wait -> ok end,
+ case spawn_request_abandon(ReqId) of
+ true ->
+ ok;
+ false ->
+ receive
+ {spawn_reply, ReqId, error, _} ->
+ exit(spawn_failed);
+ {spawn_reply, ReqId, ok, Pid} ->
+ unlink(Pid),
+ exit(Pid, bye)
+ after
+ 0 ->
+ exit(missing_spawn_reply)
+ end
+ end
+ end,
+ %% Parent exit early...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [link])
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ NoA1 = gather_parent_exits(abandoned, true),
+ %% Parent exit late...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [link]),
+ receive
+ {spawn_reply, _, _, _} ->
+ exit(unexpected_spawn_reply)
+ after
+ 1000 -> ok
+ end
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ NoA2 = gather_parent_exits(abandoned, true),
+ %% Parent exit early...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, [])
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ 0 = gather_parent_exits(abandoned, true),
+ %% Parent exit late...
+ lists:foreach(fun (N) ->
+ spawn_opt(fun () ->
+ ParentFun(N rem 50, []),
+ receive
+ {spawn_reply, _, _, _} ->
+ exit(unexpected_spawn_reply)
+ after
+ 1000 -> ok
+ end
+ end, [link,{priority,max}])
+ end,
+ lists:seq(1, TotOps)),
+ 0 = gather_parent_exits(abandoned, true),
+ stop_node(Node),
+ C = "Got " ++ integer_to_list(NoA1) ++ " and "
+ ++ integer_to_list(NoA2) ++ " abandoneds of 2*"
+ ++ integer_to_list(TotOps) ++ " ops!",
+ erlang:display(C),
+ true = NoA1 /= 0,
+ true = NoA1 /= TotOps,
+ true = NoA2 /= 0,
+ true = NoA2 /= TotOps,
+ {comment, C}.
+
+gather_parent_exits(Reason, AllowOther) ->
+ receive after 2000 -> ok end,
+ gather_parent_exits(Reason, AllowOther, 0).
+
+gather_parent_exits(Reason, AllowOther, N) ->
+ receive
+ {parent_exit, Reason} ->
+ gather_parent_exits(Reason, AllowOther, N+1);
+ {parent_exit, _} = ParentExit ->
+ case AllowOther of
+ false ->
+ ct:fail(ParentExit);
+ true ->
+ gather_parent_exits(Reason, AllowOther, N)
+ end
+ after 0 ->
+ N
+ end.
+dist_spawn_monitor(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {spawn_reply, R1, ok, P1} ->
+ receive
+ {'DOWN', R1, process, P1, Reason1} ->
+ hej = Reason1
+ end
+ end,
+ {P2, Mon2} = spawn_monitor(Node, erlang, exit, [hej]),
+ receive
+ {'DOWN', Mon2, process, P2, Reason2} ->
+ hej = Reason2
+ end,
+ {P3, Mon3} = spawn_opt(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {'DOWN', Mon3, process, P3, Reason3} ->
+ hej = Reason3
+ end,
+ stop_node(Node),
+ ok.
+
+spawn_old_node(Config) when is_list(Config) ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Rel = "22_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ {skipped, "No OTP 22 available"};
+ true ->
+ {ok, OldNode} = test_server:start_node(make_nodename(Config),
+ peer,
+ [{args, " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}]),
+ try
+ %% Spawns triggering a new connection; which
+ %% will trigger hopeful data transcoding
+ %% of spawn requests...
+ io:format("~n~nDoing initial connect tests...~n", []),
+ spawn_old_node_test(OldNode, true),
+ %% Spawns on an already existing connection...
+ io:format("~n~nDoing already connected tests...~n", []),
+ spawn_old_node_test(OldNode, false)
+ after
+ test_server:stop_node(OldNode)
+ end,
+ ok
+ end.
+
+spawn_new_node(Config) when is_list(Config) ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ %% Test that the same operations as in spawn_old_node test
+ %% works as expected on current OTP...
+ {ok, CurrNode} = test_server:start_node(make_nodename(Config),
+ peer,
+ [{args, " -setcookie "++Cookie}]),
+ try
+ %% Spawns triggering a new connection; which
+ %% will trigger hopeful data transcoding
+ %% of spawn requests...
+ io:format("~n~nDoing initial connect tests...~n", []),
+ spawn_current_node_test(CurrNode, true),
+ io:format("~n~nDoing already connected tests...~n", []),
+ %% Spawns on an already existing connection...
+ spawn_current_node_test(CurrNode, false)
+ after
+ test_server:stop_node(CurrNode)
+ end.
+
+disconnect_node(Node, Disconnect) ->
+ case Disconnect of
+ false ->
+ ok;
+ true ->
+ monitor_node(Node, true),
+ erlang:disconnect_node(Node),
+ receive {nodedown, Node} -> ok end
+ end.
+
+spawn_old_node_test(Node, Disconnect) ->
+ io:format("Testing spawn_request() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]),
+ receive
+ {a_tag, R1, Err, Notsup} ->
+ error = Err,
+ notsup = Notsup,
+ ok
+ end,
+ io:format("Testing spawn_monitor() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ try
+ spawn_monitor(Node, erlang, exit, [hej])
+ catch
+ error:notsup ->
+ ok
+ end,
+ io:format("Testing spawn_opt() with monitor on old node...", []),
+ disconnect_node(Node, Disconnect),
+ try
+ spawn_opt(Node, erlang, exit, [hej], [monitor])
+ catch
+ error:badarg ->
+ ok
+ end,
+ io:format("Testing spawn_opt() with link on old node...", []),
+ disconnect_node(Node, Disconnect),
+ process_flag(trap_exit, true),
+ P1 = spawn_opt(Node, erlang, exit, [hej], [link]),
+ Node = node(P1),
+ receive
+ {'EXIT', P1, hej} ->
+ ok
+ end,
+ io:format("Testing spawn_link() on old node...", []),
+ disconnect_node(Node, Disconnect),
+ P2 = spawn_link(Node, erlang, exit, [hej]),
+ Node = node(P2),
+ receive
+ {'EXIT', P2, hej} ->
+ ok
+ end.
+
+spawn_current_node_test(Node, Disconnect) ->
+ io:format("Testing spawn_request() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]),
+ receive
+ {a_tag, R1, ok, P1} ->
+ Node = node(P1),
+ receive
+ {'DOWN', R1, process, P1, hej} -> ok
+ end
+ end,
+ io:format("Testing spawn_monitor() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ {P2, M2} = spawn_monitor(Node, erlang, exit, [hej]),
+ receive
+ {'DOWN', M2, process, P2, hej} -> ok
+ end,
+ Node = node(P2),
+ io:format("Testing spawn_opt() with monitor on new node...", []),
+ disconnect_node(Node, Disconnect),
+ {P3, M3} = spawn_opt(Node, erlang, exit, [hej], [monitor]),
+ receive
+ {'DOWN', M3, process, P3, hej} -> ok
+ end,
+ Node = node(P3),
+ io:format("Testing spawn_opt() with link on new node...", []),
+ disconnect_node(Node, Disconnect),
+ process_flag(trap_exit, true),
+ P4 = spawn_opt(Node, erlang, exit, [hej], [link]),
+ Node = node(P4),
+ receive
+ {'EXIT', P4, hej} ->
+ ok
+ end,
+ io:format("Testing spawn_link() on new node...", []),
+ disconnect_node(Node, Disconnect),
+ P5 = spawn_link(Node, erlang, exit, [hej]),
+ Node = node(P5),
+ receive
+ {'EXIT', P5, hej} ->
+ ok
+ end.
+
+spawn_request_reply_option(Config) when is_list(Config) ->
+ spawn_request_reply_option_test(node()),
+ {ok, Node} = start_node(Config),
+ spawn_request_reply_option_test(Node).
+
+spawn_request_reply_option_test(Node) ->
+ io:format("Testing on node: ~p~n", [Node]),
+ Parent = self(),
+ Done1 = make_ref(),
+ RID1 = spawn_request(Node, fun () -> Parent ! Done1 end, [{reply, yes}]),
+ receive Done1 -> ok end,
+ receive
+ {spawn_reply, RID1, ok, _} -> ok
+ after 0 ->
+ ct:fail(missing_spawn_reply)
+ end,
+ Done2 = make_ref(),
+ RID2 = spawn_request(Node, fun () -> Parent ! Done2 end, [{reply, success_only}]),
+ receive Done2 -> ok end,
+ receive
+ {spawn_reply, RID2, ok, _} -> ok
+ after 0 ->
+ ct:fail(missing_spawn_reply)
+ end,
+ Done3 = make_ref(),
+ RID3 = spawn_request(Node, fun () -> Parent ! Done3 end, [{reply, error_only}]),
+ receive Done3 -> ok end,
+ receive
+ {spawn_reply, RID3, _, _} ->
+ ct:fail(unexpected_spawn_reply)
+ after 0 ->
+ ok
+ end,
+ Done4 = make_ref(),
+ RID4 = spawn_request(Node, fun () -> Parent ! Done4 end, [{reply, no}]),
+ receive Done4 -> ok end,
+ receive
+ {spawn_reply, RID4, _, _} ->
+ ct:fail(unexpected_spawn_reply)
+ after 0 ->
+ ok
+ end,
+ RID5 = spawn_request(Node, fun () -> ok end, [{reply, yes}, bad_option]),
+ receive
+ {spawn_reply, RID5, error, badopt} -> ok
+ end,
+ RID6 = spawn_request(Node, fun () -> ok end, [{reply, success_only}, bad_option]),
+ receive
+ {spawn_reply, RID6, error, badopt} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ RID7 = spawn_request(Node, fun () -> ok end, [{reply, error_only}, bad_option]),
+ receive
+ {spawn_reply, RID7, error, badopt} -> ok
+ end,
+ RID8 = spawn_request(Node, fun () -> ok end, [{reply, no}, bad_option]),
+ receive
+ {spawn_reply, RID8, error, badopt} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ case Node == node() of
+ true ->
+ ok;
+ false ->
+ stop_node(Node),
+ RID9 = spawn_request(Node, fun () -> ok end, [{reply, yes}]),
+ receive
+ {spawn_reply, RID9, error, noconnection} -> ok
+ end,
+ RID10 = spawn_request(Node, fun () -> ok end, [{reply, success_only}]),
+ receive
+ {spawn_reply, RID10, error, noconnection} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ RID11 = spawn_request(Node, fun () -> ok end, [{reply, error_only}]),
+ receive
+ {spawn_reply, RID11, error, noconnection} -> ok
+ end,
+ RID12 = spawn_request(Node, fun () -> ok end, [{reply, no}]),
+ receive
+ {spawn_reply, RID12, error, noconnection} -> ct:fail(unexpected_spawn_reply)
+ after 1000 -> ok
+ end,
+ ok
+ end.
+
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
@@ -2897,24 +3880,50 @@ tok_loop(hopp) ->
tok_loop(hej).
id(I) -> I.
+
+make_nodename(Config) when is_list(Config) ->
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))).
start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
- Name = list_to_atom(atom_to_list(?MODULE)
- ++ "-"
- ++ atom_to_list(proplists:get_value(testcase, Config))
- ++ "-"
- ++ integer_to_list(erlang:system_time(second))
- ++ "-"
- ++ integer_to_list(erlang:unique_integer([positive]))),
+ Name = make_nodename(Config),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
+ verify_nc(node()),
+ verify_nc(Node),
test_server:stop_node(Node).
+verify_nc(Node) ->
+ P = self(),
+ Ref = make_ref(),
+ Pid = spawn(Node,
+ fun() ->
+ R = erts_test_utils:check_node_dist(fun(E) -> E end),
+ P ! {Ref, R}
+ end),
+ MonRef = monitor(process, Pid),
+ receive
+ {Ref, ok} ->
+ demonitor(MonRef,[flush]),
+ ok;
+ {Ref, Error} ->
+ ct:log("~s",[Error]),
+ ct:fail(failed_nc_refc_check);
+ {'DOWN', MonRef, _, _, _} = Down ->
+ ct:log("~p",[Down]),
+ ct:fail(crashed_nc_refc_check)
+ end.
+
enable_internal_state() ->
case catch erts_debug:get_internal_state(available_internal_state) of
true -> true;
diff --git a/erts/emulator/test/property_test/phash2_properties.erl b/erts/emulator/test/property_test/phash2_properties.erl
new file mode 100644
index 0000000000..b1f3207c56
--- /dev/null
+++ b/erts/emulator/test/property_test/phash2_properties.erl
@@ -0,0 +1,63 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% The 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(phash2_properties).
+
+-ifdef(PROPER).
+
+-include_lib("proper/include/proper.hrl").
+-export([prop_phash2_same_with_same_input/0,
+ prop_phash2_same_with_same_long_input/0,
+ prop_phash2_same_in_different_versions/1,
+ prop_phash2_same_in_different_versions_with_long_input/1]).
+-proptest([proper]).
+
+%%--------------------------------------------------------------------
+%% Properties --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+prop_phash2_same_with_same_input() ->
+ ?FORALL(T, any(), erlang:phash2(T) =:= erlang:phash2(T)).
+
+prop_phash2_same_with_same_long_input() ->
+ ?FORALL(T, any(),
+ begin
+ BigTerm = lists:duplicate(10000, T),
+ erlang:phash2(BigTerm) =:= erlang:phash2(BigTerm)
+ end).
+
+prop_phash2_same_in_different_versions(DifferntVersionNode) ->
+ ?FORALL(T, any(),
+ erlang:phash2(T) =:= rpc:call(DifferntVersionNode,erlang,phash2,[T])).
+
+prop_phash2_same_in_different_versions_with_long_input(DifferntVersionNode) ->
+ ?FORALL(T, any(),
+ begin
+ BigTerm = lists:duplicate(10000, T),
+ RpcRes = rpc:call(DifferntVersionNode,erlang,phash2,[BigTerm]),
+ LocalRes = erlang:phash2(BigTerm),
+ RpcRes =:= LocalRes
+ end).
+
+%%--------------------------------------------------------------------
+%% Generators -------------------------------------------------------
+%%--------------------------------------------------------------------
+
+-endif.
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 59cf66d277..f477af1b5b 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -995,62 +995,81 @@ sct_cmd(Config) when is_list(Config) ->
{"db", thread_no_node_processor_spread}]).
sbt_cmd(Config) when is_list(Config) ->
- Bind = try
- OldVal = erlang:system_flag(scheduler_bind_type, default_bind),
- erlang:system_flag(scheduler_bind_type, OldVal),
- go_for_it
- catch
- error:notsup -> notsup;
- error:_ -> go_for_it
- end,
- case Bind of
- notsup ->
- {skipped, "Binding of schedulers not supported"};
- go_for_it ->
- CpuTCmd = case erlang:system_info({cpu_topology,detected}) of
- undefined ->
- case os:type() of
- linux ->
- case erlang:system_info(logical_processors) of
- 1 ->
- "+sctL0";
- N when is_integer(N) ->
- NS = integer_to_list(N-1),
- "+sctL0-"++NS++"p0-"++NS;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- ""
- end,
- case CpuTCmd of
- false ->
- {skipped, "Don't know how to create cpu topology"};
- _ ->
- case erlang:system_info(logical_processors) of
- LP when is_integer(LP) ->
- OldRelFlags = clear_erl_rel_flags(),
- try
- lists:foreach(fun ({ClBt, Bt}) ->
- sbt_test(Config,
- CpuTCmd,
- ClBt,
- Bt,
- LP)
- end,
- ?BIND_TYPES)
- after
- restore_erl_rel_flags(OldRelFlags)
- end,
- ok;
- _ ->
- {skipped,
- "Don't know the amount of logical processors"}
- end
- end
+ case sbt_check_prereqs() of
+ {skipped, _Reason}=Skipped ->
+ Skipped;
+ ok ->
+ case sbt_make_topology_args() of
+ false ->
+ {skipped, "Don't know how to create cpu topology"};
+ CpuTCmd ->
+ LP = erlang:system_info(logical_processors),
+ OldRelFlags = clear_erl_rel_flags(),
+ try
+ lists:foreach(fun ({ClBt, Bt}) ->
+ sbt_test(Config, CpuTCmd,
+ ClBt, Bt, LP)
+ end,
+ ?BIND_TYPES)
+ after
+ restore_erl_rel_flags(OldRelFlags)
+ end,
+ ok
+ end
+ end.
+
+sbt_make_topology_args() ->
+ case erlang:system_info({cpu_topology,detected}) of
+ undefined ->
+ case os:type() of
+ linux ->
+ case erlang:system_info(logical_processors) of
+ 1 ->
+ "+sctL0";
+ N ->
+ NS = integer_to_list(N - 1),
+ "+sctL0-"++NS++"p0-"++NS
+ end;
+ _ ->
+ false
+ end;
+ _ ->
+ ""
+ end.
+
+sbt_check_prereqs() ->
+ try
+ Available = erlang:system_info(logical_processors_available),
+ Quota = erlang:system_info(cpu_quota),
+ if
+ Quota =:= unknown; Quota >= Available ->
+ ok;
+ Quota < Available ->
+ throw({skipped, "Test requires that CPU quota is greater than "
+ "the number of available processors."})
+ end,
+
+ try
+ OldVal = erlang:system_flag(scheduler_bind_type, default_bind),
+ erlang:system_flag(scheduler_bind_type, OldVal)
+ catch
+ error:notsup ->
+ throw({skipped, "Scheduler binding not supported."});
+ error:_ ->
+ %% ?!
+ ok
+ end,
+
+ case erlang:system_info(logical_processors) of
+ Count when is_integer(Count) ->
+ ok;
+ unknown ->
+ throw({skipped, "Can't detect number of logical processors."})
+ end,
+
+ ok
+ catch
+ throw:{skip,_Reason}=Skip -> Skip
end.
sbt_test(Config, CpuTCmd, ClBt, Bt, LP) ->
@@ -1112,28 +1131,48 @@ scheduler_threads(Config) when is_list(Config) ->
{Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
%% Configure 2x scheduler threads only
{TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
- case {erlang:system_info(logical_processors),
- erlang:system_info(logical_processors_available)} of
- {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) ->
- %% Test resetting the scheduler counts
- ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
- {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd),
- %% Test negative +S settings, but only for SMP-enabled emulators
- case {LProc > 1, LProcAvail > 1} of
- {true, true} ->
- SchedMinus1 = LProc-1,
- SchedOnlnMinus1 = LProcAvail-1,
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
- {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
- ok;
- _ ->
- {comment, "Skipped reduced amount of schedulers test due to too few logical processors"}
- end;
- _ -> %% Skipped when missing info about logical processors...
- {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"}
+
+ LProc = erlang:system_info(logical_processors),
+ LProcAvail = erlang:system_info(logical_processors_available),
+ Quota = erlang:system_info(cpu_quota),
+
+ if
+ not is_integer(LProc); not is_integer(LProcAvail) ->
+ {comment, "Skipped reset amount of schedulers test, and reduced "
+ "amount of schedulers test due to too unknown amount of "
+ "logical processors"};
+ is_integer(LProc); is_integer(LProcAvail) ->
+ ExpectedOnln = st_expected_onln(LProcAvail, Quota),
+
+ st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln),
+
+ if
+ LProc =:= 1; LProcAvail =:= 1 ->
+ {comment, "Skipped reduced amount of schedulers test due "
+ "to too few logical processors"};
+ LProc > 1, LProcAvail > 1 ->
+ st_reduced(Config, LProc, ExpectedOnln)
+ end
end.
+st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln) ->
+ %% Test resetting # of schedulers.
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {LProc, ExpectedOnln, _} = get_sstate(Config, ResetCmd),
+ ok.
+
+st_reduced(Config, LProc, ExpectedOnln) ->
+ %% Test negative +S settings
+ SchedMinus1 = LProc-1,
+ SchedOnlnMinus1 = ExpectedOnln-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
+ ok.
+
+st_expected_onln(LProcAvail, unknown) -> LProcAvail;
+st_expected_onln(LProcAvail, Quota) -> min(LProcAvail, Quota).
+
dirty_scheduler_threads(Config) when is_list(Config) ->
case erlang:system_info(dirty_cpu_schedulers) of
0 -> {skipped, "No dirty scheduler support"};
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index 8afe4e4ac1..6fa19c47d4 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -156,27 +156,11 @@ receive_any() ->
end.
chk_temp_alloc() ->
- case erlang:system_info({allocator,temp_alloc}) of
- false ->
- %% Temp alloc is not enabled
- ok;
- TIL ->
- %% Verify that we havn't got anything allocated by temp_alloc
- lists:foreach(
- fun ({instance, _, TI}) ->
- {value, {mbcs, MBCInfo}}
- = lists:keysearch(mbcs, 1, TI),
- {value, {blocks, 0, _, _}}
- = lists:keysearch(blocks, 1, MBCInfo),
- {value, {sbcs, SBCInfo}}
- = lists:keysearch(sbcs, 1, TI),
- {value, {blocks, 0, _, _}}
- = lists:keysearch(blocks, 1, SBCInfo)
- end,
- TIL),
- ok
+ %% Verify that we haven't got any outstanding temp_alloc allocations.
+ case erts_debug:alloc_blocks_size(temp_alloc) of
+ undefined -> ok;
+ 0 -> ok
end.
-
%% Start/stop drivers.
start_driver(Config, Name) ->
diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl
index 00a02e5560..7dbe1fb4f4 100644
--- a/erts/emulator/test/small_SUITE.erl
+++ b/erts/emulator/test/small_SUITE.erl
@@ -78,6 +78,15 @@ sp2_1(N, MinS, MaxS) when N > 0 ->
[N | sp2_1(N bsl 1, MinS, MaxS)].
arith_test(A, B, MinS, MaxS) ->
+ try arith_test_1(A, B, MinS, MaxS) of
+ ok -> ok
+ catch
+ error:Reason:Stk ->
+ ct:fail("arith_test failed with ~p~n\tA = ~p~n\tB = ~p\n\t~p",
+ [Reason, A, B, Stk])
+ end.
+
+arith_test_1(A, B, MinS, MaxS) ->
verify_kind(A + B, MinS, MaxS),
verify_kind(B + A, MinS, MaxS),
verify_kind(A - B, MinS, MaxS),
@@ -97,6 +106,9 @@ arith_test(A, B, MinS, MaxS) ->
true = B =:= 0 orelse ((A * B) div id(B) =:= A),
true = A =:= 0 orelse ((B * A) div id(A) =:= B),
+ true = B =:= 0 orelse (((A div id(B)) * id(B) + A rem id(B)) =:= A),
+ true = A =:= 0 orelse (((B div id(A)) * id(A) + B rem id(A)) =:= B),
+
ok.
%% Verifies that N is a small when it should be
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index dd1f3b1086..b074ff4bea 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -95,6 +95,25 @@
api_b_sendmsg_and_recvmsg_tcpL/1,
api_b_sendmsg_and_recvmsg_sctp4/1,
+ %% *** API socket from FD ***
+ api_ffd_open_wod_and_info_udp4/1,
+ api_ffd_open_wod_and_info_udp6/1,
+ api_ffd_open_wod_and_info_tcp4/1,
+ api_ffd_open_wod_and_info_tcp6/1,
+ api_ffd_open_wd_and_info_udp4/1,
+ api_ffd_open_wd_and_info_udp6/1,
+ api_ffd_open_wd_and_info_tcp4/1,
+ api_ffd_open_wd_and_info_tcp6/1,
+ api_ffd_open_and_open_wod_and_send_udp4/1,
+ api_ffd_open_and_open_wod_and_send_udp6/1,
+ api_ffd_open_and_open_wd_and_send_udp4/1,
+ api_ffd_open_and_open_wd_and_send_udp6/1,
+ api_ffd_open_connect_and_open_wod_and_send_tcp4/1,
+ api_ffd_open_connect_and_open_wod_and_send_tcp6/1,
+ api_ffd_open_connect_and_open_wd_and_send_tcp4/1,
+ api_ffd_open_connect_and_open_wd_and_send_tcp6/1,
+
+
%% *** API async ***
api_a_connect_tcp4/1,
api_a_connect_tcp6/1,
@@ -682,6 +701,7 @@ groups() ->
[{api, [], api_cases()},
{api_misc, [], api_misc_cases()},
{api_basic, [], api_basic_cases()},
+ {api_from_fd, [], api_from_fd_cases()},
{api_async, [], api_async_cases()},
{api_options, [], api_options_cases()},
{api_options_otp, [], api_options_otp_cases()},
@@ -813,6 +833,26 @@ api_basic_cases() ->
api_b_sendmsg_and_recvmsg_sctp4
].
+api_from_fd_cases() ->
+ [
+ api_ffd_open_wod_and_info_udp4,
+ api_ffd_open_wod_and_info_udp6,
+ api_ffd_open_wod_and_info_tcp4,
+ api_ffd_open_wod_and_info_tcp6,
+ api_ffd_open_wd_and_info_udp4,
+ api_ffd_open_wd_and_info_udp6,
+ api_ffd_open_wd_and_info_tcp4,
+ api_ffd_open_wd_and_info_tcp6,
+ api_ffd_open_and_open_wod_and_send_udp4,
+ api_ffd_open_and_open_wod_and_send_udp6,
+ api_ffd_open_and_open_wd_and_send_udp4,
+ api_ffd_open_and_open_wd_and_send_udp6,
+ api_ffd_open_connect_and_open_wod_and_send_tcp4,
+ api_ffd_open_connect_and_open_wod_and_send_tcp6,
+ api_ffd_open_connect_and_open_wd_and_send_tcp4,
+ api_ffd_open_connect_and_open_wd_and_send_tcp6
+ ].
+
api_async_cases() ->
[
api_a_connect_tcp4,
@@ -4237,6 +4277,1890 @@ api_b_send_and_recv_sctp(_InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API FROM FD %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv4
+%% Without dup
+api_ffd_open_wod_and_info_udp4(suite) ->
+ [];
+api_ffd_open_wod_and_info_udp4(doc) ->
+ [];
+api_ffd_open_wod_and_info_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_udp6(suite) ->
+ [];
+api_ffd_open_wod_and_info_udp6(doc) ->
+ [];
+api_ffd_open_wod_and_info_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv4
+%% With dup
+api_ffd_open_wd_and_info_udp4(suite) ->
+ [];
+api_ffd_open_wd_and_info_udp4(doc) ->
+ [];
+api_ffd_open_wd_and_info_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_wd_open_and_info_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_udp6(suite) ->
+ [];
+api_ffd_open_wd_and_info_udp6(doc) ->
+ [];
+api_ffd_open_wd_and_info_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_wd_open_and_info_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_tcp4(suite) ->
+ [];
+api_ffd_open_wod_and_info_tcp4(doc) ->
+ [];
+api_ffd_open_wod_and_info_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% Without dup
+api_ffd_open_wod_and_info_tcp6(suite) ->
+ [];
+api_ffd_open_wod_and_info_tcp6(doc) ->
+ [];
+api_ffd_open_wod_and_info_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wod_and_info_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_tcp4(suite) ->
+ [];
+api_ffd_open_wd_and_info_tcp4(doc) ->
+ [];
+api_ffd_open_wd_and_info_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wd_and_info_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) a socket from an already existing
+%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
+%% With some extra checks...
+%% IPv6
+%% With dup
+api_ffd_open_wd_and_info_tcp6(suite) ->
+ [];
+api_ffd_open_wd_and_info_tcp6(doc) ->
+ [];
+api_ffd_open_wd_and_info_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_wd_and_info_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_and_info(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_and_info(InitState) ->
+ Seq =
+ [
+ #{desc => "open",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol} = State) ->
+ case socket:open(Domain, Type, Protocol) of
+ {ok, Sock1} ->
+ {ok, State#{sock1 => Sock1}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get socket (1) FD",
+ cmd => fun(#{sock1 := Sock1} = State) ->
+ case socket:getopt(Sock1, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "check if we need to provide protocol or not",
+ cmd => fun(#{sock1 := Sock1} = State) ->
+ case socket:getopt(Sock1, socket, protocol) of
+ {ok, _} ->
+ ?SEV_IPRINT("protocol accessible"),
+ {ok, State#{provide_protocol => false}};
+ {error, Reason} ->
+ ?SEV_IPRINT("failed get protocol: "
+ "~n ~p", [Reason]),
+ {ok, State#{provide_protocol => true}}
+ end
+ end},
+ #{desc => "open with FD",
+ cmd => fun(#{fd := FD,
+ dup := DUP,
+ provide_protocol := true,
+ protocol := Protocol} = State) ->
+ case socket:open(FD, #{dup => DUP,
+ protocol => Protocol}) of
+ {ok, Sock2} ->
+ ?SEV_IPRINT("socket 2 open"),
+ {ok, State#{sock2 => Sock2}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed open socket with FD (~w): "
+ "~n ~p", [FD, Reason]),
+ ERROR
+ end;
+ (#{fd := FD,
+ dup := DUP,
+ provide_protocol := false} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock2} ->
+ ?SEV_IPRINT("socket 2 open"),
+ {ok, State#{sock2 => Sock2}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed open socket with FD (~w): "
+ "~n ~p", [FD, Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "get socket (1) info",
+ cmd => fun(#{sock1 := Sock} = State) ->
+ %% socket:setopt(Sock, otp, debug, true),
+ Info = socket:info(Sock),
+ %% socket:setopt(Sock, otp, debug, false),
+ ?SEV_IPRINT("Got Info: "
+ "~n ~p", [Info]),
+ {ok, State#{info1 => Info}}
+ end},
+ #{desc => "get socket (2) info",
+ cmd => fun(#{sock2 := Sock} = State) ->
+ %% socket:setopt(Sock, otp, debug, true),
+ Info = socket:info(Sock),
+ %% socket:setopt(Sock, otp, debug, false),
+ ?SEV_IPRINT("Got Info: "
+ "~n ~p", [Info]),
+ {ok, State#{info2 => Info}}
+ end},
+ #{desc => "validate socket (1) info",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ info1 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := normal,
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 1: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol, normal, Info]),
+ {error, unexpected_infio}
+ end},
+ #{desc => "validate socket (2) info",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := _FD,
+ dup := false,
+ info2 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := fromfd,
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := _FD,
+ dup := false,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 2: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol,
+ fromfd, Info]),
+ {error, unexpected_info};
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := FD,
+ dup := true,
+ info2 := #{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ ctype := {fromfd, FD},
+ counters := _,
+ num_readers := 0,
+ num_writers := 0,
+ num_acceptors := 0}}) ->
+ ok;
+ (#{domain := Domain,
+ type := Type,
+ protocol := Protocol,
+ fd := FD,
+ dup := true,
+ info := Info}) ->
+ ?SEV_EPRINT("Unexpected Info for socket 2: "
+ "~n (expected) Domain: ~p"
+ "~n (expected) Type: ~p"
+ "~n (expected) Protocol: ~p"
+ "~n (expected) Create Type: ~p"
+ "~n ~p",
+ [Domain, Type, Protocol,
+ {fromfd, FD}, Info]),
+ {error, unexpected_info}
+ end},
+ #{desc => "close socket (1)",
+ cmd => fun(#{sock1 := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+ #{desc => "close socket (2)",
+ cmd => fun(#{sock2 := Sock} = _State) ->
+ socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 UDP (dgram) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_and_open_wod_and_send_udp4(suite) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp4(doc) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wod_and_send_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 UDP (dgram) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_and_open_wod_and_send_udp6(suite) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp6(doc) ->
+ [];
+api_ffd_open_and_open_wod_and_send_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wod_and_send_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => false},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 UDP (dgram) socket.
+%%
+api_ffd_open_and_open_wd_and_send_udp4(suite) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp4(doc) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wd_and_send_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1) and then create another socket (2) from
+%% its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 UDP (dgram) socket.
+%%
+api_ffd_open_and_open_wd_and_send_udp6(suite) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp6(doc) ->
+ [];
+api_ffd_open_and_open_wd_and_send_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_and_open_wd_and_send_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp,
+ dup => true},
+ ok = api_ffd_open_and_open_and_send_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_and_open_and_send_udp(InitState) ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock) ->
+ socket:recvfrom(Sock)
+ end,
+ api_ffd_open_and_open_and_send_udp2(InitState#{send => Send,
+ recv => Recv}).
+
+api_ffd_open_and_open_and_send_udp2(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, dgram, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, lsa := LSA} = State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("bound to port: ~w", [Port]),
+ {ok, State#{port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, port := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ #{desc => "await request 1 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (1) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 1 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 1 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 1",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 1 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ #{desc => "await request 2 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (2) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 2 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 2 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 2",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 2 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ #{desc => "await request 3 (recv)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, {Source, ?BASIC_REQ}} ->
+ ?SEV_IPRINT("received request (2) from: "
+ "~n ~p", [Source]),
+ {ok, State#{source => Source}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 3 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 3 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 3",
+ cmd => fun(#{sock := Sock, send := Send, source := Source}) ->
+ Send(Sock, ?BASIC_REP, Source)
+ end},
+ #{desc => "announce ready 3 (send reply)",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ {ok, maps:remove(source, State)}
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client1Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, dgram, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get socket FD",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:getopt(Sock, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester,
+ fd := FD}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, FD),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 1)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 1 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 1 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ #{desc => "await continue (send request 3)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 3 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 3 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client2Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, {Port, FD}} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ server_port => Port,
+ fd => FD}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{fd := FD,
+ dup := DUP} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 2)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 2 (to server)",
+ cmd => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
+ Send(Sock, ?BASIC_REQ, SSA)
+ end},
+ #{desc => "announce ready (send request 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 2 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, {_, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 1",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 2",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client 1
+ #{desc => "order client 1 start",
+ cmd => fun(#{client1 := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client 1 ready (init)",
+ cmd => fun(#{client1 := Pid} = State) ->
+ case ?SEV_AWAIT_READY(Pid, client1, init) of
+ {ok, FD} ->
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Client 1 init error: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% Start the client 2
+ #{desc => "order client 2 start",
+ cmd => fun(#{client2 := Pid,
+ server_port := Port,
+ fd := FD} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {Port, FD}),
+ ok
+ end},
+ #{desc => "await client 2 ready (init)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client2, init)
+ end},
+
+ %% *** The actual test ***
+
+ #{desc => "order client 1 to continue (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 1 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to continue (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 2 ready (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, send_req)
+ end},
+ #{desc => "await server ready (request recv 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 2 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 2 ready (reply recv 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to terminate",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 2 termination",
+ cmd => fun(#{client2 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client2, State),
+ {ok, State1}
+ end},
+
+
+ #{desc => "order client 1 to continue (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 3 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client 1 to terminate",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 1 termination",
+ cmd => fun(#{client1 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client1, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, maps:remove(dup, InitState)),
+
+ i("start (socket origin) client 1 evaluator"),
+ Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
+ i("await evaluator(s)"),
+
+ i("start client 2 evaluator"),
+ Client2 = ?SEV_START("client-2", Client2Seq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client1 => Client1#ev.pid,
+ client2 => Client2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 TCP (stream) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_connect_and_open_wod_and_send_tcp4(suite) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp4(doc) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *without* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 TCP (stream) socket.
+%%
+%% <WARNING>
+%%
+%% This is *not* how its intended to be used.
+%% That an erlang process creating a socket and then handing over the
+%% file descriptor to another erlang process. *But* its a convient way
+%% to test it!
+%%
+%% </WARNING>
+%%
+api_ffd_open_connect_and_open_wod_and_send_tcp6(suite) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp6(doc) ->
+ [];
+api_ffd_open_connect_and_open_wod_and_send_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => false},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv4 TCP (stream) socket.
+api_ffd_open_connect_and_open_wd_and_send_tcp4(suite) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp4(doc) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open a socket (1), connect to a server and then create
+%% another socket (2) from its file descriptor *with* dup.
+%% Exchange som data from via both "client" sockets.
+%% Finally close the second socket. Ensure that the original socket
+%% has not been closed (test by sending some data).
+%% IPv6 TCP (stream) socket.
+api_ffd_open_connect_and_open_wd_and_send_tcp6(suite) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp6(doc) ->
+ [];
+api_ffd_open_connect_and_open_wd_and_send_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp,
+ dup => true},
+ ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_ffd_open_connect_and_open_and_send_tcp(InitState) ->
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ api_ffd_open_connect_and_open_and_send_tcp2(InitState#{send => Send,
+ recv => Recv}).
+
+api_ffd_open_connect_and_open_and_send_tcp2(InitState) ->
+ process_flag(trap_exit, true),
+ ServerSeq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{lsa => LSA}}
+ end},
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, stream, Proto) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{lsock := LSock, lsa := LSA} = State) ->
+ case sock_bind(LSock, LSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("bound to port: ~w", [Port]),
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ %% This is actually not used for unix domain socket
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "await connection",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: ~n ~p", [Sock]),
+ {ok, State#{csock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await request 1 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 1 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 1 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 1",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 1 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ #{desc => "await request 2 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 2 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 2 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 2",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 2 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ #{desc => "await request 3 (recv)",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ case Recv(Sock) of
+ {ok, ?BASIC_REQ} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready 3 (recv request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
+ ok
+ end},
+ #{desc => "await continue 3 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ end},
+ #{desc => "send reply 3",
+ cmd => fun(#{csock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REP)
+ end},
+ #{desc => "announce ready 3 (send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close connection socket",
+ cmd => fun(#{csock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(csock, State)}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:close(LSock) of
+ ok ->
+ {ok, maps:remove(lsock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client1Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Port} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, server_port => Port}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "which server (local) address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ SSA = LSA#{port => Port},
+ {ok, State#{local_sa => LSA, server_sa => SSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, stream, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case sock_bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ socket:connect(Sock, SSA)
+ end},
+ #{desc => "get socket FD",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:getopt(Sock, otp, fd) of
+ {ok, FD} ->
+ ?SEV_IPRINT("FD: ~w", [FD]),
+ {ok, State#{fd => FD}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("failed get FD: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester,
+ fd := FD}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect, FD),
+ ok
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 1)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 1 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 1 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ #{desc => "await continue (send request 3)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 3 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 3 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Client2Seq =
+ [
+ %% *** Wait for start order ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, FD} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, fd => FD}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The init part ***
+ #{desc => "create socket",
+ cmd => fun(#{fd := FD,
+ dup := DUP} = State) ->
+ case socket:open(FD, #{dup => DUP}) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (send request 2)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
+ end},
+ #{desc => "send request 2 (to server)",
+ cmd => fun(#{sock := Sock, send := Send}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply 2 (from server)",
+ cmd => fun(#{sock := Sock, recv := Recv}) ->
+ {ok, ?BASIC_REP} = Recv(Sock),
+ ok
+ end},
+ #{desc => "announce ready (recv reply 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
+ ok
+ end},
+
+ %% *** Termination ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ ok = socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 1",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 2",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+
+ %% Start the client 1
+ #{desc => "order client 1 start",
+ cmd => fun(#{client1 := Pid, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Port),
+ ok
+ end},
+ #{desc => "await client 1 ready (init)",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client1, init)
+ end},
+
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client 1 to continue (with connect)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await client 1 ready (connect)",
+ cmd => fun(#{client1 := Pid} = State) ->
+ {ok, FD} = ?SEV_AWAIT_READY(Pid, client1, connect),
+ {ok, State#{fd => FD}}
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept)
+ end},
+
+ %% Start the client 2
+ #{desc => "order client 2 start",
+ cmd => fun(#{client2 := Pid, fd := FD} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, FD),
+ ok
+ end},
+ #{desc => "await client 2 ready (init)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client2, init)
+ end},
+
+ %% *** The actual test ***
+
+ #{desc => "order client 1 to continue (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 1)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 1 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 1)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to continue (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 2 ready (with send request 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, send_req)
+ end},
+ #{desc => "await server ready (request recv 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 2)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 2 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 2 ready (reply recv 2)",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client2, recv_reply)
+ end},
+
+
+ #{desc => "order client 2 to terminate",
+ cmd => fun(#{client2 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 2 termination",
+ cmd => fun(#{client2 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client2, State),
+ {ok, State1}
+ end},
+
+
+ #{desc => "order client 1 to continue (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client 1 ready (with send request 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, send_req)
+ end},
+ #{desc => "await server ready (request recv 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply 3)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply 3 sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client 1 ready (reply recv 3)",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client1, recv_reply)
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "order client 1 to terminate",
+ cmd => fun(#{client1 := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client 1 termination",
+ cmd => fun(#{client1 := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client1, State),
+ {ok, State1}
+ end},
+ #{desc => "order server to terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ Server = ?SEV_START("server", ServerSeq, maps:remove(dup, InitState)),
+
+ i("start (socket origin) client 1 evaluator"),
+ Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
+ i("await evaluator(s)"),
+
+ i("start client 2 evaluator"),
+ Client2 = ?SEV_START("client-2", Client2Seq, InitState),
+ i("await evaluator(s)"),
+
+ i("start tester evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client1 => Client1#ev.pid,
+ client2 => Client2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API ASYNC %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically establish a TCP connection via an async connect. IPv4.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index c2d5cd7023..cd60187809 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -664,7 +664,7 @@ dist_procs_trace(Config) when is_list(Config) ->
Proc1 ! {trap_exit_please, true},
Proc3 = receive {spawned, Proc1, P3} -> P3 end,
io:format("Proc3 = ~p ~n", [Proc3]),
- {trace, Proc1, getting_linked, Proc3} = receive_first_trace(),
+ {trace, Proc1, link, Proc3} = receive_first_trace(),
Reason3 = make_ref(),
Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
@@ -958,15 +958,14 @@ do_system_monitor_long_schedule() ->
{Self,L} when is_list(L) ->
ok
after 1000 ->
- ct:fail(no_trace_of_pid)
+ ct:fail(no_trace_of_pid)
end,
"ok" = erlang:port_control(Port,1,[]),
- "ok" = erlang:port_control(Port,2,[]),
receive
{Port,LL} when is_list(LL) ->
ok
after 1000 ->
- ct:fail(no_trace_of_port)
+ ct:fail(no_trace_of_port)
end,
port_close(Port),
erlang:system_monitor(undefined),
diff --git a/erts/emulator/test/trace_SUITE_data/Makefile.src b/erts/emulator/test/trace_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..645107d7b0
--- /dev/null
+++ b/erts/emulator/test/trace_SUITE_data/Makefile.src
@@ -0,0 +1,3 @@
+all: slow_drv@dll@
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/trace_SUITE_data/slow_drv.c b/erts/emulator/test/trace_SUITE_data/slow_drv.c
new file mode 100644
index 0000000000..4f7c93a69e
--- /dev/null
+++ b/erts/emulator/test/trace_SUITE_data/slow_drv.c
@@ -0,0 +1,102 @@
+#ifdef __WIN32__
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+#include "erl_driver.h"
+
+typedef struct _erl_drv_data {
+ ErlDrvPort erlang_port;
+} EchoDrvData;
+
+static EchoDrvData slow_drv_data, *slow_drv_data_p;
+
+static EchoDrvData *slow_drv_start(ErlDrvPort port, char *command);
+static void slow_drv_stop(EchoDrvData *data_p);
+static void slow_drv_output(ErlDrvData drv_data, char *buf,
+ ErlDrvSizeT len);
+static ErlDrvSSizeT slow_drv_control(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen);
+static void slow_drv_timeout(ErlDrvData drv_data);
+static void slow_drv_finish(void);
+
+static ErlDrvEntry slow_drv_entry = {
+ NULL, /* init */
+ slow_drv_start,
+ slow_drv_stop,
+ slow_drv_output,
+ NULL, /* ready_input */
+ NULL, /* ready_output */
+ "slow_drv",
+ slow_drv_finish,
+ NULL, /* handle */
+ slow_drv_control, /* control */
+ slow_drv_timeout, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL
+
+};
+
+DRIVER_INIT(slow_drv)
+{
+ slow_drv_data_p = NULL;
+ return &slow_drv_entry;
+}
+
+static EchoDrvData *slow_drv_start(ErlDrvPort port, char *command)
+{
+ if (slow_drv_data_p != NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ slow_drv_data_p = &slow_drv_data;
+ slow_drv_data_p->erlang_port = port;
+ return slow_drv_data_p;
+}
+
+static void slow_drv_stop(EchoDrvData *data_p) {
+ slow_drv_data_p = NULL;
+}
+
+static void slow_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ EchoDrvData* data_p = (EchoDrvData *) drv_data;
+ driver_output(data_p->erlang_port, buf, len);
+}
+
+static ErlDrvSSizeT slow_drv_control(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ EchoDrvData* data_p = (EchoDrvData *) drv_data;
+ memcpy(*rbuf,"ok",2);
+ if (command == 1) {
+ driver_set_timer(data_p->erlang_port, 0);
+ } else {
+ slow_drv_timeout(drv_data);
+ }
+ return 2;
+}
+
+static void slow_drv_timeout(ErlDrvData drv_data)
+{
+ /* Sleep for 150 msec */
+#ifdef __WIN32__
+ Sleep(150);
+#else
+ usleep(150000);
+#endif
+}
+
+static void slow_drv_finish() {
+ slow_drv_data_p = NULL;
+}
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 4732d42296..03efb92e2e 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -307,8 +307,7 @@ combo(Config) when is_list(Config) ->
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]),
- %% not implemented
- %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
@@ -337,9 +336,7 @@ combo(Config) when is_list(Config) ->
{value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif),
{value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif),
{value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif),
- %% not implemented
- {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif),
- %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
+ {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
{value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif),
%%
@@ -408,8 +405,12 @@ bif(Config) when is_list(Config) ->
%%
2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
+ 1 = erlang:trace_pattern({?MODULE, with_bif, 1}, true, [call_time]),
Pid = setup(),
- {L, T1} = execute(Pid, fun() -> with_bif(M) end),
+ {L, Tot1} = execute(Pid, fun() -> with_bif(M) end),
+
+ {call_time,[{Pid,_,S,Us}]} = erlang:trace_info({?MODULE,with_bif,1}, call_time),
+ T1 = Tot1 - (S*1000000 + Us),
ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2),
ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2),
@@ -418,9 +419,9 @@ bif(Config) when is_list(Config) ->
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]),
- {L, T2} = execute(Pid, fun() -> with_bif(M) end),
+ {L, _T2} = execute(Pid, fun() -> with_bif(M) end),
- ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2),
+ ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1),
ok = check_trace_info({erlang, term_to_binary, 1}, false, none),
%%
@@ -439,12 +440,14 @@ nif(Config) when is_list(Config) ->
1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]),
1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]),
Pid = setup(),
- {_, T1} = execute(Pid, fun() -> with_nif(M) end),
+ {_, Tot1} = execute(Pid, fun() -> with_nif(M) end),
+
+ {call_time,[{Pid,_,S,Us}]} = erlang:trace_info({?MODULE,with_nif,1}, call_time),
+ T1 = Tot1 - (S*1000000 + Us),
% the nif is called M - 1 times, the last time the function with 'with_nif'
% returns ok and does not call the nif.
- ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/2),
- ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/2),
+ ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
@@ -482,6 +485,8 @@ called_function(Config) when is_list(Config) ->
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dead_tracer(Config) when is_list(Config) ->
+ TracedMFAs = dead_tracer_mfas(),
+
Self = self(),
FirstTracer = tracer(),
StartTracing = fun() -> turn_on_tracing(Self) end,
@@ -496,14 +501,14 @@ dead_tracer(Config) when is_list(Config) ->
erlang:yield(),
%% Collect and check that we only get call_time info for the current process.
- Info1 = collect_all_info(),
+ Info1 = collect_all_info(TracedMFAs),
[] = 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(),
+ [] = collect_all_info(TracedMFAs),
%% When we start a second tracer process, that tracer process must
%% not inherit the tracing flags and the dead tracer (even though
@@ -512,7 +517,7 @@ dead_tracer(Config) when is_list(Config) ->
tell_tracer(SecondTracer, StartTracing),
Seq20 = lists:seq(1, 20),
Seq20 = seq(1, 20, fun(I) -> I + 1 end),
- Info2 = collect_all_info(),
+ Info2 = collect_all_info(TracedMFAs),
io:format("~p\n", [Info2]),
[] = other_than_self(Info2),
SecondTracer ! quit,
@@ -548,9 +553,21 @@ turn_on_tracing(Pid) ->
_ = now(),
ok.
-collect_all_info() ->
- collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++
- erlang:system_info(snifs)).
+%% We want to trace functions local to this module as well as all BIFs, and for
+%% the latter we need to ensure that their modules are loaded.
+dead_tracer_mfas() ->
+ Modules = [M || {M,_F,_A} <- erlang:system_info(snifs)],
+ Whitelist0 = gb_sets:from_list(Modules),
+ Whitelist = case code:ensure_modules_loaded(Modules) of
+ {error, Reasons} ->
+ Blacklist = gb_sets:from_list([M || {M, _} <- Reasons]),
+ gb_sets:subtract(Whitelist0, Blacklist);
+ ok ->
+ Whitelist0
+ end,
+ EligibleSNIFs = [MFA || {M,_F,_A}=MFA <- erlang:system_info(snifs),
+ gb_sets:is_element(M, Whitelist)],
+ [{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ EligibleSNIFs.
collect_all_info([MFA|T]) ->
CallTime = erlang:trace_info(MFA, call_time),
@@ -669,21 +686,29 @@ seq_r(Start, Stop, Succ, R) ->
seq_r(Succ(Start), Stop, Succ, [Start | R]).
% Check call time tracing data and print mismatches
-check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) ->
- case erlang:trace_info(Mfa, call_time) of
- % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME)
- % is the same.
- % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok.
- {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR ->
+check_trace_info(Mfa, [{Pid, ExpectedC,_,_}] = Expect, Time) ->
+ {call_time,[{Pid,C,S,Us}]} = erlang:trace_info(Mfa, call_time),
+ {Mod, Name, Arity} = Mfa,
+ IsBuiltin = erlang:is_builtin(Mod, Name, Arity),
+ if
+ %% Call count on BIFs may exceed number of calls as they often trap to
+ %% themselves.
+ IsBuiltin, C >= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
+ ok;
+ not IsBuiltin, C =:= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
ok;
- {call_time,[{Pid,C,S,Us}]} ->
+ true ->
Sum = S*1000000 + Us,
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n",
- [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]),
- time_error;
- Other ->
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]),
- time_count_error
+ io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w "
+ "s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, "
+ "should be 1.0)~n",
+ [Mfa, Expect, Time,
+ S, Us, Sum, Time, Sum - Time, Time/Sum]),
+ time_error
end;
check_trace_info(Mfa, Expect, _) ->
case erlang:trace_info(Mfa, call_time) of
@@ -748,7 +773,8 @@ setup() ->
setup([]).
setup(Opts) ->
- Pid = spawn_link(fun() -> loop() end),
+ Pid = spawn_opt(fun() -> loop() end,
+ [link, {max_heap_size, 10000}]),
1 = erlang:trace(Pid, true, [call|Opts]),
Pid.
@@ -772,9 +798,12 @@ loop() ->
quit ->
ok;
{Pid, execute, Fun } when is_function(Fun) ->
+ %% Make sure we always run with the same amount of reductions.
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(Fun, [])},
loop();
{Pid, execute, {M, F, A}} ->
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(M, F, A)},
loop()
end.
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index ad802352b9..699964b4fb 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -1111,12 +1111,14 @@ x_exc_body(ExcOpts, {M,F}=Func, Args, Apply) ->
end,
{value,Value}
catch
- Thrown when Nocatch ->
+ throw:Thrown:Stk when Nocatch ->
+ put(get_stacktrace, Stk),
CR = {error,{nocatch,Thrown}},
x_exc_exception(Rtt, M, F, Args, Arity, CR),
expect({eft,{?MODULE,exc,2},CR}),
CR;
- Class:Reason ->
+ Class:Reason:Stk ->
+ put(get_stacktrace, Stk),
CR = {Class,Reason},
x_exc_exception(Rtt, M, F, Args, Arity, CR),
expect({eft,{?MODULE,exc,2},CR}),
@@ -1157,7 +1159,8 @@ x_exc_exception(_Rtt, M, F, _, Arity, CR) ->
expect({eft,{M,F,Arity},CR}).
x_exc_stacktrace() ->
- x_exc_stacktrace(erlang:get_stacktrace()).
+ x_exc_stacktrace(get(get_stacktrace)).
+
%% Truncate stacktrace to below exc/2
x_exc_stacktrace([{?MODULE,x_exc,4,_}|_]) -> [];
x_exc_stacktrace([{?MODULE,x_exc_func,4,_}|_]) -> [];
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 605a402f2a..005a6e6d45 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -186,7 +186,7 @@ foreach my $type (keys %pattern_type) {
$construction_type{$type} = 1
if index($genop_types, $type) >= 0;
}
-foreach my $makes_no_sense ('f', 'j', 'o', 'p', 'q') {
+foreach my $makes_no_sense ('f', 'j', 'o', 'q') {
delete $construction_type{$makes_no_sense};
}
@@ -719,7 +719,7 @@ sub emulator_output {
print "const GenOpEntry gen_opc[] = {\n";
for ($i = 0; $i < @gen_opname; $i++) {
if ($i == $num_file_opcodes) {
- print "\n/*\n * Internal generic instructions.\n */\n\n";
+ print starred_comment("Internal generic instructions.");
}
my($name) = $gen_opname[$i];
my($arity) = $gen_arity[$i];
@@ -1315,7 +1315,7 @@ sub combine_instruction_group {
my $inc = 0;
unless ($i == $#slots) {
- $flags = "-no_next";
+ $flags = "-micro_instruction";
my $next_offset = $label_to_offset{$next};
$inc = ($offset + $size) - $next_offset;
$transfer_to_next = "I += $inc;\n" if $inc;
@@ -1553,8 +1553,10 @@ sub code_gen {
my $dispatch_next;
my $instr_offset = $group_size + $offset + 1;
- if ($flags =~ /-no_next/) {
+ if ($flags =~ /-micro_instruction/) {
$dispatch_next = "";
+ } elsif ($flags =~ /-no_next/) {
+ $dispatch_next = "ASSERT(!\"Fell through '$name' (-no_next)\");";
} elsif ($flags =~ /-no_prefetch/) {
$dispatch_next = "\nI += $instr_offset;\n" .
"ASSERT(VALID_INSTR(*I));\n" .
@@ -2238,6 +2240,7 @@ sub parse_transformation {
(undef,$_) = compile_transform(0, $rest_var, @op);
}
}
+ $orig =~ tr/ \t/ /s;
push(@transformations, [$., $orig, [@from], [reverse @to]]);
}
@@ -2396,6 +2399,13 @@ sub tr_gen {
}
#
+ # Group instructions.
+ #
+ foreach $key (sort keys %gen_transform) {
+ $gen_transform{$key} = group_tr($gen_transform{$key});
+ }
+
+ #
# Print the generated transformation engine.
#
my($offset) = 0;
@@ -2404,29 +2414,18 @@ sub tr_gen {
$gen_transform_offset{$key} = $offset;
my @instr = @{$gen_transform{$key}};
- #
- # If the last instruction is 'fail', remove it and
- # convert the previous 'try_me_else' to 'try_me_else_fail'.
- #
- if (is_instr($instr[$#instr], 'fail')) {
- pop(@instr);
- my $i = $#instr;
- $i-- while !is_instr($instr[$i], 'try_me_else');
- $instr[$i] = make_op('', 'try_me_else_fail');
- }
-
foreach $instr (@instr) {
my($size, $instr_ref, $comment) = @$instr;
my($op, @args) = @$instr_ref;
- print " ";
if (!defined $op) {
$comment =~ s/\n(.)/\n $1/g;
- print "\n", $comment;
+ print $comment;
} else {
+ print " ";
$op = "TOP_$op";
$match_engine_ops{$op} = 1;
if ($comment ne '') {
- printf "%-24s /* %s */\n", (join(", ", ($op, @args)) . ","),
+ printf "%-30s /* %s */\n", (join(", ", ($op, @args)) . ","),
$comment;
} else {
print join(", ", ($op, @args)), ",\n";
@@ -2436,9 +2435,7 @@ sub tr_gen {
}
print "\n";
}
- print "/*\n";
- print " * Total number of words: $offset\n";
- print " */\n";
+ print starred_comment("Total number of words: $offset");
print "};\n\n";
}
@@ -2452,6 +2449,7 @@ sub tr_gen_from {
my $where = "left side of transformation in line $line: ";
my $may_fail = 0;
my $is_first = 1;
+ my @instrs;
foreach $ref (@tr) {
my($name, $arity, @ops) = @$ref;
@@ -2469,20 +2467,26 @@ sub tr_gen_from {
$name = $1;
$may_fail = 1;
my $var;
- my(@args);
+ my @args;
+ my @vars;
foreach $var (@ops) {
- error($where, "variable '$var' unbound")
+ if ($var =~ /^-?\d+$/) {
+ push @args, $var;
+ next;
+ }
+ error($where, "'$var' unbound")
unless defined $var{$var};
+ push @vars, $var;
if ($var_type{$var} eq 'scalar') {
push(@args, "var[$var{$var}]");
} else {
push(@args, "rest_args");
}
}
- my $pi = tr_next_index(\@pred_table, \%pred_table, $name, @args);
- my $op = make_op("$name()", 'pred', $pi);
- my @slots = grep(/^\d+/, map { $var{$_} } @ops);
+ my $pi = tr_next_index(\@{pred_table}, \%pred_table, $name, @args);
+ my $op = make_op("$name()", 'pred', $pi);
+ my @slots = grep(/^\d+/, map { $var{$_} } @vars);
op_slot_usage($op, @slots);
push(@code, $op);
next;
@@ -2497,6 +2501,7 @@ sub tr_gen_from {
$opnum = $gen_opnum{$name,$arity};
push(@code, make_op("$name/$arity", 'next_instr', $opnum));
+ push @instrs, "$name/$arity";
foreach $op (@ops) {
my($var, $type, $type_val, $cond, $val) = @$op;
my $ignored_var = "$var (ignored)";
@@ -2587,14 +2592,47 @@ sub tr_gen_from {
#
push(@code, make_op($may_fail ? '' : 'always reached', 'commit'));
+ #
+ # Peephole optimization: combine instructions.
+ #
+ for (my $i = 0; $i < @code; $i++) {
+ if (is_instr($code[$i], 'is_type')) {
+ my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
+ if (is_instr($code[$i+1], 'set_var_next_arg')) {
+ my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
+ my $comment = "$type_comment $next_comment";
+ my $op = make_op($comment, 'is_type_set_var_next_arg',
+ $is_type_ref->[1], $next_ref->[1]);
+ splice @code, $i, 2, ($op);
+ } elsif (is_instr($code[$i+1], 'next_arg')) {
+ my $op = make_op($type_comment, 'is_type_next_arg', $is_type_ref->[1]);
+ splice @code, $i, 2, ($op);
+ }
+ } elsif (is_instr($code[$i], 'is_type_eq')) {
+ my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
+ if (is_instr($code[$i+1], 'set_var_next_arg')) {
+ my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
+ my $comment = "$type_comment $next_comment";
+ my $op = make_op($comment, 'is_type_eq_set_var_next_arg',
+ $is_type_ref->[1], $is_type_ref->[2],
+ $next_ref->[1]);
+ splice @code, $i, 2, ($op);
+ } elsif (is_instr($code[$i+1], 'next_arg')) {
+ my $op = make_op($type_comment, 'is_type_eq_next_arg',
+ $is_type_ref->[1], $is_type_ref->[2]);
+ splice @code, $i, 2, ($op);
+ }
+ }
+ }
+
$te_max_vars = $var_num
if $te_max_vars < $var_num;
- [\%var, \%var_type, \@code];
+ [\%var, \%var_type, \@instrs, \@code];
}
sub tr_gen_to {
my($line, $orig_transform, $so_far, @tr) = @_;
- my($var_ref, $var_type_ref, $code_ref) = @$so_far;
+ my($var_ref, $var_type_ref, $instrs_ref, $code_ref) = @$so_far;
my(%var) = %$var_ref;
my(%var_type) = %$var_type_ref;
my(@code) = @$code_ref;
@@ -2662,11 +2700,10 @@ sub tr_gen_to {
op_slot_usage($op, $var{$var});
push(@code, $op);
} elsif ($type ne '') {
- push(@code, make_op('', 'store_type', "TAG_$type"));
- if ($type_val) {
- push(@code, make_op('', 'store_val', $type_val));
- }
- push(@code, make_op('', 'next_arg'));
+ my $val = $type_val || 0;
+ my $comment = "$type=$val";
+ my $op = make_op($comment, 'store_val_next_arg', "TAG_$type", $val);
+ push @code, $op;
}
}
pop(@code) if is_instr($code[$#code], 'next_arg');
@@ -2677,31 +2714,133 @@ sub tr_gen_to {
tr_maybe_keep(\@code);
tr_maybe_rename(\@code);
+ combine_commit(\@code);
tr_remove_unused(\@code);
+ chain_instructions($instrs_ref, $line, $orig_transform,
+ $cannot_fail, @code);
+}
+
+#
+# Chain together all codes segments having the same first instruction.
+#
+sub chain_instructions() {
+ my($instrs_ref, $line, $orig_transform, $cannot_fail, @code) = @_;
+ my($first,$second) = @$instrs_ref;
+
+ my $tr_ref = $gen_transform{$first};
+
+ if ($tr_ref) {
+ my (undef, $cant_fail) = $$tr_ref[$#{$tr_ref}];
+ if ($cant_fail) {
+ error("Line $line: A previous transformation shadows '$orig_transform'");
+ }
+ }
+ shift @code;
+ my $comment = starred_comment("Line $line:", " $orig_transform");
+ push @$tr_ref, [$first,$second,$cannot_fail,$comment,@code];
+
+ $gen_transform{$first} = $tr_ref;
+}
+
+#
+# Optimize the code for transformations matching the same first
+# instruction.
+#
+
+sub group_tr {
+ my($lref) = @_;
+
#
- # Chain together all codes segments having the same first operation.
+ # Group tranformations (while keeping the order) that all match the same
+ # second instruction.
#
- my($first_ref) = shift(@code);
- my($size, $first, $key) = @$first_ref;
- my($dummy, $arity);
- ($dummy, $op, $arity) = @$first;
- my($comment) = "\n/*\n * Line $line:\n * $orig_transform\n */\n\n";
+ for (my $i = 0; $i < @$lref; $i++) {
+ my(undef,$current) = @{${$lref}[$i]};
+ next unless defined $current;
+
+ # Find the next instruction that as the same second instruction.
+ for (my $j = $i + 1; $j < @$lref; $j++) {
+ my(undef,$other) = @{${$lref}[$j]};
- my $prev_last;
- $prev_last = pop(@{$gen_transform{$key}})
- if defined $gen_transform{$key}; # Fail
+ # If this instruction does not match a second instruction,
+ # we must not continue the search.
+ last unless defined $other;
- if ($prev_last && !is_instr($prev_last, 'fail')) {
- error("Line $line: A previous transformation shadows '$orig_transform'");
+ if ($other eq $current) {
+ # Found an instruction. If it is already the very next
+ # instruction, place it directly after the current instruction.
+ if ($j > $i + 1) {
+ my($el) = splice @$lref, $j, 1;
+ splice @$lref, $i, 0, ($el);
+ }
+ last;
+ }
+ }
}
- unless ($cannot_fail) {
- unshift(@code, make_op('', 'try_me_else',
- tr_code_len(@code)));
- push(@code, make_op("$key", 'fail'));
+
+ #
+ # Add 'try_me_else' instructions to try the next transformation
+ # when the current transformation fails.
+ #
+ for (my $i = 0; $i < @$lref; $i++) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$i]};
+ unless ($cannot_fail) {
+ if ($i == $#{$lref}) {
+ unshift @c, make_op('', 'try_me_else_fail');
+ } else {
+ unshift @c, make_op('', 'try_me_else', code_len(@c));
+ }
+ }
+ ${$lref}[$i] = [$first,$second,$cannot_fail,$comment,@c];
+ }
+
+ #
+ # Find consecutive runs of at least two transformation matching
+ # the same second instruction. When a run is found, add a
+ # 'skip_unless' instruction that will skip all of the instructions in the run
+ # when the second instruction is wrong.
+ #
+ for (my $i = 0; $i < @$lref; $i++) {
+ my(undef,$current) = @{${$lref}[$i]};
+ next unless defined $current;
+ my $j;
+ my $skip_len = 0;
+
+ for ($j = $i; $j < @$lref; $j++) {
+ my(undef,$other,undef,undef,@c) = @{${$lref}[$j]};
+ last unless defined $other and $other eq $current;
+ $skip_len += code_len(@c);
+ }
+
+ if ($j > $i + 1) {
+ my $num_rules_skipped = $j - $i;
+ my $comment = "Skip $num_rules_skipped rules" .
+ " unless the second instruction is $current.";
+ $comment = starred_comment($comment);
+ my($name, $arity) = split('/', $current);
+ my $op = make_op('', 'skip_unless',
+ $gen_opnum{$name,$arity}, $skip_len);
+ splice @$lref, $i, 0, (['','',1,$comment,$op]);
+ $i = $j + 1;
+ if ($j == $#{$lref}) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$j]};
+ push @c, make_op('wrong second instruction', 'fail');
+ ${$lref}[$j] = [$first,$second,$cannot_fail,$comment,@c];
+ }
+ }
}
- unshift(@code, make_op($comment));
- push(@{$gen_transform{$key}}, @code),
+
+ #
+ # Flatten the code to a one-dimensional sequence of instructions.
+ #
+ my @code;
+ for (my $i = 0; $i < @$lref; $i++) {
+ my($first,$second,$cannot_fail,$comment,@c) = @{${$lref}[$i]};
+ push @code, make_op($comment);
+ push @code, @c;
+ }
+ \@code;
}
sub tr_maybe_keep {
@@ -2718,6 +2857,10 @@ sub tr_maybe_keep {
@last_instr = ($args[0]);
} elsif ($op eq 'set_var_next_arg') {
push @last_instr, $args[0];
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ push @last_instr, $args[1];
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ push @last_instr, $args[2];
} elsif ($op eq 'next_arg') {
push @last_instr, 'ignored';
} elsif ($op eq 'new_instr') {
@@ -2744,6 +2887,22 @@ sub tr_maybe_keep {
}
}
+sub combine_commit {
+ my($ref) = @_;
+
+ for (my $i = 1; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+ if ($op eq 'rest_args') {
+ return;
+ } elsif ($op eq 'new_instr' and is_instr($$ref[$i-1], 'commit')) {
+ my $op = make_op($comment, 'commit_new_instr', @args);
+ splice @$ref, $i - 1, 2, ($op);
+ }
+ }
+}
+
sub tr_maybe_rename {
my($ref) = @_;
my $s = 'left';
@@ -2764,8 +2923,22 @@ sub tr_maybe_rename {
$num_args++;
}
$a++;
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ if ($num_args == $a and $args[1] == $a) {
+ $num_args++;
+ }
+ $a++;
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ if ($num_args == $a and $args[2] == $a) {
+ $num_args++;
+ }
+ $a++;
} elsif ($op eq 'next_arg') {
$a++;
+ } elsif ($op eq 'is_type_next_arg') {
+ $a++;
+ } elsif ($op eq 'is_type_eq_next_arg') {
+ $a++;
} elsif ($op eq 'commit') {
$a = 0;
$first = $i;
@@ -2809,8 +2982,7 @@ sub tr_remove_unused {
}
}
- # Replace 'set_var_next_arg' with 'next_arg' if the variable
- # is never used.
+ # If a variable is not used, don't store the variable.
for my $instr (@$ref) {
my($size, $instr_ref, $comment) = @$instr;
my($op, @args) = @$instr_ref;
@@ -2818,7 +2990,16 @@ sub tr_remove_unused {
my $var = $args[0];
next if $used{$var};
$instr = make_op("$comment (ignored)", 'next_arg');
- }
+ } elsif ($op eq 'is_type_set_var_next_arg') {
+ my($type,$var) = @args;
+ next if $used{$var};
+ $instr = make_op("$comment (ignored)", 'is_type_next_arg', $type);
+ } elsif ($op eq 'is_type_eq_set_var_next_arg') {
+ my($type,$val,$var) = @args;
+ next if $used{$var};
+ $instr = make_op("$comment (ignored)", 'is_type_eq_next_arg',
+ $type, $val);
+ }
}
# Delete a sequence of 'next_arg' instructions when they are
@@ -2846,7 +3027,7 @@ sub tr_remove_unused {
}
}
-sub tr_code_len {
+sub code_len {
my($sum) = 0;
my($ref);
@@ -2878,6 +3059,10 @@ sub get_comment {
$ref->[2];
}
+sub starred_comment {
+ "\n/*" . join("\n * ", '', @_) . "\n */\n\n";
+}
+
sub tr_next_index {
my($lref,$href,$name,@args) = @_;
my $code = "RVAL = $name(" . join(', ', 'st', @args) . "); break;\n";
diff --git a/erts/emulator/utils/make_alloc_types b/erts/emulator/utils/make_alloc_types
index 33afe139a2..ddb8713a72 100755
--- a/erts/emulator/utils/make_alloc_types
+++ b/erts/emulator/utils/make_alloc_types
@@ -233,6 +233,7 @@ foreach my $a (@a_order) {
$a_no--;
print DST "\n#define ERTS_ALC_A_MAX ($a_no)\n";
+print DST "\n#define ERTS_ALC_A_COUNT (ERTS_ALC_A_MAX - ERTS_ALC_A_MIN + 1)\n";
# Print class numbers -----------------------------------------------------
@@ -254,6 +255,7 @@ foreach my $c (sort keys(%c_tab)) {
}
$c_no--;
print DST "\n#define ERTS_ALC_C_MAX ($c_no)\n";
+print DST "\n#define ERTS_ALC_C_COUNT (ERTS_ALC_C_MAX - ERTS_ALC_C_MIN + 1)\n";
# Print type number intervals ---------------------------------------------
@@ -291,6 +293,7 @@ foreach my $a (@a_order) {
}
$t_no--;
print DST "#define ERTS_ALC_N_MAX ($t_no)\n";
+print DST "\n#define ERTS_ALC_N_COUNT (ERTS_ALC_N_MAX - ERTS_ALC_N_MIN + 1)\n";
# Print multi thread use of allocators -------------------------------------
diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload
index 0cd3509b62..04163bc63b 100755
--- a/erts/emulator/utils/make_preload
+++ b/erts/emulator/utils/make_preload
@@ -40,7 +40,6 @@ use File::Basename;
my $gen_rc = 0;
my $gen_old = 0;
my $windres = 0;
-my $msys = 0;
my $file;
my $progname = basename($0);
@@ -51,8 +50,6 @@ while (@ARGV && $ARGV[0] =~ /^-(\w+)/) {
$gen_rc = 1;
} elsif ($opt eq '-windres') {
$windres = 1;
- } elsif ($opt eq '-msys') {
- $msys = 1;
} elsif ($opt eq '-old') {
$gen_old = 1;
} else {
@@ -73,12 +70,7 @@ foreach $file (@ARGV) {
my $module = basename($file, ".beam");
if ($gen_rc) {
my $win_file;
- if ($msys) {
- ($win_file) = split("\n", `(msys2win_path.sh $file)`);
- } else {
- ($win_file) = split("\n", `(cygpath -d $file 2>/dev/null || cygpath -w $file)`);
- }
- $win_file =~ s&\\&\\\\&g;
+ ($win_file) = split("\n", `(w32_path.sh -d $file)`);
print "$num ERLANG_CODE \"$win_file\"\n";
push(@modules, " ", -s $file, "L, $num, ",
length($module), ",\"$module\",\n");
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index deee5c2344..f87472111f 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -35,7 +35,6 @@ use File::Basename;
# Output:
# <-src>/erl_am.c
# <-src>/erl_bif_table.c
-# <-src>/erl_bif_wrap.c
# <-src>/erl_dirty_bif_wrap.c
# <-src>/erl_guard_bifs.c
# <-src>/hipe_nbif_impl.c
@@ -92,7 +91,7 @@ while (<>) {
my($type, @args) = split;
if ($type eq 'atom') {
save_atoms(@args);
- } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'gcbif') {
+ } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'hbif') {
if (@args > 2) {
error("$type only allows two arguments");
}
@@ -124,14 +123,22 @@ while (<>) {
error("invalid sched_type: $sched_type");
}
- my $wrapper;
- if ($type eq 'bif') {
- $wrapper = "wrap_$alias";
- } else {
- $wrapper = $alias;
- }
+ my $kind;
+ if ($type eq 'bif') {
+ $kind = 'BIF_KIND_REGULAR';
+ }
+ elsif ($type eq 'hbif') {
+ $kind = 'BIF_KIND_HEAVY';
+ }
+ elsif ($type eq 'ubif') {
+ $kind = 'BIF_KIND_GUARD';
+ }
+ else {
+ error("invalid bif_type: $type");
+ }
+
push(@bif, ["am_$atom_alias{$mod}","am_$atom_alias{$name}",$arity,
- $alias3,$wrapper,$alias]);
+ $alias3,$alias,$kind]);
push(@bif_info, [$type, $sched_type, $alias3, $alias]);
} elsif ($type eq 'dirty-cpu' or $type eq 'dirty-io'
or $type eq 'dirty-cpu-test' or $type eq 'dirty-io-test') {
@@ -196,7 +203,7 @@ open_file("$include/erl_bif_list.h");
my $i;
for ($i = 0; $i < @bif; $i++) {
# module atom, function atom, arity, C function, table index
- print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[5],$i)\n";
+ print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[4],$i)\n";
}
#
@@ -208,15 +215,24 @@ my $bif_size = @bif;
print <<EOF;
#ifndef __ERL_BIF_TABLE_H__
#define __ERL_BIF_TABLE_H__
+
+#include "sys.h"
+
typedef void *BifFunction;
+typedef enum {
+ BIF_KIND_REGULAR,
+ BIF_KIND_HEAVY,
+ BIF_KIND_GUARD
+} BifKind;
+
typedef struct bif_entry {
Eterm module;
Eterm name;
int arity;
BifFunction f;
- BifFunction traced;
BifFunction impl;
+ BifKind kind;
} BifEntry;
typedef struct erts_gc_bif {
@@ -231,8 +247,7 @@ typedef struct erts_u_bif {
} ErtsUBif;
extern BifEntry bif_table[];
-extern Export* bif_export[];
-extern const ErtsGcBif erts_gc_bifs[];
+extern Export bif_trap_export[];
extern const ErtsUBif erts_u_bifs[];
#define BIF_SIZE $bif_size
@@ -250,7 +265,6 @@ for ($i = 0; $i < @bif; $i++) {
my $args = join(', ', 'Process*', 'Eterm*', 'UWord*');
my $name = $bif_info[$i]->[3];
print "Eterm $name($args);\n";
- print "Eterm wrap_$name($args);\n";
print "Eterm erts_gc_$name(Process* p, Eterm* reg, Uint live);\n"
if $bif_info[$i]->[0] eq 'gcbif';
print "Eterm $bif_info[$i]->[2]($args);\n"
@@ -273,7 +287,7 @@ my $i;
includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h",
"erl_bif_table.h", "erl_atom_table.h");
-print "\nExport* bif_export[BIF_SIZE];\n";
+print "\nExport bif_trap_export[BIF_SIZE];\n";
print "BifEntry bif_table[] = {\n";
for ($i = 0; $i < @bif; $i++) {
@@ -283,25 +297,6 @@ for ($i = 0; $i < @bif; $i++) {
print "};\n\n";
#
-# Generate the bif wrappers file.
-#
-
-open_file("$src/erl_bif_wrap.c");
-my $i;
-includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
- "erl_bif_table.h", "erl_atom_table.h");
-for ($i = 0; $i < @bif; $i++) {
- next if $bif[$i]->[3] eq $bif[$i]->[4]; # Skip unwrapped bifs
- my $arity = $bif[$i]->[2];
- my $func = $bif_info[$i]->[3];
- print "Eterm\n";
- print "wrap_$func(Process* p, Eterm* args, UWord* I)\n";
- print "{\n";
- print " return erts_bif_trace($i, p, args, I);\n";
- print "}\n\n";
-}
-
-#
# Generate erl_gc_bifs.c.
#
@@ -309,18 +304,11 @@ open_file("$src/erl_guard_bifs.c");
my $i;
includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
"erl_bif_table.h");
-print "const ErtsGcBif erts_gc_bifs[] = {\n";
-for ($i = 0; $i < @bif; $i++) {
- next unless $bif_info[$i]->[0] eq 'gcbif';
- print " {$bif[$i]->[3], erts_gc_$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
-}
-print " {NULL, NULL, -1}\n";
-print "};\n";
print "const ErtsUBif erts_u_bifs[] = {\n";
for ($i = 0; $i < @bif; $i++) {
next unless $bif_info[$i]->[0] eq 'ubif';
- print " {$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
+ print " {$bif[$i]->[3], BIF_$bif[$i]->[4]},\n";
}
print " {NULL, -1}\n";
print "};\n";
@@ -368,7 +356,7 @@ EOF
my $i;
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs);
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs);
EOF
}
@@ -388,9 +376,9 @@ EOF
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs)
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs)
{
- return $bif[$i]->[3](c_p, regs, (UWord *) bif_export\[BIF_$bif[$i]->[5]\]);
+ return $bif[$i]->[3](c_p, regs, (UWord *)&bif_trap_export\[BIF_$bif[$i]->[4]\]);
}
EOF
diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile
index d3308ddedc..e4b201bd88 100644
--- a/erts/epmd/Makefile
+++ b/erts/epmd/Makefile
@@ -31,3 +31,5 @@ SPECIAL_TARGETS =
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk
index b1fd04dc04..f6889a7ff1 100644
--- a/erts/epmd/epmd.mk
+++ b/erts/epmd/epmd.mk
@@ -67,5 +67,5 @@ EPMD_NODE_TYPE=110
# Distribution format 5 contains the new md5 based handshake.
EPMD_DIST_LOW=5
-EPMD_DIST_HIGH=5
+EPMD_DIST_HIGH=6
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index da4370d5f9..f9f65cec5e 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -53,12 +53,8 @@ ERTS_INCL = -I$(ERL_TOP)/erts/include \
ifeq ($(TARGET),win32)
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
-else
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
endif
-endif
ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 8313678b5d..1abc3d2fca 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -81,48 +81,6 @@ static char *mystrdup(char *s)
return r;
}
-#ifdef VXWORKS
-int start_epmd(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
-char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
-{
- char* argarr[] = {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9};
- int i;
- char** argv = malloc(sizeof(char *)*10);
- int argvsiz = 10;
- int argc = 1;
- char* tmp = malloc(100);
- int tmpsiz = 100;
- char* pplast;
- char* token;
-
- argv[0] = mystrdup("epmd");
- argv[1] = NULL;
-
- for(i=0;i<10;++i)
- {
- if(argarr[i] == NULL || *argarr[i] == '\0')
- continue;
- if(strlen(argarr[i]) >= tmpsiz)
- tmp = realloc(tmp, tmpsiz = (strlen(argarr[i])+1));
- strcpy(tmp,argarr[i]);
- for(token = strtok_r(tmp," ",&pplast);
- token != NULL;
- token = strtok_r(NULL," ",&pplast))
- {
- if(argc >= argvsiz - 1)
- argv = realloc(argv,sizeof(char *) * (argvsiz += 10));
- argv[argc++] = mystrdup(token);
- argv[argc] = NULL;
- }
- }
- free(tmp);
- return taskSpawn("epmd",100,VX_FP_TASK,20000,epmd_main,
- argc,(int) argv,1,
- 0,0,0,0,0,0,0);
-}
-#endif /* WxWorks */
-
-
int epmd(int argc, char **argv)
{
return epmd_main(argc,argv,0);
@@ -397,13 +355,6 @@ static void run_daemon(EpmdVars *g)
}
#endif
-#if defined(VXWORKS)
-static void run_daemon(EpmdVars *g)
-{
- run(g);
-}
-#endif
-
/***************************************************************************
* Misc support routines
@@ -474,7 +425,7 @@ static void usage(EpmdVars *g)
* Error reporting - dbg_printf() & dbg_tty_printf & dbg_perror()
*
* The first form will print out on tty or syslog depending on
- * if it runs as deamon or not. The second form will never print
+ * if it runs as daemon or not. The second form will never print
* out on syslog.
*
* The arguments are
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
index cffcd4ae7a..7332294d3d 100644
--- a/erts/epmd/src/epmd.h
+++ b/erts/epmd/src/epmd.h
@@ -26,6 +26,7 @@
#define EPMD_ALIVE2_REQ 'x'
#define EPMD_PORT2_REQ 'z'
#define EPMD_ALIVE2_RESP 'y'
+#define EPMD_ALIVE2_X_RESP 'v'
#define EPMD_PORT2_RESP 'w'
#define EPMD_NAMES_REQ 'n'
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index ed9bbdb8cd..c51ff22d82 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -30,13 +30,6 @@
#define NO_DAEMON
#endif
-#ifdef VXWORKS
-#define NO_SYSCONF
-#define NO_DAEMON
-#define NO_FCNTL
-#define DONT_USE_MAIN
-#endif
-
/* ************************************************************************ */
/* Standard includes */
@@ -56,16 +49,6 @@
#include <sys/types.h>
#include <fcntl.h>
-#ifdef VXWORKS
-# include <sys/times.h>
-# include <time.h>
-# include <selectLib.h>
-# include <sysLib.h>
-# include <sockLib.h>
-# include <ioLib.h>
-# include <taskLib.h>
-# include <rpc/rpc.h>
-#else /* ! VXWORKS */
#ifndef __WIN32__
# ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
@@ -78,7 +61,6 @@
# endif
# endif
#endif
-#endif /* ! VXWORKS */
#if !defined(__WIN32__)
# include <netinet/in.h>
@@ -130,10 +112,6 @@
# define ioctl(s,r,o) ioctlsocket((s),(r),(o))
#endif /* WIN32 */
-#ifdef VXWORKS
-#define sleep(n) taskDelay((n) * sysClkRateGet())
-#endif /* VXWORKS */
-
#ifdef USE_BCOPY
# define memcpy(a, b, c) bcopy((b), (a), (c))
# define memcmp(a, b, c) bcmp((a), (b), (c))
@@ -277,6 +255,12 @@ static const struct in6_addr in6addr_loopback =
#define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
((unsigned char*)(s))[1] = (i) & 0xff;}
+#define put_int32(i, s) do {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
+ ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
+ ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
+ ((char*)(s))[3] = (char)(i) & 0xff;} \
+ while (0)
+
#if defined(__GNUC__)
# define EPMD_INLINE __inline__
#elif defined(__WIN32__)
@@ -287,7 +271,7 @@ static const struct in6_addr in6addr_loopback =
/* ************************************************************************ */
-/* Stuctures used by server */
+/* Structures used by server */
typedef struct {
int fd; /* File descriptor */
@@ -307,10 +291,10 @@ struct enode {
int fd; /* The socket in use */
unsigned short port; /* Port number of Erlang node */
char symname[MAXSYMLEN+1]; /* Name of the Erlang node */
- short creation; /* Started as a random number 1..3 */
+ unsigned int cr_counter; /* Used to generate 'creation' numbers */
char nodetype; /* 77 = normal erlang node 72 = hidden (c-node */
char protocol; /* 0 = tcp/ipv4 */
- unsigned short highvsn; /* 0 = OTP-R3 erts-4.6.x, 1 = OTP-R4 erts-4.7.x*/
+ unsigned short highvsn; /* 5: creation=1..3, 6: creation=1..(2^32-1)*/
unsigned short lowvsn;
int extralen;
char extra[MAXSYMLEN+1];
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 3c6f1fbdf4..b04a58bcad 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -38,7 +38,7 @@
* register their names with this server.
*
* To be accessible to all Erlang nodes this server listens to a well
- * known port EPMD_PORT_NO (curently port 4369) where requests
+ * known port EPMD_PORT_NO (currently port 4369) where requests
* for connections can be sent.
*
* To keep track of when registered Erlang nodes are terminated this
@@ -59,7 +59,7 @@
/* We use separate data structures for node names and connections
so that a request will not use a slot with a name that we
- want to resuse later incrementing the "creation" */
+ want to reuse later incrementing the "creation" */
/* forward declarations */
@@ -410,12 +410,11 @@ void run(EpmdVars *g)
in accept() waiting for the next request. */
#if (defined(__WIN32__) || defined(NO_FCNTL))
opt = 1;
- /* Gives warning in VxWorks */
if (ioctl(listensock[i], FIONBIO, &opt) != 0)
#else
opt = fcntl(listensock[i], F_GETFL, 0);
if (fcntl(listensock[i], F_SETFL, opt | O_NONBLOCK) == -1)
-#endif /* __WIN32__ || VXWORKS */
+#endif /* __WIN32__ */
dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
listensock[i]);
@@ -525,7 +524,7 @@ void run(EpmdVars *g)
/*
* This routine read as much of the packet as possible and
- * if completed calls "do_request()" to fullfill the request.
+ * if completed calls "do_request()" to fulfill the request.
*
*/
@@ -665,6 +664,21 @@ static int do_accept(EpmdVars *g,int listensock)
return conn_open(g,msgsock);
}
+static void bump_creation(Node* node)
+{
+ if (++node->cr_counter < 4)
+ node->cr_counter = 4;
+}
+static unsigned int get_creation(Node* node)
+{
+ if (node->highvsn >= 6) {
+ return node->cr_counter; /* 4..(2^32-1)*/
+ }
+ else {
+ return node->cr_counter % 3 + 1; /* 1..3 */
+ }
+}
+
/* buf is actually one byte larger than bsize,
giving place for null termination */
static void do_request(g, fd, s, buf, bsize)
@@ -706,8 +720,10 @@ static void do_request(g, fd, s, buf, bsize)
unsigned char protocol;
unsigned short highvsn;
unsigned short lowvsn;
+ unsigned int creation;
int namelen;
int extralen;
+ int replylen;
char *name;
char *extra;
eport = get_int16(&buf[1]);
@@ -737,17 +753,22 @@ static void do_request(g, fd, s, buf, bsize)
extra = &buf[11+namelen+2];
extra[extralen]='\000';
- wbuf[0] = EPMD_ALIVE2_RESP;
- if ((node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
- highvsn, lowvsn, extralen, extra)) == NULL) {
- wbuf[1] = 1; /* error */
- put_int16(99, wbuf+2);
- } else {
- wbuf[1] = 0; /* ok */
- put_int16(node->creation, wbuf+2);
- }
+ node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
+ highvsn, lowvsn, extralen, extra);
+ creation = node ? get_creation(node) : 99;
+ wbuf[1] = node ? 0 : 1; /* ok | error */
+ if (highvsn >= 6) {
+ wbuf[0] = EPMD_ALIVE2_X_RESP;
+ put_int32(creation, wbuf+2);
+ replylen = 6;
+ }
+ else {
+ wbuf[0] = EPMD_ALIVE2_RESP;
+ put_int16(creation, wbuf+2);
+ replylen = 4;
+ }
- if (!reply(g, fd, wbuf, 4))
+ if (!reply(g, fd, wbuf, replylen))
{
node_unreg(g, name);
dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"",
@@ -1033,23 +1054,6 @@ static int conn_open(EpmdVars *g,int fd)
int i;
Connection *s;
-#ifdef VXWORKS
- /*
- * Since file descriptors are global on VxWorks, we might get an fd that
- * does not fit in the FD_SET.
- *
- * Note: This test would be harmless on Unix, but would fail on Windows
- * because socket are numbered differently and FD_SETs are implemented
- * differently.
- */
- if (fd >= FD_SETSIZE) {
- dbg_tty_printf(g,0,"file descriptor %d: too high for FD_SETSIZE=%d",
- fd,FD_SETSIZE);
- close(fd);
- return EPMD_FALSE;
- }
-#endif
-
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
g->active_conn++;
@@ -1200,8 +1204,8 @@ static int node_unreg(EpmdVars *g,char *name)
for (; node; prev = &node->next, node = node->next)
if (is_same_str(node->symname, name))
{
- dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1235,8 +1239,8 @@ static int node_unreg_sock(EpmdVars *g,int fd)
for (; node; prev = &node->next, node = node->next)
if (node->fd == fd)
{
- dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1264,19 +1268,8 @@ static int node_unreg_sock(EpmdVars *g,int fd)
}
/*
- * Finding a node slot and a (name,creation) name is a bit tricky.
- * We try in order
- *
- * 1. If the name was used before and we can reuse that slot but use
- * a new "creation" digit in the range 1..3.
- *
- * 2. We try to find a new unused slot.
- *
- * 3. We try to use an used slot this isn't used any longer.
- * FIXME: The criteria for *what* slot to steal should be improved.
- * Perhaps use the oldest or something.
+ * Register a new node
*/
-
static Node *node_reg2(EpmdVars *g,
int namelen,
char* name,
@@ -1346,7 +1339,7 @@ static Node *node_reg2(EpmdVars *g,
}
/* Try to find the name in the used queue so that we
- can change "creation" number 1..3 */
+ can change "creation" number */
prev = NULL;
@@ -1375,9 +1368,8 @@ static Node *node_reg2(EpmdVars *g,
g->nodes.unreg_count--;
- /* When reusing we change the "creation" number 1..3 */
-
- node->creation = node->creation % 3 + 1;
+ /* When reusing we change the "creation" number */
+ bump_creation(node);
break;
}
@@ -1404,7 +1396,8 @@ static Node *node_reg2(EpmdVars *g,
exit(1);
}
- node->creation = (current_time(g) % 3) + 1; /* "random" 1-3 */
+ node->cr_counter = current_time(g); /* "random" */
+ bump_creation(node);
}
}
@@ -1423,11 +1416,11 @@ static Node *node_reg2(EpmdVars *g,
select_fd_set(g, fd);
if (highvsn == 0) {
- dbg_tty_printf(g,1,"registering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"registering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
} else {
- dbg_tty_printf(g,1,"registering '%s:%d', port %d",
- node->symname, node->creation, node->port);
+ dbg_tty_printf(g,1,"registering '%s:%u', port %d",
+ node->symname, get_creation(node), node->port);
dbg_tty_printf(g,1,"type %d proto %d highvsn %d lowvsn %d",
nodetype, protocol, highvsn, lowvsn);
}
@@ -1561,8 +1554,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.reg; node; node = node->next)
{
- fprintf(stderr,"***** active name \"%s#%d\" at port %d, fd = %d\r\n",
- node->symname, node->creation, node->port, node->fd);
+ fprintf(stderr,"***** active name \"%s#%u\" at port %d, fd = %d\r\n",
+ node->symname, get_creation(node), node->port, node->fd);
count ++;
}
@@ -1572,8 +1565,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.unreg; node; node = node->next)
{
- fprintf(stderr,"***** old/unused name \"%s#%d\"\r\n",
- node->symname, node->creation);
+ fprintf(stderr,"***** old/unused name \"%s#%u\"\r\n",
+ node->symname, get_creation(node));
count ++;
}
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
index ad1315440f..d55ccabfb2 100644
--- a/erts/epmd/test/Makefile
+++ b/erts/epmd/test/Makefile
@@ -73,7 +73,7 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) "$(RELEPMDDIR)"
- $(INSTALL_DATA) epmd.spec epmd.spec.vxworks $(ERL_FILES) \
+ $(INSTALL_DATA) epmd.spec $(ERL_FILES) \
$(EMAKEFILE) "$(RELEPMDDIR)"
chmod -R u+w "$(RELEPMDDIR)"
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 3a03374fbf..4df00377b7 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -23,7 +23,6 @@ include $(ERL_TOP)/make/target.mk
ERTS_LIB_TYPEMARKER=.$(TYPE)
-USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_VC=@MIXED_VC@
ifeq ($(TYPE),debug)
@@ -491,10 +490,8 @@ ifneq ($(INSTALL_OBJS),)
endif
$(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin"
ifneq ($(TARGET), win32)
-ifneq ($(findstring vxworks,$(TARGET)), vxworks)
$(INSTALL_SCRIPT) erl.src "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
-endif
ifneq ($(INSTALL_PROGS),)
$(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin"
endif
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index f18ed0c983..8f6095ef17 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -199,7 +199,7 @@ void error(char* format, ...);
static void usage_notsup(const char *switchname, const char *alt);
static char **build_args_from_env(char *env_var);
-static char **build_args_from_string(char *env_var);
+static char **build_args_from_string(char *env_var, int allow_comments);
static void initial_argv_massage(int *argc, char ***argv);
static void get_parameters(int argc, char** argv);
static void add_arg(char *new_arg);
@@ -648,11 +648,13 @@ int main(int argc, char **argv)
error("Conflicting -start_erl and -config options");
if (i+1 >= argc)
usage("-config");
- config_script_cnt++;
- config_scripts = erealloc(config_scripts,
- config_script_cnt * sizeof(char*));
- config_scripts[config_script_cnt-1] = strsave(argv[i+1]);
- i++;
+ do {
+ config_script_cnt++;
+ config_scripts = erealloc(config_scripts,
+ config_script_cnt * sizeof(char*));
+ config_scripts[config_script_cnt-1] = strsave(argv[i+1]);
+ i++;
+ } while ((i+1) < argc && argv[i+1][0] != '-' && argv[i+1][0] != '+');
}
#endif
else {
@@ -1238,7 +1240,7 @@ start_epmd(char *epmd)
if (!epmd) {
epmd = epmd_cmd;
#ifdef __WIN32__
- erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd", bindir);
+ erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\"", bindir);
arg1 = "-daemon";
#else
erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\" -daemon", bindir);
@@ -1657,12 +1659,12 @@ static void add_epmd_port(void)
static char **build_args_from_env(char *env_var)
{
char *value = get_env(env_var);
- char **res = build_args_from_string(value);
+ char **res = build_args_from_string(value, 0);
free_env_val(value);
return res;
}
-static char **build_args_from_string(char *string)
+static char **build_args_from_string(char *string, int allow_comments)
{
int argc = 0;
char **argv = NULL;
@@ -1671,7 +1673,7 @@ static char **build_args_from_string(char *string)
int s_alloced = 0;
int s_pos = 0;
char *p = string;
- enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state;
+ enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext, BuildComment} state;
#define ENSURE() \
if (s_pos >= s_alloced) { \
@@ -1703,12 +1705,24 @@ static char **build_args_from_string(char *string)
break;
case Build0:
switch (*p) {
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
case ' ':
++p;
break;
case '\0':
state = Start;
break;
+ case '#':
+ if (allow_comments) {
+ ++p;
+ state = BuildComment;
+ break;
+ }
+ /* fall-through */
default:
state = Build;
break;
@@ -1716,6 +1730,15 @@ static char **build_args_from_string(char *string)
break;
case Build:
switch (*p) {
+ case '#':
+ if (!allow_comments)
+ goto build_default;
+ /* fall-through */
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
case ' ':
case '\0':
ENSURE();
@@ -1736,6 +1759,7 @@ static char **build_args_from_string(char *string)
state = AcceptNext;
break;
default:
+ build_default:
ENSURE();
(*cur_s)[s_pos++] = *p++;
break;
@@ -1770,14 +1794,19 @@ static char **build_args_from_string(char *string)
}
break;
case AcceptNext:
- if (!*p) {
- state = Build;
- } else {
+ if (*p) {
ENSURE();
(*cur_s)[s_pos++] = *p++;
}
state = Build;
break;
+ case BuildComment:
+ if (*p == '\n' || *p == '\0') {
+ state = Build0;
+ } else {
+ p++;
+ }
+ break;
}
}
done:
@@ -1786,8 +1815,8 @@ done:
return NULL;
}
argv[argc++] = "--"; /* Add a -- separator in order
- for different from different environments
- to effect each other */
+ for flags from different environments
+ to not effect each other */
argv[argc++] = NULL; /* Sure to be large enough */
return argv;
#undef ENSURE
@@ -1802,30 +1831,16 @@ errno_string(void)
return str;
}
+#define FILE_BUFF_SIZE 1024
+
static char **
read_args_file(char *filename)
{
- int c, aix = 0, quote = 0, cmnt = 0, asize = 0;
- char **res, *astr = NULL;
FILE *file;
-
-#undef EAF_CMNT
-#undef EAF_QUOTE
-#undef SAVE_CHAR
-
-#define EAF_CMNT (1 << 8)
-#define EAF_QUOTE (1 << 9)
-#define SAVE_CHAR(C) \
- do { \
- if (!astr) \
- astr = emalloc(sizeof(char)*(asize = 20)); \
- if (aix == asize) \
- astr = erealloc(astr, sizeof(char)*(asize += 20)); \
- if (' ' != (char) (C)) \
- astr[aix++] = (char) (C); \
- else if (aix > 0 && astr[aix-1] != ' ') \
- astr[aix++] = ' '; \
- } while (0)
+ char buff[FILE_BUFF_SIZE+1];
+ size_t astr_sz = 0, sz;
+ char *astr = buff;
+ char **res;
do {
errno = 0;
@@ -1837,63 +1852,41 @@ read_args_file(char *filename)
errno_string());
}
- while (1) {
- c = getc(file);
- if (c == EOF) {
- if (ferror(file)) {
- if (errno == EINTR) {
- clearerr(file);
- continue;
- }
- usage_format("Failed to read arguments file \"%s\": %s\n",
- filename,
- errno_string());
- }
- break;
- }
+ sz = fread(astr, 1, FILE_BUFF_SIZE, file);
- switch (quote | cmnt | c) {
- case '\\':
- quote = EAF_QUOTE;
- break;
- case '#':
- cmnt = EAF_CMNT;
- break;
- case EAF_CMNT|'\n':
- cmnt = 0;
- /* Fall through... */
- case '\n':
- case '\f':
- case '\r':
- case '\t':
- case '\v':
- if (!quote)
- c = ' ';
- /* Fall through... */
- default:
- if (!cmnt)
- SAVE_CHAR(c);
- quote = 0;
- break;
- }
+ while (!feof(file) && sz == FILE_BUFF_SIZE) {
+ if (astr == buff) {
+ astr = emalloc(FILE_BUFF_SIZE*2+1);
+ astr_sz = FILE_BUFF_SIZE;
+ memcpy(astr, buff, sizeof(buff));
+ } else {
+ astr_sz += FILE_BUFF_SIZE;
+ astr = erealloc(astr,astr_sz+FILE_BUFF_SIZE+1);
+ }
+ sz = fread(astr+astr_sz, 1, FILE_BUFF_SIZE, file);
+ }
+
+ if (ferror(file)) {
+ usage_format("Failed to read arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
}
- SAVE_CHAR('\0');
+ astr[astr_sz + sz] = '\0';
fclose(file);
if (astr[0] == '\0')
res = NULL;
else
- res = build_args_from_string(astr);
+ res = build_args_from_string(astr, !0);
- efree(astr);
+ if (astr != buff)
+ efree(astr);
return res;
-#undef EAF_CMNT
-#undef EAF_QUOTE
-#undef SAVE_CHAR
+#undef FILE_BUFF_SIZE
}
@@ -2077,7 +2070,7 @@ initial_argv_massage(int *argc, char ***argv)
if (av)
avv[vix++].argv = av;
- if (vix == (*argc > 1 ? 1 : 0)) {
+ if (vix == (*argc > 1 ? 2 : 0)) {
/* Only command line argv; check if we can use argv as it is... */
ac = *argc;
av = *argv;
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index c841b49303..386ed4086a 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -220,6 +220,12 @@ while [ $# -gt 0 ]; do
run_valgrind=yes
skip_erlexec=yes
;;
+ "-emu_type")
+ shift
+ cargs="$cargs -$1"
+ TYPE=.$1
+ shift
+ ;;
"-rr")
shift
cargs="$cargs -rr"
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 4cc59cdd5f..9c56d92115 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1187,12 +1187,14 @@ document etp-cp
%---------------------------------------------------------------------------
% etp-cp Eterm
%
-% Take a code continuation pointer and print
+% Take a code continuation pointer or instruction pointer and print
% module, function, arity and offset.
%
-% Code continuation pointers can be found in the process structure e.g
-% process_tab[i]->cp and process_tab[i]->i, the second is the
-% program counter, which is the same thing as a continuation pointer.
+% Code continuation pointers can be found on the stack. The instruction
+% pointer is found in the process struct. For example:
+%
+% c_p->i
+% process_tab[i]->i
%---------------------------------------------------------------------------
end
@@ -1509,17 +1511,13 @@ define etp-stack-preamble
set $etp_stack_end = ($arg0)->hend
if ($arg0)->state.counter & 0x8000
printf "%%%%%% WARNING: The process is currently running, so c_p->stop will not be correct\r\n"
- printf "%%%%%% Consider using %s-emu instead\r\n", $arg1
+ printf "%%%%%% Consider using -emu variant instead\r\n"
end
printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p
if ($arg0)->i != 0
printf "I: "
etp ((Eterm)($arg0)->i)
end
- if ($arg0)->cp != 0
- printf "cp:"
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stack-preamble-emu
@@ -1528,10 +1526,6 @@ define etp-stack-preamble-emu
printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p
printf "I: "
etp ((BeamInstr)I)
- if ($arg0)->cp != 0
- printf "cp: "
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stacktrace-1
@@ -2224,13 +2218,6 @@ define etp-process-info-int
else
printf "unknown\n"
end
- printf " CP: "
- if ($etp_proc->cp)
- etp-cp-1 $etp_proc->cp
- printf "\n"
- else
- printf "unknown\n"
- end
printf " I: "
if ($etp_proc->i)
etp-cp-1 $etp_proc->i
@@ -2456,11 +2443,6 @@ define etp-process-memory-info
end
end
- if ($etp_pmem_proc->cp)
- printf " CP: "
- etp-cp-1 $etp_pmem_proc->cp
- printf " "
- end
printf "\n"
end
end
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index bfb3e1bd2c..e974630695 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -1201,7 +1201,19 @@ static void error_logf(int priority, int line, const char *format, ...)
#ifdef HAVE_SYSLOG_H
if (run_daemon) {
+#ifdef HAVE_VSYSLOG
vsyslog(priority,format,args);
+#else
+ /* Some OSes like AIX lack vsyslog. */
+ va_list ap;
+ char message[900]; /* AIX man page says truncation past this */
+
+ va_start (ap, format);
+ vsnprintf(message, 900, format, ap);
+ va_end(ap);
+
+ syslog(priority, message);
+#endif
}
else
#endif
diff --git a/erts/etc/win32/cygwin_tools/reg_query.sh b/erts/etc/win32/cygwin_tools/reg_query.sh
new file mode 100644
index 0000000000..c5c2566b7a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/reg_query.sh
@@ -0,0 +1,24 @@
+#! /bin/sh
+BAT_FILE=/tmp/w$$.bat
+if [ -z "$1" -o -z "$2" ]; then
+ echo "Usage:" "$0" '<key> <valuename>'
+ exit 1
+fi
+BACKED=`echo "$1" | sed 's,/,\\\\,g'`
+# We need to get the 64bit part of the registry, hence we need to execute
+# a 64bit reg.exe, but c:\windows\system32 is redirected to 32bit versions
+# if we are in the 32bit virtual environment, so we need to use the
+# sysnative trick to get to the 64bit executable of reg.exe (ouch!)
+if [ -d $WINDIR/sysnative ]; then
+ REG_CMD="$WINDIR\\sysnative\\reg.exe"
+else
+ REG_CMD="reg.exe"
+fi
+cat > $BAT_FILE <<EOF
+@echo off
+$REG_CMD query "$BACKED" /v "$2"
+EOF
+#echo $BAT_FILE
+#cat $BAT_FILE
+RESULT=`cmd.exe //C $BAT_FILE`
+echo $RESULT | sed "s,.*$2 REG_[^ ]* ,,"
diff --git a/erts/etc/win32/cygwin_tools/w32_path.sh b/erts/etc/win32/cygwin_tools/w32_path.sh
new file mode 100755
index 0000000000..3b67ec853a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/w32_path.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ echo `cygpath $ABSOLUTE -u $1`
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `cygpath $ABSOLUTE -m $1`;
+ ;;
+ backslash)
+ echo `cygpath $ABSOLUTE -w $1`;
+ ;;
+ double)
+ DOUBLE=`cygpath $ABSOLUTE -w $1 | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
index b230aa6a40..7cbd0d027c 100644
--- a/erts/etc/win32/erl.c
+++ b/erts/etc/win32/erl.c
@@ -53,8 +53,9 @@ int wmain(int argc, wchar_t **argv)
HANDLE erlexec_handle; /* Instance */
ErlexecFunction *win_erlexec;
wchar_t *path = malloc(100*sizeof(wchar_t));
+ wchar_t *wslpath = malloc(100*sizeof(wchar_t));
wchar_t *npath;
- int pathlen;
+ int pathlen, wslpathlen;
char ** utf8argv;
int i, len;
@@ -66,9 +67,23 @@ int wmain(int argc, wchar_t **argv)
path = realloc(path,pathlen*sizeof(wchar_t));
GetEnvironmentVariableW(L"PATH",path,pathlen);
}
- pathlen = (wcslen(path) + wcslen(erlexec_dir) + 2);
+
+ if ((wslpathlen = GetEnvironmentVariableW(L"WSLENV",wslpath,100)) > 0) {
+ if ((wslpathlen = GetEnvironmentVariableW(L"WSLPATH",wslpath,100)) > 0) {
+ if (wslpathlen > 100) {
+ wslpath = realloc(wslpath,wslpathlen*sizeof(wchar_t));
+ GetEnvironmentVariableW(L"WSLPATH",wslpath,wslpathlen);
+ }
+ wslpathlen = wcslen(wslpath);
+ }
+ }
+ pathlen = (wcslen(path) + wslpathlen + wcslen(erlexec_dir) + 2);
npath = (wchar_t *) malloc(pathlen*sizeof(wchar_t));
- swprintf(npath,pathlen,L"%s;%s",erlexec_dir,path);
+ if(wslpathlen > 0) {
+ swprintf(npath,pathlen,L"%s;%s;%s",erlexec_dir,path,wslpath);
+ } else {
+ swprintf(npath,pathlen,L"%s;%s",erlexec_dir,path);
+ }
SetEnvironmentVariableW(L"PATH",npath);
if ((erlexec_handle = LoadLibraryW(erlexec_name)) == NULL) {
diff --git a/erts/etc/win32/msys_tools/reg_query.sh b/erts/etc/win32/msys_tools/reg_query.sh
index ae6d5c3218..70a7bf0d17 100644
--- a/erts/etc/win32/msys_tools/reg_query.sh
+++ b/erts/etc/win32/msys_tools/reg_query.sh
@@ -7,7 +7,7 @@ fi
BACKED=`echo "$1" | sed 's,/,\\\\,g'`
# We need to get the 64bit part of the registry, hence we need to execute
# a 64bit reg.exe, but c:\windows\system32 is redirected to 32bit versions
-# if we ate in the 32bit virtual environment, why we need to use the
+# if we are in the 32bit virtual environment, so we need to use the
# sysnative trick to get to the 64bit executable of reg.exe (ouch!)
if [ -d $WINDIR/sysnative ]; then
REG_CMD="$WINDIR\\sysnative\\reg.exe"
diff --git a/erts/etc/win32/msys_tools/w32_path.sh b/erts/etc/win32/msys_tools/w32_path.sh
new file mode 100755
index 0000000000..9a5089391d
--- /dev/null
+++ b/erts/etc/win32/msys_tools/w32_path.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ echo `win2msys_path.sh $ABSOLUTE $1`
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `msys2win_path.sh -m $ABSOLUTE -m $1`;
+ ;;
+ backslash)
+ echo `msys2win_path.sh $ABSOLUTE $1`;
+ ;;
+ double)
+ DOUBLE=`msys2win_path.sh $ABSOLUTE $1 | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
index 0b4e0d0359..4c5e5325b3 100644
--- a/erts/etc/win32/nsis/Makefile
+++ b/erts/etc/win32/nsis/Makefile
@@ -23,11 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include $(ERL_TOP)/erts/vsn.mk
VERSION_HEADER = erlang.nsh
-MAKENSIS = makensis
+MAKENSIS = makensis.exe
MAKENSISFLAGS = /V2
CUSTOM_MODERN=custom_modern.exe
-# This is not the way we usually do in our makefiles,
+# This is not the way we usually do in our makefiles,
# but making release is the ONLY thing we do with this one,
# Its not called during ordinary recursive make.
all: release
@@ -44,43 +44,32 @@ TARGET_DIR = $(RELEASE_PATH)
ifdef MSYSTEM
ifeq ($(MSYSTEM),$(filter $(MSYSTEM),MSYS MINGW32 MINGW64))
- USEMSYS := true
+ MAKENSISFLAGS = //V2
endif
endif
-ifeq ($(USEMSYS),true)
-
- MAKENSISFLAGS = //V2
- WTESTROOT=$(shell (msys2win_path.sh "$(RELEASE_PATH)"))
- WTARGET_DIR=$(shell (msys2win_path.sh "$(TARGET_DIR)"))
-
-else
-
- MAKENSISFLAGS = /V2
- WTESTROOT=$(shell (cygpath -d "$(RELEASE_PATH)" 2>/dev/null || cygpath -w "$(RELEASE_PATH)"))
- WTARGET_DIR=$(shell (cygpath -d "$(TARGET_DIR)" 2>/dev/null || cygpath -d "$(TARGET_DIR)"))
-
-endif
+WTESTROOT=$(shell (w32_path.sh -d "$(RELEASE_PATH)"))
+WTARGET_DIR=$(shell (w32_path.sh -d "$(TARGET_DIR)"))
ifeq ($(CONFIG_SUBTYPE),win64)
WINTYPE=win64
+ REDIST_TARGET=vcredist_x64.exe
else
WINTYPE=win32
+ REDIST_TARGET=vcredist_x86.exe
endif
-REDIST_FILE=$(shell (sh ./find_redist.sh || echo ""))
-ifeq ($(USEMSYS),true)
- NICEREDISTFILE=$(shell (msys2win_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
-else
- NICEREDISTFILE=$(shell (cygpath -d -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
-endif
-
-REDIST_TARGET=$(shell (sh ./find_redist.sh -n || echo ""))
-REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh $(NICEREDISTFILE) || echo ""))
-REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n $(NICEREDISTFILE) || echo ""))
+REDIST_FILE=$(shell (sh ./find_redist.sh $(WINTYPE) || echo ""))
+NICEREDISTFILE=$(shell (w32_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo "NOTFOUND"))
+# $(info $$NICEREDISTFILE = [${NICEREDISTFILE}])
+REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh "$(NICEREDISTFILE)" || echo ""))
+REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n "$(NICEREDISTFILE)" || echo ""))
+# $(info $$REDIST_DLL_VERSION = [${REDIST_DLL_VERSION}])
+# $(info $$REDIST_DLL_NAME = [${REDIST_DLL_NAME}])
+# $(info $$REDIST_FILE = [${REDIST_FILE}])
release_spec:
- @NSIS_VER=`makensis /hdrinfo | head -1 | awk '{print $$2}'`; \
+ @NSIS_VER=`makensis.exe -version`; \
case $$NSIS_VER in \
v2.0b*) \
echo '!define MUI_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
@@ -92,7 +81,7 @@ release_spec:
echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \
fi;\
$(MAKENSIS) erlang.nsi;;\
- v2.*) \
+ v2.* | v3.*) \
echo '!define OTP_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\
echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\
@@ -104,7 +93,7 @@ release_spec:
fi;\
if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\
then \
- cp $(REDIST_FILE) "$(RELEASE_PATH)/$(REDIST_TARGET)";\
+ cp "$(REDIST_FILE)" "$(RELEASE_PATH)/$(REDIST_TARGET)";\
echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \
echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\
echo '!define REDIST_DLL_NAME "$(REDIST_DLL_NAME)"' >> $(VERSION_HEADER);\
@@ -117,7 +106,7 @@ release_spec:
echo "Running $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi";\
$(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi;;\
*) \
- echo 'Unsupported NSIS version';;\
+ echo "Unsupported NSIS version: $$NSIS_VER";;\
esac
release_docs release_docs_spec docs:
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
index 9eafb6ce0e..1b295aa89a 100755
--- a/erts/etc/win32/nsis/dll_version_helper.sh
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -44,12 +44,16 @@ int main(void)
}
EOF
-cl -MD hello.c > /dev/null 2>&1
+cl.exe -MD hello.c > /dev/null 2>&1
if [ '!' -f hello.exe.manifest ]; then
# Gah - VC 2010 changes the way it handles DLL's and manifests... Again...
# need another way of getting the version
DLLNAME=`dumpbin.exe -imports hello.exe | egrep MSVCR.*dll`
DLLNAME=`echo $DLLNAME`
+ if [ -z "$DLLNAME" ]; then
+ DLLNAME=`dumpbin.exe -imports hello.exe | egrep VCRUNTIME.*dll`
+ DLLNAME=`echo $DLLNAME`
+ fi
if [ '!' -z "$1" ]; then
FILETOLOOKIN=$1
else
@@ -92,22 +96,25 @@ int main(void)
for(i=0; i < n; ++i) {
sprintf(buff,"\\\\StringFileInfo\\\\%04x%04x\\\\FileVersion",
translate[i*2],translate[i*2+1]);
- if (VerQueryValue(versinfo,buff,&vs_verinfo,&vs_ver_size)) {
- printf("%s\n",(char *) vs_verinfo);
- return 0;
+ if (VerQueryValue(versinfo,buff,&vs_verinfo,&vs_ver_size) && vs_ver_size > 2) {
+ if(vs_verinfo[1] == 0) // Wide char (depends on compiler version!!)
+ printf("%S\n",(unsigned short *) vs_verinfo);
+ else
+ printf("%s\n",(char *) vs_verinfo);
+ return 0;
}
}
fprintf(stderr,"Failed to find file version of %s\n",REQ_MODULE);
return 0;
}
EOF
- cl -MD helper.c version.lib > /dev/null 2>&1
+ cl.exe -MD helper.c version.lib > /dev/null 2>&1
if [ '!' -f helper.exe ]; then
echo "Failed to build helper program." >&2
exit 1
fi
NAME=$DLLNAME
- VERSION=`./helper`
+ VERSION=`./helper.exe`
else
VERSION=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*version=.\([0-9\.]*\).*,\1,g' | grep -v '<'`
NAME=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*name=.[A-Za-z\.]*\([0-9]*\).*,msvcr\1.dll,g' | grep -v '<'`
diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh
index c070ad469a..deb5480e1a 100755
--- a/erts/etc/win32/nsis/find_redist.sh
+++ b/erts/etc/win32/nsis/find_redist.sh
@@ -26,17 +26,9 @@ lookup_prog_in_path ()
save_ifs=$IFS
IFS=:
for p in $PATH; do
- # In cygwin the programs are not always executable and have .exe suffix...
- if [ "X$TARGET" = "Xwin32" ]; then
- if [ -f $p/$PROG.exe ]; then
- echo $p/$PROG
- break;
- fi
- else
- if [ -x $p/$PROG ]; then
- echo $p/$PROG
- break;
- fi
+ if [ -f $p/$PROG.exe ]; then
+ echo $p/$PROG
+ break;
fi
done
IFS=$save_ifs
@@ -89,36 +81,38 @@ add_path_element()
echo "$PA"
}
-
-CLPATH=`lookup_prog_in_path cl`
-
-if [ -z "$CLPATH" ]; then
- echo "Can not locate cl.exe and vcredist_x86/x64.exe - OK if using mingw" >&2
- exit 1
-fi
-
# Look to see if it's 64bit
-XX=`remove_path_element cl "$CLPATH"`
-YY=`remove_path_element amd64 "$XX"`
-if [ "$YY" != "$XX" ]; then
+if [ "$1" = "win64" ]; then
AMD64DIR=true
VCREDIST=vcredist_x64
COMPONENTS="cl amd64 bin vc"
-else
+elif [ "$1" = "win32" ]; then
AMD64DIR=false
VCREDIST=vcredist_x86
COMPONENTS="cl bin vc"
+else
+ echo "TARGET argument should win32 or win64"
+ exit 2
fi
-if [ X"$1" = X"-n" ]; then
- echo $VCREDIST.exe
- exit 0
+if [ x"$VCToolsRedistDir" != x"" ]; then
+ File="$VCToolsRedistDir/$VCREDIST.exe"
+ if [ -r "$File" ]; then
+ echo "$File"
+ exit 0
+ fi
+fi
+
+CLPATH=`lookup_prog_in_path cl`
+if [ -z "$CLPATH" ]; then
+ echo "Can not locate cl.exe and vcredist_x86/x64.exe - OK if using mingw" >&2
+ exit 1
fi
-# echo $CLPATH
+echo $CLPATH
BPATH=$CLPATH
for x in $COMPONENTS; do
- # echo $x
+ #echo $x
NBPATH=`remove_path_element $x "$BPATH"`
if [ "$NBPATH" = "$BPATH" ]; then
echo "Failed to locate $VCREDIST.exe because cl.exe was in an unexpected location" >&2
diff --git a/erts/etc/win32/wsl_tools/SetupWSLcross.bat b/erts/etc/win32/wsl_tools/SetupWSLcross.bat
new file mode 100644
index 0000000000..860a7d5e60
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/SetupWSLcross.bat
@@ -0,0 +1,66 @@
+@echo off
+rem Setup MCL and echo the environment
+rem Usage: eval `cmd.exe /c SetupWSLcross.bat x64`
+
+IF "%~1"=="x86" GOTO search
+IF "%~1"=="x64" GOTO search
+
+GOTO badarg
+
+:search
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat". (
+ call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %~1 > nul
+ goto continue
+)
+
+GOTO no_vcvars
+
+:continue
+
+FOR /F "delims==" %%F IN ('where cl.exe') DO SET _cl_exec_=%%F
+FOR %%F IN ("%_cl_exec_%") DO SET CL_PATH=%%~dpF
+
+FOR /F "delims==" %%F IN ('where rc.exe') DO SET _rc_exec_=%%F
+FOR %%F IN ("%_rc_exec_%") DO SET RC_PATH=%%~dpF
+
+rem Order is important for some unknown reason
+set WSLENV=VCToolsRedistDir/up:CL_PATH/up:RC_PATH/up:LIBPATH/ul:LIB/ul:INCLUDE/ul
+wsl.exe echo INCLUDE=\"$INCLUDE\";
+wsl.exe echo LIB=\"$LIB\";
+wsl.exe echo LIBPATH=\"$LIBPATH\";
+wsl.exe echo VCToolsRedistDir=\"$VCToolsRedistDir\";
+wsl.exe echo PATH=\"$CL_PATH\":\"$RC_PATH\":'$PATH';
+wsl.exe echo WSLENV='$WSLENV:LIBPATH/l:LIB/l:INCLUDE/l';
+rem wsl.exe echo export 'INCLUDE LIB LIBPATH VCToolsRedistDir WSLENV PATH';
+wsl.exe echo "# Eval this file eval \`cmd.exe /c SetupWSLcross.bat\`"
+
+exit
+
+:badarg
+echo "Bad TARGET or not specified: %~1 expected x86 or x64"
+exit
+
+:no_vcvars
+echo "Error: SetupWSLcross.bat: Could not find vcvarsall.bat"
+echo " edit erts/etc/win32/wsl_tools/SetupWSLcross.bat"
+exit
diff --git a/erts/etc/win32/wsl_tools/erl b/erts/etc/win32/wsl_tools/erl
new file mode 100755
index 0000000000..db24f6b4fe
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/erl
@@ -0,0 +1,45 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Note! This shellscript expects to be run in a wsl environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command wslpath on whatever is a path...
+
+CMD=""
+for x in "$@"; do
+ case "$x" in
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ /*)
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+ERL_TOP=`wslpath -m $ERL_TOP`
+WSLENV="ERL_TOP/w:$WSLENV"
+export WSLENV
+export ERL_TOP
+eval erl.exe $CMD
diff --git a/erts/etc/win32/wsl_tools/erlc b/erts/etc/win32/wsl_tools/erlc
new file mode 100755
index 0000000000..956ac19abd
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/erlc
@@ -0,0 +1,60 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+
+CMD=""
+ECHO_ONLY=false
+for x in "$@"; do
+ case "$x" in
+ --echo_only)
+ ECHO_ONLY=true;;
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z$MPATH";;
+ -pa/*)
+ y=`echo $x | sed 's,^-pa\(/.*\),\1,g'`;
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -pa $MPATH";;
+ /*)
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+# Needed for +'{preproc_flags,whatever}'
+ +{preproc_flags,*})
+ y=`echo $x | sed 's,^+{preproc_flags\,"\(.*\)"},\1,g'`;
+ z=`eval $0 --echo_only $y`;
+ case "$z" in # Dont "doubledoublequote"
+ \"*\")
+ CMD="$CMD +'{preproc_flags,$z}'";;
+ *)
+ CMD="$CMD +'{preproc_flags,\"$z\"}'";;
+ esac;;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+if [ $ECHO_ONLY = true ]; then
+ echo $CMD
+else
+ eval erlc.exe $CMD
+fi
diff --git a/erts/etc/win32/wsl_tools/javac.sh b/erts/etc/win32/wsl_tools/javac.sh
new file mode 100755
index 0000000000..b1f142adfd
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/javac.sh
@@ -0,0 +1,53 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Note! This shellscript expects to be run in a WSL environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command wslpath on whatever is a path...
+
+CMD=""
+
+SAVE="$@"
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -I/*|-o/*|-d/*)
+ y=`echo $x | sed 's,^-[Iod]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Iod]\)\(/.*\),\1,g'`;
+ #echo "Foooo:$z"
+ MPATH=`wslpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -d|-I|-o)
+ shift;
+ MPATH=`wslpath -m $1`;
+ CMD="$CMD $x $MPATH";;
+ /*)
+ #echo "absolute:"$x;
+ MPATH=`wslpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+#echo javac.exe "$CMD"
+export WSLENV=CLASSPATH/l
+eval javac.exe "$CMD"
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.first b/erts/etc/win32/wsl_tools/make_bootstrap_ini.sh
index 0ea872ef49..c33d328ea0 100644..100755
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.first
+++ b/erts/etc/win32/wsl_tools/make_bootstrap_ini.sh
@@ -1,8 +1,9 @@
+#! /bin/bash
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,9 +15,29 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ echo "error: $0: No rootdir given"
+ exit 1
+else
+ RDIR=$1
+fi
+if [ -z "$2" ]; then
+ echo "error: $0: No bindir given"
+ exit 1
+else
+ BDIR=$2
+fi
+
+DRDIR=`w32_path.sh -d $RDIR`
+DBDIR=`w32_path.sh -d $BDIR`
-eterm_test_decl.c: eterm_test.c
- erl -noinput -pa ../all_SUITE_data -s init_tc run eterm_test -s erlang halt
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DBDIR
+Progname=erl
+Rootdir=$DRDIR
+EOF
diff --git a/erts/doc/Makefile b/erts/etc/win32/wsl_tools/make_local_ini.sh
index f26a43592e..85ec4e40aa 100644..100755
--- a/erts/doc/Makefile
+++ b/erts/etc/win32/wsl_tools/make_local_ini.sh
@@ -1,8 +1,9 @@
-#
+#! /bin/bash
+#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1996-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-2016. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,24 +15,26 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-#
-# Default Rules
#
-OTP_MAKE_ROOT=/home/super/otp/otp_make
-include $(OTP_MAKE_ROOT)/otp.mk
-
-#
-# Macros
+# %CopyrightEnd%
#
-SUB_DIRECTORIES = src
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ if [ -z $ERL_TOP ]; then
+ echo "error: $0: No rootdir available"
+ exit 1
+ else
+ RDIR=$ERL_TOP
+ fi
+else
+ RDIR=$1
+fi
-SPECIAL_TARGETS =
+DDIR=`w32_path.sh -d $RDIR`
-#
-# Default Subdir Targets
-#
-include $(OTP_MAKE_ROOT)/otp_subdir.mk
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DDIR\\\\bin\\\\win32
+Progname=erl
+Rootdir=$DDIR
+EOF
diff --git a/erts/etc/win32/wsl_tools/reg_query.sh b/erts/etc/win32/wsl_tools/reg_query.sh
new file mode 100755
index 0000000000..c05f00dfa1
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/reg_query.sh
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+mkdir -p $ERL_TOP/tmp
+BAT_FILE=$ERL_TOP/tmp/w$$.bat
+if [ -z "$1" -o -z "$2" ]; then
+ echo "Usage:" "$0" '<key> <valuename>'
+ exit 1
+fi
+BACKED=`echo "$1" | sed 's,/,\\\\,g'`
+
+if [ $CONFIG_SUBTYPE = "win64" ]; then
+ REG_OPT=" /reg:64"
+else
+ REG_OPT=" /reg:32"
+fi
+
+WIN_BAT_FILE=`w32_path.sh -w $BAT_FILE`
+RESULT=`reg.exe query "$BACKED" /v "$2" $REG_OPT | sed 's@\\\@/@g' | tr -d '\r\n'`
+echo "$RESULT" | sed "s,.*REG_[^ ]* *,,g"
diff --git a/lib/erl_interface/test/erl_format_SUITE_data/Makefile.first b/erts/etc/win32/wsl_tools/vc/ar.sh
index acbb8c98bb..4d3b8ffdb5 100644..100755
--- a/lib/erl_interface/test/erl_format_SUITE_data/Makefile.first
+++ b/erts/etc/win32/wsl_tools/vc/ar.sh
@@ -1,8 +1,9 @@
+#! /bin/sh
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -14,9 +15,34 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
+#
# %CopyrightEnd%
#
+CMD=""
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -out:)
+ shift
+ case "$1" in
+ /*)
+ MPATH=`w32_path.sh -d $1`;;
+ *)
+ MPATH=$1;;
+ esac
+ CMD="$CMD -out:\"$MPATH\"";;
+ -out:/*)
+ y=`echo $x | sed 's,^-out:\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -out:\"$MPATH\"";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
-format_test_decl.c: format_test.c
- erl -noinput -pa ../all_SUITE_data -s init_tc run format_test -s erlang halt
+eval lib.exe /nologo $CMD
diff --git a/erts/etc/win32/wsl_tools/vc/cc.sh b/erts/etc/win32/wsl_tools/vc/cc.sh
new file mode 100755
index 0000000000..036e00681c
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/cc.sh
@@ -0,0 +1,382 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Icky cl wrapper that does it's best to behave like a Unixish cc.
+# Made to work for Erlang builds and to make configure happy, not really
+# general I suspect.
+# set -x
+# Save the command line for debug outputs
+
+SAVE="$@"
+
+# Constants
+COMMON_CFLAGS="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE"
+
+# Variables
+# The stdout and stderr for the compiler
+MSG_FILE=/tmp/cl.exe.$$.1
+ERR_FILE=/tmp/cl.exe.$$.2
+
+# "Booleans" determined during "command line parsing"
+# If the stdlib option is explicitly passed to this program
+MD_FORCED=false
+# If we're preprocession (only) i.e. -E
+PREPROCESSING=false
+# If we're generating dependencies (implies preprocesing)
+DEPENDENCIES=false
+# If this is supposed to be a debug build
+DEBUG_BUILD=false
+# If this is supposed to be an optimized build (there can only be one...)
+OPTIMIZED_BUILD=false
+# If we're linking or only compiling
+LINKING=true
+
+# This data is accumulated during command line "parsing"
+# The stdlibrary option, default multithreaded dynamic
+MD=-MD
+# Flags for debug compilation
+DEBUG_FLAGS=""
+# Flags for optimization
+OPTIMIZE_FLAGS=""
+# The specified output filename (if any), may be either object or exe.
+OUTFILE=""
+# Unspecified command line options for the compiler
+CMD=""
+# All the c source files, in unix style
+SOURCES=""
+# All the options to pass to the linker, kept in Unix style
+LINKCMD=""
+
+
+# Loop through the parameters and set the above variables accordingly
+# Also convert some filenames to "windows style"
+# except for anything passed to the linker, that script
+# handles those and the sources, which are also kept unixish for now
+
+# If we are in "unix" directory we can't use relative paths
+# since cl.exe can't find that path
+WINCHECK=`w32_path.sh -m $PWD`
+case $WINCHECK in
+ //wsl$/*)
+ USEABSPATH=true
+ ;;
+ *)
+ USEABSPATH=false
+ ;;
+esac
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -Wall)
+ ;;
+ -c)
+ LINKING=false;;
+ #CMD="$CMD -c";;
+ -MM)
+ PREPROCESSING=true;
+ LINKING=false;
+ DEPENDENCIES=true;;
+ -E)
+ PREPROCESSING=true;
+ LINKING=false;; # Obviously...
+ #CMD="$CMD -E";;
+ -Owx)
+ # Optimization hardcoded of wxErlang
+ OPTIMIZE_FLAGS="-Ob2ity -Gs -Z7";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -O*)
+ # Optimization hardcoded
+ OPTIMIZE_FLAGS="-Ox -Z7";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -g|-ggdb)
+ if [ $OPTIMIZED_BUILD = false ];then
+ # Hardcoded windows debug flags
+ DEBUG_FLAGS="-Z7";
+ if [ $MD_FORCED = false ]; then
+ MD=-MDd;
+ fi
+ LINKCMD="$LINKCMD -g";
+ DEBUG_BUILD=true;
+ fi;;
+ # Allow forcing of stdlib
+ -mt|-MT)
+ MD="-MT";
+ MD_FORCED=true;;
+ -md|-MD)
+ MD="-MD";
+ MD_FORCED=true;;
+ -ml|-ML)
+ MD="-ML";
+ MD_FORCED=true;;
+ -mdd|-MDD|-MDd)
+ MD="-MDd";
+ MD_FORCED=true;;
+ -mtd|-MTD|-MTd)
+ MD="-MTd";
+ MD_FORCED=true;;
+ -mld|-MLD|-MLd)
+ MD="-MLd";
+ MD_FORCED=true;;
+ -o)
+ shift;
+ OUTFILE="$1";;
+ -o*)
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ OUTFILE="$y";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d "$y"`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I\"/*)
+ y=`echo $x | sed 's,^\"-[Io]\(/.*\)\",\1,g'`;
+ z=`echo $x | sed 's,^\"-\([Io]\)\(/.*\)\",\1,g'`;
+ MPATH=`w32_path.sh -d "$y"`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I*)
+ if [ $USEABSPATH = true ]; then
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -a -d "$y"`;
+ CMD="$CMD -$z$MPATH";
+ else
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y"
+ fi;;
+ -D*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -EH*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -TP|-Tp)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ LINKCMD="$LINKCMD $x";;
+ /*.c)
+ SOURCES="$SOURCES $x";;
+ *.c)
+ SOURCES="$SOURCES $x";;
+ /*.cc)
+ SOURCES="$SOURCES $x";;
+ *.cc)
+ SOURCES="$SOURCES $x";;
+ /*.cpp)
+ SOURCES="$SOURCES $x";;
+ *.cpp)
+ SOURCES="$SOURCES $x";;
+ /*.o)
+ LINKCMD="$LINKCMD $x";;
+ *.o)
+ LINKCMD="$LINKCMD $x";;
+ *)
+ # Try to quote uninterpreted options
+ y=`echo $x | sed 's,",\\\",g'`;
+ LINKCMD="$LINKCMD $y";;
+ esac
+ shift
+done
+
+#Return code from compiler, linker.sh and finally this script...
+RES=0
+
+# Accumulated object names
+ACCUM_OBJECTS=""
+
+# A temporary object file location
+TMPOBJDIR=$ERL_TOP/tmpobj$$
+mkdir $TMPOBJDIR
+
+WINTMPDIR=`w32_path.sh -w $TMPOBJDIR`
+
+# Sometimes the file server doesn't keep up (paralell file creation)
+while true ; do
+ DIR_EXISTS=$(cd /mnt/c; cmd.exe /C "IF EXIST $WINTMPDIR (echo yes) ELSE (echo no)")
+ case $DIR_EXISTS in # Contains trash in the end of string
+ yes*)
+ break
+ ;;
+ *)
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "sleep 1" >> $CC_SH_DEBUG_LOG
+ fi;
+ echo sleep $WINTMPDIR does not exist >&2
+ sleep 1
+ esac
+done
+
+# Compile
+for x in $SOURCES; do
+ # Compile each source
+ if [ $LINKING = false ]; then
+ # We should have an output defined, which is a directory
+ # or an object file
+ case $OUTFILE in
+ /*.o)
+ # Simple output, SOURCES should be one single
+ n=`echo $SOURCES | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ else
+ output_filename=`echo $OUTFILE`;
+ fi;;
+ *.o)
+ # Relative path needs no translation
+ n=`echo $SOURCES | wc -w`
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output."
+ exit 1
+ else
+ output_filename=$OUTFILE
+ fi;;
+ /*)
+ # Absolute directory
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=`echo $OUTFILE`
+ output_filename="$output_filename/${o}";;
+ *)
+ # Relative_directory or empty string (.//x.o is valid)
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'`
+ output_filename="./${OUTFILE}/${o}";;
+ esac
+ else
+ # We are linking, which means we build objects in a temporary
+ # directory and link from there. We should retain the basename
+ # of each source to make examining the exe easier...
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=$TMPOBJDIR/$o
+ ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename"
+ fi
+ # Now we know enough, lets try a compilation...
+ if [ $USEABSPATH = true ]; then
+ MPATH=`w32_path.sh -a -d $x`
+ else
+ MPATH=`w32_path.sh -d $x`
+ fi
+ if [ $PREPROCESSING = true ]; then
+ output_flag="-E"
+ else
+ output_flag="/FS -c -Fo`w32_path.sh -a -d ${output_filename}`"
+ fi
+ params="$COMMON_CFLAGS $MD $DEBUG_FLAGS $OPTIMIZE_FLAGS \
+ $CMD ${output_flag} $MPATH"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG
+ echo cl.exe $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval cl.exe $params >$MSG_FILE 2>$ERR_FILE
+ RES=$?
+ if test $PREPROCESSING = false; then
+ cat $ERR_FILE >&2
+ tail -n +2 $MSG_FILE
+ else
+ tail -n +2 $ERR_FILE >&2
+ if test $DEPENDENCIES = true; then
+ perl -e '
+my $file = "'$x'";
+while (<>) {
+ next unless /^#line/;
+ next if /$file/o;
+ (undef,$_) = split(/\"/);
+ next if / /;
+ $all{$_} = 1;
+}
+foreach (sort keys %all) {
+ my $w_file;
+ ($w_file) = split("\n",`(w32_path.sh -u $_)`);
+ push @f, "\\\n $w_file ";
+}
+if (@f) {
+ my $oname = $file;
+ $oname =~ s@.*/@@;
+ $oname =~ s@[.]cp*@.o@;
+ print $oname, ":", @f;
+ print "\n\n";
+ print STDERR "Made dependencies for $file\n";
+}' $MSG_FILE
+ else
+ cat $MSG_FILE
+ fi
+ fi
+ rm -f $ERR_FILE $MSG_FILE
+ if [ $RES != 0 ]; then
+ echo Failed: cl.exe $params
+ rm -rf $TMPOBJDIR
+ exit $RES
+ fi
+done
+
+# If we got here, we succeeded in compiling (if there were anything to compile)
+# The output filename should name an executable if we're linking
+if [ $LINKING = true ]; then
+ case $OUTFILE in
+ "")
+ # Use the first source name to name the executable
+ first_source=""
+ for x in $SOURCES; do first_source=$x; break; done;
+ if [ -n "$first_source" ]; then
+ e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`;
+ out_spec="-o $e";
+ else
+ out_spec="";
+ fi;;
+ *)
+ out_spec="-o $OUTFILE";;
+ esac
+ # Descide which standard library to link against
+ case $MD in
+ -ML)
+ stdlib="-lLIBC";;
+ -MLd)
+ stdlib="-lLIBCD";;
+ -MD)
+ stdlib="-lMSVCRT";;
+ -MDd)
+ stdlib="-lMSVCRTD";;
+ -MT)
+ stdlib="-lLIBCMT";;
+ -MTd)
+ stdlib="-lLIBMTD";;
+ esac
+ # And finally call the next script to do the linking...
+ params="$out_spec $LINKCMD $stdlib"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh $ACCUM_OBJECTS $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval ld.sh $ACCUM_OBJECTS $params
+ RES=$?
+fi
+rm -rf $TMPOBJDIR
+
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/coffix.c b/erts/etc/win32/wsl_tools/vc/coffix.c
new file mode 100755
index 0000000000..7428f9cd41
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/coffix.c
@@ -0,0 +1,161 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+** This mini tool fixes an incompatibility between
+** Microsoft's tools, who dont like the virtual size being put in
+** the physical address field, but rely on the raw size field for
+** sizing the ".bss" section.
+** This fixes some of the problems with linking gcc compiled objects
+** together with MSVC dito.
+**
+** Courtesy DJ Delorie for describing the COFF file format on
+** http://www.delorie.com/djgpp/doc/coff/
+** The coff structures are fetched from Microsofts headers though.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <winnt.h> /* Structure definitions for PE (COFF) */
+
+static int dump_edit(char *filename, int edit);
+static int v_printf(char *format, ...);
+
+
+char *progname;
+int verbouse = 0;
+
+int main(int argc, char **argv)
+{
+ int findex = 1;
+ int edit = 0;
+ int ret;
+
+ progname = argv[0];
+ if (argc == 1) {
+ fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname);
+ return 1;
+ }
+ for (findex = 1;
+ findex < argc && (*argv[findex] == '-' || *argv[findex] == '/');
+ ++findex)
+ switch (argv[findex][1]) {
+ case 'e':
+ case 'E':
+ edit = 1;
+ break;
+ case 'v':
+ case 'V':
+ verbouse = 1;
+ default:
+ fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]);
+ break;
+ }
+ if (findex == argc) {
+ fprintf(stderr,"%s: No filenames given.\n", progname);
+ return 1;
+ }
+ for(; findex < argc; ++findex)
+ if ((ret = dump_edit(argv[findex],edit)) != 0)
+ return ret;
+ return 0;
+}
+
+int dump_edit(char *filename, int edit)
+{
+ FILE *f = fopen(filename, (edit) ? "r+b" : "rb");
+ IMAGE_FILE_HEADER filhdr;
+ IMAGE_SECTION_HEADER scnhdr;
+ int i;
+
+ if (f == NULL) {
+ fprintf(stderr, "%s: cannot open %s.\n", progname, filename);
+ return 1;
+ }
+
+ if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read COFF header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("File: %s\n", filename);
+ v_printf("Magic number: 0x%08x\n", filhdr.Machine);
+ v_printf("Number of sections: %d\n",filhdr.NumberOfSections);
+
+ if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) {
+ fprintf(stderr,"%s: Could not read COFF optional header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+
+ for (i = 0; i < filhdr.NumberOfSections; ++i) {
+ if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read section header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("Section %s:\n", scnhdr.Name);
+ v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress);
+ v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData);
+ if (scnhdr.Misc.PhysicalAddress != 0 &&
+ scnhdr.SizeOfRawData == 0) {
+ printf("Section header %s in file %s will confuse MSC linker, "
+ "virtual size is 0x%08x and raw size is 0\n",
+ scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress,
+ scnhdr.SizeOfRawData);
+ if (edit) {
+ scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress;
+ scnhdr.Misc.PhysicalAddress = 0;
+ if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 ||
+ fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: could not edit file %s.\n",
+ progname, filename);
+ fclose(f);
+ return 1;
+ }
+ printf("Edited object, virtual size is now 0, and "
+ "raw size is 0x%08x.\n", scnhdr.SizeOfRawData);
+ } else {
+ printf("Specify option '-e' to correct the problem.\n");
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+
+static int v_printf(char *format, ...)
+{
+ va_list ap;
+ int ret = 0;
+ if (verbouse) {
+ va_start(ap, format);
+ ret = vfprintf(stdout, format, ap);
+ va_end(ap);
+ }
+ return ret;
+}
diff --git a/erts/etc/win32/wsl_tools/vc/emu_cc.sh b/erts/etc/win32/wsl_tools/vc/emu_cc.sh
new file mode 100755
index 0000000000..00b8555d2b
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/emu_cc.sh
@@ -0,0 +1,100 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+# sudo apt-get install gcc-mingw-w64 on (wsl ubuntu)
+
+if [ X"$CONFIG_SUBTYPE" = X"win64" ]; then
+ GCC="x86_64-w64-mingw32-gcc -m64"
+else
+ GCC="x86_64-w64-mingw32-gcc -m32"
+fi
+TOOLDIR=$ERL_TOP/erts/etc/win32/wsl_tools/vc
+COFFIX=$TOOLDIR/coffix
+WTOOLDIR=`w32_path.sh -d "$TOOLDIR"`
+# Do primitive 'make'
+newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print`
+
+if [ -z $newer_exe ]; then
+ echo recompiling $COFFIX.exe
+ cl.exe -Fe${WTOOLDIR}/coffix.exe ${WTOOLDIR}/coffix.c
+ rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb
+fi
+
+# Try to find out the output filename and remove it from command line
+CMD=""
+OUTFILE=""
+INFILE=""
+SKIP_COFFIX=false
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o/*)
+ OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;;
+ -o)
+ shift
+ OUTFILE=$1;;
+ -MM)
+ SKIP_COFFIX=true
+ CMD="$CMD \"$x\"";;
+ *.c)
+ INFILE="$INFILE $x";
+ CMD="$CMD \"$x\"";;
+ *)
+ CMD="$CMD \"$x\"";;
+ esac
+ shift
+done
+if [ -z "$INFILE" ]; then
+ echo 'emu_cc.sh: please give an input filename for the compiler' >&2
+ exit 1
+fi
+if [ -z "$OUTFILE" ]; then
+ OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'`
+fi
+
+if [ $SKIP_COFFIX = false ]; then
+ n=`echo $INFILE | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "emu_cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ fi
+ mkdir -p $ERL_TOP/tmp
+ TEMPFILE=$ERL_TOP/tmp/tmp_emu_cc$$.o
+ if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "$GCC -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1
+ fi
+ eval $GCC -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD
+ RES=$?
+ if [ $RES = 0 ]; then
+ $COFFIX.exe -e `w32_path.sh -w $TEMPFILE`
+ RES=$?
+ if [ $RES = 0 ]; then
+ cp $TEMPFILE $OUTFILE
+ else
+ echo "emu_cc.sh: fatal: coffix failed!" >&2
+ fi
+ fi
+ rm -f $TEMPFILE
+ exit $RES
+else
+ eval $GCC -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer -fno-tree-copyrename $CMD 2>/dev/null
+ exit $?
+fi
diff --git a/erts/etc/win32/wsl_tools/vc/ld.sh b/erts/etc/win32/wsl_tools/vc/ld.sh
new file mode 100755
index 0000000000..fc115bec8c
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/ld.sh
@@ -0,0 +1,210 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+
+SAVE="$@"
+kernel_libs="kernel32.lib advapi32.lib"
+gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib"
+DEFAULT_LIBRARIES="$kernel_libs $gdi_libs"
+
+CMD=""
+STDLIB=MSVCRT.LIB
+DEBUG_BUILD=false
+STDLIB_FORCED=false
+BUILD_DLL=false
+OUTPUT_FILENAME=""
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -dll| -DLL)
+ BUILD_DLL=true;;
+ -L/*|-L.*)
+ y=`echo $x | sed 's,^-L\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -libpath:\"$MPATH\"";;
+ -lMSVCRT|-lmsvcrt)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRT.LIB;;
+ -lMSVCRTD|-lmsvcrtd)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRTD.LIB;;
+ -lLIBCMT|-llibcmt)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMT.LIB;;
+ -lLIBCMTD|-llibcmtd)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMTD.LIB;;
+ -lsocket)
+ DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES WS2_32.LIB IPHLPAPI.LIB";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD \"${MPATH}.lib\"";;
+ -g)
+ DEBUG_BUILD=true;;
+ -pdb:none|-incremental:no)
+ ;;
+ -implib:*)
+ y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -implib:\"${MPATH}\"";;
+ -def:*)
+ y=`echo $x | sed 's,^-def:\(.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -def:\"${MPATH}\"";;
+ -o)
+ shift
+ MPATH=`w32_path.sh -a -d $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -a -d $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+if [ $DEBUG_BUILD = true ]; then
+ linktype="-debug -pdb:none"
+ if [ $STDLIB_FORCED = false ]; then
+ STDLIB=MSVCRTD.LIB
+ fi
+fi
+# Generate a PDB
+linkadd_pdb=""
+case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[eE][xX][eE]$,,g'`;
+ # fn=`w32_path.sh -a -d $fn0`
+ # echo EXE "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ *.dll|*.DLL)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[dD][lL][lL]$,,g'`;
+ # fn=`w32_path.sh -a -d $fn0`
+ # echo DLL "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ "")
+ linkadd_pdb="-pdb:\"a.pdb\"";;
+ *)
+ fn="$OUTPUT_FILENAME"
+ # fn=`w32_path.sh -a -d $OUTPUT_FILENAME`
+ # echo * "$OUTPUT_FILENAME" $fn
+ linkadd_pdb="-pdb:\"${fn}.pdb\"";;
+esac
+
+linktype="-debug $linkadd_pdb"
+
+CHMOD_FILE=""
+
+if [ $BUILD_DLL = true ];then
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ echo "Warning, output set to .exe when building DLL" >&2
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CMD="-dll -out:\"a.dll\" $CMD";
+ OUTPUTRES="a.dll\;2";
+ MANIFEST="a.dll.manifest";;
+ *)
+ CMD="-dll -out:\"${OUTPUT_FILENAME}.dll\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.dll\;2";
+ MANIFEST="${OUTPUT_FILENAME}.dll.manifest";;
+ esac
+else
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1"
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ echo "Warning, output set to .dll when building EXE" >&2
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CHMOD_FILE="a.exe";
+ CMD="-out:\"a.exe\" $CMD";
+ OUTPUTRES="a.exe\;1";
+ MANIFEST="a.exe.manifest";;
+ *)
+ CMD="-out:\"${OUTPUT_FILENAME}.exe\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.exe\;1";
+ MANIFEST="${OUTPUT_FILENAME}.exe.manifest";;
+ esac
+fi
+
+p=$$
+CMD="$linktype -nologo -incremental:no $CMD $STDLIB $DEFAULT_LIBRARIES"
+if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG
+ echo link.exe $CMD >>$LD_SH_DEBUG_LOG
+fi
+eval link.exe "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2
+RES=$?
+
+CMANIFEST=`w32_path.sh -u $MANIFEST`
+
+if [ "$RES" = "0" -a -f "$CMANIFEST" ]; then
+ # Add stuff to manifest to turn off "virtualization"
+ sed -n -i '1h;1!H;${;g;s,<trustInfo.*</trustInfo>.,,g;p;}' $CMANIFEST 2>/dev/null
+ sed -i "s/<\/assembly>/ <ms_asmv2:trustInfo xmlns:ms_asmv2=\"urn:schemas-microsoft-com:asm.v2\">\n <ms_asmv2:security>\n <ms_asmv2:requestedPrivileges>\n <ms_asmv2:requestedExecutionLevel level=\"AsInvoker\" uiAccess=\"false\"\/>\n <\/ms_asmv2:requestedPrivileges>\n <\/ms_asmv2:security>\n <\/ms_asmv2:trustInfo>\n<\/assembly>/" $CMANIFEST 2>/dev/null
+
+ eval mt.exe -nologo -manifest "$MANIFEST" -outputresource:"$OUTPUTRES" >>/tmp/link.exe.${p}.1 2>>/tmp/link.exe.${p}.2
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ REMOVE=`echo "$OUTPUTRES" | sed 's,\\\;[12]$,,g'`
+ CREMOVE=`wslpath -u $REMOVE`
+ ## If Defender or Search are enabled, they will lock just created files
+ ## and then mt will fail :/
+ echo "If you get this error, make sure Windows Defender AND Windows Search is disabled">>/tmp/link.exe.${p}.1
+ rm -f "$CREMOVE"
+ fi
+ rm -f "$CMANIFEST"
+fi
+
+# This works around some strange behaviour
+# in cygwin 1.7 Beta on Windows 7 with samba drive.
+# Configure will think the compiler failed if test -x fails,
+# which it might do as we might not be the owner of the
+# file.
+if [ '!' -z "$CHMOD_FILE" -a -s "$CHMOD_FILE" -a '!' -x "$CHMOD_FILE" ]; then
+ chmod +x $CHMOD_FILE
+fi
+
+tail -n +2 /tmp/link.exe.${p}.2 >&2
+cat /tmp/link.exe.${p}.1
+rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/mc.sh b/erts/etc/win32/wsl_tools/vc/mc.sh
new file mode 100755
index 0000000000..4ed7e7e2a3
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/mc.sh
@@ -0,0 +1,96 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_DIRNAME=""
+
+# Find the correct mc.exe. This could be done by the configure script,
+# But as we seldom use the message compiler, it might as well be done here...
+MCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/mc.exe ]; then
+ if [ -n "`$p/mc.exe -? 2>&1 >/dev/null </dev/null \
+ | grep -i \"message compiler\"`" ]; then
+ MCC=`echo "mc.exe" | sed 's/ /\\\\ /g'`
+ break
+ else
+ echo "Bad mc.exe in path" >&2
+ exit 1
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$MCC" ]; then
+ echo 'mc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ OUTPUT_DIRNAME="$1";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ OUTPUT_DIRNAME="$y";;
+ -I)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ *)
+ MPATH=`w32_path.sh -d -a $x`;
+ CMD="$CMD \"$MPATH\"";;
+ esac
+ shift
+done
+p=$$
+if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then
+ echo mc.sh "$SAVE" >>$MC_SH_DEBUG_LOG
+ echo mc.exe $CMD >>$MC_SH_DEBUG_LOG
+fi
+if [ -n "$OUTPUT_DIRNAME" ]; then
+ cd $OUTPUT_DIRNAME
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2
+ exit $RES
+ fi
+fi
+
+eval $MCC "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2
+RES=$?
+if [ $RES != 0 ]; then
+ echo Failed: $MCC "$CMD"
+fi
+tail -n +2 /tmp/mc.exe.${p}.2 >&2
+cat /tmp/mc.exe.${p}.1
+rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/vc/rc.sh b/erts/etc/win32/wsl_tools/vc/rc.sh
new file mode 100755
index 0000000000..bbd5c9a773
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/vc/rc.sh
@@ -0,0 +1,94 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_FILENAME=""
+
+# Find the correct rc.exe. This could be done by the configure script,
+# But as we seldom use the resource compiler, it might as well be done here...
+RCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/rc.exe ]; then
+ if [ -n "`$p/rc.exe -? 2>&1 | grep -i "resource compiler"`" ]; then
+ RCC="rc.exe /nologo"
+ break
+ else
+ echo "Bad rc.exe in path" >&2
+ exit 1
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$RCC" ]; then
+ echo 'rc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ -I)
+ shift
+ MPATH=`w32_path.sh -d $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`w32_path.sh -d $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ /*)
+ MPATH=`w32_path.sh -d $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+p=$$
+if [ -n "$OUTPUT_FILENAME" ]; then
+ CMD="-Fo$OUTPUT_FILENAME $CMD"
+fi
+if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then
+ echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG
+ echo rc.exe /nologo $CMD >>$RC_SH_DEBUG_LOG
+fi
+eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2
+RES=$?
+if [ $RES != 0 ]; then
+ echo Failed: $RCC "$CMD"
+fi
+tail -n +2 /tmp/rc.exe.${p}.2 >&2
+cat /tmp/rc.exe.${p}.1
+rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/wsl_tools/w32_path.sh b/erts/etc/win32/wsl_tools/w32_path.sh
new file mode 100755
index 0000000000..55fbd76174
--- /dev/null
+++ b/erts/etc/win32/wsl_tools/w32_path.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+WIN32=false
+SEPARATOR=""
+ABSOLUTE=""
+UNIX=false
+done=false
+while [ $done = false ]; do
+ case "$1" in
+ -w)
+ WIN32=true;
+ SEPARATOR=backslash;
+ shift;;
+ -d)
+ WIN32=true;
+ SEPARATOR=double;
+ shift;;
+ -m)
+ WIN32=true;
+ SEPARATOR=slash;
+ shift;;
+ -u)
+ UNIX=true;
+ shift;;
+ -a)
+ ABSOLUTE="-a";
+ shift;;
+
+ *)
+ done=true;;
+ esac
+done
+
+if [ $WIN32 = false -a $UNIX = false ]; then
+ echo "Usage: $0 -m|-w|-d|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 -m|-w|-u [-a] <path>" >&2
+ exit 1;
+fi
+
+if [ $UNIX = true ]; then
+ # cl.exe loses //// in the beginning which make dependencies fail
+ # and sometimes lowercases the path
+ case $1 in
+ \\*wsl$\\*)
+ y=`echo $1 | sed 's,\\\\\+,/,g'`;
+ z=`echo $y | sed 's,^/wsl$/[^/]*\(.*\),\1,g' | sed 's, ,\\ ,g'`;
+ echo "$z";
+ ;;
+ *)
+ echo `wslpath -u $ABSOLUTE "$1" | sed 's, ,\\ ,g'`
+ ;;
+ esac
+else
+ case "$SEPARATOR" in
+ slash)
+ echo `wslpath -m $ABSOLUTE "$1"`;
+ ;;
+ backslash)
+ echo `wslpath -w $ABSOLUTE "$1"`;
+ ;;
+ double)
+ DOUBLE=`wslpath -w $ABSOLUTE "$1" | sed 's,\\\\,\\\\\\\\,g'`;
+ echo $DOUBLE
+ ;;
+ esac
+fi
diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h
index 55566ddf74..59933ade4b 100644
--- a/erts/include/internal/erl_misc_utils.h
+++ b/erts/include/internal/erl_misc_utils.h
@@ -39,6 +39,7 @@ int erts_cpu_info_update(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_configured(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_online(erts_cpu_info_t *cpuinfo);
int erts_get_cpu_available(erts_cpu_info_t *cpuinfo);
+int erts_get_cpu_quota(erts_cpu_info_t *cpuinfo);
char *erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo);
int erts_get_available_cpu(erts_cpu_info_t *cpuinfo, int no);
int erts_get_cpu_topology_size(erts_cpu_info_t *cpuinfo);
diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h
index 56ec032bd1..27fe914caa 100644
--- a/erts/include/internal/erl_printf_format.h
+++ b/erts/include/internal/erl_printf_format.h
@@ -21,10 +21,6 @@
#ifndef ERL_PRINTF_FORMAT_H__
#define ERL_PRINTF_FORMAT_H__
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index fa35bf3d0b..2fe826226c 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -54,8 +54,7 @@
#endif
#if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \
- || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) \
- || (defined(__GNUC__) && defined(ERTS_MIXED_MSYS_VC))
+ || (defined(__GNUC__) && defined(ERTS_MIXED_VC))
# undef ETHR_INLINE
# define ETHR_INLINE
# undef ETHR_FORCE_INLINE
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index 1da11c2d0a..85250c4358 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -152,7 +152,6 @@ ERTS_INCL_INT=../include/internal
INCLUDES=-I$(ERTS_INCL) -I$(ERTS_INCL)/$(TARGET) -I$(ERTS_INCL_INT) -I$(ERTS_INCL_INT)/$(TARGET)
INCLUDES += -I../emulator/beam -I../emulator/sys/$(ERLANG_OSTYPE)
-USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_VC=@MIXED_VC@
ifeq ($(USING_VC),yes)
@@ -657,8 +656,10 @@ endif
endif
@echo "# EOF" >> $(DEPEND_MK);
+ifneq ($(ERTS_SKIP_DEPEND),true)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPEND_MK)
endif
+endif
# eof
diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c
index c75c492c9d..e047c0eec7 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -29,10 +29,7 @@
#include "ethread_inline.h"
#include "erl_misc_utils.h"
-#if defined(__WIN32__)
-#elif defined(VXWORKS)
-# include <selectLib.h>
-#else /* UNIX */
+#if !defined(__WIN32__) /* UNIX */
# include <stdio.h>
# include <sys/types.h>
# include <sys/param.h>
@@ -54,6 +51,7 @@
# endif
# endif
# include <string.h>
+# include <stdio.h>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
@@ -133,6 +131,7 @@
#endif
static int read_topology(erts_cpu_info_t *cpuinfo);
+static int read_cpu_quota(int limit);
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
static int
@@ -176,6 +175,7 @@ struct erts_cpu_info_t_ {
int online;
int available;
int topology_size;
+ int quota;
erts_cpu_topology_t *topology;
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
char *affinity_str;
@@ -238,6 +238,7 @@ erts_cpu_info_create(void)
cpuinfo->configured = -1;
cpuinfo->online = -1;
cpuinfo->available = -1;
+ cpuinfo->quota = -1;
erts_cpu_info_update(cpuinfo);
return cpuinfo;
}
@@ -269,6 +270,7 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo)
int configured = 0;
int online = 0;
int available = 0;
+ int quota = 0;
erts_cpu_topology_t *old_topology;
int old_topology_size;
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
@@ -412,9 +414,14 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo)
if (cpuinfo->available != available)
changed = 1;
+ quota = read_cpu_quota(online);
+ if (cpuinfo->quota != quota)
+ changed = 1;
+
cpuinfo->configured = configured;
cpuinfo->online = online;
cpuinfo->available = available;
+ cpuinfo->quota = quota;
old_topology = cpuinfo->topology;
old_topology_size = cpuinfo->topology_size;
@@ -471,6 +478,16 @@ erts_get_cpu_available(erts_cpu_info_t *cpuinfo)
return cpuinfo->available;
}
+int
+erts_get_cpu_quota(erts_cpu_info_t *cpuinfo)
+{
+ if (!cpuinfo)
+ return -EINVAL;
+ if (cpuinfo->quota <= 0)
+ return -ENOTSUP;
+ return cpuinfo->quota;
+}
+
char *
erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo)
{
@@ -775,7 +792,7 @@ adjust_processor_nodes(erts_cpu_info_t *cpuinfo, int no_nodes)
#ifdef __linux__
static int
-read_file(char *path, char *buf, int size)
+read_file(const char *path, char *buf, int size)
{
int ix = 0;
ssize_t sz = size-1;
@@ -1004,6 +1021,211 @@ read_topology(erts_cpu_info_t *cpuinfo)
return res;
}
+static int
+csv_contains(const char *haystack,
+ const char *element,
+ char separator) {
+ size_t element_len;
+ const char *ptr;
+
+ element_len = strlen(element);
+ ptr = strstr(haystack, element);
+
+ while (ptr) {
+ if (!ptr[element_len] || ptr[element_len] == separator) {
+ if (ptr == haystack || ptr[-1] == separator) {
+ return 1;
+ }
+ }
+
+ ptr = strstr(&ptr[1], element);
+ }
+
+ return 0;
+}
+
+static const char*
+str_combine(const char *a, const char *b) {
+ size_t a_len, b_len;
+ char *result;
+
+ a_len = strlen(a);
+ b_len = strlen(b);
+
+ result = malloc(a_len + b_len + 1);
+
+ memcpy(&result[0], a, a_len);
+ memcpy(&result[a_len], b, b_len + 1);
+
+ return result;
+}
+
+static const char*
+get_cgroup_v1_base_dir(const char *controller) {
+ char line_buf[5 << 10];
+ FILE *var_file;
+
+ var_file = fopen("/proc/self/cgroup", "r");
+
+ if (var_file == NULL) {
+ return NULL;
+ }
+
+ while (fgets(line_buf, sizeof(line_buf), var_file)) {
+ /* sscanf_s requires C11, so we use hardcoded sizes (rather than rely
+ * on macros like MAXPATHLEN) so we can specify them directly in the
+ * format string. */
+ char base_dir[4 << 10];
+ char controllers[256];
+
+ if (sscanf(line_buf, "%*d:%255[^:]:%4095s\n",
+ controllers, base_dir) != 2) {
+ continue;
+ }
+
+ if (csv_contains(controllers, controller, ',')) {
+ fclose(var_file);
+ return strdup(base_dir);
+ }
+ }
+
+ fclose(var_file);
+ return NULL;
+}
+
+static const char*
+get_cgroup_path(const char *controller) {
+ char line_buf[10 << 10];
+ FILE *var_file;
+
+ var_file = fopen("/proc/self/mountinfo", "r");
+
+ if (var_file == NULL) {
+ return NULL;
+ }
+
+ while (fgets(line_buf, sizeof(line_buf), var_file)) {
+ char mount_path[4 << 10];
+ char root_path[4 << 10];
+ char fs_flags[512];
+ char fs_type[64];
+
+ /* Format:
+ * [Mount id] [Parent id] [Major] [Minor] [Root] [Mounted at] \
+ * [Mount flags] ... (options terminated by a single hyphen) ... \
+ * [FS type] [Mount source] [Flags]
+ *
+ * (See proc(5) for a more complete description.)
+ *
+ * This fails if any of the fs options contain a hyphen, but this is
+ * not likely to happen on a cgroup, so we just skip such lines. */
+ if (sscanf(line_buf,
+ "%*d %*d %*d:%*d %4095s %4095s %*s %*[^-]- "
+ "%63s %*s %511[^\n]\n",
+ root_path, mount_path,
+ fs_type, fs_flags) != 4) {
+ continue;
+ }
+
+ if (!strcmp(fs_type, "cgroup2")) {
+ char controllers[256];
+ const char *cgc_path;
+
+ cgc_path = str_combine(mount_path, "/cgroup.controllers");
+ if (read_file(cgc_path, controllers, sizeof(controllers)) > 0) {
+ if (csv_contains(controllers, controller, ' ')) {
+ free((void*)cgc_path);
+ fclose(var_file);
+ return strdup(mount_path);
+ }
+ }
+ free((void*)cgc_path);
+ } else if (!strcmp(fs_type, "cgroup")) {
+ if (csv_contains(fs_flags, controller, ',')) {
+ const char *base_dir = get_cgroup_v1_base_dir(controller);
+
+ if (base_dir) {
+ const char *result;
+
+ if (strcmp(root_path, base_dir)) {
+ result = str_combine(mount_path, base_dir);
+ } else {
+ result = strdup(mount_path);
+ }
+
+ free((void*)base_dir);
+ fclose(var_file);
+ return result;
+ }
+ }
+ }
+ }
+
+ fclose(var_file);
+ return NULL;
+}
+
+static int read_cgroup_var(const char *group_path, const char *var_name,
+ ssize_t *out) {
+ const char *var_path;
+ int res;
+
+ var_path = str_combine(group_path, var_name);
+ res = 0;
+
+ if (var_path) {
+ FILE *var_file = fopen(var_path, "r");
+ free((void*)var_path);
+
+ if (var_file) {
+ if (fscanf(var_file, "%zi", out) == 1) {
+ res = 1;
+ }
+ fclose(var_file);
+ }
+ }
+
+ return res;
+}
+
+/* CPU quotas are read from the cgroup configuration, which can be pretty hairy
+ * as we need to support both v1 and v2, and it's possible for both versions to
+ * be active at the same time. */
+
+static int
+read_cpu_quota(int limit)
+{
+ const char *cgroup_path = get_cgroup_path("cpu");
+
+ if (cgroup_path) {
+ ssize_t cfs_period_us, cfs_quota_us;
+ int succeeded;
+
+ cfs_period_us = -1;
+ cfs_quota_us = -1;
+
+ succeeded =
+ read_cgroup_var(cgroup_path, "/cpu.cfs_quota_us", &cfs_quota_us) &&
+ read_cgroup_var(cgroup_path, "/cpu.cfs_period_us", &cfs_period_us);
+
+ free((void*)cgroup_path);
+
+ if (succeeded) {
+ if (cfs_period_us > 0 && cfs_quota_us > 0) {
+ size_t quota = cfs_quota_us / cfs_period_us;
+
+ if (quota > 0 && quota <= (size_t)limit) {
+ return quota;
+ }
+ }
+
+ return limit;
+ }
+ }
+
+ return 0;
+}
+
#elif defined(HAVE_KSTAT) /* SunOS kstat */
#include <kstat.h>
@@ -1157,6 +1379,13 @@ read_topology(erts_cpu_info_t *cpuinfo)
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#elif defined(__WIN32__)
/*
@@ -1431,6 +1660,13 @@ read_topology(erts_cpu_info_t *cpuinfo)
return res;
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#elif defined(__FreeBSD__)
/**
@@ -1670,9 +1906,23 @@ error:
return res;
}
+static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
#else
static int
+read_cpu_quota(int limit)
+{
+ (void)limit;
+ return 0;
+}
+
+static int
read_topology(erts_cpu_info_t *cpuinfo)
{
return -ENOTSUP;
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index dd808eb158..9c33a107d1 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -18,11 +18,6 @@
* %CopyrightEnd%
*/
-/* Without this, variable argument lists break on VxWorks */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 8f9e0b4a90..c8256532b5 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -31,11 +31,6 @@
* sz: 8 | 16 | 32 | 64 | p | e
*/
-/* Without this, variable argument lists break on VxWorks */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 7b156fe01a..931469b386 100644
--- a/erts/lib_src/common/ethr_aux.c
+++ b/erts/lib_src/common/ethr_aux.c
@@ -109,7 +109,8 @@ x86_init(void)
if (eax > 0
&& (ETHR_IS_X86_VENDOR("GenuineIntel", ebx, ecx, edx)
- || ETHR_IS_X86_VENDOR("AuthenticAMD", ebx, ecx, edx))) {
+ || ETHR_IS_X86_VENDOR("AuthenticAMD", ebx, ecx, edx)
+ || ETHR_IS_X86_VENDOR("HygonGenuine", ebx, ecx, edx))) {
eax = 1;
ethr_x86_cpuid__(&eax, &ebx, &ecx, &edx);
}
diff --git a/erts/lib_src/yielding_c_fun/.gitignore b/erts/lib_src/yielding_c_fun/.gitignore
new file mode 100644
index 0000000000..eb74315e49
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/.gitignore
@@ -0,0 +1,16 @@
+*.o
+*~
+bin/yielding_c_fun.bin
+core
+test/test_trap_out.c
+GPATH
+GRTAGS
+GTAGS
+test/tmp_dir
+compile_commands.json
+compile_commands_old.json
+CMakeLists.txt
+cmake_mkdir/
+.idea/
+cmake-build-debug/
+ycf_malloc_log.txt \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/GNUmakefile b/erts/lib_src/yielding_c_fun/GNUmakefile
new file mode 100644
index 0000000000..1a18214c21
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/GNUmakefile
@@ -0,0 +1,212 @@
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+
+#
+# Author: Kjell Winblad
+#
+
+#
+# Note: The main target of this makefile generates another makefile
+# which is named "Makefile". The generated file "Makefile" should be
+# compatible with other make implementations than GNU make while this
+# file is only compatible with GNU make. GNU make will automatically
+# use this file as makefile when executed in the directory where this
+# file is located.
+#
+# Erlang/OTP note: The build system for Erlang/OTP does not use this
+# file to build Yielding C Fun (YCF). The file "main_target.mk"
+# contains the necessary rules to build YCF and is included both by
+# this file and the Erlang/OTP file
+# $(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for
+# building YCF independently of Erlang/OTP. This file also contains
+# several special make targets (e.g., to invoke tests).
+#
+
+
+ifdef MODERN_CC
+ EXTRA_C_FLAGS = -g -O02 -std=c99 -pedantic -Wall
+endif
+
+ifdef CC_32_BIT
+ EXTRA_C_FLAGS = -m32 -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef USE_GC
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef ADD_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=address -fno-omit-frame-pointer
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef MEM_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=memory -fno-omit-frame-pointer
+endif
+
+ifdef UB_SAN
+ V_CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=lib/simple_c_gc/.misc/clang_blacklist.txt -fsanitize=undefined -fno-omit-frame-pointer
+endif
+
+
+YCF_SOURCE_DIR = .
+
+ifndef V_CC
+ V_CC = cc
+endif
+
+ifndef V_LD
+ V_LD = $(CC)
+endif
+
+CFLAGS = $(EXTRA_C_FLAGS)
+
+all: ./bin/yielding_c_fun.bin Makefile
+
+include main_target.mk
+
+##############################################################
+# Generate a simple Makefile intended to work with other make
+# implementations than GNU make
+##############################################################
+
+Makefile: $(YCF_SOURCES)
+ echo '#' > Makefile
+ echo '# !!!!!!!!!!! OBS Generated by GNUmakefile !!!!!!!!!!!!' >> Makefile
+ echo '# This file is supposed to be compatible with as many make implementations as possible' >> Makefile
+ echo '# Do not modify this file manually unless you cannot use GNU make.' >> Makefile
+ echo '# Instead, just run GNU make in the directory where this file is located' >> Makefile
+ echo '# and a new version of this file will be generated when needed.' >> Makefile
+ echo '# GNU make will use the file GNUmakefile which is located in the same' >> Makefile
+ echo '# directory.' >> Makefile
+ echo '#' >> Makefile
+ echo '#' >> Makefile
+ echo '# Erlang/OTP note: The build system for Erlang/OTP does not use this' >> Makefile
+ echo '# file to build Yielding C Fun (YCF). The file "main_target.mk"' >> Makefile
+ echo '# contains the necessary rules to build YCF and is included both by' >> Makefile
+ echo '# this file and the Erlang/OTP file' >> Makefile
+ echo '# $$(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for' >> Makefile
+ echo '# building YCF independently of Erlang/OTP.' >> Makefile
+ echo '#' >> Makefile
+ echo >> Makefile
+ O_FILES="";\
+ for FILE in $(YCF_SOURCES);\
+ do\
+ OFILE_FULL=`echo $$FILE | sed 's/\.c/.o/'` ; \
+ OFILE=`basename $$OFILE_FULL` ; \
+ O_FILES="$$O_FILES $$OFILE" ; \
+ done ; \
+ echo $(YCF_EXECUTABLE)':' $$O_FILES >> Makefile ; \
+ echo ' $$(CC) $(YCF_INCLUDE_DIRS) $$(CFLAGS) -o '$(YCF_EXECUTABLE)' ' $$O_FILES >> Makefile
+ for FILE in $(YCF_SOURCES);\
+ do\
+ echo >> Makefile ; \
+ OFILE_FULL=`echo $$FILE | sed 's/\.c/.o/'` ; \
+ OFILE=`basename $$OFILE_FULL` ; \
+ echo "$$OFILE: $$FILE" >> Makefile ;\
+ echo ' $$(CC) $(YCF_INCLUDE_DIRS) $$(CFLAGS) ' -c -o $$OFILE $$FILE >> Makefile ;\
+ done
+ echo >> Makefile
+
+##############################################
+# Special targets for testing etc:
+##############################################
+
+.PHONY: all clean test run_test_continusly CMakeLists.txt cmake_compile clang_format test_add_san test_mem_san test_modern_cc test_sanitizers test_gcc_clang_tcc clang_tidy test_bmake test_all
+
+test: $(YCF_EXECUTABLE)
+ ./test/test.sh $(USE_GC_STRING) ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_add_san:
+ make clean && \
+ make V_CC=clang V_LD=clang ADD_SAN=1 test
+
+test_mem_san:
+ make clean && \
+ make V_CC=clang V_LD=clang MEM_SAN=1 test
+
+test_ub_san:
+ make clean && \
+ make V_CC=clang V_LD=clang UB_SAN=1 test
+
+test_modern_cc:
+ make clean && \
+ make V_CC=clang V_LD=clang MODERN_CC=1 test
+
+test_sanitizers:
+ make test_add_san && \
+ make test_mem_san && \
+ make test_ub_san
+
+test_gcc_clang_tcc:
+ make V_CC=gcc V_LD=gcc EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE) && \
+ make V_CC=clang V_LD=clang EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE) && \
+ make V_CC=tcc V_LD=tcc EXTRA_C_FLAGS="-g -O01 -std=c99 -pedantic -Wall -Werror" clean $(YCF_EXECUTABLE)
+
+test_32_bit:
+ make clean && \
+ make CC_32_BIT=1 test && \
+ make clean
+
+# sudo apt-get install bmake
+# test that something else than GNU make can compile the tool
+test_bmake:
+ make clean && \
+ make Makefile && \
+ bmake
+
+test_all:
+ make test_gcc_clang_tcc && \
+ make clang_tidy && \
+ make test_sanitizers && \
+ make test_modern_cc && \
+ make test_32_bit && \
+ make test_bmake
+
+run_test_continusly:
+ inotifywait -e close_write,moved_to,create -m ./*.c ./*.h -m test -m test/examples | while read -r directory events filename; do gtags ; make test_all ; done
+
+cmake_compile: CMakeLists.txt
+ mkdir cmake_mkdir || true
+ cd cmake_mkdir && cmake ..
+
+clang_tidy:
+ (ls *.c ; echo lib/tiny_regex_c/re.c ; echo lib/simple_c_gc/simple_c_gc.c) | xargs -I{} -n1 clang-tidy -warnings-as-errors=* {} -- $(YCF_INCLUDE_DIRS) $(YCF_CFLAGS)
+
+clang_format:
+ clang-format -style="{BasedOnStyle: LLVM}" -i *.c *.h
+
+clean:
+ rm -f lib/simple_c_gc/*.o lib/tiny_regex_c/*.o ./*.o ./*~ core trap parse $(YCF_EXECUTABLE) CMakeLists.txt
+
+# Produce a CMakeLists.txt to build with cmake
+CMakeLists.txt: $(YCF_SOURCES)
+ echo "cmake_minimum_required (VERSION 2.6)" > CMakeLists.txt
+ echo "project (YIELDING_C_FUN C)" >> CMakeLists.txt
+ echo "add_executable(cmake.out " >> CMakeLists.txt
+ echo $(YCF_SOURCES) >> CMakeLists.txt
+ echo ")" >> CMakeLists.txt
+
diff --git a/erts/lib_src/yielding_c_fun/Makefile b/erts/lib_src/yielding_c_fun/Makefile
new file mode 100644
index 0000000000..35de0a86ac
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/Makefile
@@ -0,0 +1,54 @@
+#
+# !!!!!!!!!!! OBS Generated by GNUmakefile !!!!!!!!!!!!
+# This file is supposed to be compatible with as many make implementations as possible
+# Do not modify this file manually unless you cannot use GNU make.
+# Instead, just run GNU make in the directory where this file is located
+# and a new version of this file will be generated when needed.
+# GNU make will use the file GNUmakefile which is located in the same
+# directory.
+#
+#
+# Erlang/OTP note: The build system for Erlang/OTP does not use this
+# file to build Yielding C Fun (YCF). The file "main_target.mk"
+# contains the necessary rules to build YCF and is included both by
+# this file and the Erlang/OTP file
+# $(ERL_TOP)/erts/emulator/Makefile.in. This file is intended for
+# building YCF independently of Erlang/OTP.
+#
+
+./bin/yielding_c_fun.bin: simple_c_gc.o re.o ycf_lexer.o ycf_main.o ycf_node.o ycf_parser.o ycf_printers.o ycf_string.o ycf_symbol.o ycf_utils.o ycf_yield_fun.o
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -o ./bin/yielding_c_fun.bin simple_c_gc.o re.o ycf_lexer.o ycf_main.o ycf_node.o ycf_parser.o ycf_printers.o ycf_string.o ycf_symbol.o ycf_utils.o ycf_yield_fun.o
+
+simple_c_gc.o: ./lib/simple_c_gc/simple_c_gc.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o simple_c_gc.o ./lib/simple_c_gc/simple_c_gc.c
+
+re.o: ./lib/tiny_regex_c/re.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o re.o ./lib/tiny_regex_c/re.c
+
+ycf_lexer.o: ./ycf_lexer.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_lexer.o ./ycf_lexer.c
+
+ycf_main.o: ./ycf_main.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_main.o ./ycf_main.c
+
+ycf_node.o: ./ycf_node.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_node.o ./ycf_node.c
+
+ycf_parser.o: ./ycf_parser.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_parser.o ./ycf_parser.c
+
+ycf_printers.o: ./ycf_printers.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_printers.o ./ycf_printers.c
+
+ycf_string.o: ./ycf_string.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_string.o ./ycf_string.c
+
+ycf_symbol.o: ./ycf_symbol.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_symbol.o ./ycf_symbol.c
+
+ycf_utils.o: ./ycf_utils.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_utils.o ./ycf_utils.c
+
+ycf_yield_fun.o: ./ycf_yield_fun.c
+ $(CC) -I. -I./lib/tiny_regex_c -I./lib/simple_c_gc $(CFLAGS) -c -o ycf_yield_fun.o ./ycf_yield_fun.c
+
diff --git a/erts/lib_src/yielding_c_fun/README.md b/erts/lib_src/yielding_c_fun/README.md
new file mode 100644
index 0000000000..f63475e6a8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/README.md
@@ -0,0 +1,610 @@
+Yielding C Fun
+==============
+
+Introduction
+------------
+
+Yielding C Fun (YCF) is a tool that transforms functions written in a
+subset of the C programming language so that they become yieldable. A
+yieldable function can be suspended/yielded/paused/trapped (either
+automatically or where the user has inserted a particular statement)
+and then be resumed at a later point. Yileldable functions are also
+called [coroutines](https://en.wikipedia.org/wiki/Coroutine).
+
+Difference Between Yielding C Fun and Coroutine Libraries
+---------------------------------------------------------
+
+Several libraries implement [coroutine support for the C programming
+language](https://en.wikipedia.org/wiki/Coroutine#Implementations_for_C)
+(e.g., \[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11],
+[12], [13]\]). These libraries either rely on platform-specific code
+or do not save call stack variables. Yielding C Fun (YCF) does not
+have any of these two limitations. YCF can accomplish this as it is a
+source-to-source transformation tool and not only a library.
+
+YCF has been created to make it easier to implement yielding Erlang
+[NIFs](http://erlang.org/doc/tutorial/nif.html) and
+[BIFs](http://erlang.org/pipermail/erlang-questions/2009-October/046899.html)
+(i.e., Erlang functions that are written in C). Below are examples of
+YCF features that are useful when implementing yielding Erlang NIFs
+and BIFs:
+
+ * YCF automatically generates a destroy function for each yieldable
+ function. The destroy function frees resources that are used by a
+ suspended function. The destroy function is useful when a suspended
+ function needs to abort (e.g., when the Erlang process that invoked
+ the function has died).
+
+ * YCF can automatically insert code that yields functions after a
+ user specifiable number of loop iterations and goto statements.
+
+ * YCF has a hook system that lets the user insert code that is
+ triggered when certain events happen (e.g., when a function
+ yields).
+
+The main limitations of YCF are that it cannot handle all valid C code
+and that it cannot make library functions without source code
+yieldable. Pointers to stack-allocated data are also not allowed (YCF
+has a memory allocation function called `YCF_STACK_ALLOC` to work
+around this issue).
+
+Requirements
+------------
+
+* A C99 compatible C compiler
+* make (optional but useful for building)
+
+Compile and Test
+----------------
+
+Build the executable `$YCF_ROOT/bin/yielding_c_fun.bin`:
+
+ make
+
+Build the executable and run all tests:
+
+ make test
+
+Getting Started
+---------------
+
+A brief introduction tutorial can be found
+[here](doc/thread_tutorial.md). This tutorial is a perfect place to
+start!
+
+The "[test/examples/](test/examples/)" folder in this repository
+contains many small examples that are useful when learning about
+YCF. YCF's automatic tests use these examples as well. The driver for
+these tests is located in `test/test.sh`.
+
+[This Erlang NIF example](test/examples/sha256_erlang_nif/) shows how
+one can use YCF to write a yielding Erlang NIF library.
+
+
+Command Line Parameters
+-----------------------
+
+```
+Usage: yielding_c_fun [-h]
+ yielding_c_fun [-use_gc [-print_gc_info]]
+ [-log_max_mem_usage log_file]
+ [(( -f | -frec | -fnoauto ) function_name)...
+ [-output_file_name output_file]
+ [-header_file_name header_file]
+ [-debug]
+ [-only_yielding_funs]
+ [-static_aux_funs]
+ input_c_file]]
+```
+
+* `-h`
+
+ Print help text
+
+* `-use_gc`
+
+ Use garbage collection. The garbage collection system assumes that
+ the C call stack consists of a continuous memory block and is
+ therefore not enabled by default even though this assumption is
+ valid on all major platforms. YCF does not reclaim any allocated
+ memory if the `-use_gc` flag is not set.
+
+* `-print_gc_info`
+
+ (For debugging) Print garbage collection information to `stderr`
+
+* `-log_max_mem_usage log_file`
+
+ (For debugging) Print the peak memory usage of the tool to the file
+ `log_file`
+
+* `-fnoauto function_name`
+
+ Generate a yieldable version of the function named
+ function_name. The user can use `YCF_YIELD()`,
+ `YCF_YIELD_NO_REDS()`, and `YCF_CONSUME_REDS(N)` to control
+ when and where the function should yield. See the section titled
+ "Special Statements and Macros" for more information.
+
+* `-f function_name`
+
+ Generate a yieldable version of the function named
+ `function_name`. The generated function automatically decrements the
+ reduction counter by one at the beginning of loop bodies and before
+ goto statements. The function yields automatically if the reduction
+ counter reaches a value that is zero or smaller after it has been
+ decremented.
+
+* `-frec function_name`
+
+ Same as the -f option with the exception that the generated function
+ also decrements one reduction before calls to other yieldable
+ functions and before returning. The function yields automatically if
+ the reduction counter reaches a value that is zero or smaller after
+ it has been decremented.
+
+* `-output_file_name output_file`
+
+ Output the generated code to a file named output_file. The output
+ is printed to standard output if this parameter is unspecified.
+
+* `-header_file_name header_file`
+
+ Generate a header file containing only declarations for the generated
+ functions and write the header file to the file named header_file.
+
+* `-debug`
+
+ Generate debug code that executes when a function yields. The debug
+ code searches the call stack of the yielding functions for pointers
+ to data that is allocated on the call stack. The program crashes
+ with an error message if any such pointer is found.
+
+* `-only_yielding_funs`
+
+ Print only the generated functions and struct declarations. The
+ default behavior is to insert the generated functions into a copy of
+ the input source file.
+
+* `-static_aux_funs`
+
+ Make the generated auxiliary functions static (i.e., local to the C
+ compilation unit)
+
+* `input_c_file`
+
+ The source file containing the functions that YCF shall create
+ yieldable versions of. YCF does not do any macro expansions. There
+ are several restrictions on the code that YCF can handle that are
+ described in the "Code Restrictions" section below.
+
+Generated Functions
+-------------------
+
+YCF generates three functions for each function name that it is
+given. These functions have the original function name as prefix and
+different suffixes. Descriptions of the functions that YCF generates
+follows below:
+
+
+```c
+
+/* Shall return a pointer to a memory block of size size bytes. */
+typedef void* (*ycf_yield_alloc_type) (size_t size ,void* ctx);
+/* Shall free the memory block which block points to. */
+typedef void (*ycf_yield_free_type) (void* block,void* ctx);
+
+return_type_of_orginal_fun
+original_fun_name_ycf_gen_yielding(
+ long * ycf_nr_of_reductions,
+ void ** ycf_yield_state,
+ void * ycf_extra_context,
+ ycf_yield_alloc_type ycf_yield_alloc,
+ ycf_yield_free_type ycf_yield_free,
+ void * ycf_yield_alloc_free_context,
+ size_t ycf_stack_alloc_size_or_max_size,
+ void* ycf_stack_alloc_data
+ paremeters_of_orginal_function);
+```
+
+The generated function with suffix `_ycf_gen_yielding` initiates the
+call of a yieldable function. Its parameters and return types are
+described below:
+
+* `return_type_of_orginal_fun`
+
+ The return type is the same as the return type of the original
+ function. The return value is the return value of the function if
+ the `_ycf_gen_yielding` function returns without yielding and is
+ uninitialized otherwise.
+
+* `long * ycf_nr_of_reductions`
+
+ (input/output parameter) Gives the yieldable function the number of
+ reductions it can consume before yielding and is also used to write
+ back the number of reductions that are left when the function
+ returns or yields.
+
+* `void ** ycf_yield_state`
+
+ (input/output parameter) Should be a pointer to a pointer to NULL
+ when the `_ycf_gen_yielding` function is called. The value pointed
+ to by ycf_yield_state is NULL when the `_ycf_gen_yielding` function
+ has returned if it did not yield and points to the yield state
+ otherwise.
+
+* `void * ycf_extra_context`
+
+ This parameter is useful if the yieldable function needs to access
+ data that may change when it resumes after having been yielded. The
+ extra context can be accessed from within the yieldable function
+ with the `YCF_GET_EXTRA_CONTEXT()` function.
+
+* `ycf_yield_alloc_type ycf_yield_alloc`
+
+ A memory allocator function that is used by the yieldable function
+ to allocate memory (e.g., to save the state when the function
+ yields).
+
+* `ycf_yield_free ycf_yield_free`
+
+ A memory free function that should free a memory block that has been
+ allocated with ycf_yield_alloc.
+
+* `void * ycf_yield_alloc_free_context`
+
+ A context that is passed as the second argument to `ycf_yield_alloc`
+ and `ycf_yield_free`.
+
+* `size_t ycf_stack_alloc_size_or_max_size`
+
+ The max number of total bytes that can be allocated with the special
+ allocator `YCF_STACK_ALLOC(n)`. This can be set to 0 if
+ `YCF_STACK_ALLOC(n)` is unused. See the documentation of
+ `YCF_STACK_ALLOC(n)` below for more information.
+
+* `void* ycf_stack_alloc_data`
+
+ A pointer to a data block that will be used by
+ `YCF_STACK_ALLOC(n)`. The value of `ycf_stack_alloc_data` should be
+ `NULL` or a pointer to a data block that is least
+ `ycf_stack_alloc_size_or_max_size` bytes large if
+ `YCF_STACK_ALLOC(n)` is used within the yieldable function or any
+ yieldable function that is called by the yieldable function. The
+ `ycf_yield_alloc` and `ycf_yield_free` functions will be used to
+ automatically alloc and free a data block when needed, if
+ `ycf_stack_alloc_data` is set to `NULL`. The value of
+ `ycf_stack_alloc_data` does not matter if `YCF_STACK_ALLOC(n)` is
+ unused.
+
+* `paremeters_of_orginal_function`
+
+ Parameters that the original function takes will be placed in the
+ end of the parameter list of the `ycf_gen_yielding` function.
+
+
+```c
+return_type_of_orginal_fun
+original_fun_name_ycf_gen_continue(
+ long * ycf_nr_of_reduction,
+ void ** ycf_yield_state,
+ void * ycf_extra_context);
+```
+
+The generated function with the suffix `_ycf_gen_continue` is used to
+resume a yielded function. The descriptions of the parameters and
+return type for the `_ycf_gen_yielding` function above are valid for
+the `_ycf_gen_continue` function as well, with the exception that the
+parameter `ycf_yield_state` should point to a pointer to a yield state
+(created in the previous call to `_ycf_gen_yielding` or
+`_ycf_gen_continue`).
+
+```c
+void original_fun_name_ycf_gen_destroy(void * ycf_yield_state);
+```
+
+The `_gen_destroy` function frees the state of a yieldable function
+that has been suspended. Note that the parameter `ycf_yield_state`
+points directly to the yield state, unlike the parameter of the
+`_ycf_gen_yielding` and `_ycf_gen_continue` functions with the same
+name.
+
+
+
+The `YCF_YIELD_CODE_GENERATED` Macro
+------------------------------------
+
+YCF also generates code that defines the macro
+`YCF_YIELD_CODE_GENERATED`. This macro may be useful if one wants to
+compile one version of a program with yieldable functions and another
+without yieldable functions.
+
+Special Statements and Macros
+-----------------------------
+
+Some special statements and macros can be used from within a yieldable
+function. Descriptions of those follow below:
+
+* `YCF_YIELD();`
+
+ The `YCF_YIELD();` statement sets the reduction counter to zero
+ and yields the function when it is executed.
+
+* `YCF_YIELD_NO_REDS();`
+
+ The `YCF_YIELD_NO_REDS();` statement yields the function
+ without changing the reduction counter when it is executed.
+
+* `YCF_CONSUME_REDS(N);`
+
+ The `YCF_CONSUME_REDS(N);` statement decrements the
+ reductions counter by N and yields if the reduction counter is less
+ than or equal to zero after the decrement.
+
+* `YCF_STACK_ALLOC(N)`
+
+ The `YCF_STACK_ALLOC(N)` macro uses an allocator that is included in
+ the code generated by YCF to allocate a block with `N` bytes and
+ return a pointer to these bytes. A block that has been allocated
+ with `YCF_STACK_ALLOC(N)` is automatically freed when the function
+ that allocated the block returns. Memory blocks that are allocated
+ with `YCF_STACK_ALLOC(N)` do not move when a yieldable function
+ yields and then resumes again. In contrast, data that is allocated
+ directly on the call stack may move when a function yields and
+ resumes. `YCF_STACK_ALLOC(N)` can thus be useful if one wants to
+ port C code that has variables that point to data that is allocated
+ on the call stack. The parameters `ycf_stack_alloc_size_or_max_size`
+ and `ycf_stack_alloc_data` of the `_ycf_gen_yielding` function need
+ to be set correctly if `YCF_STACK_ALLOC(N)` is used. Please see the
+ description of the `_ycf_gen_yielding` function in the "Generated
+ Functions" section above for details about those parameters. Notice
+ also that the `-debug` flag that is described in the "Command Line
+ Parameters" section above can be useful when one wants to find out
+ if a function points to data that is allocated on the call stack of
+ a yieldable function.
+
+* `YCF_GET_EXTRA_CONTEXT()`
+
+ The `YCF_GET_EXTRA_CONTEXT()` macro returns the value of the
+ `ycf_extra_context` parameter that was passed to the latest call of
+ one of the corresponding `_ycf_gen_yielding` or `_ycf_gen_continue`
+ functions. See the "Generated Functions" section above for
+ information about the parameters of `_ycf_gen_yielding` and
+ `_ycf_gen_continue` functions.
+
+* `YCF_NR_OF_REDS_LEFT()`
+
+ The `YCF_NR_OF_REDS_LEFT()` macro returns the current value of
+ the reduction counter (a value of type `long`).
+
+* `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)`
+
+ The `YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT)` macro sets
+ the value that the reduction counter (which stores a value of type
+ `long`) to `NEW_NR_OF_REDS_LEFT`.
+
+* `YCF_MAX_NR_OF_REDS`
+
+ The `YCF_MAX_NR_OF_REDS` macro returns the maximum value that the
+ reduction counter may have.
+
+Code Restrictions
+-----------------
+
+YCF cannot parse all valid C code. The code restrictions that
+yieldable functions need to follow are described below. It is
+recommended to check that the generated code is correct.
+
+* **Declarations**
+
+ Variable declarations and parameters of yieldable functions need to
+ be in the following form:
+
+ ```
+ "(optional) type descriptor (i.e., struct, union or enum)"
+
+ "type name"
+
+ "(optional) one or more star characters (i.e., *)"
+
+ "variable name"
+
+ "(optional) one or more square brackets with a number inside (e.g, [3])"
+
+ "(optional) one or more empty square brackets (e.g, [])"
+
+ "(optional) equal sign followed by an expression (automatic array
+ initialization and struct initialization of the form
+ {.filed_name=value...} are not allowed)"
+
+ "semicolon"
+ ```
+
+ Here are some examples of declarations that are **correct**:
+
+ ```c
+ int var1;
+ int var2 = 1;
+ int var3 = var2 + 1;
+ int var4 = function(var3);
+ int * var5 = malloc(sizeof(int*));
+ int ** var6 = malloc(sizeof(int*)*10);
+ int ***** var7;
+ struct struct_name var8;
+ struct struct_name var9 = function2();
+ double var10[128];
+ double var11[128][];
+ double * var12[128];
+ ```
+
+ Here are examples of declarations that are **incorrect**:
+
+ ```c
+ int var1, var2;
+ int var1 = 1, var2 = 10;
+ void (*printer_t)(int);
+ ```
+
+ Note that one has to use a `typedef` to be able to declare a
+ function pointer variable.
+
+* **Pointers**
+
+ Pointers to call-stack-allocated data are not allowed. The
+ `YCF_YIELD_ALLOC(N)` function, which is described in the "Special
+ Statements and Macros" section above, can be used to work around
+ this limitation. The `-debug` flag that is described in the "Command
+ Line Parameters" section above, can be useful when one wants to find
+ out if a yieldable function points to call-stack-allocated data.
+
+
+* **Macros**
+
+ YCF does not expand macros so macros in functions that YCF
+ transforms should not "hide" variables or any other code that is
+ relevant for yielding.
+
+* **Calls to a Yieldable Function from Another Yieldable Function**
+
+ Calls to a yieldable function from another yieldable function need
+ to be in a form that YCF recognizes. Such calls need to be in one of
+ the following forms:
+
+ * As a separate statement:
+
+ Examples:
+ ```c
+ my_fun(my_param_expression + 1, 10);
+ my_fun2();
+ ```
+
+ * A separate assignment statement to a variable. The function call
+ expression may be negated but is not allowed to be nested in other
+ types of expressions.
+
+ Examples of **correct** ways of calling yieldable functions:
+ ```c
+ int var_name_1 = my_fun();
+ int var_name_2 = !my_fun();
+ var_name_3 = my_fun();
+ var_name_4 = !my_fun();
+ ```
+
+ Examples of **incorrect** ways of calling yieldable functions:
+ ```c
+ int var_name_1 = (my_fun());
+ var_name_2 = 1 + my_fun();
+ t->name = my_fun();
+ *ptr = my_fun();
+ ```
+
+ * As the expression of `while`-statements, `do-while`-statements or
+ 'if`-statements:
+
+ Examples of **correct** ways of calling yieldable functions:
+ ```c
+ if(my_fun()) printf("hej\n");
+ if(0) else if(my_fun()) printf("hej\n");
+ while(!my_fun()) printf("hej\n");
+ do { printf("hej\n"); } while(my_fun());
+ var_name_3 = my_fun();
+ var_name_4 = !my_fun();
+ ```
+
+ Examples of **incorrect** ways of calling yieldable functions:
+ ```
+ if(3+my_fun()) printf("hej\n");
+ if(hej=my_fun()) printf("hej\n");
+ ```
+
+ YCF ignores calls to yieldable functions that are not in any of
+ the forms described above, so it is important to check the
+ generated source code.
+
+Hooks
+-----
+
+It is possible to insert special hooks in yieldable functions. Hooks
+execute when certain events are happening. Hooks may read and write to
+variables (changes to variables are visible after the code block has
+executed). Hooks can be placed anywhere one can place a normal
+statement within the function body. There are two ways to write hooks:
+
+**Hook Style 1:**
+
+```c
+ YCF_SPECIAL_CODE_START(ON_EVENT_NAME);
+ printf("This will be printed when EVENT_NAME is happening\n");
+ YCF_SPECIAL_CODE_END();
+```
+
+**Hook Style 2:**
+
+```c
+ /*special_code_start:ON_EVENT_NAME*/
+ if(0){
+ printf("This will be printed when EVENT_NAME is happening\n");
+ }
+ /*special_code_end*/
+```
+
+The following hook events are currently available:
+
+* `ON_SAVE_YIELD_STATE`
+
+ Triggered when the function yields.
+
+* `ON_RESTORE_YIELD_STATE`
+
+ Triggered before a function resumes after a yield.
+
+* `ON_DESTROY_STATE`
+
+ Triggered if and when the corresponding `_ycf_gen_destroy` function
+ is executing.
+
+* `ON_DESTROY_STATE_OR_RETURN`
+
+ Triggered if and when the corresponding `_ycf_gen_destroy` function
+ for the function is executing or when the function is returning.
+
+* `ON_RETURN`
+
+ Triggered when the function is returning.
+
+License
+-------
+
+Yielding C Fun is released under the [Apache License
+2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+
+> Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+>
+> Licensed under the Apache License, Version 2.0 (the "License");
+> you may not use this file except in compliance with the License.
+> You may obtain a copy of the License at
+>
+> http://www.apache.org/licenses/LICENSE-2.0
+>
+> Unless required by applicable law or agreed to in writing, software
+> distributed under the License is distributed on an "AS IS" BASIS,
+> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+> See the License for the specific language governing permissions and
+> limitations under the License.
+
+
+
+[1]: http://swtch.com/libtask/ "libtask"
+[2]: http://xmailserver.org/libpcl.html
+[3]: https://web.archive.org/web/20060110123338/http://www.goron.de/~froese/coro/
+[4]: https://github.com/halayli/lthread
+[5]: http://dekorte.com/projects/opensource/libcoroutine/
+[6]: http://code.google.com/p/libconcurrency/libconcurrency
+[7]: http://software.schmorp.de/pkg/libcoro.html
+[8]: https://github.com/Adaptv/ribs2
+[9]: http://libdill.org/
+[10]: https://github.com/hnes/libaco
+[11]: https://byuu.org/library/libco/
+[12]: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+[13]: https://github.com/jsseldenthuis/coroutine
diff --git a/erts/lib_src/yielding_c_fun/TODO b/erts/lib_src/yielding_c_fun/TODO
new file mode 100644
index 0000000000..f3ded2dd22
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/TODO
@@ -0,0 +1,4 @@
+* Refactor and beautify the code so it does not look like spaghetti
+* Handle "automatic" array size initialization
+* Handle struct initialization (e.g., struct t my_struct = {.field = hej});
+* Print warning or error message when calling yieldable function from a position that YCF can't handle
diff --git a/erts/lib_src/yielding_c_fun/bin/yielding_c_fun b/erts/lib_src/yielding_c_fun/bin/yielding_c_fun
new file mode 100755
index 0000000000..87745e7aad
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/bin/yielding_c_fun
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+
+#Code to find directory of this file from https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+
+
+FOUND="no"
+
+for FILE in "$@"
+do
+ if [ "${FILE: -2}" == ".c" ]
+ then
+ if ! cc -c $FILE -o $DIR/a.out ; then
+ echo "$0: error: Could not compile file with cc"
+ exit 1
+ fi
+ rm $DIR/a.out
+ if [ $1 == "-rr" ]
+ then
+ rr record $DIR/yielding_c_fun.bin "${@:2}" > $DIR/TMP_OUT_WITH_RR
+ # rr does not print when stdout is redirected
+ cat $DIR/TMP_OUT_WITH_RR
+ rm $DIR/TMP_OUT_WITH_RR
+ else
+ $DIR/yielding_c_fun.bin $@
+ fi
+ fi
+ FOUND="yes"
+done
+
+if [ $FOUND == "no" ]
+then
+ echo "$0: error: Expected a file name with .c ending"
+ exit 1
+fi
diff --git a/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md b/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md
new file mode 100644
index 0000000000..c818032bc5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/doc/thread_tutorial.md
@@ -0,0 +1,222 @@
+Yielding C Fun Thread Example Tutorial
+======================================
+
+This tutorial goes through how Yielding C Fun can be used to simulate
+multi-threading in a single thread. You can find the source code that
+we use in the tutorial below or in
+[../test/examples/thread_example.c](../test/examples/thread_example.c).
+
+```c
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+static int f_2(char* name, int n) {
+ for(int y = 0; y < 2; y++) {
+ printf("%s f_2: y=%d\n", name, y);
+ }
+ return n*2;
+}
+
+static void f_1(char* name, int x) {
+ YCF_YIELD_NO_REDS();
+ while (x > 0) {
+ int f_2_ret = f_2(name, x);
+ printf("%s f_1: x=%d f_2_ret=%d\n", name, x, f_2_ret);
+ x--;
+ }
+ printf("%s f_1: DONE\n", name);
+}
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+static void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] ) {
+#ifdef YCF_YIELD_CODE_GENERATED
+ long t1_nr_of_reds = 1;
+ void* t1_state = NULL;
+ long t2_nr_of_reds = 2;
+ void* t2_state = NULL;
+ long t3_nr_of_reds = 1000;
+ void* t3_state = NULL;
+ /* Start t1, t2 and t3*/
+ f_1_ycf_gen_yielding(&t1_nr_of_reds, &t1_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t1", 2);
+ f_1_ycf_gen_yielding(&t2_nr_of_reds, &t2_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t2", 4);
+ f_1_ycf_gen_yielding(&t3_nr_of_reds, &t3_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t3", 2);
+ printf("THREADS STARTED\n");
+ /* Execute t1, t2 and t3*/
+ while (t1_state != NULL ||
+ t2_state != NULL ||
+ t3_state != NULL) {
+ t1_nr_of_reds = 1;
+ t2_nr_of_reds = 2;
+ if (t1_state != NULL) {
+ printf("SCHEDULING THREAD: t1\n");
+ f_1_ycf_gen_continue(&t1_nr_of_reds, &t1_state, NULL);
+ if (t1_state == NULL) {
+ printf("THREAD t1 DONE (number of reductions left = %ld)\n",
+ t1_nr_of_reds);
+ }
+ }
+ if (t2_state != NULL) {
+ printf("SCHEDULING THREAD: t2\n");
+ f_1_ycf_gen_continue(&t2_nr_of_reds, &t2_state, NULL);
+ if (t2_state == NULL) {
+ printf("THREAD t2 DONE (number of reductions left = %ld)\n",
+ t2_nr_of_reds);
+ }
+ }
+ if (t3_state != NULL) {
+ printf("SCHEDULING THREAD: t3\n");
+ f_1_ycf_gen_continue(&t3_nr_of_reds, &t3_state, NULL);
+ if (t3_state == NULL) {
+ printf("THREAD t3 DONE (number of reductions left = %ld)\n",
+ t3_nr_of_reds);
+ }
+ }
+ }
+#endif
+ (void)f_1;
+ printf("DONE\n");
+ return 0;
+}
+```
+
+To run this example, you need to compile YCF itself, if you have not
+done so already:
+
+ cd directory_where_the_ycf_source_code_is_located
+ YCF_ROOT=`pwd`
+ make
+
+Now, you can transform `test/examples/thread_example.c` by executing
+the following command:
+
+ "$YCF_ROOT"/bin/yielding_c_fun.bin \
+ -f f_1 -f f_2 \
+ -output_file_name modified_thread_example.c \
+ "$YCF_ROOT"/test/examples/thread_example.c
+
+A new file should now exist in your current directory called
+`modified_thread_example.c`. This file contains a transformed
+version of `test/examples/thread_example.c`. The parameters `-f
+f_1` and `-f f_2` tells YCF to generate yieldable versions of the
+functions named `f_1` and `f_2`.
+
+Before you inspect the generated file to see what the tool has done,
+it is smart to format the generated source code with `clang-format` or
+some other tool to make the code more readable:
+
+ clang-format -i modified_thread_example.c
+
+You can now open the generated source code in your favorite editor:
+
+ emacs modified_thread_example.c &
+
+The yieldable functions get the suffix `_ycf_gen_yielding`. For
+example, the yielding version of `f_1` is called
+`f_1_ycf_gen_yielding`. As you can see, several parameters have been
+added to the yieldable versions of `f_1`. We explain the first two of
+these parameters here. [The main documentation of YCF](../README.md)
+explains the rest of the parameters. The first parameter is an
+input/output parameter that tells the yieldable function how many
+reductions that can be "consumed" before it yields. The first
+parameter can also be used by the caller to get the number of
+reductions that have been consumed. The second parameter is also an
+input/output parameter that should be a pointer to a `NULL` pointer
+when the yieldable function is first started. The pointer that is
+pointed to by the second parameter is set to a value which is
+different from `NULL` when the function yields.
+
+Let us now compile and run the generated source code so we can see
+what is happening:
+
+
+ cc -g modified_thread_example.c
+
+```
+$ ./a.out
+THREADS STARTED
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+SCHEDULING THREAD: t3
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=2 f_2_ret=4
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=1 f_2_ret=2
+t3 f_1: DONE
+THREAD t3 DONE (number of reductions left = 994)
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=4 f_2_ret=8
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=3 f_2_ret=6
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=1 f_2_ret=2
+t1 f_1: DONE
+THREAD t1 DONE (number of reductions left = 1)
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=1 f_2_ret=2
+t2 f_1: DONE
+THREAD t2 DONE (number of reductions left = 2)
+DONE
+$
+```
+
+We can see that all three calls to the yieldable version of `f_1`
+yield before printing anything. This is due to the
+`YCF_YIELD_NO_REDS()` statement at the beginning of `f_1`'s
+body. We can also see that the "thread" named `t3` runs to completion
+without yielding when we call `f_1_ycf_gen_continue` with `t3_state`
+in the while loop and that `t3` consumed (1000-994=6) reductions. This
+is expected as we gave `t3` 1000 reductions when we started it with
+the `f_1_ycf_gen_yielding` function. The "threads" `t1` and `t2` get
+much fewer reductions (i.e., 1 and 2) each time they get to execute
+and they are therefore interleaved.
+
+You can now experiment with other transformation options (e.g.,
+`-frec` and `-fnoauto`) and the special statements that you can use
+inside functions that can yield. All these options and statements are
+documented [here](../README.md).
+
+The best way to figure out how YCF works under the hood is probably to
+step through the transformed program in a debugger.
+
+Good Luck! \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore
new file mode 100644
index 0000000000..e72b2fe033
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.gitignore
@@ -0,0 +1,11 @@
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+*~
+GPATH
+GRTAGS
+GTAGS
+test.bin
+.tmp_out \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt
new file mode 100644
index 0000000000..d212057d10
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/.misc/clang_blacklist.txt
@@ -0,0 +1,10 @@
+
+[memory]
+
+fun:scgc_mark_reachable_objects_in_region
+fun:reverse_bits
+
+[address]
+
+fun:scgc_mark_reachable_objects_in_region
+fun:reverse_bits
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION
new file mode 100644
index 0000000000..8cd6ceac63
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/GIT_VERSION
@@ -0,0 +1,73 @@
+origin https://github.com/kjellwinblad/simple_c_gc.git (fetch)
+origin https://github.com/kjellwinblad/simple_c_gc.git (push)
+commit 76577516b6e9bd8e8f647d869fb19361f42f9f9f
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Wed Sep 25 16:04:40 2019 +0200
+
+ Fix more Microsoft VS C/C++ compiler errors
+
+commit 4b2734f051ca1386bb970af6b0308e5f2d338403
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 24 16:36:48 2019 +0200
+
+ Make compatible with Microsoft VS C/C++ compiler and fix make test
+
+commit 41e6f3558756d9b6da44ca5247049c23bceadad9
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 23 16:25:55 2019 +0200
+
+ Small fix
+
+commit 8fbd03f880a1eb741462c395e689d844f8d58e06
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 23 16:13:56 2019 +0200
+
+ Fix compile warnings when compiling on a 32-bit system
+
+commit fc051d92bbef9ad1a22f855bc24b46d4efadda93
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 10 15:14:20 2019 +0200
+
+ Add clang-tidy Makefile target
+
+commit c6c3c1adc24f500472c22aad7f467d0a925108f0
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Tue Sep 10 15:07:39 2019 +0200
+
+ Add undefined behavior sanitizer and fix undefined behavior
+
+commit c734e684030a24ae694ed1fe7477c0eb5719bc12
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Mon Sep 9 09:06:19 2019 +0200
+
+ Improve Makefile and add info support
+
+commit 286c6ace1a71f2b021b76b602d5bfe4813f0b519
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 12:08:19 2019 +0200
+
+ Make clang_format target .PHONY
+
+commit eacdda4f9c71b5d917111bb9285671739d1cb671
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 11:33:03 2019 +0200
+
+ Add clang-format make target and reformat files
+
+commit c617afa556535620633117bdd6cc232ecc8c62f2
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 11:07:17 2019 +0200
+
+ Small fixes
+
+commit fb043e17d6e63100ca89ec41c34f667d35da7fb8
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Thu Sep 5 10:55:18 2019 +0200
+
+ Fix compiler warning and make c99 compatible
+
+commit d4b8333a49e62d8ffdbf60ab8382de39209891e2
+Author: Kjell Winblad <kjellwinblad@gmail.com>
+Date: Sat Aug 17 05:59:27 2019 +0200
+
+ First version of Simple C GC
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile
new file mode 100644
index 0000000000..1b326a9988
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/Makefile
@@ -0,0 +1,121 @@
+IDIR = .
+CC = cc
+
+ifdef MODERN_CC
+ EXTRA_C_FLAGS = -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef CC_32_BIT
+ EXTRA_C_FLAGS = -m32 -g -O03 -std=c99 -pedantic -Wall
+endif
+
+ifdef ADD_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=address -fno-omit-frame-pointer
+ USE_GC_STRING = -use_gc
+endif
+
+ifdef MEM_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=memory -fno-omit-frame-pointer
+endif
+
+ifdef UB_SAN
+ CC = clang
+ EXTRA_C_FLAGS = -std=c99 -Wall -pedantic -g -O00 -fsanitize-blacklist=.misc/clang_blacklist.txt -fsanitize=undefined -fno-omit-frame-pointer
+endif
+
+CFLAGS = -I$(IDIR) $(EXTRA_C_FLAGS)
+
+ODIR = .
+LDIR =
+
+_DEPS = bitreversal.h simple_c_gc.h chained_hash_set.h sorted_list_set.h
+DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
+
+_OBJ = simple_c_gc.o test.o
+OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
+C_FILES = $(patsubst %.o,%.c,$(_OBJ))
+
+$(ODIR)/%.o: %.c $(DEPS)
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+test.bin: $(OBJ)
+ $(CC) -o $@ $^ $(CFLAGS)
+
+
+.PHONY: clean test run_test_continusly CMakeLists.txt cmake_compile clang_format test_add_san test_ub_san test_mem_san test_sanitizers test_modern_cc test_valgrind
+
+test: test.bin
+ ./test.bin ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_valgrind:
+ make clean && \
+ make EXTRA_C_FLAGS="-g -O01" && \
+ valgrind --undef-value-errors=no ./test.bin ;\
+ RESULT=$$? &&\
+ (exit $$RESULT) &&\
+ printf "\n\n\033[0;32mALL TESTS PASSED!\033[0m\n\n\n" ||\
+ printf "\n\n\033[0;31mTEST FAILED!\033[0m\n\n\n" &&\
+ exit $$RESULT
+
+test_add_san:
+ make clean && \
+ make ADD_SAN=1 test
+
+test_mem_san:
+ make clean && \
+ make MEM_SAN=1 test
+
+test_ub_san:
+ make clean && \
+ make UB_SAN=1 test
+
+test_sanitizers:
+ make test_add_san && \
+ make test_mem_san && \
+ make test_ub_san
+
+test_modern_cc:
+ make clean && \
+ make MODERN_CC=1 test
+
+test_32_bit:
+ make clean && \
+ make CC_32_BIT=1 test && \
+ make clean
+
+test_all:
+ make test_valgrind && \
+ make test_sanitizers && \
+ make test_modern_cc && \
+ make test_32_bit
+
+run_test_continusly:
+ inotifywait -e close_write,moved_to,create -m ./*.c ./*.h | while read -r directory events filename; do gtags ; make test ; done
+
+CMakeLists.txt: $(C_FILES)
+ echo "cmake_minimum_required (VERSION 2.6)" > CMakeLists.txt
+ echo "project (SIMPLE_C_GC)" >> CMakeLists.txt
+ echo "add_executable(cmake.out" >> CMakeLists.txt
+ echo $(C_FILES) >> CMakeLists.txt
+ echo ")" >> CMakeLists.txt
+
+
+cmake_compile: CMakeLists.txt
+ mkdir cmake_mkdir || true
+ cd cmake_mkdir && cmake ..
+
+clang_tidy:
+ ls *.c | xargs -I{} -n1 clang-tidy -warnings-as-errors=* {} -- $(CFLAGS)
+
+clang_format:
+ clang-format -style="{BasedOnStyle: LLVM}" -i *.c *.h
+
+clean:
+ rm -f $(ODIR)/*.o *~ core $(IDIR)/*~ test.bin CMakeLists.txt
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md
new file mode 100644
index 0000000000..c1f6834505
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/README.md
@@ -0,0 +1,69 @@
+Simple C GC
+===========
+
+This is the readme file for Simple C GC. Simple C GC is a simple
+garbage collection system for the C programming language inspired by
+[Daniel Holden's article about Cello's garbage collection
+system](http://libcello.org/learn/garbage-collection).
+
+Usage
+-----
+
+The interface of Simple C GC consists of only two functions:
+
+```C
+/**
+ * This function starts code that will be garbage collected
+ *
+ * @param main The function that will be started and garbage collected
+ * @param argc Passed to main
+ * @param argv Passed to main
+ * @param my_malloc Simple C GC will use this to allocated memory
+ * @param my_free Simple C GC will use this to free memory
+ */
+int scgc_start_gced_code(int (*main)(int, char *[]),
+ int argc,
+ char *argv[],
+ void* (*my_malloc)( size_t ),
+ void (*my_free)( void* ));
+
+/**
+ * Allocate a new memory block that will be garbage collected
+ *
+ * @param size The size of the new memory block
+ */
+void* scgc_new(size_t size);
+```
+
+An example that illustrates how these functions can be used in
+practice is located in the `test.c` file.
+
+
+Notes
+-----
+
+The garbage collector assumes that all data that should be garbage
+collected is pointed to directly or indirectly from the "C stack" and
+that the "C stack" is implemented as a continuous block of memory.
+
+Compile and Test
+----------------
+
+ make test
+
+License
+-------
+
+ Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h
new file mode 100644
index 0000000000..ee508e5ccc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/chained_hash_set.h
@@ -0,0 +1,307 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifndef CHAINED_HASH_SET_H
+#define CHAINED_HASH_SET_H
+
+
+#if defined(_MSC_VER)
+#define inline __inline
+#endif
+
+#include "sorted_list_set.h"
+#include <stdint.h>
+
+#define CHAIN_LENGHT_EXPAND_THRESHOLD 2
+#define CHAIN_LENGHT_SHRINK_THRESHOLD 0.5
+/*Must be power of two*/
+#define INITIAL_NUMBER_OF_BUCKETS 4
+
+typedef struct {
+ unsigned int keyPosition;
+ void *(*extract_key)(void *v, int keyPos);
+ unsigned int (*hash_key)(void *key);
+ bool (*are_equal)(void *v1, void *v2);
+ char *(*to_string)(void *v1);
+ void *(*malloc)(size_t size);
+ void (*free)(void *ptr);
+ unsigned int numberOfBuckets;
+ unsigned int expandTreshold;
+ unsigned int shrinkTreshold;
+ unsigned int size;
+ SortedListSetNode **buckets;
+} ChainedHashSet;
+
+/*
+ * The reverse_bits function is inspired by:
+ * https://stackoverflow.com/questions/746171/efficient-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
+ */
+static const unsigned char BitReverseTable256[] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
+ 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4,
+ 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC,
+ 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA,
+ 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6,
+ 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1,
+ 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
+ 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD,
+ 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3,
+ 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7,
+ 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF,
+ 0x3F, 0xBF, 0x7F, 0xFF};
+
+static inline uint32_t reverse_bits(uint32_t v) {
+ return (((uint32_t)BitReverseTable256[v & 0xff]) << 24) |
+ (((uint32_t)BitReverseTable256[(v >> 8) & 0xff]) << 16) |
+ (((uint32_t)BitReverseTable256[(v >> 16) & 0xff]) << 8) |
+ (((uint32_t)BitReverseTable256[(v >> 24) & 0xff]));
+}
+
+static inline void ch_set_increase_size(ChainedHashSet *set) {
+ set->size = set->size + 1;
+ if (set->size > set->expandTreshold) {
+ unsigned int oldNumberOfBuckets = set->numberOfBuckets;
+ unsigned int newNumberOfBuckets = oldNumberOfBuckets * 2;
+ unsigned int splitUpMask = reverse_bits(newNumberOfBuckets - 1) ^
+ reverse_bits(oldNumberOfBuckets - 1);
+ SortedListSetNode **newBuckets =
+ set->malloc(sizeof(SortedListSetNode *) * newNumberOfBuckets);
+ SortedListSetNode **oldBuckets = set->buckets;
+ SortedListSetNode *moveTemp;
+ for (unsigned int i = 0; i < oldNumberOfBuckets; i++) {
+ moveTemp = sl_set_split_opt(&oldBuckets[i], splitUpMask);
+ newBuckets[i] = oldBuckets[i];
+ newBuckets[i + oldNumberOfBuckets] = moveTemp;
+ }
+ set->free(oldBuckets);
+ set->buckets = newBuckets;
+ set->numberOfBuckets = newNumberOfBuckets;
+ set->expandTreshold = newNumberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->shrinkTreshold = newNumberOfBuckets * CHAIN_LENGHT_SHRINK_THRESHOLD;
+ }
+}
+
+static inline void ch_set_decrease_size(ChainedHashSet *set) {
+ set->size = set->size - 1;
+ if (set->size < set->shrinkTreshold) {
+ unsigned int oldNumberOfBuckets = set->numberOfBuckets;
+ unsigned int newNumberOfBuckets = oldNumberOfBuckets / 2;
+ SortedListSetNode **newBuckets =
+ set->malloc(sizeof(SortedListSetNode *) * newNumberOfBuckets);
+ SortedListSetNode **oldBuckets = set->buckets;
+ for (unsigned int i = 0; i < newNumberOfBuckets; i++) {
+ newBuckets[i] = oldBuckets[i];
+ sl_set_concat_opt(&newBuckets[i], oldBuckets[i + newNumberOfBuckets]);
+ }
+ set->free(oldBuckets);
+ set->buckets = newBuckets;
+ set->numberOfBuckets = newNumberOfBuckets;
+ set->expandTreshold = newNumberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ if (set->numberOfBuckets == INITIAL_NUMBER_OF_BUCKETS) {
+ set->shrinkTreshold = 0;
+ } else {
+ set->shrinkTreshold = newNumberOfBuckets * CHAIN_LENGHT_SHRINK_THRESHOLD;
+ }
+ }
+}
+
+static inline void ch_set_initialize(ChainedHashSet *set,
+ unsigned int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k),
+ bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1),
+ void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ set->keyPosition = keyPosition;
+ set->extract_key = extract_key;
+ set->hash_key = hash_key;
+ set->are_equal = are_equal;
+ set->to_string = to_string;
+ set->malloc = my_malloc;
+ set->free = my_free;
+ set->numberOfBuckets = INITIAL_NUMBER_OF_BUCKETS;
+ set->expandTreshold = set->numberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->shrinkTreshold = set->numberOfBuckets * CHAIN_LENGHT_EXPAND_THRESHOLD;
+ set->size = 0;
+ set->buckets =
+ set->malloc(sizeof(SortedListSetNode *) * INITIAL_NUMBER_OF_BUCKETS);
+ for (int i = 0; i < INITIAL_NUMBER_OF_BUCKETS; i++) {
+ set->buckets[i] = NULL;
+ }
+}
+
+static inline ChainedHashSet *ch_set_create(
+ unsigned int keyPosition, void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k), bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1), void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ ChainedHashSet *set = my_malloc(sizeof(ChainedHashSet));
+ ch_set_initialize(set, keyPosition, extract_key, hash_key, are_equal,
+ to_string, my_malloc, my_free);
+ return set;
+}
+
+static inline bool ch_set_insert_opt(ChainedHashSet *set, void *value,
+ unsigned int valueSize,
+ unsigned int hashValue, bool overwrite) {
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ bool oneAdded = sl_set_insert_opt(
+ bucket, value, valueSize, reverse_bits(hashValue), set->keyPosition,
+ overwrite, set->extract_key, set->are_equal, set->malloc, set->free);
+ if (oneAdded) {
+ ch_set_increase_size(set);
+ }
+ return oneAdded;
+}
+
+static inline void ch_set_insert(void *setParam, void *value,
+ unsigned int valueSize) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ void *key = set->extract_key(value, set->keyPosition);
+ unsigned int hashValue = set->hash_key(key);
+ ch_set_insert_opt(set, value, valueSize, hashValue, true);
+}
+
+static inline bool ch_set_insert_new(void *setParam, void *value,
+ unsigned int valueSize) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ void *key = set->extract_key(value, set->keyPosition);
+ unsigned int hashValue = set->hash_key(key);
+ return ch_set_insert_opt(set, value, valueSize, hashValue, false);
+}
+
+static inline void *ch_set_lookup(void *setParam, void *key) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int hashValue = set->hash_key(key);
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ return sl_set_lookup_opt(bucket, key, reverse_bits(hashValue),
+ set->keyPosition, set->extract_key, set->are_equal,
+ false, set->malloc);
+}
+
+static inline void ch_set_delete(void *setParam, void *key,
+ unsigned int keySize) {
+ (void)keySize;
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int hashValue = set->hash_key(key);
+ unsigned int bucketIndex = hashValue & (set->numberOfBuckets - 1);
+ SortedListSetNode **bucket = &set->buckets[bucketIndex];
+ bool oneRemoved =
+ sl_set_delete_opt(bucket, key, reverse_bits(hashValue), set->keyPosition,
+ set->extract_key, set->are_equal, set->free);
+ if (oneRemoved) {
+ ch_set_decrease_size(set);
+ }
+}
+
+static inline void ch_set_traverse(void *setParam,
+ void (*traverser)(size_t index, void *v,
+ void *context),
+ void *context) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ SortedListSetNode *itemNode;
+ size_t index = 0;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ itemNode = set->buckets[i];
+ while (itemNode != NULL) {
+ traverser(index, (void *)itemNode->value, context);
+ index++;
+ itemNode = itemNode->next;
+ }
+ }
+}
+
+static inline void ch_set_free(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ sl_set_free_opt(&set->buckets[i], set->free);
+ }
+ set->free(set->buckets);
+ set->free(set);
+}
+
+static inline char *ch_set_to_string(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ unsigned int numberOfBuckets = set->numberOfBuckets;
+ char **bucketStrings = malloc(sizeof(char*)*numberOfBuckets);
+ unsigned int *bucketStringSizes = malloc(sizeof(unsigned int)*numberOfBuckets);
+ unsigned int totalBucketsStringSize = 0;
+ SortedListSet *tempListSet = plain_sl_set_create(
+ set->keyPosition, set->extract_key, set->hash_key, set->are_equal,
+ set->to_string, set->malloc, set->free);
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ tempListSet->head = set->buckets[i];
+ bucketStrings[i] = sl_set_to_string(tempListSet);
+ bucketStringSizes[i] = strlen(bucketStrings[i]);
+ totalBucketsStringSize = totalBucketsStringSize + bucketStringSizes[i];
+ }
+ tempListSet->head = NULL;
+ sl_set_free(tempListSet);
+ char *resultString =
+ set->malloc(totalBucketsStringSize + numberOfBuckets * 3 - 3 + 3);
+ resultString[0] = '[';
+ unsigned int currentPosition = 1;
+ for (unsigned int i = 0; i < numberOfBuckets; i++) {
+ sprintf(&resultString[currentPosition], "%s", bucketStrings[i]);
+ set->free(bucketStrings[i]);
+ currentPosition = currentPosition + bucketStringSizes[i];
+ if (i != (numberOfBuckets - 1)) {
+ sprintf(&resultString[currentPosition], ",\n ");
+ currentPosition = currentPosition + 3;
+ }
+ }
+ sprintf(&resultString[currentPosition], "]");
+ free(bucketStrings);
+ free(bucketStringSizes);
+ return resultString;
+}
+
+static inline void ch_set_print(void *setParam) {
+ ChainedHashSet *set = (ChainedHashSet *)setParam;
+ char *str = ch_set_to_string(set);
+ printf("%s\n", str);
+ set->free(str);
+}
+
+static inline bool ch_set_is_concurrent() { return false; }
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c
new file mode 100644
index 0000000000..a6932ab99b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.c
@@ -0,0 +1,310 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include "simple_c_gc.h"
+#include "chained_hash_set.h"
+
+#include <setjmp.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool scgc_print_gc_info = false;
+static ChainedHashSet *scgc_objects;
+static void *scgc_stack_top;
+
+#define SCGC_MIN_ALLOCS_UNTIL_FREE 100
+static int scgc_allocs_until_gc = SCGC_MIN_ALLOCS_UNTIL_FREE;
+static void *(*scgc_user_malloc)(size_t size);
+static void (*scgc_user_free)(void *ptr);
+
+typedef enum { scgc_blank_state, scgc_marked } scgc_object_state;
+
+typedef struct {
+ unsigned long magic_number;
+ size_t size;
+ scgc_object_state state;
+ void *data[1];
+} scgc_object;
+
+typedef struct {
+ void *address;
+ scgc_object *base;
+ unsigned long magic_number;
+} scgc_object_ref;
+
+static void *scgc_malloc(size_t size) {
+ void *(*my_malloc)(size_t size);
+ if (scgc_user_malloc == NULL) {
+ my_malloc = malloc;
+ } else {
+ my_malloc = scgc_user_malloc;
+ }
+ void *res = my_malloc(size);
+ if (res == NULL) {
+ printf("GCGC: Allocator returned NULL.\n");
+ exit(1);
+ }
+ return res;
+}
+
+static void scgc_free(void *ptr) {
+ void (*my_free)(void *ptr);
+ if (scgc_user_free == NULL) {
+ my_free = free;
+ } else {
+ my_free = scgc_user_free;
+ }
+ my_free(ptr);
+}
+
+static void *scgc_extract_key(void *v, int keyPos) {
+ (void)keyPos;
+ return v;
+}
+
+static unsigned int scgc_hash_key(void *keyPtr) {
+ /* From
+ * https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere*/
+ int64_t x = (long)*((void **)keyPtr);
+ int64_t a = 2348923;
+ int64_t b = 3292;
+ int64_t c = 9893487421;
+ int32_t low = (int)x;
+ int32_t high = (int)(x >> 32);
+ return (unsigned int)((a * low + b * high + c) >> 32);
+}
+
+static bool scgc_are_equal(void *v1p, void *v2p) {
+ scgc_object_ref *v1 = ((scgc_object_ref *)v1p);
+ scgc_object_ref *v2 = ((scgc_object_ref *)v2p);
+ return v1->address == v2->address;
+}
+
+static char *scgc_to_string(void *vp) {
+ scgc_object_ref *v = ((scgc_object_ref *)vp);
+ char *str = scgc_malloc(200);
+ sprintf(str, "{.address=%p, .base_address=%p, magic_number=%lu}", v->address,
+ (void *)v->base, v->magic_number);
+ return str;
+}
+
+static void scgc_initialize_global_state() {
+ scgc_objects =
+ ch_set_create(0, scgc_extract_key, scgc_hash_key, scgc_are_equal,
+ scgc_to_string, scgc_malloc, scgc_free);
+ srand((int)(long)scgc_objects * 2654435761);
+}
+
+static void scgc_do_gc(bool no_stack);
+
+static void scgc_destroy_global_state() {
+ scgc_do_gc(true);
+ ch_set_free(scgc_objects);
+}
+
+static int scgc_start_gced_code_2(int (*main)(int, char *[]), int argc,
+ char **argv[]) {
+ volatile int noinline = 1;
+ volatile char **my_argv = (volatile char **)*argv;
+ scgc_stack_top = (void *)my_argv;
+ int (*next)(int, char *[]) = noinline ? main : (int (*)(int, char *[]))(NULL);
+ {
+ int to_return;
+ scgc_initialize_global_state();
+ to_return = next(argc, (char **)my_argv);
+ scgc_destroy_global_state();
+ return to_return;
+ }
+}
+
+static void scgc_global_set_put(scgc_object_ref ref) {
+ ch_set_insert(scgc_objects, &ref, sizeof(scgc_object_ref));
+}
+
+static void scgc_global_set_del(void *key) {
+ ch_set_delete(scgc_objects, &key, sizeof(void *));
+}
+
+static scgc_object_ref *scgc_global_set_get(void *key) {
+ scgc_object_ref *ret = ch_set_lookup(scgc_objects, &key);
+ return ret;
+}
+
+static void scgc_mark_reachable_objects_in_region(void *start, void *end);
+
+static void *scgc_min(void *a, void *b) { return a <= b ? a : b; }
+
+static void *scgc_max(void *a, void *b) { return a > b ? a : b; }
+
+static void ycf_find_stack_bottom_and_mark_conservative_helper(void) {
+ volatile void *p = NULL;
+ volatile intptr_t stack_bottom = (intptr_t)&p;
+ scgc_mark_reachable_objects_in_region(
+ scgc_min(scgc_stack_top, (void *)&stack_bottom),
+ scgc_max((void *)&stack_bottom, scgc_stack_top));
+}
+
+static void scgc_get_stack_bottom_and_mark() {
+ jmp_buf env;
+ setjmp(env);
+
+ volatile int noinline = 1;
+
+ void (*bottom)(void) =
+ noinline ? ycf_find_stack_bottom_and_mark_conservative_helper
+ : (void (*)(void))(NULL);
+
+ bottom();
+}
+
+static void scgc_mark_reachable_objects_in_region(void *start, void *end) {
+ void **iter = start;
+ void **iter_end = end;
+ scgc_object *object;
+ while (iter <= iter_end) {
+ scgc_object_ref *ref = scgc_global_set_get(*iter);
+ if (ref != NULL && ref->base->data == *iter &&
+ ref->magic_number == ref->base->magic_number &&
+ ref->base->state == scgc_blank_state) {
+ object = ref->base;
+ object->state = scgc_marked;
+ scgc_mark_reachable_objects_in_region(
+ &object->data[0], &((char *)object->data)[object->size - 1]);
+ }
+ iter = iter + 1;
+ }
+}
+
+static void scgc_mark_reachable_objects(bool no_stack) {
+ void *tmp = NULL;
+ if (no_stack) {
+ scgc_mark_reachable_objects_in_region(&tmp, &tmp);
+ } else {
+ scgc_get_stack_bottom_and_mark();
+ }
+}
+
+static void scgc_collect_unmarked_traverser(size_t index, void *v,
+ void *context) {
+ void **objects_to_remove = context;
+ scgc_object_ref *ref = v;
+ if (ref->base->state == scgc_blank_state) {
+ scgc_free(ref->base);
+ objects_to_remove[index] = ref->address;
+ } else {
+ objects_to_remove[index] = NULL;
+ }
+}
+
+static void scgc_remove_unmarked_objects() {
+ size_t nr_of_candidates = scgc_objects->size;
+ void **objects_to_remove = scgc_malloc(sizeof(void *) * nr_of_candidates);
+ for (size_t i = 0; i < nr_of_candidates; i++) {
+ objects_to_remove[i] = NULL;
+ }
+ ch_set_traverse(scgc_objects, scgc_collect_unmarked_traverser,
+ objects_to_remove);
+ for (size_t i = 0; i < nr_of_candidates; i++) {
+ if (objects_to_remove[i] != NULL) {
+ scgc_global_set_del(objects_to_remove[i]);
+ }
+ }
+ scgc_free(objects_to_remove);
+}
+
+static void scgc_unmark_traverser(size_t index, void *v, void *context) {
+ scgc_object_ref *ref = v;
+ ref->base->state = scgc_blank_state;
+}
+
+static void scgc_unmark_objects() {
+ ch_set_traverse(scgc_objects, scgc_unmark_traverser, NULL);
+}
+
+static void scgc_do_gc(bool no_stack) {
+ unsigned int objects_before = scgc_objects->size;
+ scgc_mark_reachable_objects(no_stack);
+ scgc_remove_unmarked_objects();
+ scgc_unmark_objects();
+ if (scgc_print_gc_info) {
+ unsigned int objects_after = scgc_objects->size;
+ unsigned int objects_removed = objects_before - objects_after;
+ fprintf(stderr, "GC: before=%u, after=%u, removed=%u\n", objects_before,
+ objects_after, objects_removed);
+ }
+}
+
+static void scgc_gc() {
+ scgc_allocs_until_gc--;
+ if (scgc_allocs_until_gc <= 0) {
+ scgc_do_gc(false);
+ unsigned int objects_after = scgc_objects->size;
+ scgc_allocs_until_gc = objects_after * 2;
+ if (scgc_allocs_until_gc < SCGC_MIN_ALLOCS_UNTIL_FREE) {
+ scgc_allocs_until_gc = SCGC_MIN_ALLOCS_UNTIL_FREE;
+ }
+ }
+}
+
+/* Public interface */
+
+int scgc_start_gced_code(int (*main)(int, char *[]), int argc, char *argv[],
+ void *(*my_malloc)(size_t), void (*my_free)(void *)) {
+ volatile int noinline = 1;
+ int (*next)(int (*)(int, char *[]), int, char **[]) =
+ (noinline ? scgc_start_gced_code_2
+ : (int (*)(int (*)(int, char *[]), int, char **[]))(NULL));
+ volatile char **my_argv = (volatile char **)argv;
+ int res;
+ scgc_user_malloc = my_malloc;
+ scgc_user_free = my_free;
+ if (my_argv == NULL) {
+ fprintf(stderr,
+ "scgc_start_gced_code: the argv parameter should not be NULL!");
+ exit(1);
+ }
+ res = next(main, argc, (char ***)&my_argv);
+ return res;
+}
+
+void *scgc_new(size_t size) {
+ scgc_gc();
+ scgc_object *new = scgc_malloc(size + sizeof(scgc_object));
+ scgc_object_ref new_ref;
+ unsigned long magic_number = (unsigned long)rand();
+ new->state = scgc_blank_state;
+ new->magic_number = magic_number;
+ new->size = size;
+ new_ref.address = new->data;
+ new_ref.base = new;
+ new_ref.magic_number = magic_number;
+ scgc_global_set_put(new_ref);
+ return new->data;
+}
+
+void scgc_enable_print_gc_info() { scgc_print_gc_info = true; }
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h
new file mode 100644
index 0000000000..0e9aff84b8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/simple_c_gc.h
@@ -0,0 +1,57 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/**
+ * Pulic interface for Simple C GC.
+ *
+ * @author Kjell Winblad
+ *
+ */
+
+#ifndef SIMPLE_C_GC_H
+#define SIMPLE_C_GC_H
+
+#include <stddef.h>
+
+/**
+ * This function starts code that will be garbage collected
+ *
+ * @param main The function that will be started and garbage collected
+ * @param argc Passed to main
+ * @param argv Passed to main
+ * @param my_malloc Simple C GC will use this to allocated memory
+ * @param my_free Simple C GC will use this to free memory
+ */
+int scgc_start_gced_code(int (*main)(int, char *[]), int argc, char *argv[],
+ void *(*my_malloc)(size_t), void (*my_free)(void *));
+
+/**
+ * Allocate a new memory block that will be garbage collected
+ *
+ * @param size The size of the new memory block
+ */
+void *scgc_new(size_t size);
+
+/**
+ * Enables printing of garbage collection information
+ */
+void scgc_enable_print_gc_info(void);
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h
new file mode 100644
index 0000000000..1c216d2ed9
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/sorted_list_set.h
@@ -0,0 +1,370 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 2019 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifndef SORTED_LIST_SET_H
+#define SORTED_LIST_SET_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct SortedListSetNodeImpl {
+ struct SortedListSetNodeImpl *next;
+ unsigned int key_hash_value;
+ unsigned int valueSize;
+ char value[]; /* Flexible size array member */
+} SortedListSetNode;
+
+typedef struct {
+ SortedListSetNode *head;
+ unsigned int keyPosition;
+ void *(*extract_key)(void *v, int keyPos);
+ unsigned int (*hash_key)(void *k);
+ bool (*are_equal)(void *v1, void *v2);
+ char *(*to_string)(void *v1);
+ void *(*malloc)(size_t size);
+ void (*free)(void *ptr);
+} SortedListSet;
+
+static inline SortedListSet *plain_sl_set_create(
+ unsigned int keyPosition, void *(*extract_key)(void *v, int keyPos),
+ unsigned int (*hash_key)(void *k), bool (*are_equal)(void *v1, void *v2),
+ char *(*to_string)(void *v1), void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ SortedListSet *set = my_malloc(sizeof(SortedListSet));
+ set->head = NULL;
+ set->keyPosition = keyPosition;
+ set->extract_key = extract_key;
+ set->hash_key = hash_key;
+ set->are_equal = are_equal;
+ set->to_string = to_string;
+ set->malloc = my_malloc;
+ set->free = my_free;
+ return set;
+}
+
+static inline int compare_hash_codes(unsigned int code1, unsigned int code2) {
+ return code1 - code2;
+}
+
+static inline bool sl_set_insert_opt(SortedListSetNode **root, void *valuePtr,
+ unsigned int valueSize,
+ unsigned int keyHashValue, int keyPosition,
+ bool overwrite,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ void *(*my_malloc)(size_t size),
+ void (*my_free)(void *ptr)) {
+ void *key = extract_key(valuePtr, keyPosition);
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ bool oneMore = true;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ break;
+ } else {
+ if (are_equal(extract_key(current->value, keyPosition), key)) {
+ if (overwrite) {
+ SortedListSetNode *oldCurrent = current;
+ current = current->next;
+ previous->next = current;
+ my_free(oldCurrent);
+ oneMore = false;
+ break;
+ } else {
+ return false;
+ }
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ }
+ SortedListSetNode *newNode = my_malloc(sizeof(SortedListSetNode) + valueSize);
+ previous->next = newNode;
+ newNode->next = current;
+ newNode->key_hash_value = keyHashValue;
+ memcpy(newNode->value, valuePtr, valueSize);
+ newNode->valueSize = valueSize;
+ return oneMore;
+}
+
+static inline void sl_set_insert(void *setParam, void *valuePtr,
+ unsigned int valueSize) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_insert_opt(&set->head, valuePtr, valueSize,
+ set->hash_key(set->extract_key(valuePtr, set->keyPosition)),
+ set->keyPosition, true, set->extract_key, set->are_equal,
+ set->malloc, set->free);
+}
+
+static inline bool sl_set_insert_new(void *setParam, void *valuePtr,
+ unsigned int valueSize) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ return sl_set_insert_opt(
+ &set->head, valuePtr, valueSize,
+ set->hash_key(set->extract_key(valuePtr, set->keyPosition)),
+ set->keyPosition, false, set->extract_key, set->are_equal, set->malloc,
+ set->free);
+}
+
+static inline void *sl_set_lookup_opt(SortedListSetNode **root, void *key,
+ unsigned int keyHashValue,
+ int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ bool copyOut,
+ void *(*my_malloc)(size_t size)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ return NULL;
+ } else if (are_equal(extract_key(current->value, keyPosition), key)) {
+ if (copyOut) {
+ void *toReturn = my_malloc(current->valueSize);
+ memcpy(toReturn, current->value, current->valueSize);
+ return toReturn;
+ } else {
+ return current->value;
+ }
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ return NULL;
+}
+
+static inline void *sl_set_lookup(void *setParam, void *key) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ return sl_set_lookup_opt(&set->head, key, set->hash_key(key),
+ set->keyPosition, set->extract_key, set->are_equal,
+ false, set->malloc);
+}
+
+static inline bool sl_set_delete_opt(SortedListSetNode **root, void *key,
+ unsigned int keyHashValue, int keyPosition,
+ void *(*extract_key)(void *v, int keyPos),
+ bool (*are_equal)(void *v1, void *v2),
+ void (*my_free)(void *ptr)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ int compareResult;
+ while (current != NULL) {
+ compareResult = compare_hash_codes(current->key_hash_value, keyHashValue);
+ if (compareResult < 0) {
+ previous = current;
+ current = previous->next;
+ } else if (compareResult > 0) {
+ return false;
+ } else if (are_equal(extract_key(current->value, keyPosition), key)) {
+ previous->next = current->next;
+ my_free(current);
+ return true;
+ } else {
+ previous = current;
+ current = previous->next;
+ }
+ }
+ return false;
+}
+
+static inline void sl_set_delete(void *setParam, void *key,
+ unsigned int keySize) {
+ (void)keySize;
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_delete_opt(&set->head, key, set->hash_key(key), set->keyPosition,
+ set->extract_key, set->are_equal, set->free);
+}
+
+static inline void sl_set_free_opt(SortedListSetNode **root,
+ void (*my_free)(void *ptr)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = current->next;
+ my_free(previous);
+ }
+}
+
+static inline void sl_set_free(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ sl_set_free_opt(&set->head, set->free);
+ set->free(set);
+}
+
+static inline SortedListSetNode *sl_set_split_opt(SortedListSetNode **root,
+ unsigned int splitPattern) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ if (current->key_hash_value & splitPattern) {
+ previous->next = NULL;
+ return current;
+ }
+ previous = current;
+ current = previous->next;
+ }
+ return NULL;
+}
+
+static inline void sl_set_concat_opt(SortedListSetNode **root,
+ SortedListSetNode *list) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = previous->next;
+ }
+ previous->next = list;
+}
+
+static inline void sl_set_append_opt(SortedListSetNode **root,
+ SortedListSetNode *list) {
+ if (list == NULL) {
+ return;
+ }
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ while (current != NULL) {
+ previous = current;
+ current = previous->next;
+ }
+ previous->next = list;
+}
+
+static inline void *
+sl_set_fold_opt(SortedListSetNode **root, void *initialValue,
+ void *(*f)(void *soFar, void *currentValue)) {
+ SortedListSetNode *previous = (SortedListSetNode *)root;
+ SortedListSetNode *current = previous->next;
+ void *soFar = initialValue;
+ while (current != NULL) {
+ soFar = f(soFar, current->value);
+ current = current->next;
+ }
+ return soFar;
+}
+
+static inline void *sl_set_fold(SortedListSet *set, void *initialValue,
+ void *(*f)(void *soFar, void *currentValue)) {
+ return sl_set_fold_opt(&set->head, initialValue, f);
+}
+
+static inline void *_______size_helper(void *soFarParam, void *currentValue) {
+ unsigned int *soFar = (unsigned int *)soFarParam;
+ (void)currentValue;
+ unsigned int prev = *soFar;
+ *soFar = prev + 1;
+ return soFar;
+}
+
+static inline unsigned int sl_set_size_opt(SortedListSetNode **root) {
+ unsigned int size = 0;
+ sl_set_fold_opt(root, &size, _______size_helper);
+ return size;
+}
+
+static inline unsigned int sl_set_size(SortedListSet *set) {
+ return sl_set_size_opt(&set->head);
+}
+
+static inline void sl_set_print(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ SortedListSetNode *previous = (SortedListSetNode *)set;
+ SortedListSetNode *current = previous->next;
+ printf("[");
+ while (current != NULL) {
+ char *string = set->to_string(current->value);
+ printf("%s", string);
+ set->free(string);
+ previous = current;
+ current = previous->next;
+ if (current != NULL) {
+ printf(",");
+ }
+ }
+ printf("]\n");
+}
+
+static inline char *sl_set_to_string(void *setParam) {
+ SortedListSet *set = (SortedListSet *)setParam;
+ unsigned int numberOfElements = sl_set_size(set);
+ char **stringArray = malloc(sizeof(char*)*numberOfElements);
+ unsigned int *stringLengths = malloc(sizeof(unsigned int)*numberOfElements);
+ SortedListSetNode *previous = (SortedListSetNode *)set;
+ SortedListSetNode *current = previous->next;
+ unsigned int elementNumber = 0;
+ unsigned int totalCharCount = 2;
+ if (numberOfElements == 0) {
+ char *buffer = (char *)set->malloc(3);
+ sprintf(buffer, "[]");
+ free(stringArray);
+ free(stringLengths);
+ return buffer;
+ }
+ while (current != NULL) {
+ stringArray[elementNumber] = set->to_string(current->value);
+ stringLengths[elementNumber] = strlen(stringArray[elementNumber]);
+ totalCharCount = totalCharCount + stringLengths[elementNumber];
+ current = current->next;
+ elementNumber++;
+ }
+ totalCharCount = totalCharCount + numberOfElements;
+ char *stringBuffer = set->malloc(totalCharCount);
+ stringBuffer[0] = '[';
+ unsigned int currentPosition = 1;
+ for (unsigned int i = 0; i < numberOfElements; i++) {
+ sprintf(&stringBuffer[currentPosition], "%s", stringArray[i]);
+ set->free(stringArray[i]);
+ currentPosition = currentPosition + stringLengths[i];
+ if (i != (numberOfElements - 1)) {
+ sprintf(&stringBuffer[currentPosition], ",");
+ currentPosition = currentPosition + 1;
+ }
+ }
+ sprintf(&stringBuffer[currentPosition], "]");
+ free(stringArray);
+ free(stringLengths);
+ return stringBuffer;
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c
new file mode 100644
index 0000000000..f118db4361
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/simple_c_gc/test.c
@@ -0,0 +1,53 @@
+#include "simple_c_gc.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static size_t nr_of_objects_created = 0;
+static size_t nr_of_objects = 0;
+static size_t peek_nr_of_objects = 0;
+
+static void *my_malloc(size_t size) {
+ nr_of_objects++;
+ nr_of_objects_created++;
+ if (nr_of_objects > peek_nr_of_objects) {
+ peek_nr_of_objects = nr_of_objects;
+ }
+ return calloc(1, size);
+}
+
+static void my_free(void *ptr) {
+ nr_of_objects--;
+ free(ptr);
+}
+
+int my_main(int argc, char *argv[]) {
+ if (argc == 2 && strcmp(argv[1], "-enable_gc_info") == 0) {
+ scgc_enable_print_gc_info();
+ }
+ void **my = scgc_new(100);
+ FILE *fp = fopen(".tmp_out", "w");
+ fprintf(fp, "my %p %p\n", (void *)my, (void *)&my);
+ for (int i = 0; i < 100000; i++) {
+ my[0] = scgc_new(32);
+ my[1] = scgc_new(32);
+ my[2] = scgc_new(32);
+ ((void **)my[2])[0] = scgc_new(32);
+ ((void **)my[2])[1] = scgc_new(32);
+ fprintf(fp, "test1 %p\n", ((void **)my[0])[0]);
+ fprintf(fp, "test2 %p\n", ((void **)my[1])[0]);
+ fprintf(fp, "test3 %p\n", ((void ***)my[2])[0][0]);
+ fprintf(fp, "test4 %p\n", ((void ***)my[2])[1][0]);
+ }
+ fclose(fp);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ /*Test the gc*/
+ scgc_start_gced_code(my_main, argc, argv, my_malloc, my_free);
+ fprintf(stderr, "Peek nr of live objects: %zu\n", peek_nr_of_objects);
+ fprintf(stderr, "Nr of objects after: %zu\n", nr_of_objects);
+ fprintf(stderr, "Nr of objects created: %zu\n", nr_of_objects_created);
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION
new file mode 100644
index 0000000000..0bbde9c80d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/GIT_VERSION
@@ -0,0 +1,468 @@
+origin https://github.com/kokke/tiny-regex-c.git (fetch)
+origin https://github.com/kokke/tiny-regex-c.git (push)
+commit d3058f271f7a06ff298dff0a6a9a1e0753a5fa17
+Merge: 28882c4 c2ed772
+Author: kokke <spam@rowdy.dk>
+Date: Fri Oct 26 23:20:07 2018 +0200
+
+ Merge pull request #22 from monolifed/master
+
+ Update re.c for #20
+
+commit c2ed77267c86e30aa342f48528e5ffce6ab4d103
+Author: monolifed <6624464+monolifed@users.noreply.github.com>
+Date: Thu Oct 25 19:19:24 2018 +0300
+
+ Update re.c
+
+commit 28882c4a39fbc9ddd44e69caa22aac4c4a208934
+Author: kokke <spam@rowdy.dk>
+Date: Tue Oct 23 11:27:23 2018 +0200
+
+ Update README.md
+
+commit 4583018febd2d28277b512f09427e2ba01b4cbd5
+Author: kokke <spam@rowdy.dk>
+Date: Tue Oct 23 11:26:47 2018 +0200
+
+ Update README.md
+
+commit 2211111107da75f5574d5f0140e4396ae701d947
+Author: kokke <spam@rowdy.dk>
+Date: Mon Oct 22 16:04:02 2018 +0200
+
+ Update test1.c
+
+ Adding failing test-case for question-mark '?', brought to my attention by @tobermory in https://github.com/kokke/tiny-regex-c/issues/20
+
+commit 679aebd38a245afb9f9d107d066b68765b94865b
+Author: kokke <spam@rowdy.dk>
+Date: Mon Oct 22 15:41:33 2018 +0200
+
+ Update re.c
+
+ fixing typo, noticed by @tobermory -> https://github.com/kokke/tiny-regex-c/issues/19
+
+commit 2f225fa5e355ad3a99cdd5e953768399fe0b6607
+Author: kokke <spam@rowdy.dk>
+Date: Wed Jun 6 18:15:48 2018 +0200
+
+ Update test1.c
+
+commit b587a65abf0f1347a3b7c7050b73c5dbb94d9cb7
+Merge: 89a479f 96a8f77
+Author: kokke <spam@rowdy.dk>
+Date: Wed Jun 6 18:10:57 2018 +0200
+
+ Merge pull request #17 from monolifed/patch-1
+
+ Update re.c
+
+commit 96a8f770c2922505699c9a4d6ba9b9584be5ee29
+Author: monolifed <6624464+monolifed@users.noreply.github.com>
+Date: Thu May 31 02:06:49 2018 +0300
+
+ Update re.c
+
+ hopefully fixes #12
+
+commit 89a479f985cb25284c4e11870c1531c299255790
+Merge: bf9b2f0 e5f3564
+Author: kokke <spam@rowdy.dk>
+Date: Tue May 15 11:25:53 2018 +0200
+
+ Merge pull request #16 from TermoSINteZ/master
+
+ Fix pattern ".?" issues
+
+commit e5f3564a1de7230cec207cb6aec3866b0b7931e0
+Author: TermoSINteZ <termo.sintez@gmail.com>
+Date: Tue May 15 10:41:17 2018 +0300
+
+ Remove tabs
+
+commit acb0a441470808c99a08ecc8d8716d258866835c
+Author: TermoSINteZ <termo.sintez@gmail.com>
+Date: Tue May 15 00:10:55 2018 +0300
+
+ Fix pattern ".?" issues
+
+commit bf9b2f0c5e91dd12e1fea8cbc7ae7a6193e7b4ed
+Merge: cb80dee 84af23d
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 17 14:09:03 2018 +0200
+
+ Merge pull request #14 from roflcopter4/master
+
+ Check for correct python2 binary in Makefile
+
+commit 84af23dde1c6785ca680d5aced93e20e484efa8d
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:45:34 2018 -0600
+
+ Fix dumb typos
+
+commit 0cb0b1348392b795971be9472d8dd2854403a2cb
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:42:13 2018 -0600
+
+ Add back '@' signs I accidentally removed
+
+commit 81d12dfd3de805d0969e66650731e4df9158169c
+Author: roflcopter4 <brendan.leason.4@gmail.com>
+Date: Mon Apr 16 14:26:28 2018 -0600
+
+ Check for correct python2 binry in Makefile
+
+commit cb80dee0644f41df67b1740fefd8573d18d84a53
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:39:17 2018 +0100
+
+ Update README.md
+
+commit 005de160fa2d8796eb2bce75b52eeaac3ac13d8d
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:32:37 2018 +0100
+
+ Update Makefile
+
+commit 9ec0029e83e7cba718f6b6f8b107a0133d22b4a7
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:31:40 2018 +0100
+
+ Create regex_test_neg.py
+
+commit 960dd3ebec78d2ac61969840d4e23c97d443bdc9
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 15:31:11 2018 +0100
+
+ Create test_rand_neg.c
+
+commit 3d472f3d78d9702ffcdf4439ac842e3250da7c49
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 14:02:23 2018 +0100
+
+ Update README.md
+
+commit cdf61829adc1c94a9f2d019b2683c34c7732ca60
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 13:06:49 2018 +0100
+
+ Update Makefile
+
+commit 98812bdcafa58c7f35fc40fae9ba64d6d2a9eac1
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:52:34 2018 +0100
+
+ Update README.md
+
+commit 9c192d4199e4e1e6a764f1b1699deb5b159b161e
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:47:27 2018 +0100
+
+ Update re.c
+
+commit fb677f315fd159e13c2c2fc5b40f199148c0fb0f
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:45:20 2018 +0100
+
+ Update test1.c
+
+commit dc1b3ee8fc4354e5dfa1846ee6b778452609d50e
+Author: kokke <spam@rowdy.dk>
+Date: Fri Mar 23 12:08:34 2018 +0100
+
+ Update README.md
+
+commit eac0cef080a32c4b606bd10994ddcf8a72249d78
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:26:53 2017 +0100
+
+ Update README.md
+
+commit dc9f34d74b6ac80cd0bed17fca76ef35fca3b101
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:23:40 2017 +0100
+
+ Update re.c
+
+commit d76301fa18f3575cca94816cff291a01fda58ad7
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 23:23:22 2017 +0100
+
+ Update test1.c
+
+commit b72898ef7a67a0650c7d00b7a09f36abc1fa57a1
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 21:35:55 2017 +0100
+
+ Update re.c
+
+commit 3cd275c9c55ec51a01ad01c317232247aeec9bab
+Merge: baf3a15 881f634
+Author: kokke <spam@rowdy.dk>
+Date: Mon Dec 11 21:32:26 2017 +0100
+
+ Merge pull request #9 from mrigger/out-of-bounds-fixes
+
+ Out of bounds fixes.
+
+commit 881f634e9a905933d1889a4e0b9b09920337478a
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sun Dec 10 11:42:17 2017 +0100
+
+ Fix out-of-bunds access found by AFL (input: [00000000000000000000000000000000000000][).
+
+commit e6c91ab986f1ed7d7e39d58e6fdfae44d3110c99
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sun Dec 10 10:30:06 2017 +0100
+
+ Fix out-of-bounds accesses found by AFL (input: [00000000000000000000000000000000000000).
+
+commit 619a9c654df6a471d7e043081b3fb4bf0e1cd642
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Sat Dec 9 22:56:12 2017 +0100
+
+ Fix out-of-bounds access found by AFL.
+
+commit 43051e257141740e99c32f1cffea78d2a72fe10e
+Author: Manuel Rigger <manuel.rigger@jku.at>
+Date: Fri Dec 8 21:25:01 2017 +0100
+
+ Fix out-of-bounds access found by AFL.
+
+commit baf3a15d7b99b856e82d3dd69555d3bac6750049
+Author: kokke <spam@rowdy.dk>
+Date: Thu Oct 12 00:54:16 2017 +0200
+
+ Update re.h
+
+ To make the C++ crowd and their compilers happy ;)
+
+commit 107352174172b05f25d153c651b327890ab3b574
+Author: kokke <spam@rowdy.dk>
+Date: Sat Jul 8 03:32:29 2017 +0200
+
+ Update re.c
+
+commit ef6b2416b17388da413d3b9cce95ef6897255970
+Author: kokke <spam@rowdy.dk>
+Date: Sat Jul 8 03:26:59 2017 +0200
+
+ Update test1.c
+
+commit b8446ecba1c59b7a9f29e7d8174661deb38a74a5
+Author: kokke <spam@rowdy.dk>
+Date: Wed May 3 22:55:11 2017 +0200
+
+ Update README.md
+
+commit 1600de0a66b11610183c02c4f778866dce6927af
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:43:47 2017 +0200
+
+ Update regex_test.py
+
+commit e5dafc83fe1672de45ab1bc40e7f34876e2d1d15
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:34:22 2017 +0200
+
+ Update test_rand.c
+
+commit a39262a534a628d7c07bbb753a66d031a4b4a43a
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:27:17 2017 +0200
+
+ Update test_print.c
+
+commit 96b9af356129a263ef60355426de0c339308cd26
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:25:11 2017 +0200
+
+ Update re.c
+
+commit 529889acae614c91697af87feba5abe5334a4274
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:18:40 2017 +0200
+
+ Update README.md
+
+commit 1afc07dc2936f17dc0df5178ef61caeb19d2c5b1
+Author: kokke <spam@rowdy.dk>
+Date: Mon May 1 21:17:57 2017 +0200
+
+ Update README.md
+
+commit 407f4fe08fb219d68026817d47d1a453a0b6c1ba
+Author: kokke <spam@rowdy.dk>
+Date: Fri Apr 28 17:27:12 2017 +0200
+
+ Update README.md
+
+commit 2d2ebe66d55349ba6add2a4335b24b0a13fb26d2
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 23:48:18 2017 +0200
+
+ Update README.md
+
+commit 5c76b8cc4e77af9c4a3cb1bbd1cfda3e73e1be56
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 23:33:34 2017 +0200
+
+ Update README.md
+
+commit e4c7cb9d63d1b284f92053f535402738ee2c3a6a
+Author: kokke <spam@rowdy.dk>
+Date: Mon Apr 24 09:28:24 2017 +0200
+
+ Update README.md
+
+commit 1e694fe184be1261e9f684ea3556103e45649c3b
+Author: kokke <spam@rowdy.dk>
+Date: Thu Apr 20 17:58:33 2017 +0200
+
+ Update README.md
+
+commit 52810460dd877f337e23d58e627b5ae3fbbee430
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 02:38:00 2017 +0200
+
+ Update Makefile
+
+commit d44fb891007d56e0344f7372521f3ff583630d61
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 01:19:38 2017 +0200
+
+ Update README.md
+
+commit af6e7b642b75255bc2a608383a1b0969816bbd83
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 01:16:23 2017 +0200
+
+ Update README.md
+
+commit 858d217db95390b6e7bd1fd1ba7be950142852c9
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:32:20 2017 +0200
+
+ Update README.md
+
+commit 28fffd4d32b56cd9b318091cacc8a5c71fbfffed
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:31:44 2017 +0200
+
+ Update README.md
+
+commit bd874728041ad0444744a9e739dd5d078f249150
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:28:26 2017 +0200
+
+ Update README.md
+
+commit 02110e89252e76c1a2b2a44430f0f402e523f50a
+Author: kokke <spam@rowdy.dk>
+Date: Wed Apr 19 00:26:41 2017 +0200
+
+ Update test1.c
+
+commit 939bc7572148c77d398b22892857b769ec63ac54
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:31:14 2017 +0200
+
+ Update README.md
+
+commit 5fef3901aabb6100b28460dccf3154d3e1cc18e7
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:09:12 2017 +0200
+
+ Update README.md
+
+commit cae4b96ced1679ea703890eff00deefb0b80cc16
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 23:08:34 2017 +0200
+
+ Update README.md
+
+commit 72398075ee66c49e3e804be94146956dc8bafff6
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:58:10 2017 +0200
+
+ Update Makefile
+
+commit 2994559b99506b35e71c8e18aa99dd706f3b7e38
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:53:05 2017 +0200
+
+ Update re.c
+
+commit 124052b32f302c5a570e79089f951a7d1d56cb38
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:52:01 2017 +0200
+
+ Update Makefile
+
+commit ba8caa931b36b6c274c82214b636770857f7872d
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:50:11 2017 +0200
+
+ Create regex_test.py
+
+commit 9630b56d1faffea799575bb9a81f9c0278906a16
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:49:39 2017 +0200
+
+ Create exrex.py
+
+commit 7da49b443622deaaa472d233a80c58a378fb8646
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:47:37 2017 +0200
+
+ Create test_rand.c
+
+commit 29384927296062ecf0fec81a1c7e35d315b5d84a
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:47:09 2017 +0200
+
+ Create test_print.c
+
+commit 475c199a1b1af4fdd8422b8639117ec97f53b350
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:46:34 2017 +0200
+
+ Create test2.c
+
+commit 2e6e69622061daa740fa03c726f4cc66c5fbc38c
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:45:08 2017 +0200
+
+ Create test1.c
+
+commit e9e4ce1b91609d839a2996bb918397b502f908b7
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:44:33 2017 +0200
+
+ Create Makefile
+
+commit 5a8f0e60718fbfdca7c1fc079a77051af68bdc87
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:41:01 2017 +0200
+
+ Create re.c
+
+commit 52b0aeb459cf1682b636fabe01a97de17579b036
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:38:55 2017 +0200
+
+ Create re.h
+
+commit d061985f504096a59e320a4760784a4090eaf1e4
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:38:01 2017 +0200
+
+ Update README.md
+
+commit 32cbf08728415efb96a3f562f31b780c024502ff
+Author: kokke <spam@rowdy.dk>
+Date: Tue Apr 18 22:37:02 2017 +0200
+
+ Initial commit
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE
new file mode 100644
index 0000000000..cf1ab25da0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile
new file mode 100644
index 0000000000..deb9fea33a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/Makefile
@@ -0,0 +1,106 @@
+# Compiler to use - can be replaced by clang for instance
+CC := gcc
+
+# Number of random text expressions to generate, for random testing
+NRAND_TESTS := 1000
+
+PYTHON != if (python --version 2>&1 | grep -q 'Python 2\..*'); then \
+ echo 'python'; \
+ elif command -v python2 >/dev/null 2>&1; then \
+ echo 'python2'; \
+ else \
+ echo 'Error: no compatible python version found.' >&2; \
+ exit 1; \
+ fi
+
+# Flags to pass to compiler
+CFLAGS := -O3 -Wall -Wextra -std=c99 -I.
+
+all:
+ @$(CC) $(CFLAGS) re.c tests/test1.c -o tests/test1
+ @$(CC) $(CFLAGS) re.c tests/test2.c -o tests/test2
+ @$(CC) $(CFLAGS) re.c tests/test_rand.c -o tests/test_rand
+ @$(CC) $(CFLAGS) re.c tests/test_rand_neg.c -o tests/test_rand_neg
+
+clean:
+ @rm -f tests/test1 tests/test2 tests/test_rand
+ @#@$(foreach test_bin,$(TEST_BINS), rm -f $(test_bin) ; )
+ @rm -f a.out
+ @rm -f *.o
+
+
+test: all
+ @$(test $(PYTHON))
+ @echo
+ @echo Testing hand-picked regex\'s:
+ @./tests/test1
+ @echo Testing patterns against $(NRAND_TESTS) random strings matching the Python implementation and comparing:
+ @echo
+ @$(PYTHON) ./scripts/regex_test.py \\d+\\w?\\D\\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\w*\\d?\\w\\? $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d]+\\\\?\\s $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\w][^-1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\w] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^-1-4] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d]+\\s?[\\w]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py a+b*[ac]*.+.*.[\\.]. $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py a?b[ac*]*.?[\\]+[?]? $(NRAND_TESTS)
+ @#python ./scripts/regex_test.py [1-5-]+[-1-2]-[-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [-1-3]-[-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [1-5]+[-1-2]-[\\-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [-1-2]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s?[a-fKL098]+-? $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\-]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\\\]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [0-9a-fA-F]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [1379][2468][abcdef] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [012345-9]?[0123-789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [012345-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [0-56789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [abc-zABC-Z] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [a\d]?1234 $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py .*123faerdig $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py .?\\w+jsj$ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [?to][+to][?ta][*ta] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\d+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [a-z]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\w $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py \\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test.py [^\\d] $(NRAND_TESTS)
+ @#python ./scripts/regex_test.py [^-1-4] $(NRAND_TESTS)
+ @echo
+ @echo
+ @echo
+ @echo Testing rejection of patterns against $(NRAND_TESTS) random strings also rejected by the Python implementation:
+ @echo
+ @$(PYTHON) ./scripts/regex_test_neg.py \\d+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [a-z]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py \\s+[a-zA-Z0-9?]* $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^\\w $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^\\d $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[^\\d] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [^\\w]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[\\w]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py ^[^0-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [a-z].[A-Z] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [-1-3]-[-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [1-5]+[-1-2]-[\\-] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [-0-9]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\-]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [\\\\]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [0-9a-fA-F]+ $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [1379][2468][abcdef] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [012345-9] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py [0-56789] $(NRAND_TESTS)
+ @$(PYTHON) ./scripts/regex_test_neg.py .*123faerdig $(NRAND_TESTS)
+ @echo
+ @echo
+ @./tests/test2
+ @echo
+ @echo
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md
new file mode 100644
index 0000000000..ab89576f08
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/README.md
@@ -0,0 +1,138 @@
+# tiny-regex-c
+# A small regex implementation in C
+### Description
+Small and portable [Regular Expression](https://en.wikipedia.org/wiki/Regular_expression) (regex) library written in C.
+
+Design is inspired by Rob Pike's regex-code for the book *"Beautiful Code"* [available online here](http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html).
+
+Supports a subset of the syntax and semantics of the Python standard library implementation (the `re`-module).
+
+### Current status
+All supported regex-operators seem to work properly according to the test-set, with the following exception:
+
+There is a problem with ranges (e.g. `[0-9]` for a digit 0-9) combined with inverted character-cases, e.g. `[^ab]` for anything but 'a' or 'b' - like `[^-0-9]` for anything not '-' or a digit 0-9. I think the code mathces too broadly in that case.
+
+I think you should test the patterns you are going to use. You can easily modify the test-harness to generate tests for your intended patterns to check for compliance.
+
+**I will gladly accept patches correcting bugs.**
+
+### Design goals
+The main design goal of this library is to be small, correct, self contained and use few resources while retaining acceptable performance and feature completeness. Clarity of the code is also highly valued.
+
+### Notable features and omissions
+- Small code and binary size: <500 SLOC, ~3kb binary for x86. Statically #define'd memory usage / allocation.
+- No use of dynamic memory allocation (i.e. no calls to `malloc` / `free`).
+- To avoid call-stack exhaustion, iterative searching is preferred over recursive by default (can be changed with a pre-processor flag).
+- No support for capturing groups or named capture: `(^P<name>group)` etc.
+- Thorough testing : [exrex](https://github.com/asciimoo/exrex) is used to randomly generate test-cases from regex patterns, which are fed into the regex code for verification. Try `make test` to generate a few thousand tests cases yourself.
+- Compiled for x86 using GCC 4.7.4 and optimizing for size, the binary takes up ~2-3kb code space and allocates ~0.5kb RAM :
+ ```
+ > gcc -Os -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 2319 0 544 2863 b2f re.o
+
+ ```
+ For ARM/Thumb using GCC 4.8.1 it's around 1.5kb code and less RAM :
+ ```
+ > arm-none-eabi-gcc -Os -mthumb -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 1418 0 280 1698 6a2 re.o
+
+ ```
+ For 8-bit AVR using AVR-GCC 4.8.1 it's around 2kb code and less RAM :
+ ```
+ > avr-gcc -Os -c re.c
+ > size re.o
+ text data bss dec hex filename
+ 2128 0 130 2258 8d2 re.o
+ ```
+
+
+
+### API
+This is the public / exported API:
+```C
+/* Typedef'd pointer to hide implementation details. */
+typedef struct regex_t* re_t;
+
+/* Compiles regex string pattern to a regex_t-array. */
+re_t re_compile(const char* pattern);
+
+/* Finds matches of the compiled pattern inside text. */
+int re_matchp(re_t pattern, const char* text);
+
+/* Finds matches of pattern inside text (compiles first automatically). */
+int re_match(const char* pattern, const char* text);
+```
+
+### Supported regex-operators
+The following features / regex-operators are supported by this library.
+
+NOTE: inverted character classes are buggy - see the test harness for concrete examples.
+
+
+ - `.` Dot, matches any character
+ - `^` Start anchor, matches beginning of string
+ - `$` End anchor, matches end of string
+ - `*` Asterisk, match zero or more (greedy)
+ - `+` Plus, match one or more (greedy)
+ - `?` Question, match zero or one (non-greedy)
+ - `[abc]` Character class, match if one of {'a', 'b', 'c'}
+ - `[^abc]` Inverted class, match if NOT one of {'a', 'b', 'c'}
+ **`NOTE: This feature is currently broken for some usage of character ranges!`**
+ - `[a-zA-Z]` Character ranges, the character set of the ranges { a-z | A-Z }
+ - `\s` Whitespace, \t \f \r \n \v and spaces
+ - `\S` Non-whitespace
+ - `\w` Alphanumeric, [a-zA-Z0-9_]
+ - `\W` Non-alphanumeric
+ - `\d` Digits, [0-9]
+ - `\D` Non-digits
+
+### Usage
+Compile a regex from ASCII-string (char-array) to a custom pattern structure using `re_compile()`.
+
+Search a text-string for a regex and get an index into the string, using `re_match()` or `re_matchp()`.
+
+The returned index points to the first place in the string, where the regex pattern matches.
+
+If the regular expression doesn't match, the matching function returns an index of -1 to indicate failure.
+
+### Examples
+Example of usage:
+```C
+/* Standard null-terminated C-string to search: */
+const char* string_to_search = "ahem.. 'hello world !' ..";
+
+/* Compile a simple regular expression using character classes, meta-char and greedy + non-greedy quantifiers: */
+re_t pattern = re_compile("[Hh]ello [Ww]orld\\s*[!]?");
+
+/* Check if the regex matches the text: */
+int match_idx = re_matchp(pattern, string_to_search);
+if (match_idx != -1)
+{
+ printf("match at idx %d.\n", match_idx);
+}
+```
+
+For more usage examples I encourage you to look at the code in the `tests`-folder.
+
+### TODO
+- Fix the implementation of inverted character classes.
+- Fix implementation of branches (`|`), and see if that can lead us closer to groups as well, e.g. `(a|b)+`.
+- Add `example.c` that demonstrates usage.
+- Add `tests/test_perf.c` for performance and time measurements.
+- Testing: Improve pattern rejection testing.
+
+### FAQ
+- *Q: What differentiates this library from other C regex implementations?*
+
+ A: Well, the small size for one. <500 lines of C-code compiling to 2-3kb ROM, using very little RAM.
+
+### License
+All material in this repository is in the public domain.
+
+
+
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c
new file mode 100644
index 0000000000..76d4066247
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.c
@@ -0,0 +1,470 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+
+
+#include "re.h"
+#include <stdio.h>
+
+/* Definitions: */
+
+#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */
+#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */
+
+
+enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, /* BRANCH */ };
+
+typedef struct regex_t
+{
+ unsigned char type; /* CHAR, STAR, etc. */
+ union
+ {
+ unsigned char ch; /* the character itself */
+ unsigned char* ccl; /* OR a pointer to characters in class */
+ } u;
+} regex_t;
+
+
+
+/* Private function declarations: */
+static int matchpattern(regex_t* pattern, const char* text);
+static int matchcharclass(char c, const char* str);
+static int matchstar(regex_t p, regex_t* pattern, const char* text);
+static int matchplus(regex_t p, regex_t* pattern, const char* text);
+static int matchone(regex_t p, char c);
+static int matchdigit(char c);
+static int matchalpha(char c);
+static int matchwhitespace(char c);
+static int matchmetachar(char c, const char* str);
+static int matchrange(char c, const char* str);
+static int ismetachar(char c);
+
+
+
+/* Public functions: */
+int re_match(const char* pattern, const char* text)
+{
+ return re_matchp(re_compile(pattern), text);
+}
+
+int re_matchp(re_t pattern, const char* text)
+{
+ if (pattern != 0)
+ {
+ if (pattern[0].type == BEGIN)
+ {
+ return ((matchpattern(&pattern[1], text)) ? 0 : -1);
+ }
+ else
+ {
+ int idx = -1;
+
+ do
+ {
+ idx += 1;
+
+ if (matchpattern(pattern, text))
+ {
+ if (text[0] == '\0')
+ return -1;
+
+ return idx;
+ }
+ }
+ while (*text++ != '\0');
+ }
+ }
+ return -1;
+}
+
+re_t re_compile(const char* pattern)
+{
+ /* The sizes of the two static arrays below substantiates the static RAM usage of this module.
+ MAX_REGEXP_OBJECTS is the max number of symbols in the expression.
+ MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */
+ static regex_t re_compiled[MAX_REGEXP_OBJECTS];
+ static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN];
+ int ccl_bufidx = 1;
+
+ char c; /* current char in pattern */
+ int i = 0; /* index into pattern */
+ int j = 0; /* index into re_compiled */
+
+ while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS))
+ {
+ c = pattern[i];
+
+ switch (c)
+ {
+ /* Meta-characters: */
+ case '^': { re_compiled[j].type = BEGIN; } break;
+ case '$': { re_compiled[j].type = END; } break;
+ case '.': { re_compiled[j].type = DOT; } break;
+ case '*': { re_compiled[j].type = STAR; } break;
+ case '+': { re_compiled[j].type = PLUS; } break;
+ case '?': { re_compiled[j].type = QUESTIONMARK; } break;
+/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */
+
+ /* Escaped character-classes (\s \w ...): */
+ case '\\':
+ {
+ if (pattern[i+1] != '\0')
+ {
+ /* Skip the escape-char '\\' */
+ i += 1;
+ /* ... and check the next */
+ switch (pattern[i])
+ {
+ /* Meta-character: */
+ case 'd': { re_compiled[j].type = DIGIT; } break;
+ case 'D': { re_compiled[j].type = NOT_DIGIT; } break;
+ case 'w': { re_compiled[j].type = ALPHA; } break;
+ case 'W': { re_compiled[j].type = NOT_ALPHA; } break;
+ case 's': { re_compiled[j].type = WHITESPACE; } break;
+ case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break;
+
+ /* Escaped character, e.g. '.' or '$' */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].u.ch = pattern[i];
+ } break;
+ }
+ }
+ /* '\\' as last char in pattern -> invalid regular expression. */
+/*
+ else
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].ch = pattern[i];
+ }
+*/
+ } break;
+
+ /* Character class: */
+ case '[':
+ {
+ /* Remember where the char-buffer starts. */
+ int buf_begin = ccl_bufidx;
+
+ /* Look-ahead to determine if negated */
+ if (pattern[i+1] == '^')
+ {
+ re_compiled[j].type = INV_CHAR_CLASS;
+ i += 1; /* Increment i to avoid including '^' in the char-buffer */
+ }
+ else
+ {
+ re_compiled[j].type = CHAR_CLASS;
+ }
+
+ /* Copy characters inside [..] to buffer */
+ while ( (pattern[++i] != ']')
+ && (pattern[i] != '\0')) /* Missing ] */
+ {
+ if (pattern[i] == '\\')
+ {
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i++];
+ }
+ else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i];
+ }
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ /* Catches cases such as [00000000000000000000000000000000000000][ */
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ /* Null-terminate string end */
+ ccl_buf[ccl_bufidx++] = 0;
+ re_compiled[j].u.ccl = &ccl_buf[buf_begin];
+ } break;
+
+ /* Other characters: */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].u.ch = c;
+ } break;
+ }
+ i += 1;
+ j += 1;
+ }
+ /* 'UNUSED' is a sentinel used to indicate end-of-pattern */
+ re_compiled[j].type = UNUSED;
+
+ return (re_t) re_compiled;
+}
+
+void re_print(regex_t* pattern)
+{
+ const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" };
+
+ int i;
+ for (i = 0; i < MAX_REGEXP_OBJECTS; ++i)
+ {
+ if (pattern[i].type == UNUSED)
+ {
+ break;
+ }
+
+ printf("type: %s", types[pattern[i].type]);
+ if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS)
+ {
+ printf(" [");
+ int j;
+ char c;
+ for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j)
+ {
+ c = pattern[i].u.ccl[j];
+ if ((c == '\0') || (c == ']'))
+ {
+ break;
+ }
+ printf("%c", c);
+ }
+ printf("]");
+ }
+ else if (pattern[i].type == CHAR)
+ {
+ printf(" '%c'", pattern[i].u.ch);
+ }
+ printf("\n");
+ }
+}
+
+
+
+/* Private functions: */
+static int matchdigit(char c)
+{
+ return ((c >= '0') && (c <= '9'));
+}
+static int matchalpha(char c)
+{
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+}
+static int matchwhitespace(char c)
+{
+ return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f') || (c == '\v'));
+}
+static int matchalphanum(char c)
+{
+ return ((c == '_') || matchalpha(c) || matchdigit(c));
+}
+static int matchrange(char c, const char* str)
+{
+ return ((c != '-') && (str[0] != '\0') && (str[0] != '-') &&
+ (str[1] == '-') && (str[1] != '\0') &&
+ (str[2] != '\0') && ((c >= str[0]) && (c <= str[2])));
+}
+static int ismetachar(char c)
+{
+ return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D'));
+}
+
+static int matchmetachar(char c, const char* str)
+{
+ switch (str[0])
+ {
+ case 'd': return matchdigit(c);
+ case 'D': return !matchdigit(c);
+ case 'w': return matchalphanum(c);
+ case 'W': return !matchalphanum(c);
+ case 's': return matchwhitespace(c);
+ case 'S': return !matchwhitespace(c);
+ default: return (c == str[0]);
+ }
+}
+
+static int matchcharclass(char c, const char* str)
+{
+ do
+ {
+ if (matchrange(c, str))
+ {
+ return 1;
+ }
+ else if (str[0] == '\\')
+ {
+ /* Escape-char: increment str-ptr and match on next char */
+ str += 1;
+ if (matchmetachar(c, str))
+ {
+ return 1;
+ }
+ else if ((c == str[0]) && !ismetachar(c))
+ {
+ return 1;
+ }
+ }
+ else if (c == str[0])
+ {
+ if (c == '-')
+ {
+ return ((str[-1] == '\0') || (str[1] == '\0'));
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ while (*str++ != '\0');
+
+ return 0;
+}
+
+static int matchone(regex_t p, char c)
+{
+ switch (p.type)
+ {
+ case DOT: return 1;
+ case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl);
+ case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl);
+ case DIGIT: return matchdigit(c);
+ case NOT_DIGIT: return !matchdigit(c);
+ case ALPHA: return matchalphanum(c);
+ case NOT_ALPHA: return !matchalphanum(c);
+ case WHITESPACE: return matchwhitespace(c);
+ case NOT_WHITESPACE: return !matchwhitespace(c);
+ default: return (p.u.ch == c);
+ }
+}
+
+static int matchstar(regex_t p, regex_t* pattern, const char* text)
+{
+ do
+ {
+ if (matchpattern(pattern, text))
+ return 1;
+ }
+ while ((text[0] != '\0') && matchone(p, *text++));
+
+ return 0;
+}
+
+static int matchplus(regex_t p, regex_t* pattern, const char* text)
+{
+ while ((text[0] != '\0') && matchone(p, *text++))
+ {
+ if (matchpattern(pattern, text))
+ return 1;
+ }
+ return 0;
+}
+
+static int matchquestion(regex_t p, regex_t* pattern, const char* text)
+{
+ if (p.type == UNUSED)
+ return 1;
+ if (matchpattern(pattern, text))
+ return 1;
+ if (*text && matchone(p, *text++))
+ return matchpattern(pattern, text);
+ return 0;
+}
+
+
+#if 0
+
+/* Recursive matching */
+static int matchpattern(regex_t* pattern, const char* text)
+{
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[1], &pattern[2], text);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return text[0] == '\0';
+ }
+ else if ((text[0] != '\0') && matchone(pattern[0], text[0]))
+ {
+ return matchpattern(&pattern[1], text+1);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#else
+
+/* Iterative matching */
+static int matchpattern(regex_t* pattern, const char* text)
+{
+ do
+ {
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return (text[0] == '\0');
+ }
+/* Branching is not working properly
+ else if (pattern[1].type == BRANCH)
+ {
+ return (matchpattern(pattern, text) || matchpattern(&pattern[2], text));
+ }
+*/
+ }
+ while ((text[0] != '\0') && matchone(*pattern++, *text++));
+
+ return 0;
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h
new file mode 100644
index 0000000000..fd36412100
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/re.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+
+/* Typedef'd pointer to get abstract datatype. */
+typedef struct regex_t* re_t;
+
+
+/* Compile regex string pattern to a regex_t-array. */
+re_t re_compile(const char* pattern);
+
+
+/* Find matches of the compiled pattern inside text. */
+int re_matchp(re_t pattern, const char* text);
+
+
+/* Find matches of the txt pattern inside text (will compile automatically first). */
+int re_match(const char* pattern, const char* text);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py
new file mode 100644
index 0000000000..fc6fa7d5cc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/exrex.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+# This file is part of exrex.
+#
+# exrex is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# exrex is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with exrex. If not, see < http://www.gnu.org/licenses/ >.
+#
+# (C) 2012- by Adam Tauber, <asciimoo@gmail.com>
+
+try:
+ from future_builtins import map, range
+except:
+ pass
+from re import sre_parse
+from itertools import product, chain, tee
+from random import choice,randint
+import string
+
+__all__ = ('generate', 'CATEGORIES', 'count', 'parse', 'getone')
+
+CATEGORIES = {'category_space' : sorted(sre_parse.WHITESPACE)
+ ,'category_digit' : sorted(sre_parse.DIGITS)
+ ,'category_any' : [chr(x) for x in range(32, 123)]
+ ,'category_word' : sorted( frozenset(string.ascii_letters + string.digits + "_") )
+ }
+
+def comb(g, i):
+ for c in g:
+ g2,i = tee(i)
+ for c2 in g2:
+ yield c+c2
+
+def mappend(g, c):
+ for cc in g:
+ yield cc+c
+
+def _in(d):
+ ret = []
+ neg = False
+ for i in d:
+ if i[0] == 'range':
+ subs = map(chr, range(i[1][0], i[1][1]+1))
+ if neg:
+ for char in subs:
+ try:
+ ret.remove(char)
+ except:
+ pass
+ else:
+ ret.extend(subs)
+ elif i[0] == 'literal':
+ if neg:
+ try:
+ ret.remove(chr(i[1]))
+ except:
+ pass
+ else:
+ ret.append(chr(i[1]))
+ elif i[0] == 'category':
+ subs = CATEGORIES.get(i[1], [''])
+ if neg:
+ for char in subs:
+ try:
+ ret.remove(char)
+ except:
+ pass
+ else:
+ ret.extend(subs)
+ elif i[0] == 'negate':
+ ret = list(CATEGORIES['category_any'])
+ neg = True
+ return ret
+
+
+def prods(orig, ran, items):
+ for o in orig:
+ for r in ran:
+ for s in product(items, repeat=r):
+ yield o+''.join(s)
+
+def ggen(g1, f, *args, **kwargs):
+ for a in g1:
+ g2 = f(*args, **kwargs)
+ if isinstance(g2, int):
+ yield g2
+ else:
+ for b in g2:
+ yield a+b
+
+def _gen(d, limit=20, count=False):
+ """docstring for _gen"""
+ ret = ['']
+ strings = 0
+ for i in d:
+ if i[0] == 'in':
+ subs = _in(i[1])
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'literal':
+ ret = mappend(ret, chr(i[1]))
+ elif i[0] == 'category':
+ subs = CATEGORIES.get(i[1], [''])
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'any':
+ subs = CATEGORIES['category_any']
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'max_repeat':
+ chars = filter(None, _gen(list(i[1][2]), limit))
+ if i[1][1]+1 - i[1][0] >= limit:
+ ran = range(i[1][0], i[1][0]+limit)
+ else:
+ ran = range(i[1][0], i[1][1]+1)
+ if count:
+ for i in ran:
+ strings += pow(len(chars), i)
+ ret = prods(ret, ran, chars)
+ elif i[0] == 'branch':
+ subs = list(chain.from_iterable(_gen(list(x), limit) for x in i[1][1]))
+ if count:
+ strings = (strings or 1) * (len(subs) or 1)
+ ret = comb(ret, subs)
+ elif i[0] == 'subpattern':
+ if count:
+ strings = (strings or 1) * (sum(ggen([0], _gen, i[1][1], limit=limit, count=True)) or 1)
+ ret = ggen(ret, _gen, i[1][1], limit=limit, count=False)
+ # ignore ^ and $
+ elif i[0] == 'at':
+ continue
+ elif i[0] == 'not_literal':
+ subs = list(CATEGORIES['category_any'])
+ subs.remove(chr(i[1]))
+ if count:
+ strings = (strings or 1) * len(subs)
+ ret = comb(ret, subs)
+ elif i[0] == 'assert':
+ print i[1][1]
+ continue
+ else:
+ #print('[!] cannot handle expression ' + repr(i))
+ raise Exception('[!] cannot handle expression ' + repr(i))
+
+ if count:
+ return strings
+
+ return ret
+
+def _randone(d, limit=20):
+ """docstring for _randone"""
+ ret = ''
+ for i in d:
+ if i[0] == 'in':
+ ret += choice(_in(i[1]))
+ elif i[0] == 'literal':
+ ret += chr(i[1])
+ elif i[0] == 'category':
+ ret += choice(CATEGORIES.get(i[1], ['']))
+ elif i[0] == 'any':
+ ret += choice(CATEGORIES['category_any'])
+ elif i[0] == 'max_repeat':
+ chars = filter(None, _gen(list(i[1][2]), limit))
+ if i[1][1]+1 - i[1][0] >= limit:
+ min,max = i[1][0], i[1][0]+limit
+ else:
+ min,max = i[1][0], i[1][1]
+ for _ in range(randint(min, max)):
+ ret += choice(chars)
+ elif i[0] == 'branch':
+ ret += choice(list(chain.from_iterable(_gen(list(x), limit) for x in i[1][1])))
+ elif i[0] == 'subpattern':
+ ret += _randone(i[1][1], limit)
+ elif i[0] == 'at':
+ continue
+ elif i[0] == 'not_literal':
+ c=list(CATEGORIES['category_any'])
+ c.remove(chr(i[1]))
+ ret += choice(c)
+ else:
+ #print('[!] cannot handle expression "%s"' % str(i))
+ raise Exception('[!] cannot handle expression "%s"' % str(i))
+
+ return ret
+
+
+def parse(s):
+ """Regular expression parser
+ :param s: Regular expression
+ :type s: str
+ :rtype: list
+ """
+ r = sre_parse.parse(s)
+ return list(r)
+
+def generate(s, limit=20):
+ """Creates a generator that generates all matching strings to a given regular expression
+ :param s: Regular expression
+ :type s: str
+ :param limit: Range limit
+ :type limit: int
+ :returns: string generator object
+ """
+ return _gen(parse(s), limit)
+
+def count(s, limit=20):
+ """Counts all matching strings to a given regular expression
+ :param s: Regular expression
+ :type s: str
+ :param limit: Range limit
+ :type limit: int
+ :rtype: int
+ :returns: number of matching strings
+ """
+ return _gen(parse(s), limit, count=True)
+
+def getone(regex_string, limit=20):
+ """Returns a random matching string to a given regular expression
+ """
+ return _randone(parse(regex_string), limit)
+
+def argparser():
+ import argparse
+ from sys import stdout
+ argp = argparse.ArgumentParser(description='exrex - regular expression string generator')
+ argp.add_argument('-o', '--output'
+ ,help = 'Output file - default is STDOUT'
+ ,metavar = 'FILE'
+ ,default = stdout
+ ,type = argparse.FileType('w')
+ )
+ argp.add_argument('-l', '--limit'
+ ,help = 'Max limit for range size - default is 20'
+ ,default = 20
+ ,action = 'store'
+ ,type = int
+ ,metavar = 'N'
+ )
+ argp.add_argument('-c', '--count'
+ ,help = 'Count matching strings'
+ ,default = False
+ ,action = 'store_true'
+ )
+ argp.add_argument('-r', '--random'
+ ,help = 'Returns a random string that matches to the regex'
+ ,default = False
+ ,action = 'store_true'
+ )
+ argp.add_argument('-d', '--delimiter'
+ ,help = 'Delimiter - default is \\n'
+ ,default = '\n'
+ )
+ argp.add_argument('-v', '--verbose'
+ ,action = 'store_true'
+ ,help = 'Verbose mode'
+ ,default = False
+ )
+ argp.add_argument('regex'
+ ,metavar = 'REGEX'
+ ,help = 'REGEX string'
+ )
+ return vars(argp.parse_args())
+
+def __main__():
+ from sys import exit, stderr
+ # 'as(d|f)qw(e|r|s)[a-zA-Z]{2,3}'
+ # 'as(QWE|Z([XC]|Y|U)V){2,3}asdf'
+ # '.?'
+ # '.+'
+ # 'asdf.{1,4}qwer{2,5}'
+ # 'a(b)?(c)?(d)?'
+ # 'a[b][c][d]?[e]?
+ args = argparser()
+ if args['verbose']:
+ args['output'].write('%r%s' % (parse(args['regex'], limit=args['limit']), args['delimiter']))
+ if args['count']:
+ args['output'].write('%d%s' % (count(args['regex'], limit=args['limit']), args['delimiter']))
+ exit(0)
+ if args['random']:
+ args['output'].write('%s%s' % (getone(args['regex'], limit=args['limit']), args['delimiter']))
+ exit(0)
+ try:
+ g = generate(args['regex'], args['limit'])
+ except Exception, e:
+ print >> stderr, '[!] Error: ', e
+ exit(1)
+ for s in g:
+ try:
+ args['output'].write(s+args['delimiter'])
+ except:
+ break
+
+if __name__ == '__main__':
+ __main__()
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py
new file mode 100644
index 0000000000..0d3d00702c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+"""
+ This program generates random text that matches a given regex-pattern.
+ The pattern is given via sys.argv and the generated text is passed to
+ the binary 'tests/test_rand' to check if the generated text also matches
+ the regex-pattern in the C implementation.
+ The exit-code of the testing program, is used to determine test success.
+
+ This script is called by the Makefile when doing 'make test'
+"""
+
+
+import re
+import sys
+import exrex
+from subprocess import call
+
+
+prog = "./tests/test_rand"
+
+if len(sys.argv) < 2:
+ print("")
+ print("usage: %s pattern [nrepeat]" % sys.argv[0])
+ print(" where [nrepeat] is optional")
+ print("")
+ sys.exit(-1)
+
+own_prog = sys.argv[0]
+pattern = sys.argv[1]
+if len(sys.argv) > 2:
+ ntests = int(sys.argv[2])
+else:
+ ntests = 10
+nfails = 0
+repeats = ntests
+
+
+try:
+ repeats = int(sys.argv[2])
+except:
+ pass
+
+r = 50
+while r < 0:
+ try:
+ g = exrex.generate(pattern)
+ break
+ except:
+ pass
+
+
+sys.stdout.write("%-35s" % (" pattern '%s': " % pattern))
+
+
+while repeats >= 0:
+ try:
+ repeats -= 1
+ example = exrex.getone(pattern)
+ #print("%s %s %s" % (prog, pattern, example))
+ ret = call([prog, "\"%s\"" % pattern, "\"%s\"" % example])
+ if ret != 0:
+ escaped = repr(example) # escapes special chars for better printing
+ print(" FAIL : doesn't match %s as expected [%s]." % (escaped, ", ".join([("0x%02x" % ord(e)) for e in example]) ))
+ nfails += 1
+
+ except:
+ #import traceback
+ #print("EXCEPTION!")
+ #raw_input(traceback.format_exc())
+ ntests -= 1
+ repeats += 1
+ #nfails += 1
+
+sys.stdout.write("%4d/%d tests succeeded \n" % (ntests - nfails, ntests))
+#print("")
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py
new file mode 100644
index 0000000000..c3daad62c0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/scripts/regex_test_neg.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+"""
+ This program generates random text that matches a given regex-pattern.
+ The pattern is given via sys.argv and the generated text is passed to
+ the binary 'tests/test_rand' to check if the generated text also matches
+ the regex-pattern in the C implementation.
+ The exit-code of the testing program, is used to determine test success.
+
+ This script is called by the Makefile when doing 'make test'
+"""
+
+
+import re
+import sys
+import string
+import random
+from subprocess import call
+
+
+prog = "./tests/test_rand_neg"
+
+if len(sys.argv) < 2:
+ print("")
+ print("usage: %s pattern [nrepeat]" % sys.argv[0])
+ print(" where [nrepeat] is optional")
+ print("")
+ sys.exit(-1)
+
+own_prog = sys.argv[0]
+pattern = sys.argv[1]
+if len(sys.argv) > 2:
+ ntests = int(sys.argv[2])
+else:
+ ntests = 10
+nfails = 0
+repeats = ntests
+
+
+try:
+ repeats = int(sys.argv[2])
+except:
+ pass
+
+sys.stdout.write("%-35s" % (" pattern '%s': " % pattern))
+
+
+
+
+def gen_no_match(pattern, minlen=1, maxlen=50, maxattempts=500):
+ nattempts = 0
+ while True:
+ nattempts += 1
+ ret = "".join([random.choice(string.printable) for i in range(random.Random().randint(minlen, maxlen))])
+ if re.findall(pattern, ret) == []:
+ return ret
+ if nattempts >= maxattempts:
+ raise Exception("Could not generate string that did not match the regex pattern '%s' after %d attempts" % (pattern, nattempts))
+
+
+
+while repeats >= 0:
+ try:
+ repeats -= 1
+ example = gen_no_match(pattern)
+ #print("%s %s %s" % (prog, pattern, example))
+ ret = call([prog, "\"%s\"" % pattern, "\"%s\"" % example])
+ if ret != 0:
+ escaped = repr(example) # escapes special chars for better printing
+ print(" FAIL : matches %s unexpectedly [%s]." % (escaped, ", ".join([("0x%02x" % ord(e)) for e in example]) ))
+ nfails += 1
+
+ except:
+ #import traceback
+ #print("EXCEPTION!")
+ #raw_input(traceback.format_exc())
+ ntests -= 1
+ repeats += 1
+ #nfails += 1
+
+sys.stdout.write("%4d/%d tests succeeded \n" % (ntests - nfails, ntests))
+#print("")
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c
new file mode 100644
index 0000000000..ab54cc2ebe
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test1.c
@@ -0,0 +1,141 @@
+/*
+ * Testing various regex-patterns
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "re.h"
+
+
+#define OK ((char*) 1)
+#define NOK ((char*) 0)
+
+
+char* test_vector[][3] =
+{
+ { OK, "\\d", "5" },
+ { OK, "\\w+", "hej" },
+ { OK, "\\s", "\t \n" },
+ { NOK, "\\S", "\t \n" },
+ { OK, "[\\s]", "\t \n" },
+ { NOK, "[\\S]", "\t \n" },
+ { NOK, "\\D", "5" },
+ { NOK, "\\W+", "hej" },
+ { OK, "[0-9]+", "12345" },
+ { OK, "\\D", "hej" },
+ { NOK, "\\d", "hej" },
+ { OK, "[^\\w]", "\\" },
+ { OK, "[\\W]", "\\" },
+ { NOK, "[\\w]", "\\" },
+ { OK, "[^\\d]", "d" },
+ { NOK, "[\\d]", "d" },
+ { NOK, "[^\\D]", "d" },
+ { OK, "[\\D]", "d" },
+ { OK, "^.*\\\\.*$", "c:\\Tools" },
+ { OK, "^[\\+-]*[\\d]+$", "+27" },
+ { OK, "[abc]", "1c2" },
+ { NOK, "[abc]", "1C2" },
+ { OK, "[1-5]+", "0123456789" },
+ { OK, "[.2]", "1C2" },
+ { OK, "a*$", "Xaa" },
+ { OK, "a*$", "Xaa" },
+ { OK, "[a-h]+", "abcdefghxxx" },
+ { NOK, "[a-h]+", "ABCDEFGH" },
+ { OK, "[A-H]+", "ABCDEFGH" },
+ { NOK, "[A-H]+", "abcdefgh" },
+ { OK, "[^\\s]+", "abc def" },
+ { OK, "[^fc]+", "abc def" },
+ { OK, "[^d\\sf]+", "abc def" },
+ { OK, "\n", "abc\ndef" },
+ { OK, "b.\\s*\n", "aa\r\nbb\r\ncc\r\n\r\n" },
+ { OK, ".*c", "abcabc" },
+ { OK, ".+c", "abcabc" },
+ { OK, "[b-z].*", "ab" },
+ { OK, "b[k-z]*", "ab" },
+ { NOK, "[0-9]", " - " },
+ { OK, "[^0-9]", " - " },
+ { OK, "0|", "0|" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "0s:00:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "000:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:0000" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "100:0:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:100:00" },
+ { NOK, "\\d\\d:\\d\\d:\\d\\d", "0:00:100" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:0" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:00" },
+ { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:00" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello World !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world! " },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !" },
+ { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello World !" },
+ { NOK, "\\d\\d?:\\d\\d?:\\d\\d?", "a:0" }, /* Failing test case reported in https://github.com/kokke/tiny-regex-c/issues/12 */
+/*
+ { OK, "[^\\w][^-1-4]", ")T" },
+ { OK, "[^\\w][^-1-4]", ")^" },
+ { OK, "[^\\w][^-1-4]", "*)" },
+ { OK, "[^\\w][^-1-4]", "!." },
+ { OK, "[^\\w][^-1-4]", " x" },
+ { OK, "[^\\w][^-1-4]", "$b" },
+*/
+ { OK, ".?bar", "real_bar" },
+ { NOK, ".?bar", "real_foo" },
+ { NOK, "X?Y", "Z" },
+};
+
+
+void re_print(re_t);
+
+int main()
+{
+ char* text;
+ char* pattern;
+ int should_fail;
+ size_t ntests = sizeof(test_vector) / sizeof(*test_vector);
+ size_t nfailed = 0;
+ size_t i;
+
+ for (i = 0; i < ntests; ++i)
+ {
+ pattern = test_vector[i][1];
+ text = test_vector[i][2];
+ should_fail = (test_vector[i][0] == NOK);
+
+ int m = re_match(pattern, text);
+
+ if (should_fail)
+ {
+ if (m != (-1))
+ {
+ printf("\n");
+ re_print(re_compile(pattern));
+ fprintf(stderr, "[%lu/%lu]: pattern '%s' matched '%s' unexpectedly. \n", (i+1), ntests, pattern, text);
+ nfailed += 1;
+ }
+ }
+ else
+ {
+ if (m == (-1))
+ {
+ printf("\n");
+ re_print(re_compile(pattern));
+ fprintf(stderr, "[%lu/%lu]: pattern '%s' didn't match '%s' as expected. \n", (i+1), ntests, pattern, text);
+ nfailed += 1;
+ }
+ }
+ }
+
+ // printf("\n");
+ printf("%lu/%lu tests succeeded.\n", ntests - nfailed, ntests);
+ printf("\n");
+ printf("\n");
+ printf("\n");
+
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c
new file mode 100644
index 0000000000..f234f2d49a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test2.c
@@ -0,0 +1,2097 @@
+/*
+ * A small performance test, to see how the library performs on a few MBs of text.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "re.h"
+
+
+char buf[] = {
+ 0x79, 0x66, 0x75, 0x66, 0x63, 0x6f, 0x75, 0x62, 0x74, 0x77, 0x66, 0x6f, 0x69, 0x72, 0x71, 0x78,
+ 0x66, 0x69, 0x63, 0x72, 0x6c, 0x61, 0x6a, 0x79, 0x68, 0x6d, 0x67, 0x66, 0x67, 0x75, 0x69, 0x65,
+ 0x62, 0x6d, 0x72, 0x63, 0x67, 0x74, 0x64, 0x62, 0x67, 0x64, 0x6c, 0x66, 0x78, 0x68, 0x75, 0x6f,
+ 0x73, 0x6b, 0x68, 0x68, 0x64, 0x68, 0x71, 0x66, 0x79, 0x6a, 0x6a, 0x67, 0x70, 0x6e, 0x6f, 0x77,
+ 0x6e, 0x68, 0x79, 0x65, 0x76, 0x61, 0x73, 0x6e, 0x77, 0x71, 0x74, 0x64, 0x77, 0x76, 0x74, 0x6d,
+ 0x6e, 0x76, 0x79, 0x71, 0x79, 0x79, 0x6a, 0x63, 0x74, 0x75, 0x6f, 0x74, 0x6d, 0x66, 0x67, 0x64,
+ 0x77, 0x61, 0x75, 0x79, 0x72, 0x79, 0x69, 0x73, 0x6f, 0x69, 0x67, 0x65, 0x6a, 0x6b, 0x66, 0x69,
+ 0x63, 0x79, 0x64, 0x71, 0x68, 0x6c, 0x67, 0x66, 0x6c, 0x6e, 0x79, 0x64, 0x67, 0x76, 0x64, 0x6a,
+ 0x73, 0x64, 0x6a, 0x61, 0x79, 0x6d, 0x71, 0x72, 0x68, 0x67, 0x68, 0x67, 0x66, 0x64, 0x63, 0x77,
+ 0x6f, 0x73, 0x67, 0x6d, 0x63, 0x74, 0x77, 0x72, 0x6b, 0x6f, 0x6f, 0x63, 0x68, 0x6f, 0x66, 0x67,
+ 0x73, 0x66, 0x6f, 0x75, 0x77, 0x62, 0x71, 0x6b, 0x77, 0x72, 0x68, 0x6d, 0x66, 0x71, 0x78, 0x75,
+ 0x6a, 0x74, 0x75, 0x6c, 0x69, 0x62, 0x64, 0x61, 0x78, 0x78, 0x66, 0x71, 0x61, 0x6d, 0x76, 0x76,
+ 0x6f, 0x65, 0x68, 0x78, 0x70, 0x79, 0x65, 0x68, 0x76, 0x62, 0x73, 0x6f, 0x64, 0x76, 0x63, 0x64,
+ 0x74, 0x73, 0x6e, 0x6a, 0x6a, 0x61, 0x72, 0x62, 0x66, 0x76, 0x63, 0x71, 0x6d, 0x74, 0x67, 0x75,
+ 0x6a, 0x6b, 0x6a, 0x6e, 0x75, 0x72, 0x73, 0x6d, 0x65, 0x69, 0x79, 0x62, 0x6d, 0x67, 0x63, 0x63,
+ 0x6c, 0x6c, 0x67, 0x6e, 0x6b, 0x76, 0x6e, 0x61, 0x79, 0x6a, 0x62, 0x6d, 0x70, 0x69, 0x67, 0x68,
+ 0x70, 0x74, 0x6b, 0x70, 0x74, 0x6e, 0x62, 0x6f, 0x6e, 0x6e, 0x61, 0x76, 0x68, 0x78, 0x77, 0x6f,
+ 0x77, 0x76, 0x6d, 0x6d, 0x75, 0x6b, 0x73, 0x6e, 0x79, 0x69, 0x6f, 0x72, 0x6a, 0x78, 0x67, 0x65,
+ 0x6b, 0x71, 0x61, 0x6b, 0x72, 0x75, 0x69, 0x65, 0x6b, 0x70, 0x69, 0x72, 0x70, 0x76, 0x79, 0x76,
+ 0x72, 0x76, 0x69, 0x76, 0x67, 0x66, 0x61, 0x72, 0x6c, 0x71, 0x66, 0x79, 0x74, 0x78, 0x6c, 0x6e,
+ 0x64, 0x68, 0x61, 0x66, 0x6f, 0x76, 0x6e, 0x67, 0x63, 0x78, 0x61, 0x75, 0x73, 0x65, 0x69, 0x77,
+ 0x67, 0x75, 0x77, 0x62, 0x75, 0x6b, 0x70, 0x77, 0x75, 0x69, 0x70, 0x62, 0x76, 0x73, 0x71, 0x70,
+ 0x78, 0x71, 0x77, 0x78, 0x66, 0x6e, 0x75, 0x65, 0x76, 0x6b, 0x79, 0x6d, 0x74, 0x61, 0x68, 0x61,
+ 0x78, 0x6e, 0x74, 0x77, 0x63, 0x79, 0x6e, 0x6d, 0x77, 0x71, 0x6f, 0x66, 0x6f, 0x65, 0x65, 0x72,
+ 0x61, 0x76, 0x64, 0x64, 0x67, 0x77, 0x69, 0x78, 0x6a, 0x75, 0x67, 0x66, 0x63, 0x79, 0x65, 0x6d,
+ 0x62, 0x68, 0x72, 0x6e, 0x78, 0x6d, 0x71, 0x6c, 0x6e, 0x6a, 0x62, 0x62, 0x64, 0x75, 0x6c, 0x6d,
+ 0x61, 0x71, 0x67, 0x6d, 0x68, 0x6f, 0x67, 0x71, 0x69, 0x74, 0x75, 0x78, 0x69, 0x6c, 0x65, 0x79,
+ 0x6e, 0x65, 0x76, 0x61, 0x75, 0x6a, 0x65, 0x73, 0x68, 0x63, 0x62, 0x6d, 0x6b, 0x6d, 0x6f, 0x6d,
+ 0x6c, 0x69, 0x79, 0x69, 0x6e, 0x76, 0x74, 0x66, 0x6f, 0x63, 0x6c, 0x6e, 0x6a, 0x75, 0x70, 0x70,
+ 0x62, 0x78, 0x71, 0x63, 0x6d, 0x6e, 0x67, 0x6e, 0x66, 0x65, 0x72, 0x62, 0x62, 0x74, 0x78, 0x77,
+ 0x6f, 0x69, 0x66, 0x75, 0x76, 0x79, 0x6c, 0x73, 0x62, 0x6e, 0x6c, 0x70, 0x6a, 0x76, 0x6c, 0x78,
+ 0x6e, 0x66, 0x6f, 0x68, 0x65, 0x6c, 0x75, 0x70, 0x62, 0x62, 0x78, 0x61, 0x62, 0x6b, 0x61, 0x6e,
+ 0x61, 0x78, 0x71, 0x71, 0x73, 0x6e, 0x6c, 0x6f, 0x6a, 0x67, 0x61, 0x65, 0x61, 0x6b, 0x74, 0x78,
+ 0x70, 0x67, 0x73, 0x65, 0x78, 0x79, 0x74, 0x76, 0x6f, 0x74, 0x73, 0x6f, 0x63, 0x76, 0x63, 0x70,
+ 0x66, 0x6d, 0x6a, 0x71, 0x62, 0x78, 0x68, 0x74, 0x71, 0x70, 0x69, 0x65, 0x77, 0x65, 0x77, 0x65,
+ 0x78, 0x6a, 0x72, 0x71, 0x6e, 0x65, 0x79, 0x64, 0x6b, 0x73, 0x79, 0x63, 0x64, 0x6b, 0x78, 0x64,
+ 0x66, 0x69, 0x77, 0x62, 0x6f, 0x71, 0x6c, 0x73, 0x6c, 0x68, 0x78, 0x70, 0x71, 0x79, 0x72, 0x79,
+ 0x6f, 0x6b, 0x6c, 0x77, 0x63, 0x6a, 0x68, 0x67, 0x69, 0x70, 0x68, 0x6e, 0x61, 0x71, 0x77, 0x61,
+ 0x70, 0x65, 0x76, 0x6c, 0x72, 0x71, 0x66, 0x66, 0x6e, 0x67, 0x71, 0x6c, 0x77, 0x63, 0x6c, 0x65,
+ 0x6b, 0x72, 0x66, 0x63, 0x73, 0x61, 0x70, 0x71, 0x64, 0x69, 0x6e, 0x71, 0x78, 0x6c, 0x74, 0x61,
+ 0x77, 0x65, 0x71, 0x61, 0x64, 0x66, 0x68, 0x67, 0x6f, 0x75, 0x66, 0x69, 0x73, 0x6e, 0x76, 0x6e,
+ 0x66, 0x63, 0x66, 0x74, 0x6c, 0x6b, 0x68, 0x62, 0x6e, 0x6c, 0x78, 0x65, 0x73, 0x70, 0x76, 0x78,
+ 0x61, 0x70, 0x66, 0x70, 0x79, 0x67, 0x69, 0x67, 0x68, 0x78, 0x70, 0x76, 0x73, 0x63, 0x6b, 0x68,
+ 0x71, 0x67, 0x66, 0x6c, 0x78, 0x77, 0x6c, 0x72, 0x76, 0x6c, 0x73, 0x68, 0x68, 0x61, 0x68, 0x6c,
+ 0x6c, 0x79, 0x6a, 0x66, 0x74, 0x77, 0x65, 0x62, 0x6e, 0x71, 0x6b, 0x61, 0x71, 0x75, 0x67, 0x62,
+ 0x76, 0x62, 0x6b, 0x71, 0x71, 0x6b, 0x65, 0x69, 0x74, 0x75, 0x79, 0x77, 0x6c, 0x65, 0x64, 0x67,
+ 0x70, 0x6a, 0x71, 0x73, 0x6c, 0x73, 0x61, 0x69, 0x70, 0x68, 0x73, 0x76, 0x74, 0x69, 0x77, 0x69,
+ 0x6b, 0x6b, 0x70, 0x68, 0x78, 0x79, 0x67, 0x79, 0x62, 0x63, 0x74, 0x68, 0x69, 0x63, 0x77, 0x70,
+ 0x6b, 0x6a, 0x74, 0x6a, 0x66, 0x75, 0x75, 0x73, 0x65, 0x68, 0x63, 0x6a, 0x64, 0x76, 0x64, 0x72,
+ 0x79, 0x77, 0x6d, 0x74, 0x69, 0x6a, 0x66, 0x65, 0x67, 0x77, 0x67, 0x73, 0x73, 0x74, 0x6a, 0x75,
+ 0x65, 0x6e, 0x72, 0x61, 0x63, 0x6e, 0x67, 0x74, 0x6d, 0x6d, 0x65, 0x76, 0x76, 0x65, 0x61, 0x64,
+ 0x70, 0x77, 0x78, 0x72, 0x77, 0x70, 0x6f, 0x69, 0x66, 0x71, 0x6e, 0x6c, 0x75, 0x68, 0x68, 0x6f,
+ 0x63, 0x6f, 0x6d, 0x62, 0x61, 0x70, 0x72, 0x77, 0x6c, 0x76, 0x6b, 0x70, 0x68, 0x6e, 0x6d, 0x78,
+ 0x68, 0x65, 0x6c, 0x6f, 0x65, 0x73, 0x63, 0x69, 0x65, 0x74, 0x77, 0x66, 0x64, 0x74, 0x68, 0x66,
+ 0x71, 0x62, 0x67, 0x72, 0x6a, 0x64, 0x68, 0x77, 0x6d, 0x72, 0x6b, 0x78, 0x72, 0x6b, 0x72, 0x78,
+ 0x70, 0x65, 0x69, 0x65, 0x72, 0x63, 0x6a, 0x69, 0x62, 0x75, 0x72, 0x6b, 0x69, 0x6a, 0x78, 0x65,
+ 0x68, 0x69, 0x6d, 0x6a, 0x75, 0x72, 0x66, 0x76, 0x79, 0x66, 0x67, 0x74, 0x79, 0x69, 0x76, 0x6d,
+ 0x77, 0x77, 0x65, 0x6b, 0x73, 0x61, 0x73, 0x6c, 0x70, 0x74, 0x6f, 0x76, 0x79, 0x6a, 0x66, 0x64,
+ 0x67, 0x6d, 0x72, 0x66, 0x66, 0x6e, 0x68, 0x63, 0x64, 0x73, 0x6c, 0x63, 0x6c, 0x71, 0x6d, 0x6c,
+ 0x63, 0x69, 0x65, 0x6b, 0x73, 0x64, 0x6b, 0x76, 0x68, 0x73, 0x71, 0x61, 0x72, 0x67, 0x6a, 0x79,
+ 0x78, 0x6a, 0x6f, 0x65, 0x71, 0x65, 0x72, 0x66, 0x62, 0x6d, 0x63, 0x73, 0x64, 0x77, 0x78, 0x77,
+ 0x6f, 0x67, 0x70, 0x6d, 0x6f, 0x66, 0x79, 0x79, 0x74, 0x75, 0x65, 0x75, 0x6e, 0x72, 0x76, 0x72,
+ 0x6a, 0x77, 0x78, 0x6e, 0x6e, 0x67, 0x74, 0x6d, 0x6f, 0x66, 0x6f, 0x6a, 0x67, 0x65, 0x6c, 0x75,
+ 0x63, 0x78, 0x6c, 0x67, 0x78, 0x79, 0x72, 0x61, 0x6b, 0x71, 0x76, 0x77, 0x6d, 0x6d, 0x69, 0x76,
+ 0x6f, 0x6e, 0x66, 0x61, 0x61, 0x62, 0x62, 0x79, 0x61, 0x66, 0x6d, 0x78, 0x74, 0x78, 0x69, 0x6f,
+ 0x63, 0x79, 0x70, 0x74, 0x75, 0x79, 0x63, 0x63, 0x70, 0x6d, 0x70, 0x66, 0x67, 0x65, 0x61, 0x70,
+ 0x79, 0x79, 0x6d, 0x6a, 0x6d, 0x77, 0x6a, 0x75, 0x62, 0x6b, 0x69, 0x64, 0x71, 0x67, 0x64, 0x63,
+ 0x62, 0x68, 0x6c, 0x68, 0x72, 0x6a, 0x67, 0x6d, 0x72, 0x73, 0x78, 0x72, 0x64, 0x65, 0x77, 0x72,
+ 0x72, 0x71, 0x6a, 0x64, 0x70, 0x71, 0x76, 0x73, 0x65, 0x75, 0x6d, 0x76, 0x6f, 0x6b, 0x66, 0x73,
+ 0x6b, 0x69, 0x6b, 0x66, 0x68, 0x79, 0x64, 0x78, 0x6b, 0x67, 0x76, 0x6e, 0x74, 0x70, 0x67, 0x69,
+ 0x62, 0x64, 0x72, 0x6a, 0x6e, 0x72, 0x72, 0x6b, 0x73, 0x6f, 0x6d, 0x6c, 0x6c, 0x66, 0x78, 0x75,
+ 0x66, 0x66, 0x6e, 0x6b, 0x69, 0x74, 0x6c, 0x79, 0x69, 0x70, 0x6c, 0x72, 0x6e, 0x74, 0x6e, 0x6e,
+ 0x67, 0x6f, 0x62, 0x70, 0x65, 0x62, 0x62, 0x6a, 0x68, 0x66, 0x76, 0x6a, 0x74, 0x6a, 0x69, 0x71,
+ 0x6c, 0x76, 0x77, 0x6b, 0x6b, 0x73, 0x6f, 0x64, 0x62, 0x67, 0x6a, 0x6b, 0x68, 0x65, 0x61, 0x69,
+ 0x77, 0x78, 0x74, 0x62, 0x70, 0x73, 0x70, 0x68, 0x6b, 0x75, 0x72, 0x70, 0x68, 0x6b, 0x75, 0x6d,
+ 0x62, 0x74, 0x77, 0x65, 0x71, 0x77, 0x6d, 0x65, 0x70, 0x66, 0x70, 0x6c, 0x69, 0x68, 0x75, 0x75,
+ 0x62, 0x62, 0x75, 0x69, 0x6d, 0x68, 0x63, 0x6e, 0x6d, 0x6b, 0x78, 0x70, 0x77, 0x61, 0x77, 0x69,
+ 0x78, 0x69, 0x6a, 0x68, 0x64, 0x74, 0x6e, 0x72, 0x6c, 0x6d, 0x72, 0x75, 0x6e, 0x66, 0x66, 0x67,
+ 0x6f, 0x73, 0x6e, 0x6f, 0x78, 0x72, 0x72, 0x6c, 0x66, 0x70, 0x66, 0x69, 0x6a, 0x71, 0x6e, 0x67,
+ 0x6c, 0x74, 0x76, 0x78, 0x74, 0x77, 0x73, 0x66, 0x73, 0x75, 0x70, 0x67, 0x67, 0x62, 0x75, 0x77,
+ 0x67, 0x78, 0x76, 0x6d, 0x6a, 0x78, 0x69, 0x75, 0x63, 0x6a, 0x62, 0x6e, 0x6d, 0x66, 0x61, 0x6b,
+ 0x73, 0x68, 0x72, 0x78, 0x79, 0x6d, 0x69, 0x68, 0x6f, 0x73, 0x6d, 0x62, 0x68, 0x70, 0x78, 0x6f,
+ 0x6e, 0x63, 0x63, 0x6f, 0x78, 0x65, 0x74, 0x6a, 0x77, 0x78, 0x79, 0x6a, 0x62, 0x78, 0x70, 0x69,
+ 0x65, 0x79, 0x77, 0x6d, 0x6b, 0x6c, 0x6b, 0x62, 0x6e, 0x75, 0x73, 0x6e, 0x63, 0x67, 0x6c, 0x63,
+ 0x63, 0x6a, 0x71, 0x62, 0x61, 0x78, 0x6c, 0x6d, 0x69, 0x64, 0x6f, 0x71, 0x62, 0x6f, 0x62, 0x72,
+ 0x6b, 0x61, 0x65, 0x77, 0x62, 0x75, 0x79, 0x6e, 0x73, 0x79, 0x67, 0x76, 0x63, 0x70, 0x69, 0x6a,
+ 0x6c, 0x62, 0x70, 0x62, 0x61, 0x77, 0x69, 0x6e, 0x62, 0x71, 0x6e, 0x6b, 0x72, 0x76, 0x76, 0x64,
+ 0x77, 0x68, 0x66, 0x78, 0x77, 0x64, 0x6b, 0x6a, 0x74, 0x66, 0x67, 0x69, 0x70, 0x65, 0x6a, 0x68,
+ 0x71, 0x76, 0x71, 0x69, 0x75, 0x64, 0x6b, 0x71, 0x71, 0x61, 0x67, 0x6d, 0x67, 0x62, 0x69, 0x6b,
+ 0x61, 0x66, 0x62, 0x63, 0x76, 0x64, 0x66, 0x73, 0x63, 0x78, 0x74, 0x77, 0x6e, 0x63, 0x68, 0x61,
+ 0x6c, 0x71, 0x70, 0x69, 0x65, 0x77, 0x6d, 0x69, 0x72, 0x6b, 0x6e, 0x6f, 0x75, 0x78, 0x6d, 0x74,
+ 0x6a, 0x68, 0x72, 0x6b, 0x69, 0x72, 0x6b, 0x78, 0x72, 0x68, 0x65, 0x68, 0x76, 0x6d, 0x67, 0x71,
+ 0x65, 0x6f, 0x61, 0x72, 0x6f, 0x63, 0x70, 0x67, 0x78, 0x73, 0x6f, 0x62, 0x6b, 0x65, 0x64, 0x6b,
+ 0x62, 0x76, 0x76, 0x77, 0x63, 0x6a, 0x77, 0x78, 0x6e, 0x79, 0x63, 0x75, 0x62, 0x75, 0x67, 0x67,
+ 0x62, 0x6a, 0x6a, 0x74, 0x6f, 0x6a, 0x6c, 0x75, 0x70, 0x65, 0x61, 0x6f, 0x68, 0x6b, 0x67, 0x6e,
+ 0x6a, 0x6c, 0x69, 0x71, 0x63, 0x75, 0x6a, 0x72, 0x71, 0x6d, 0x75, 0x73, 0x78, 0x62, 0x78, 0x66,
+ 0x79, 0x79, 0x70, 0x6e, 0x75, 0x6c, 0x66, 0x6e, 0x6b, 0x6b, 0x73, 0x71, 0x6d, 0x77, 0x68, 0x6d,
+ 0x76, 0x75, 0x6c, 0x77, 0x6f, 0x71, 0x6c, 0x67, 0x70, 0x6f, 0x76, 0x70, 0x72, 0x64, 0x74, 0x77,
+ 0x63, 0x78, 0x69, 0x73, 0x6f, 0x77, 0x6f, 0x6a, 0x64, 0x64, 0x77, 0x6b, 0x79, 0x72, 0x65, 0x6f,
+ 0x74, 0x61, 0x71, 0x71, 0x6d, 0x6d, 0x68, 0x66, 0x63, 0x6d, 0x73, 0x76, 0x6f, 0x76, 0x68, 0x6c,
+ 0x61, 0x6b, 0x76, 0x6a, 0x69, 0x73, 0x76, 0x72, 0x6c, 0x63, 0x78, 0x74, 0x73, 0x78, 0x62, 0x74,
+ 0x70, 0x68, 0x62, 0x6a, 0x70, 0x66, 0x74, 0x66, 0x61, 0x66, 0x72, 0x74, 0x6f, 0x67, 0x76, 0x67,
+ 0x6c, 0x64, 0x73, 0x69, 0x6d, 0x69, 0x6a, 0x74, 0x67, 0x73, 0x78, 0x69, 0x75, 0x72, 0x6e, 0x61,
+ 0x76, 0x6a, 0x69, 0x73, 0x6a, 0x77, 0x71, 0x67, 0x66, 0x6e, 0x66, 0x74, 0x71, 0x6a, 0x6f, 0x64,
+ 0x63, 0x6c, 0x67, 0x70, 0x77, 0x78, 0x64, 0x6f, 0x6b, 0x69, 0x76, 0x75, 0x66, 0x6c, 0x6d, 0x6d,
+ 0x65, 0x75, 0x65, 0x75, 0x77, 0x6a, 0x72, 0x6b, 0x77, 0x67, 0x65, 0x63, 0x6f, 0x6e, 0x6d, 0x6a,
+ 0x6d, 0x66, 0x78, 0x66, 0x66, 0x76, 0x61, 0x6f, 0x74, 0x6f, 0x63, 0x75, 0x79, 0x74, 0x61, 0x6a,
+ 0x64, 0x62, 0x75, 0x79, 0x77, 0x6a, 0x68, 0x76, 0x67, 0x61, 0x74, 0x64, 0x6d, 0x64, 0x75, 0x65,
+ 0x64, 0x6c, 0x70, 0x75, 0x68, 0x6c, 0x70, 0x74, 0x70, 0x71, 0x6a, 0x6d, 0x61, 0x79, 0x67, 0x76,
+ 0x69, 0x6c, 0x6a, 0x63, 0x6b, 0x69, 0x77, 0x63, 0x77, 0x63, 0x61, 0x72, 0x66, 0x76, 0x69, 0x78,
+ 0x72, 0x68, 0x72, 0x75, 0x74, 0x63, 0x64, 0x6a, 0x73, 0x79, 0x70, 0x6f, 0x69, 0x65, 0x6b, 0x73,
+ 0x71, 0x70, 0x6b, 0x68, 0x61, 0x68, 0x79, 0x62, 0x75, 0x65, 0x79, 0x68, 0x65, 0x6f, 0x67, 0x70,
+ 0x77, 0x78, 0x6f, 0x77, 0x72, 0x65, 0x75, 0x73, 0x6a, 0x64, 0x6c, 0x75, 0x6e, 0x65, 0x76, 0x78,
+ 0x73, 0x6a, 0x69, 0x6d, 0x61, 0x6d, 0x6a, 0x6d, 0x71, 0x6d, 0x71, 0x6b, 0x64, 0x6c, 0x65, 0x61,
+ 0x67, 0x6a, 0x6c, 0x64, 0x78, 0x64, 0x64, 0x78, 0x75, 0x73, 0x70, 0x76, 0x69, 0x66, 0x6e, 0x74,
+ 0x72, 0x73, 0x79, 0x78, 0x72, 0x74, 0x6b, 0x78, 0x71, 0x63, 0x62, 0x72, 0x75, 0x6d, 0x68, 0x63,
+ 0x69, 0x78, 0x6d, 0x71, 0x68, 0x6d, 0x64, 0x65, 0x75, 0x70, 0x6b, 0x73, 0x63, 0x72, 0x69, 0x75,
+ 0x77, 0x76, 0x67, 0x72, 0x75, 0x6c, 0x76, 0x65, 0x6e, 0x70, 0x63, 0x6f, 0x66, 0x71, 0x65, 0x62,
+ 0x73, 0x64, 0x70, 0x63, 0x78, 0x74, 0x6f, 0x73, 0x75, 0x6f, 0x78, 0x73, 0x71, 0x6e, 0x77, 0x75,
+ 0x70, 0x76, 0x74, 0x75, 0x61, 0x70, 0x68, 0x72, 0x71, 0x79, 0x6d, 0x72, 0x69, 0x79, 0x69, 0x74,
+ 0x61, 0x72, 0x75, 0x68, 0x72, 0x79, 0x62, 0x65, 0x6d, 0x61, 0x71, 0x6c, 0x77, 0x67, 0x73, 0x66,
+ 0x67, 0x69, 0x6e, 0x62, 0x6c, 0x74, 0x76, 0x70, 0x71, 0x64, 0x71, 0x79, 0x70, 0x73, 0x63, 0x6c,
+ 0x67, 0x63, 0x73, 0x6f, 0x68, 0x78, 0x73, 0x66, 0x6c, 0x6b, 0x62, 0x78, 0x70, 0x68, 0x62, 0x6c,
+ 0x66, 0x67, 0x74, 0x77, 0x61, 0x6c, 0x71, 0x70, 0x6a, 0x6f, 0x6c, 0x75, 0x6e, 0x61, 0x79, 0x68,
+ 0x73, 0x61, 0x79, 0x69, 0x71, 0x6e, 0x6d, 0x66, 0x6c, 0x68, 0x74, 0x72, 0x64, 0x64, 0x6a, 0x75,
+ 0x70, 0x6c, 0x69, 0x69, 0x68, 0x72, 0x66, 0x75, 0x61, 0x61, 0x69, 0x64, 0x66, 0x69, 0x68, 0x63,
+ 0x67, 0x6d, 0x76, 0x71, 0x6a, 0x6e, 0x6a, 0x78, 0x61, 0x77, 0x67, 0x77, 0x69, 0x77, 0x71, 0x6d,
+ 0x6b, 0x6a, 0x72, 0x71, 0x72, 0x6e, 0x61, 0x65, 0x6f, 0x71, 0x6f, 0x64, 0x6d, 0x79, 0x61, 0x66,
+ 0x6b, 0x77, 0x76, 0x64, 0x67, 0x6f, 0x66, 0x6e, 0x79, 0x64, 0x77, 0x75, 0x6d, 0x76, 0x64, 0x69,
+ 0x78, 0x6b, 0x72, 0x6f, 0x75, 0x74, 0x71, 0x6c, 0x77, 0x61, 0x6d, 0x69, 0x61, 0x75, 0x72, 0x61,
+ 0x6d, 0x78, 0x6c, 0x71, 0x6e, 0x74, 0x6c, 0x6e, 0x71, 0x78, 0x77, 0x6f, 0x64, 0x76, 0x66, 0x62,
+ 0x62, 0x75, 0x6b, 0x75, 0x66, 0x75, 0x73, 0x6a, 0x76, 0x75, 0x70, 0x73, 0x68, 0x6c, 0x77, 0x6d,
+ 0x75, 0x71, 0x78, 0x73, 0x64, 0x73, 0x78, 0x69, 0x76, 0x64, 0x61, 0x6e, 0x62, 0x6a, 0x79, 0x6f,
+ 0x70, 0x6a, 0x6d, 0x75, 0x6e, 0x75, 0x63, 0x66, 0x65, 0x75, 0x6a, 0x64, 0x6b, 0x65, 0x66, 0x64,
+ 0x62, 0x66, 0x78, 0x67, 0x77, 0x75, 0x74, 0x62, 0x67, 0x61, 0x6e, 0x67, 0x68, 0x6b, 0x6c, 0x73,
+ 0x6d, 0x69, 0x69, 0x75, 0x6e, 0x6c, 0x69, 0x73, 0x77, 0x69, 0x63, 0x64, 0x75, 0x6c, 0x6b, 0x78,
+ 0x69, 0x78, 0x67, 0x73, 0x72, 0x6a, 0x64, 0x78, 0x6a, 0x72, 0x78, 0x6f, 0x61, 0x76, 0x71, 0x66,
+ 0x76, 0x63, 0x77, 0x67, 0x75, 0x65, 0x73, 0x64, 0x64, 0x6a, 0x77, 0x68, 0x6f, 0x61, 0x6f, 0x78,
+ 0x6e, 0x6d, 0x78, 0x70, 0x76, 0x6b, 0x67, 0x67, 0x75, 0x6d, 0x6e, 0x62, 0x70, 0x74, 0x66, 0x6a,
+ 0x70, 0x68, 0x70, 0x6c, 0x74, 0x6d, 0x62, 0x66, 0x69, 0x73, 0x6a, 0x70, 0x66, 0x69, 0x6b, 0x73,
+ 0x62, 0x70, 0x6f, 0x61, 0x63, 0x63, 0x71, 0x6f, 0x70, 0x6a, 0x75, 0x64, 0x6d, 0x74, 0x68, 0x76,
+ 0x61, 0x6e, 0x72, 0x71, 0x62, 0x70, 0x64, 0x66, 0x70, 0x71, 0x61, 0x69, 0x68, 0x64, 0x61, 0x65,
+ 0x79, 0x78, 0x6b, 0x72, 0x64, 0x63, 0x6d, 0x6c, 0x67, 0x61, 0x6a, 0x67, 0x64, 0x73, 0x70, 0x76,
+ 0x74, 0x70, 0x6c, 0x66, 0x77, 0x71, 0x66, 0x72, 0x6c, 0x67, 0x69, 0x66, 0x70, 0x68, 0x67, 0x6e,
+ 0x70, 0x69, 0x67, 0x71, 0x73, 0x72, 0x6d, 0x71, 0x74, 0x64, 0x6b, 0x71, 0x73, 0x70, 0x78, 0x70,
+ 0x75, 0x76, 0x74, 0x6e, 0x73, 0x63, 0x6d, 0x64, 0x6e, 0x73, 0x79, 0x68, 0x6b, 0x6e, 0x6a, 0x77,
+ 0x6e, 0x62, 0x67, 0x6d, 0x72, 0x68, 0x6c, 0x76, 0x64, 0x6f, 0x73, 0x6d, 0x62, 0x67, 0x69, 0x68,
+ 0x64, 0x65, 0x72, 0x67, 0x75, 0x66, 0x68, 0x64, 0x61, 0x67, 0x6a, 0x75, 0x75, 0x6c, 0x72, 0x71,
+ 0x75, 0x74, 0x73, 0x78, 0x74, 0x6d, 0x62, 0x79, 0x6f, 0x63, 0x6f, 0x62, 0x67, 0x73, 0x70, 0x70,
+ 0x72, 0x6e, 0x68, 0x79, 0x76, 0x77, 0x76, 0x6e, 0x6e, 0x6e, 0x78, 0x65, 0x65, 0x63, 0x64, 0x61,
+ 0x62, 0x78, 0x71, 0x72, 0x63, 0x68, 0x71, 0x63, 0x73, 0x70, 0x70, 0x61, 0x71, 0x76, 0x64, 0x67,
+ 0x74, 0x71, 0x74, 0x67, 0x74, 0x77, 0x6e, 0x79, 0x73, 0x67, 0x79, 0x70, 0x77, 0x6f, 0x6b, 0x79,
+ 0x74, 0x79, 0x67, 0x6a, 0x6e, 0x6a, 0x70, 0x76, 0x62, 0x77, 0x74, 0x70, 0x72, 0x63, 0x6c, 0x74,
+ 0x65, 0x64, 0x63, 0x78, 0x64, 0x6d, 0x73, 0x69, 0x65, 0x70, 0x6d, 0x71, 0x6e, 0x6c, 0x62, 0x64,
+ 0x67, 0x6c, 0x72, 0x65, 0x6b, 0x63, 0x77, 0x6d, 0x63, 0x70, 0x72, 0x65, 0x6d, 0x74, 0x6d, 0x63,
+ 0x78, 0x75, 0x68, 0x70, 0x73, 0x63, 0x6e, 0x77, 0x71, 0x61, 0x73, 0x6b, 0x75, 0x6e, 0x76, 0x74,
+ 0x63, 0x70, 0x72, 0x65, 0x62, 0x75, 0x61, 0x6a, 0x76, 0x66, 0x75, 0x73, 0x64, 0x67, 0x6d, 0x77,
+ 0x64, 0x6a, 0x66, 0x6b, 0x6b, 0x6c, 0x66, 0x64, 0x69, 0x72, 0x70, 0x76, 0x67, 0x74, 0x6c, 0x6a,
+ 0x72, 0x67, 0x74, 0x66, 0x6a, 0x62, 0x66, 0x73, 0x79, 0x77, 0x6b, 0x62, 0x77, 0x6a, 0x75, 0x73,
+ 0x61, 0x69, 0x78, 0x6b, 0x66, 0x67, 0x62, 0x70, 0x65, 0x64, 0x76, 0x79, 0x77, 0x6c, 0x75, 0x74,
+ 0x77, 0x63, 0x73, 0x62, 0x63, 0x73, 0x6f, 0x6c, 0x64, 0x65, 0x6e, 0x6c, 0x6a, 0x72, 0x77, 0x6b,
+ 0x61, 0x78, 0x75, 0x77, 0x6c, 0x61, 0x64, 0x63, 0x64, 0x6a, 0x61, 0x68, 0x6e, 0x65, 0x68, 0x66,
+ 0x71, 0x6b, 0x73, 0x6f, 0x62, 0x6e, 0x69, 0x72, 0x79, 0x63, 0x6f, 0x79, 0x79, 0x75, 0x70, 0x77,
+ 0x75, 0x76, 0x63, 0x77, 0x76, 0x75, 0x70, 0x68, 0x65, 0x68, 0x63, 0x6c, 0x6d, 0x62, 0x73, 0x68,
+ 0x70, 0x68, 0x6b, 0x70, 0x63, 0x70, 0x79, 0x66, 0x68, 0x70, 0x72, 0x77, 0x65, 0x6e, 0x65, 0x74,
+ 0x62, 0x64, 0x74, 0x78, 0x6d, 0x70, 0x79, 0x74, 0x74, 0x6f, 0x6e, 0x6d, 0x78, 0x73, 0x6d, 0x6a,
+ 0x74, 0x6d, 0x75, 0x6f, 0x79, 0x66, 0x6c, 0x6b, 0x62, 0x68, 0x77, 0x67, 0x75, 0x71, 0x6e, 0x6d,
+ 0x6b, 0x63, 0x6a, 0x6a, 0x78, 0x78, 0x70, 0x68, 0x74, 0x72, 0x74, 0x65, 0x6f, 0x77, 0x6c, 0x72,
+ 0x74, 0x6c, 0x66, 0x78, 0x77, 0x78, 0x72, 0x73, 0x6f, 0x78, 0x6d, 0x66, 0x63, 0x61, 0x63, 0x6e,
+ 0x69, 0x73, 0x79, 0x72, 0x6f, 0x72, 0x62, 0x76, 0x75, 0x71, 0x70, 0x61, 0x78, 0x77, 0x76, 0x70,
+ 0x78, 0x69, 0x77, 0x65, 0x75, 0x63, 0x71, 0x6f, 0x6b, 0x6a, 0x74, 0x61, 0x63, 0x61, 0x64, 0x72,
+ 0x64, 0x62, 0x67, 0x72, 0x79, 0x6b, 0x74, 0x70, 0x6e, 0x6b, 0x63, 0x79, 0x66, 0x74, 0x6c, 0x6e,
+ 0x78, 0x6c, 0x70, 0x73, 0x6c, 0x69, 0x66, 0x6d, 0x71, 0x77, 0x79, 0x76, 0x71, 0x6e, 0x67, 0x78,
+ 0x74, 0x6b, 0x6b, 0x64, 0x6f, 0x63, 0x77, 0x61, 0x75, 0x6c, 0x6a, 0x6d, 0x6e, 0x72, 0x66, 0x6a,
+ 0x76, 0x75, 0x71, 0x6f, 0x6d, 0x71, 0x77, 0x75, 0x70, 0x77, 0x76, 0x74, 0x70, 0x71, 0x6b, 0x63,
+ 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x68, 0x61, 0x71, 0x6a, 0x71, 0x75, 0x65, 0x69, 0x6b, 0x63, 0x76,
+ 0x79, 0x6b, 0x76, 0x62, 0x77, 0x76, 0x73, 0x78, 0x6d, 0x76, 0x66, 0x6f, 0x69, 0x64, 0x72, 0x6d,
+ 0x6c, 0x62, 0x71, 0x61, 0x72, 0x6c, 0x6c, 0x70, 0x64, 0x69, 0x76, 0x6e, 0x62, 0x62, 0x69, 0x75,
+ 0x64, 0x73, 0x6b, 0x76, 0x76, 0x67, 0x6b, 0x78, 0x76, 0x70, 0x78, 0x70, 0x68, 0x67, 0x72, 0x6e,
+ 0x6b, 0x68, 0x6f, 0x73, 0x69, 0x6d, 0x71, 0x6b, 0x74, 0x6f, 0x79, 0x73, 0x65, 0x78, 0x6c, 0x67,
+ 0x74, 0x73, 0x6f, 0x62, 0x61, 0x73, 0x61, 0x63, 0x62, 0x74, 0x74, 0x6c, 0x68, 0x70, 0x76, 0x77,
+ 0x6a, 0x6a, 0x6a, 0x6f, 0x77, 0x6f, 0x66, 0x68, 0x78, 0x67, 0x64, 0x73, 0x75, 0x69, 0x6d, 0x69,
+ 0x62, 0x6b, 0x77, 0x63, 0x69, 0x6f, 0x61, 0x72, 0x61, 0x6f, 0x61, 0x77, 0x6a, 0x63, 0x69, 0x70,
+ 0x62, 0x79, 0x61, 0x61, 0x66, 0x71, 0x75, 0x72, 0x75, 0x61, 0x69, 0x6c, 0x78, 0x63, 0x78, 0x6c,
+ 0x74, 0x78, 0x6f, 0x78, 0x6a, 0x76, 0x72, 0x61, 0x69, 0x68, 0x6c, 0x67, 0x65, 0x79, 0x75, 0x6d,
+ 0x76, 0x65, 0x62, 0x67, 0x6b, 0x74, 0x6a, 0x62, 0x74, 0x6a, 0x63, 0x66, 0x62, 0x6c, 0x75, 0x6a,
+ 0x78, 0x61, 0x6f, 0x79, 0x62, 0x64, 0x61, 0x64, 0x77, 0x69, 0x77, 0x77, 0x75, 0x78, 0x77, 0x69,
+ 0x72, 0x69, 0x67, 0x65, 0x73, 0x65, 0x71, 0x6d, 0x6c, 0x65, 0x73, 0x68, 0x6e, 0x68, 0x69, 0x6b,
+ 0x72, 0x74, 0x76, 0x78, 0x62, 0x6a, 0x78, 0x72, 0x77, 0x76, 0x78, 0x68, 0x74, 0x74, 0x62, 0x6a,
+ 0x6c, 0x73, 0x76, 0x73, 0x79, 0x71, 0x72, 0x77, 0x65, 0x6f, 0x6a, 0x6e, 0x63, 0x64, 0x73, 0x65,
+ 0x69, 0x74, 0x72, 0x76, 0x68, 0x79, 0x67, 0x67, 0x78, 0x77, 0x75, 0x78, 0x72, 0x72, 0x68, 0x64,
+ 0x76, 0x73, 0x64, 0x61, 0x64, 0x78, 0x6f, 0x67, 0x6c, 0x6e, 0x61, 0x6a, 0x6b, 0x79, 0x71, 0x6b,
+ 0x62, 0x74, 0x71, 0x6b, 0x76, 0x6e, 0x6e, 0x6e, 0x6e, 0x74, 0x6b, 0x76, 0x61, 0x6e, 0x67, 0x65,
+ 0x73, 0x72, 0x79, 0x66, 0x61, 0x65, 0x65, 0x74, 0x6e, 0x61, 0x66, 0x74, 0x70, 0x69, 0x65, 0x67,
+ 0x67, 0x76, 0x62, 0x61, 0x6c, 0x66, 0x67, 0x76, 0x65, 0x76, 0x6a, 0x62, 0x64, 0x64, 0x6b, 0x6d,
+ 0x70, 0x70, 0x63, 0x74, 0x63, 0x67, 0x78, 0x73, 0x72, 0x6d, 0x63, 0x78, 0x6d, 0x70, 0x6e, 0x75,
+ 0x76, 0x74, 0x79, 0x6b, 0x61, 0x69, 0x6d, 0x73, 0x73, 0x69, 0x64, 0x66, 0x6e, 0x76, 0x67, 0x67,
+ 0x62, 0x77, 0x74, 0x68, 0x74, 0x68, 0x65, 0x64, 0x75, 0x68, 0x70, 0x69, 0x73, 0x6f, 0x67, 0x6b,
+ 0x74, 0x79, 0x68, 0x6c, 0x77, 0x73, 0x68, 0x61, 0x64, 0x68, 0x76, 0x61, 0x62, 0x74, 0x71, 0x6f,
+ 0x79, 0x6b, 0x6d, 0x61, 0x63, 0x65, 0x64, 0x74, 0x67, 0x75, 0x73, 0x6d, 0x65, 0x6a, 0x76, 0x69,
+ 0x75, 0x63, 0x72, 0x6c, 0x69, 0x70, 0x73, 0x70, 0x62, 0x77, 0x61, 0x76, 0x65, 0x69, 0x62, 0x70,
+ 0x6f, 0x6d, 0x70, 0x77, 0x78, 0x64, 0x78, 0x62, 0x77, 0x6e, 0x65, 0x71, 0x63, 0x6d, 0x79, 0x74,
+ 0x72, 0x78, 0x6e, 0x6b, 0x74, 0x62, 0x6f, 0x68, 0x74, 0x71, 0x66, 0x6e, 0x6d, 0x73, 0x76, 0x76,
+ 0x6f, 0x76, 0x73, 0x66, 0x66, 0x62, 0x62, 0x68, 0x66, 0x6e, 0x72, 0x72, 0x73, 0x6a, 0x73, 0x72,
+ 0x69, 0x63, 0x73, 0x79, 0x69, 0x6b, 0x66, 0x6e, 0x6c, 0x71, 0x66, 0x66, 0x74, 0x78, 0x62, 0x6e,
+ 0x75, 0x61, 0x74, 0x62, 0x68, 0x6f, 0x76, 0x6c, 0x75, 0x6c, 0x6a, 0x69, 0x67, 0x77, 0x61, 0x6b,
+ 0x76, 0x76, 0x6f, 0x66, 0x62, 0x6f, 0x66, 0x62, 0x67, 0x62, 0x75, 0x6e, 0x72, 0x6d, 0x71, 0x6d,
+ 0x6f, 0x73, 0x69, 0x6c, 0x70, 0x69, 0x69, 0x6a, 0x6d, 0x6e, 0x68, 0x70, 0x63, 0x63, 0x69, 0x68,
+ 0x68, 0x65, 0x73, 0x6a, 0x67, 0x65, 0x74, 0x79, 0x77, 0x67, 0x6d, 0x76, 0x75, 0x69, 0x71, 0x68,
+ 0x62, 0x6c, 0x72, 0x66, 0x76, 0x65, 0x76, 0x71, 0x78, 0x74, 0x68, 0x62, 0x6c, 0x68, 0x68, 0x66,
+ 0x64, 0x62, 0x6d, 0x6f, 0x77, 0x68, 0x69, 0x78, 0x65, 0x71, 0x68, 0x79, 0x6d, 0x67, 0x77, 0x6b,
+ 0x64, 0x6a, 0x79, 0x6b, 0x68, 0x71, 0x65, 0x68, 0x65, 0x6d, 0x74, 0x63, 0x72, 0x67, 0x66, 0x76,
+ 0x6c, 0x61, 0x78, 0x74, 0x6c, 0x67, 0x62, 0x73, 0x75, 0x73, 0x70, 0x66, 0x69, 0x64, 0x61, 0x72,
+ 0x6b, 0x6e, 0x66, 0x73, 0x6d, 0x71, 0x76, 0x75, 0x6a, 0x61, 0x6a, 0x70, 0x78, 0x61, 0x6c, 0x63,
+ 0x75, 0x6d, 0x79, 0x65, 0x66, 0x61, 0x79, 0x68, 0x74, 0x72, 0x6b, 0x77, 0x74, 0x63, 0x77, 0x67,
+ 0x61, 0x73, 0x6b, 0x79, 0x67, 0x63, 0x73, 0x6d, 0x67, 0x79, 0x65, 0x73, 0x64, 0x72, 0x71, 0x65,
+ 0x68, 0x66, 0x6b, 0x64, 0x73, 0x76, 0x63, 0x70, 0x78, 0x70, 0x76, 0x78, 0x6d, 0x66, 0x79, 0x63,
+ 0x6a, 0x6a, 0x6c, 0x75, 0x77, 0x64, 0x62, 0x67, 0x77, 0x64, 0x74, 0x76, 0x69, 0x6d, 0x72, 0x64,
+ 0x73, 0x75, 0x71, 0x6e, 0x74, 0x66, 0x65, 0x6e, 0x76, 0x6b, 0x6c, 0x70, 0x70, 0x6e, 0x68, 0x78,
+ 0x75, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x76, 0x63, 0x72, 0x70, 0x71, 0x67, 0x67, 0x70, 0x64, 0x66,
+ 0x67, 0x68, 0x79, 0x70, 0x63, 0x67, 0x6f, 0x68, 0x70, 0x6f, 0x68, 0x6f, 0x6b, 0x71, 0x79, 0x62,
+ 0x76, 0x6a, 0x75, 0x6e, 0x77, 0x65, 0x70, 0x6f, 0x72, 0x6f, 0x61, 0x66, 0x65, 0x6b, 0x73, 0x63,
+ 0x61, 0x62, 0x66, 0x65, 0x67, 0x77, 0x77, 0x79, 0x72, 0x72, 0x6b, 0x6e, 0x6d, 0x75, 0x6b, 0x65,
+ 0x68, 0x6e, 0x6a, 0x6e, 0x65, 0x65, 0x61, 0x78, 0x66, 0x68, 0x64, 0x62, 0x79, 0x66, 0x63, 0x72,
+ 0x64, 0x74, 0x71, 0x6a, 0x72, 0x78, 0x76, 0x64, 0x6c, 0x66, 0x6d, 0x75, 0x6b, 0x64, 0x6d, 0x78,
+ 0x68, 0x74, 0x63, 0x69, 0x6c, 0x6d, 0x65, 0x63, 0x63, 0x69, 0x70, 0x76, 0x6d, 0x6e, 0x70, 0x67,
+ 0x6d, 0x64, 0x6d, 0x6f, 0x69, 0x6d, 0x76, 0x75, 0x65, 0x6b, 0x6f, 0x73, 0x78, 0x70, 0x63, 0x6c,
+ 0x73, 0x6e, 0x77, 0x6f, 0x6d, 0x79, 0x70, 0x77, 0x63, 0x77, 0x69, 0x65, 0x71, 0x6d, 0x74, 0x6d,
+ 0x78, 0x78, 0x61, 0x70, 0x65, 0x65, 0x64, 0x65, 0x75, 0x71, 0x67, 0x63, 0x64, 0x74, 0x6c, 0x68,
+ 0x78, 0x65, 0x6d, 0x6f, 0x68, 0x72, 0x65, 0x72, 0x68, 0x6b, 0x65, 0x74, 0x68, 0x78, 0x66, 0x64,
+ 0x69, 0x77, 0x79, 0x72, 0x77, 0x73, 0x71, 0x78, 0x71, 0x76, 0x67, 0x74, 0x79, 0x74, 0x6b, 0x77,
+ 0x63, 0x6d, 0x75, 0x76, 0x6f, 0x67, 0x62, 0x76, 0x67, 0x72, 0x72, 0x72, 0x61, 0x6d, 0x73, 0x74,
+ 0x78, 0x65, 0x64, 0x64, 0x67, 0x77, 0x67, 0x6f, 0x78, 0x74, 0x76, 0x6b, 0x75, 0x69, 0x75, 0x68,
+ 0x62, 0x72, 0x67, 0x73, 0x6f, 0x75, 0x73, 0x79, 0x6a, 0x6b, 0x70, 0x79, 0x76, 0x64, 0x6b, 0x71,
+ 0x64, 0x79, 0x68, 0x76, 0x6a, 0x71, 0x63, 0x65, 0x70, 0x78, 0x61, 0x74, 0x72, 0x65, 0x6f, 0x68,
+ 0x76, 0x75, 0x6a, 0x75, 0x6a, 0x62, 0x65, 0x61, 0x62, 0x6b, 0x74, 0x64, 0x70, 0x6d, 0x6e, 0x74,
+ 0x74, 0x72, 0x6b, 0x72, 0x71, 0x66, 0x72, 0x72, 0x75, 0x6b, 0x6b, 0x6a, 0x63, 0x73, 0x6c, 0x76,
+ 0x77, 0x71, 0x6e, 0x70, 0x65, 0x71, 0x61, 0x72, 0x68, 0x79, 0x79, 0x74, 0x78, 0x62, 0x6a, 0x63,
+ 0x72, 0x78, 0x6d, 0x61, 0x72, 0x73, 0x69, 0x61, 0x61, 0x78, 0x69, 0x63, 0x6b, 0x64, 0x79, 0x75,
+ 0x66, 0x72, 0x63, 0x67, 0x77, 0x6f, 0x77, 0x65, 0x75, 0x6f, 0x6a, 0x72, 0x61, 0x6a, 0x6c, 0x66,
+ 0x72, 0x6e, 0x70, 0x69, 0x68, 0x6a, 0x62, 0x6c, 0x6c, 0x70, 0x71, 0x6e, 0x75, 0x69, 0x76, 0x76,
+ 0x66, 0x6e, 0x77, 0x76, 0x66, 0x69, 0x78, 0x74, 0x68, 0x78, 0x64, 0x79, 0x66, 0x75, 0x74, 0x68,
+ 0x6f, 0x67, 0x79, 0x78, 0x73, 0x6d, 0x78, 0x79, 0x66, 0x63, 0x74, 0x75, 0x6f, 0x62, 0x62, 0x62,
+ 0x68, 0x78, 0x6c, 0x64, 0x76, 0x64, 0x72, 0x6e, 0x65, 0x71, 0x66, 0x69, 0x77, 0x76, 0x62, 0x66,
+ 0x71, 0x63, 0x74, 0x61, 0x78, 0x62, 0x74, 0x72, 0x6b, 0x67, 0x69, 0x78, 0x63, 0x6a, 0x6e, 0x76,
+ 0x62, 0x61, 0x6f, 0x68, 0x6a, 0x67, 0x72, 0x72, 0x79, 0x74, 0x78, 0x67, 0x65, 0x72, 0x6a, 0x68,
+ 0x6d, 0x6f, 0x61, 0x71, 0x65, 0x72, 0x64, 0x61, 0x79, 0x73, 0x6a, 0x64, 0x76, 0x77, 0x6a, 0x6b,
+ 0x71, 0x6c, 0x75, 0x75, 0x6c, 0x78, 0x67, 0x77, 0x76, 0x78, 0x69, 0x74, 0x62, 0x74, 0x75, 0x71,
+ 0x77, 0x77, 0x70, 0x69, 0x6c, 0x61, 0x71, 0x66, 0x6c, 0x78, 0x6a, 0x67, 0x77, 0x68, 0x65, 0x63,
+ 0x77, 0x72, 0x79, 0x6c, 0x6f, 0x64, 0x78, 0x77, 0x68, 0x78, 0x63, 0x68, 0x66, 0x70, 0x78, 0x6f,
+ 0x70, 0x78, 0x66, 0x79, 0x76, 0x63, 0x6a, 0x65, 0x79, 0x66, 0x78, 0x63, 0x6e, 0x6b, 0x75, 0x61,
+ 0x70, 0x6c, 0x62, 0x68, 0x79, 0x63, 0x6b, 0x6d, 0x62, 0x77, 0x68, 0x76, 0x62, 0x6f, 0x68, 0x62,
+ 0x78, 0x78, 0x72, 0x72, 0x6c, 0x74, 0x79, 0x74, 0x6b, 0x68, 0x6e, 0x74, 0x74, 0x70, 0x73, 0x67,
+ 0x73, 0x71, 0x6b, 0x6f, 0x69, 0x6a, 0x71, 0x78, 0x6a, 0x77, 0x70, 0x76, 0x72, 0x64, 0x6a, 0x68,
+ 0x6e, 0x6a, 0x69, 0x79, 0x73, 0x78, 0x71, 0x77, 0x71, 0x68, 0x6d, 0x6b, 0x68, 0x79, 0x63, 0x62,
+ 0x63, 0x6b, 0x67, 0x6f, 0x69, 0x68, 0x6e, 0x6d, 0x67, 0x65, 0x6a, 0x74, 0x67, 0x66, 0x73, 0x62,
+ 0x67, 0x78, 0x6d, 0x69, 0x62, 0x73, 0x67, 0x76, 0x71, 0x73, 0x79, 0x6d, 0x6a, 0x77, 0x65, 0x67,
+ 0x70, 0x6e, 0x64, 0x76, 0x66, 0x6b, 0x70, 0x73, 0x6e, 0x61, 0x69, 0x71, 0x71, 0x62, 0x75, 0x70,
+ 0x72, 0x63, 0x69, 0x68, 0x6e, 0x6d, 0x65, 0x6e, 0x6a, 0x73, 0x62, 0x70, 0x6a, 0x69, 0x67, 0x69,
+ 0x6f, 0x76, 0x6f, 0x70, 0x74, 0x6f, 0x66, 0x6f, 0x64, 0x64, 0x76, 0x66, 0x6b, 0x62, 0x73, 0x6c,
+ 0x6a, 0x6e, 0x73, 0x6b, 0x75, 0x6f, 0x66, 0x6e, 0x78, 0x6b, 0x64, 0x6c, 0x6d, 0x73, 0x6e, 0x69,
+ 0x6a, 0x6f, 0x78, 0x62, 0x6b, 0x78, 0x74, 0x65, 0x67, 0x72, 0x66, 0x66, 0x69, 0x69, 0x63, 0x73,
+ 0x75, 0x79, 0x75, 0x73, 0x77, 0x72, 0x77, 0x77, 0x74, 0x74, 0x68, 0x78, 0x76, 0x71, 0x79, 0x78,
+ 0x61, 0x6c, 0x6b, 0x6e, 0x61, 0x76, 0x6b, 0x6d, 0x76, 0x63, 0x70, 0x79, 0x6e, 0x78, 0x72, 0x61,
+ 0x75, 0x6c, 0x64, 0x69, 0x6f, 0x78, 0x61, 0x71, 0x68, 0x6c, 0x61, 0x77, 0x69, 0x69, 0x74, 0x75,
+ 0x6f, 0x66, 0x75, 0x6e, 0x62, 0x6c, 0x6d, 0x67, 0x79, 0x6f, 0x69, 0x71, 0x76, 0x71, 0x78, 0x67,
+ 0x75, 0x66, 0x6d, 0x69, 0x67, 0x75, 0x6f, 0x6b, 0x63, 0x61, 0x6f, 0x63, 0x70, 0x63, 0x76, 0x75,
+ 0x63, 0x74, 0x70, 0x78, 0x64, 0x77, 0x6e, 0x69, 0x78, 0x6c, 0x63, 0x70, 0x66, 0x6d, 0x68, 0x64,
+ 0x6a, 0x75, 0x64, 0x71, 0x6b, 0x64, 0x67, 0x78, 0x73, 0x63, 0x6e, 0x75, 0x74, 0x77, 0x76, 0x6a,
+ 0x62, 0x74, 0x73, 0x76, 0x67, 0x70, 0x6f, 0x78, 0x6f, 0x63, 0x75, 0x64, 0x61, 0x79, 0x6b, 0x70,
+ 0x73, 0x75, 0x65, 0x64, 0x74, 0x70, 0x61, 0x6f, 0x77, 0x6d, 0x74, 0x6e, 0x78, 0x65, 0x72, 0x75,
+ 0x63, 0x71, 0x69, 0x66, 0x79, 0x65, 0x74, 0x77, 0x6f, 0x6d, 0x66, 0x6a, 0x77, 0x6f, 0x78, 0x76,
+ 0x66, 0x67, 0x65, 0x64, 0x66, 0x61, 0x62, 0x61, 0x71, 0x79, 0x64, 0x6c, 0x79, 0x6b, 0x64, 0x75,
+ 0x79, 0x6c, 0x67, 0x6d, 0x76, 0x67, 0x77, 0x71, 0x69, 0x65, 0x66, 0x71, 0x6b, 0x70, 0x62, 0x64,
+ 0x73, 0x6e, 0x6d, 0x67, 0x67, 0x6f, 0x75, 0x70, 0x65, 0x79, 0x63, 0x62, 0x61, 0x6b, 0x62, 0x6a,
+ 0x66, 0x68, 0x67, 0x75, 0x78, 0x68, 0x6b, 0x77, 0x66, 0x79, 0x67, 0x6c, 0x6f, 0x6a, 0x6a, 0x73,
+ 0x6f, 0x73, 0x63, 0x78, 0x6a, 0x67, 0x74, 0x6d, 0x6f, 0x68, 0x6b, 0x62, 0x70, 0x66, 0x6a, 0x6e,
+ 0x6e, 0x67, 0x6c, 0x65, 0x74, 0x6f, 0x6c, 0x67, 0x68, 0x6d, 0x67, 0x79, 0x61, 0x6a, 0x6e, 0x70,
+ 0x64, 0x6f, 0x68, 0x6b, 0x6a, 0x64, 0x64, 0x6f, 0x6a, 0x64, 0x66, 0x67, 0x73, 0x6a, 0x76, 0x78,
+ 0x6c, 0x67, 0x66, 0x75, 0x6c, 0x79, 0x69, 0x65, 0x71, 0x6c, 0x6b, 0x63, 0x61, 0x6d, 0x61, 0x78,
+ 0x6a, 0x74, 0x6a, 0x78, 0x65, 0x75, 0x62, 0x6b, 0x6b, 0x75, 0x77, 0x78, 0x73, 0x77, 0x64, 0x61,
+ 0x61, 0x6b, 0x71, 0x6f, 0x6f, 0x6c, 0x67, 0x6e, 0x6d, 0x77, 0x64, 0x6d, 0x64, 0x6d, 0x64, 0x76,
+ 0x77, 0x6b, 0x69, 0x76, 0x72, 0x70, 0x77, 0x62, 0x75, 0x69, 0x73, 0x68, 0x65, 0x76, 0x70, 0x76,
+ 0x76, 0x6c, 0x6a, 0x69, 0x77, 0x73, 0x69, 0x66, 0x67, 0x6a, 0x74, 0x6a, 0x70, 0x6f, 0x6c, 0x77,
+ 0x70, 0x6a, 0x76, 0x6a, 0x61, 0x68, 0x78, 0x73, 0x70, 0x72, 0x78, 0x62, 0x76, 0x6b, 0x66, 0x6b,
+ 0x64, 0x71, 0x6d, 0x62, 0x6d, 0x70, 0x79, 0x6a, 0x66, 0x6c, 0x79, 0x76, 0x66, 0x75, 0x69, 0x71,
+ 0x66, 0x79, 0x78, 0x77, 0x75, 0x75, 0x6b, 0x77, 0x6d, 0x67, 0x67, 0x6b, 0x75, 0x75, 0x64, 0x75,
+ 0x62, 0x73, 0x6c, 0x6a, 0x6c, 0x64, 0x69, 0x74, 0x6e, 0x65, 0x63, 0x79, 0x6e, 0x6d, 0x6c, 0x77,
+ 0x6a, 0x69, 0x6d, 0x78, 0x62, 0x74, 0x69, 0x6a, 0x66, 0x75, 0x6e, 0x6f, 0x6e, 0x78, 0x73, 0x6d,
+ 0x74, 0x74, 0x62, 0x6c, 0x68, 0x6b, 0x70, 0x61, 0x6c, 0x6c, 0x63, 0x64, 0x67, 0x65, 0x68, 0x73,
+ 0x74, 0x6d, 0x64, 0x78, 0x68, 0x71, 0x61, 0x66, 0x77, 0x66, 0x6a, 0x66, 0x6f, 0x61, 0x65, 0x6f,
+ 0x6f, 0x74, 0x6a, 0x69, 0x6a, 0x75, 0x68, 0x6c, 0x73, 0x6b, 0x72, 0x75, 0x69, 0x63, 0x63, 0x62,
+ 0x6e, 0x64, 0x76, 0x75, 0x79, 0x71, 0x64, 0x61, 0x6a, 0x65, 0x65, 0x61, 0x61, 0x6e, 0x66, 0x65,
+ 0x74, 0x77, 0x61, 0x62, 0x68, 0x68, 0x67, 0x71, 0x69, 0x68, 0x6b, 0x73, 0x6b, 0x76, 0x68, 0x73,
+ 0x62, 0x63, 0x65, 0x65, 0x73, 0x72, 0x6a, 0x71, 0x6e, 0x61, 0x6a, 0x63, 0x61, 0x62, 0x73, 0x6e,
+ 0x6e, 0x70, 0x67, 0x6f, 0x63, 0x70, 0x64, 0x66, 0x62, 0x61, 0x64, 0x6a, 0x78, 0x70, 0x61, 0x6f,
+ 0x69, 0x79, 0x66, 0x77, 0x74, 0x69, 0x62, 0x6a, 0x73, 0x75, 0x6d, 0x76, 0x74, 0x6d, 0x61, 0x77,
+ 0x67, 0x67, 0x72, 0x6f, 0x79, 0x75, 0x61, 0x72, 0x65, 0x6c, 0x67, 0x69, 0x63, 0x77, 0x71, 0x61,
+ 0x62, 0x63, 0x61, 0x71, 0x6d, 0x6d, 0x64, 0x6a, 0x6a, 0x70, 0x63, 0x71, 0x72, 0x6b, 0x6a, 0x66,
+ 0x62, 0x76, 0x6c, 0x61, 0x72, 0x6f, 0x75, 0x72, 0x75, 0x65, 0x6b, 0x75, 0x73, 0x64, 0x76, 0x6b,
+ 0x6c, 0x6d, 0x73, 0x74, 0x6a, 0x70, 0x61, 0x64, 0x74, 0x71, 0x6c, 0x63, 0x6f, 0x6c, 0x68, 0x75,
+ 0x69, 0x70, 0x73, 0x73, 0x76, 0x78, 0x6b, 0x67, 0x73, 0x74, 0x6f, 0x70, 0x72, 0x61, 0x75, 0x6c,
+ 0x66, 0x66, 0x69, 0x6f, 0x75, 0x6b, 0x6d, 0x64, 0x74, 0x79, 0x6b, 0x66, 0x6b, 0x65, 0x65, 0x6c,
+ 0x6b, 0x61, 0x6c, 0x73, 0x63, 0x66, 0x67, 0x76, 0x6d, 0x74, 0x61, 0x65, 0x6e, 0x62, 0x61, 0x72,
+ 0x63, 0x6d, 0x6c, 0x6a, 0x75, 0x67, 0x6d, 0x62, 0x6e, 0x70, 0x76, 0x6b, 0x64, 0x77, 0x6b, 0x68,
+ 0x75, 0x74, 0x77, 0x61, 0x73, 0x62, 0x6c, 0x79, 0x79, 0x77, 0x6f, 0x66, 0x63, 0x73, 0x67, 0x64,
+ 0x63, 0x62, 0x66, 0x71, 0x73, 0x79, 0x66, 0x73, 0x69, 0x72, 0x71, 0x70, 0x72, 0x67, 0x68, 0x72,
+ 0x6d, 0x6d, 0x79, 0x68, 0x77, 0x6f, 0x68, 0x62, 0x64, 0x70, 0x64, 0x70, 0x6e, 0x72, 0x69, 0x66,
+ 0x6b, 0x72, 0x64, 0x65, 0x63, 0x77, 0x61, 0x61, 0x6b, 0x73, 0x73, 0x77, 0x78, 0x77, 0x6c, 0x64,
+ 0x61, 0x69, 0x62, 0x72, 0x72, 0x6d, 0x6e, 0x76, 0x70, 0x75, 0x72, 0x63, 0x77, 0x69, 0x65, 0x62,
+ 0x73, 0x73, 0x6b, 0x70, 0x70, 0x64, 0x67, 0x71, 0x72, 0x6a, 0x6f, 0x79, 0x75, 0x75, 0x64, 0x75,
+ 0x6f, 0x73, 0x78, 0x67, 0x79, 0x79, 0x78, 0x6e, 0x76, 0x63, 0x6c, 0x73, 0x6a, 0x6c, 0x65, 0x6e,
+ 0x6c, 0x70, 0x69, 0x76, 0x63, 0x6a, 0x6f, 0x6a, 0x6a, 0x6f, 0x6b, 0x66, 0x75, 0x73, 0x66, 0x71,
+ 0x61, 0x75, 0x78, 0x68, 0x6b, 0x67, 0x71, 0x72, 0x6b, 0x65, 0x67, 0x6d, 0x68, 0x74, 0x62, 0x78,
+ 0x65, 0x6f, 0x69, 0x74, 0x71, 0x79, 0x63, 0x67, 0x79, 0x77, 0x6b, 0x69, 0x61, 0x6e, 0x6b, 0x69,
+ 0x70, 0x78, 0x74, 0x75, 0x72, 0x66, 0x6f, 0x64, 0x77, 0x62, 0x79, 0x73, 0x79, 0x73, 0x78, 0x65,
+ 0x62, 0x6b, 0x6f, 0x6b, 0x76, 0x62, 0x6e, 0x68, 0x6a, 0x72, 0x61, 0x61, 0x73, 0x6f, 0x78, 0x61,
+ 0x68, 0x76, 0x63, 0x6c, 0x77, 0x6c, 0x63, 0x6e, 0x6d, 0x6c, 0x71, 0x6f, 0x70, 0x72, 0x63, 0x75,
+ 0x68, 0x61, 0x67, 0x69, 0x6f, 0x6a, 0x6e, 0x61, 0x71, 0x72, 0x6b, 0x66, 0x73, 0x6c, 0x64, 0x79,
+ 0x79, 0x75, 0x79, 0x6d, 0x6e, 0x6c, 0x67, 0x65, 0x6a, 0x76, 0x66, 0x76, 0x77, 0x6c, 0x6b, 0x77,
+ 0x75, 0x64, 0x72, 0x73, 0x77, 0x69, 0x64, 0x74, 0x72, 0x69, 0x66, 0x6b, 0x6f, 0x6d, 0x76, 0x74,
+ 0x64, 0x64, 0x73, 0x74, 0x69, 0x6f, 0x71, 0x79, 0x65, 0x64, 0x71, 0x69, 0x6d, 0x62, 0x6d, 0x70,
+ 0x6c, 0x76, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x77, 0x6a, 0x67, 0x72, 0x78, 0x74, 0x72, 0x69, 0x62,
+ 0x6f, 0x71, 0x71, 0x6a, 0x69, 0x70, 0x73, 0x79, 0x6a, 0x70, 0x70, 0x6d, 0x6f, 0x6f, 0x6d, 0x79,
+ 0x6f, 0x6e, 0x6a, 0x6e, 0x75, 0x67, 0x76, 0x78, 0x65, 0x72, 0x77, 0x64, 0x69, 0x6f, 0x63, 0x69,
+ 0x73, 0x68, 0x72, 0x79, 0x6c, 0x6a, 0x61, 0x77, 0x6b, 0x72, 0x78, 0x63, 0x72, 0x63, 0x79, 0x69,
+ 0x6a, 0x72, 0x6e, 0x79, 0x62, 0x67, 0x6f, 0x72, 0x63, 0x6b, 0x6d, 0x61, 0x76, 0x6e, 0x73, 0x64,
+ 0x62, 0x6a, 0x78, 0x74, 0x72, 0x74, 0x64, 0x79, 0x6b, 0x71, 0x72, 0x68, 0x67, 0x63, 0x63, 0x76,
+ 0x6f, 0x77, 0x76, 0x61, 0x64, 0x65, 0x70, 0x71, 0x72, 0x61, 0x63, 0x63, 0x66, 0x75, 0x73, 0x62,
+ 0x70, 0x70, 0x67, 0x68, 0x70, 0x63, 0x6c, 0x64, 0x6d, 0x76, 0x6d, 0x67, 0x73, 0x72, 0x6a, 0x6a,
+ 0x78, 0x6b, 0x64, 0x78, 0x71, 0x71, 0x66, 0x68, 0x79, 0x6b, 0x6b, 0x69, 0x67, 0x70, 0x6e, 0x66,
+ 0x73, 0x73, 0x78, 0x72, 0x63, 0x76, 0x79, 0x65, 0x63, 0x67, 0x79, 0x64, 0x69, 0x71, 0x75, 0x68,
+ 0x79, 0x6c, 0x6b, 0x61, 0x78, 0x6a, 0x6c, 0x76, 0x63, 0x61, 0x70, 0x79, 0x71, 0x6c, 0x77, 0x6c,
+ 0x6f, 0x66, 0x63, 0x6a, 0x69, 0x64, 0x6f, 0x6f, 0x75, 0x64, 0x76, 0x70, 0x65, 0x63, 0x72, 0x70,
+ 0x62, 0x71, 0x6c, 0x73, 0x74, 0x74, 0x6b, 0x6f, 0x6e, 0x79, 0x72, 0x64, 0x66, 0x6d, 0x79, 0x73,
+ 0x6d, 0x64, 0x67, 0x6b, 0x69, 0x6d, 0x76, 0x65, 0x63, 0x73, 0x6c, 0x6a, 0x66, 0x64, 0x64, 0x70,
+ 0x72, 0x67, 0x69, 0x6c, 0x63, 0x63, 0x71, 0x63, 0x69, 0x73, 0x78, 0x67, 0x77, 0x6a, 0x6d, 0x74,
+ 0x63, 0x63, 0x68, 0x78, 0x61, 0x73, 0x6b, 0x6b, 0x73, 0x69, 0x73, 0x72, 0x73, 0x71, 0x67, 0x68,
+ 0x65, 0x76, 0x77, 0x65, 0x6e, 0x68, 0x72, 0x6a, 0x63, 0x6d, 0x67, 0x6e, 0x65, 0x73, 0x78, 0x73,
+ 0x6a, 0x6d, 0x74, 0x61, 0x61, 0x73, 0x74, 0x62, 0x6a, 0x6a, 0x62, 0x63, 0x65, 0x63, 0x70, 0x69,
+ 0x66, 0x71, 0x70, 0x68, 0x6a, 0x66, 0x67, 0x78, 0x61, 0x63, 0x6c, 0x75, 0x79, 0x6a, 0x63, 0x6b,
+ 0x72, 0x78, 0x65, 0x65, 0x77, 0x77, 0x73, 0x71, 0x68, 0x6f, 0x6b, 0x64, 0x6e, 0x66, 0x64, 0x6e,
+ 0x6b, 0x78, 0x67, 0x73, 0x6c, 0x75, 0x64, 0x6d, 0x66, 0x6b, 0x77, 0x73, 0x74, 0x70, 0x68, 0x66,
+ 0x6c, 0x70, 0x6a, 0x62, 0x6b, 0x78, 0x6e, 0x65, 0x61, 0x6a, 0x68, 0x63, 0x6a, 0x64, 0x75, 0x69,
+ 0x71, 0x77, 0x6e, 0x67, 0x72, 0x70, 0x70, 0x76, 0x70, 0x6e, 0x6b, 0x74, 0x75, 0x76, 0x6b, 0x62,
+ 0x79, 0x63, 0x78, 0x6c, 0x63, 0x6f, 0x69, 0x68, 0x76, 0x6f, 0x62, 0x6a, 0x61, 0x66, 0x6b, 0x72,
+ 0x75, 0x76, 0x67, 0x74, 0x78, 0x73, 0x69, 0x63, 0x63, 0x72, 0x75, 0x61, 0x64, 0x75, 0x69, 0x70,
+ 0x63, 0x68, 0x72, 0x67, 0x69, 0x6f, 0x6d, 0x62, 0x61, 0x68, 0x62, 0x76, 0x6e, 0x6f, 0x62, 0x76,
+ 0x70, 0x6a, 0x67, 0x6b, 0x74, 0x75, 0x75, 0x70, 0x70, 0x6c, 0x76, 0x6d, 0x71, 0x6b, 0x6f, 0x72,
+ 0x70, 0x67, 0x63, 0x79, 0x72, 0x64, 0x67, 0x6f, 0x6d, 0x77, 0x64, 0x61, 0x6f, 0x68, 0x63, 0x61,
+ 0x64, 0x61, 0x67, 0x62, 0x6e, 0x65, 0x6a, 0x6e, 0x77, 0x66, 0x6e, 0x75, 0x79, 0x64, 0x75, 0x79,
+ 0x79, 0x61, 0x63, 0x6f, 0x63, 0x79, 0x77, 0x64, 0x73, 0x74, 0x6a, 0x71, 0x63, 0x61, 0x6b, 0x76,
+ 0x63, 0x6e, 0x73, 0x72, 0x75, 0x62, 0x75, 0x75, 0x67, 0x68, 0x71, 0x6f, 0x6e, 0x61, 0x65, 0x75,
+ 0x6e, 0x6a, 0x76, 0x71, 0x68, 0x74, 0x79, 0x68, 0x65, 0x70, 0x65, 0x6e, 0x6c, 0x62, 0x78, 0x6e,
+ 0x66, 0x64, 0x63, 0x77, 0x68, 0x75, 0x78, 0x6e, 0x71, 0x6d, 0x79, 0x71, 0x68, 0x68, 0x75, 0x73,
+ 0x6c, 0x67, 0x73, 0x75, 0x6d, 0x74, 0x72, 0x6d, 0x77, 0x65, 0x67, 0x67, 0x6f, 0x67, 0x66, 0x75,
+ 0x6d, 0x63, 0x75, 0x6d, 0x6d, 0x74, 0x72, 0x6f, 0x64, 0x74, 0x6f, 0x76, 0x73, 0x75, 0x67, 0x65,
+ 0x73, 0x78, 0x77, 0x67, 0x66, 0x77, 0x66, 0x69, 0x73, 0x75, 0x61, 0x65, 0x6a, 0x61, 0x6b, 0x72,
+ 0x6c, 0x6a, 0x71, 0x71, 0x73, 0x6e, 0x6f, 0x70, 0x65, 0x6e, 0x72, 0x6a, 0x74, 0x6f, 0x72, 0x76,
+ 0x61, 0x69, 0x74, 0x67, 0x72, 0x6f, 0x6d, 0x74, 0x75, 0x74, 0x72, 0x63, 0x65, 0x78, 0x71, 0x6b,
+ 0x79, 0x73, 0x63, 0x6c, 0x6f, 0x75, 0x73, 0x62, 0x70, 0x70, 0x73, 0x65, 0x79, 0x6d, 0x6f, 0x76,
+ 0x64, 0x76, 0x67, 0x69, 0x64, 0x6a, 0x72, 0x69, 0x75, 0x69, 0x79, 0x6e, 0x6d, 0x78, 0x69, 0x62,
+ 0x71, 0x6e, 0x6b, 0x66, 0x75, 0x73, 0x6b, 0x74, 0x6e, 0x61, 0x6c, 0x69, 0x76, 0x63, 0x66, 0x6d,
+ 0x69, 0x61, 0x62, 0x73, 0x74, 0x6a, 0x79, 0x64, 0x6f, 0x67, 0x77, 0x68, 0x6b, 0x63, 0x75, 0x63,
+ 0x74, 0x6d, 0x76, 0x68, 0x72, 0x6e, 0x69, 0x6d, 0x74, 0x6f, 0x79, 0x78, 0x62, 0x76, 0x62, 0x6f,
+ 0x70, 0x6c, 0x6f, 0x62, 0x65, 0x78, 0x63, 0x65, 0x72, 0x77, 0x64, 0x76, 0x65, 0x66, 0x74, 0x63,
+ 0x63, 0x6a, 0x6d, 0x6b, 0x6e, 0x64, 0x73, 0x62, 0x66, 0x6d, 0x62, 0x66, 0x6b, 0x76, 0x63, 0x68,
+ 0x6c, 0x65, 0x69, 0x72, 0x75, 0x62, 0x6e, 0x64, 0x6a, 0x6a, 0x6d, 0x75, 0x74, 0x79, 0x6d, 0x67,
+ 0x65, 0x6e, 0x6b, 0x67, 0x6a, 0x6e, 0x63, 0x63, 0x63, 0x6d, 0x6b, 0x64, 0x6f, 0x6e, 0x6a, 0x75,
+ 0x77, 0x71, 0x74, 0x65, 0x73, 0x71, 0x62, 0x61, 0x76, 0x67, 0x6d, 0x6b, 0x74, 0x78, 0x6c, 0x66,
+ 0x65, 0x74, 0x6f, 0x68, 0x61, 0x73, 0x66, 0x63, 0x79, 0x64, 0x6a, 0x79, 0x74, 0x67, 0x67, 0x6b,
+ 0x78, 0x70, 0x6f, 0x6c, 0x64, 0x68, 0x69, 0x76, 0x6b, 0x77, 0x67, 0x6d, 0x62, 0x72, 0x71, 0x76,
+ 0x69, 0x76, 0x6e, 0x76, 0x71, 0x6c, 0x74, 0x74, 0x71, 0x75, 0x6b, 0x6c, 0x70, 0x6e, 0x69, 0x78,
+ 0x71, 0x6d, 0x62, 0x6d, 0x69, 0x65, 0x6c, 0x77, 0x68, 0x68, 0x6d, 0x6d, 0x62, 0x70, 0x75, 0x74,
+ 0x77, 0x6f, 0x65, 0x69, 0x78, 0x62, 0x78, 0x74, 0x6b, 0x76, 0x72, 0x76, 0x65, 0x78, 0x77, 0x74,
+ 0x63, 0x75, 0x71, 0x66, 0x6e, 0x6c, 0x6d, 0x62, 0x76, 0x6b, 0x77, 0x70, 0x76, 0x79, 0x6b, 0x78,
+ 0x76, 0x6e, 0x6d, 0x66, 0x6d, 0x6b, 0x69, 0x6b, 0x64, 0x79, 0x77, 0x62, 0x65, 0x61, 0x67, 0x6c,
+ 0x66, 0x6f, 0x70, 0x73, 0x73, 0x66, 0x68, 0x71, 0x68, 0x70, 0x65, 0x65, 0x74, 0x69, 0x65, 0x6e,
+ 0x65, 0x68, 0x76, 0x79, 0x66, 0x76, 0x67, 0x66, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x6d, 0x69, 0x79,
+ 0x63, 0x6f, 0x64, 0x64, 0x79, 0x68, 0x71, 0x75, 0x73, 0x6d, 0x63, 0x67, 0x75, 0x64, 0x6b, 0x65,
+ 0x69, 0x76, 0x70, 0x65, 0x6a, 0x63, 0x70, 0x68, 0x61, 0x66, 0x73, 0x73, 0x68, 0x74, 0x71, 0x6c,
+ 0x63, 0x61, 0x62, 0x73, 0x75, 0x73, 0x78, 0x6d, 0x66, 0x74, 0x74, 0x73, 0x63, 0x74, 0x64, 0x75,
+ 0x6b, 0x6c, 0x6a, 0x72, 0x72, 0x61, 0x64, 0x74, 0x71, 0x70, 0x6b, 0x69, 0x70, 0x75, 0x61, 0x6e,
+ 0x71, 0x77, 0x73, 0x6f, 0x72, 0x77, 0x67, 0x67, 0x67, 0x61, 0x73, 0x73, 0x61, 0x6e, 0x63, 0x70,
+ 0x6c, 0x69, 0x68, 0x67, 0x62, 0x76, 0x71, 0x72, 0x72, 0x72, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x6d,
+ 0x73, 0x73, 0x69, 0x68, 0x74, 0x76, 0x71, 0x66, 0x67, 0x67, 0x70, 0x66, 0x69, 0x78, 0x62, 0x78,
+ 0x72, 0x6d, 0x78, 0x76, 0x79, 0x6a, 0x6f, 0x79, 0x76, 0x65, 0x61, 0x61, 0x6f, 0x6c, 0x6c, 0x79,
+ 0x6e, 0x62, 0x63, 0x76, 0x69, 0x69, 0x77, 0x76, 0x6d, 0x66, 0x69, 0x79, 0x79, 0x6f, 0x75, 0x71,
+ 0x64, 0x75, 0x6b, 0x6c, 0x6d, 0x73, 0x6c, 0x71, 0x78, 0x6e, 0x6d, 0x74, 0x6e, 0x71, 0x6b, 0x66,
+ 0x6d, 0x67, 0x69, 0x6b, 0x77, 0x66, 0x69, 0x6d, 0x6d, 0x6d, 0x6a, 0x74, 0x77, 0x6f, 0x63, 0x76,
+ 0x6d, 0x67, 0x65, 0x65, 0x64, 0x76, 0x68, 0x67, 0x68, 0x66, 0x68, 0x6f, 0x61, 0x63, 0x78, 0x6d,
+ 0x76, 0x64, 0x74, 0x6e, 0x6d, 0x69, 0x66, 0x71, 0x62, 0x70, 0x71, 0x66, 0x77, 0x77, 0x63, 0x6b,
+ 0x76, 0x78, 0x72, 0x66, 0x70, 0x76, 0x78, 0x65, 0x72, 0x6f, 0x62, 0x62, 0x67, 0x65, 0x62, 0x76,
+ 0x66, 0x6e, 0x79, 0x69, 0x64, 0x69, 0x68, 0x67, 0x66, 0x70, 0x77, 0x6e, 0x76, 0x64, 0x75, 0x6c,
+ 0x71, 0x68, 0x6a, 0x71, 0x71, 0x70, 0x65, 0x68, 0x61, 0x71, 0x78, 0x75, 0x6c, 0x76, 0x68, 0x6d,
+ 0x64, 0x65, 0x65, 0x79, 0x6f, 0x78, 0x66, 0x77, 0x70, 0x66, 0x66, 0x6f, 0x74, 0x73, 0x71, 0x6e,
+ 0x65, 0x65, 0x75, 0x68, 0x6e, 0x64, 0x6e, 0x67, 0x72, 0x76, 0x71, 0x6b, 0x65, 0x71, 0x6d, 0x6f,
+ 0x63, 0x73, 0x63, 0x6e, 0x66, 0x73, 0x68, 0x6d, 0x6b, 0x72, 0x67, 0x6d, 0x6f, 0x6a, 0x77, 0x70,
+ 0x64, 0x77, 0x62, 0x79, 0x6e, 0x65, 0x6c, 0x62, 0x66, 0x77, 0x6e, 0x73, 0x77, 0x64, 0x74, 0x74,
+ 0x77, 0x64, 0x65, 0x66, 0x6e, 0x73, 0x78, 0x6d, 0x79, 0x76, 0x73, 0x71, 0x76, 0x61, 0x66, 0x6e,
+ 0x76, 0x77, 0x6f, 0x73, 0x6c, 0x6e, 0x6d, 0x78, 0x73, 0x71, 0x64, 0x66, 0x78, 0x65, 0x73, 0x63,
+ 0x69, 0x73, 0x69, 0x62, 0x64, 0x63, 0x74, 0x6f, 0x6e, 0x69, 0x79, 0x66, 0x75, 0x71, 0x70, 0x79,
+ 0x6c, 0x6d, 0x67, 0x72, 0x67, 0x65, 0x72, 0x75, 0x78, 0x68, 0x73, 0x6f, 0x76, 0x62, 0x62, 0x78,
+ 0x68, 0x78, 0x79, 0x72, 0x65, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x67, 0x79, 0x6d, 0x78, 0x73, 0x78,
+ 0x68, 0x67, 0x77, 0x71, 0x76, 0x78, 0x74, 0x61, 0x6c, 0x76, 0x78, 0x65, 0x78, 0x6a, 0x76, 0x64,
+ 0x6f, 0x6d, 0x71, 0x6f, 0x6a, 0x6f, 0x77, 0x79, 0x69, 0x6e, 0x66, 0x71, 0x6e, 0x62, 0x66, 0x6f,
+ 0x61, 0x68, 0x73, 0x72, 0x69, 0x68, 0x68, 0x73, 0x63, 0x67, 0x69, 0x74, 0x6c, 0x76, 0x79, 0x66,
+ 0x6f, 0x75, 0x6c, 0x69, 0x73, 0x72, 0x74, 0x6c, 0x6f, 0x62, 0x78, 0x68, 0x6b, 0x6d, 0x77, 0x63,
+ 0x6d, 0x67, 0x64, 0x77, 0x6f, 0x64, 0x72, 0x77, 0x6b, 0x6f, 0x6c, 0x6c, 0x6e, 0x6c, 0x63, 0x77,
+ 0x73, 0x77, 0x6c, 0x71, 0x73, 0x73, 0x6f, 0x61, 0x64, 0x65, 0x69, 0x78, 0x63, 0x66, 0x6f, 0x66,
+ 0x72, 0x77, 0x69, 0x64, 0x6c, 0x72, 0x6b, 0x6d, 0x72, 0x6c, 0x6d, 0x64, 0x71, 0x6b, 0x69, 0x61,
+ 0x75, 0x66, 0x65, 0x62, 0x6e, 0x6f, 0x68, 0x75, 0x6a, 0x6d, 0x71, 0x74, 0x78, 0x71, 0x75, 0x6a,
+ 0x75, 0x6f, 0x68, 0x6d, 0x68, 0x68, 0x78, 0x61, 0x75, 0x71, 0x75, 0x62, 0x79, 0x6c, 0x75, 0x73,
+ 0x72, 0x6b, 0x66, 0x62, 0x71, 0x71, 0x79, 0x69, 0x6f, 0x6d, 0x69, 0x6d, 0x79, 0x6a, 0x66, 0x69,
+ 0x6a, 0x66, 0x79, 0x61, 0x63, 0x65, 0x69, 0x67, 0x61, 0x73, 0x71, 0x65, 0x72, 0x73, 0x6b, 0x6b,
+ 0x70, 0x6f, 0x78, 0x69, 0x78, 0x6c, 0x78, 0x61, 0x66, 0x6b, 0x69, 0x69, 0x64, 0x6c, 0x74, 0x70,
+ 0x78, 0x75, 0x6f, 0x73, 0x79, 0x6f, 0x6a, 0x6e, 0x74, 0x70, 0x70, 0x66, 0x6c, 0x64, 0x79, 0x67,
+ 0x78, 0x6e, 0x6c, 0x78, 0x71, 0x6c, 0x74, 0x62, 0x6e, 0x77, 0x6c, 0x66, 0x71, 0x6f, 0x76, 0x79,
+ 0x70, 0x71, 0x76, 0x67, 0x73, 0x76, 0x76, 0x77, 0x64, 0x61, 0x6c, 0x68, 0x61, 0x61, 0x78, 0x74,
+ 0x72, 0x78, 0x6d, 0x6d, 0x64, 0x6d, 0x72, 0x65, 0x6a, 0x6a, 0x79, 0x63, 0x6c, 0x62, 0x66, 0x75,
+ 0x79, 0x72, 0x68, 0x71, 0x71, 0x64, 0x6f, 0x79, 0x63, 0x72, 0x6c, 0x6b, 0x64, 0x63, 0x75, 0x68,
+ 0x71, 0x72, 0x77, 0x72, 0x6c, 0x6b, 0x75, 0x6d, 0x6e, 0x6c, 0x67, 0x75, 0x66, 0x65, 0x68, 0x6d,
+ 0x79, 0x70, 0x6c, 0x73, 0x79, 0x6b, 0x6c, 0x6d, 0x72, 0x6f, 0x70, 0x76, 0x78, 0x6d, 0x6f, 0x61,
+ 0x68, 0x76, 0x6f, 0x6a, 0x66, 0x77, 0x72, 0x6e, 0x62, 0x71, 0x6b, 0x71, 0x63, 0x65, 0x71, 0x65,
+ 0x6d, 0x70, 0x65, 0x6e, 0x6d, 0x77, 0x61, 0x66, 0x76, 0x6e, 0x79, 0x71, 0x76, 0x63, 0x70, 0x76,
+ 0x75, 0x6b, 0x74, 0x65, 0x6f, 0x67, 0x6e, 0x79, 0x6f, 0x61, 0x72, 0x6e, 0x6a, 0x61, 0x6d, 0x63,
+ 0x66, 0x6c, 0x63, 0x6a, 0x6e, 0x62, 0x62, 0x77, 0x63, 0x75, 0x71, 0x78, 0x65, 0x79, 0x6e, 0x79,
+ 0x6a, 0x65, 0x78, 0x73, 0x73, 0x63, 0x69, 0x74, 0x79, 0x66, 0x73, 0x71, 0x74, 0x63, 0x62, 0x76,
+ 0x73, 0x6a, 0x67, 0x78, 0x64, 0x77, 0x69, 0x78, 0x6b, 0x62, 0x79, 0x78, 0x67, 0x67, 0x76, 0x72,
+ 0x75, 0x70, 0x66, 0x68, 0x76, 0x6e, 0x6e, 0x6e, 0x75, 0x64, 0x77, 0x6a, 0x65, 0x6a, 0x70, 0x77,
+ 0x65, 0x79, 0x67, 0x6e, 0x61, 0x79, 0x6b, 0x77, 0x77, 0x78, 0x6e, 0x63, 0x72, 0x68, 0x79, 0x67,
+ 0x76, 0x77, 0x68, 0x6c, 0x78, 0x72, 0x65, 0x73, 0x71, 0x6f, 0x78, 0x78, 0x77, 0x71, 0x70, 0x64,
+ 0x6a, 0x63, 0x76, 0x6f, 0x68, 0x70, 0x72, 0x6f, 0x63, 0x72, 0x67, 0x67, 0x76, 0x77, 0x65, 0x72,
+ 0x6d, 0x75, 0x74, 0x63, 0x6b, 0x73, 0x6b, 0x73, 0x6c, 0x6b, 0x6e, 0x70, 0x75, 0x76, 0x67, 0x6e,
+ 0x68, 0x66, 0x75, 0x70, 0x78, 0x69, 0x69, 0x6c, 0x6b, 0x66, 0x77, 0x67, 0x78, 0x66, 0x69, 0x6b,
+ 0x72, 0x61, 0x64, 0x64, 0x78, 0x62, 0x68, 0x72, 0x76, 0x73, 0x78, 0x71, 0x65, 0x6f, 0x63, 0x79,
+ 0x70, 0x74, 0x77, 0x78, 0x75, 0x79, 0x75, 0x66, 0x65, 0x72, 0x70, 0x78, 0x6c, 0x79, 0x70, 0x76,
+ 0x61, 0x73, 0x64, 0x79, 0x76, 0x74, 0x79, 0x77, 0x66, 0x65, 0x61, 0x63, 0x76, 0x75, 0x64, 0x75,
+ 0x6e, 0x70, 0x63, 0x6b, 0x6c, 0x79, 0x62, 0x67, 0x67, 0x74, 0x73, 0x72, 0x68, 0x6f, 0x67, 0x62,
+ 0x6e, 0x6b, 0x68, 0x62, 0x64, 0x62, 0x70, 0x71, 0x61, 0x74, 0x6d, 0x6c, 0x78, 0x6b, 0x6b, 0x75,
+ 0x68, 0x69, 0x6e, 0x6d, 0x66, 0x76, 0x6a, 0x63, 0x68, 0x72, 0x6d, 0x63, 0x6d, 0x6b, 0x6c, 0x78,
+ 0x73, 0x72, 0x6b, 0x6c, 0x64, 0x73, 0x71, 0x6e, 0x76, 0x73, 0x69, 0x6c, 0x77, 0x72, 0x73, 0x72,
+ 0x74, 0x78, 0x61, 0x65, 0x6b, 0x6e, 0x6f, 0x62, 0x75, 0x6c, 0x69, 0x63, 0x77, 0x6d, 0x78, 0x76,
+ 0x72, 0x65, 0x75, 0x6d, 0x6f, 0x70, 0x63, 0x6b, 0x62, 0x64, 0x75, 0x6d, 0x62, 0x64, 0x77, 0x6d,
+ 0x6b, 0x70, 0x70, 0x72, 0x77, 0x64, 0x65, 0x74, 0x62, 0x69, 0x63, 0x65, 0x69, 0x71, 0x71, 0x6f,
+ 0x62, 0x67, 0x67, 0x6a, 0x77, 0x79, 0x75, 0x69, 0x77, 0x6e, 0x62, 0x77, 0x61, 0x67, 0x62, 0x74,
+ 0x79, 0x76, 0x78, 0x71, 0x78, 0x70, 0x65, 0x61, 0x65, 0x76, 0x76, 0x70, 0x69, 0x73, 0x6d, 0x75,
+ 0x6a, 0x76, 0x6a, 0x72, 0x6a, 0x73, 0x74, 0x6a, 0x76, 0x74, 0x6d, 0x70, 0x79, 0x74, 0x6b, 0x76,
+ 0x64, 0x68, 0x62, 0x76, 0x6c, 0x79, 0x77, 0x71, 0x64, 0x75, 0x6a, 0x77, 0x76, 0x6e, 0x6d, 0x74,
+ 0x65, 0x71, 0x76, 0x6a, 0x67, 0x62, 0x70, 0x71, 0x64, 0x6b, 0x71, 0x63, 0x6b, 0x72, 0x71, 0x70,
+ 0x68, 0x64, 0x72, 0x70, 0x70, 0x6d, 0x6e, 0x61, 0x77, 0x64, 0x78, 0x67, 0x70, 0x6b, 0x71, 0x63,
+ 0x66, 0x74, 0x64, 0x75, 0x72, 0x72, 0x67, 0x66, 0x75, 0x74, 0x63, 0x62, 0x71, 0x76, 0x6f, 0x69,
+ 0x65, 0x6e, 0x6f, 0x62, 0x70, 0x72, 0x74, 0x6a, 0x6c, 0x72, 0x78, 0x72, 0x66, 0x78, 0x6d, 0x75,
+ 0x79, 0x63, 0x67, 0x6c, 0x74, 0x66, 0x79, 0x71, 0x67, 0x68, 0x75, 0x67, 0x78, 0x78, 0x71, 0x6a,
+ 0x74, 0x6f, 0x62, 0x76, 0x63, 0x63, 0x65, 0x76, 0x64, 0x68, 0x71, 0x6f, 0x65, 0x61, 0x6b, 0x72,
+ 0x67, 0x64, 0x66, 0x63, 0x6e, 0x68, 0x74, 0x66, 0x6d, 0x67, 0x70, 0x6d, 0x6c, 0x65, 0x68, 0x6f,
+ 0x62, 0x65, 0x63, 0x68, 0x62, 0x66, 0x76, 0x61, 0x67, 0x61, 0x62, 0x6f, 0x74, 0x71, 0x78, 0x65,
+ 0x68, 0x6a, 0x6c, 0x66, 0x79, 0x61, 0x6b, 0x6c, 0x6c, 0x6d, 0x64, 0x62, 0x72, 0x67, 0x77, 0x75,
+ 0x6d, 0x69, 0x70, 0x6b, 0x62, 0x6b, 0x70, 0x62, 0x75, 0x79, 0x6e, 0x78, 0x6f, 0x75, 0x6a, 0x66,
+ 0x73, 0x68, 0x71, 0x6b, 0x77, 0x76, 0x66, 0x72, 0x6e, 0x6c, 0x72, 0x6d, 0x68, 0x6e, 0x6b, 0x6c,
+ 0x72, 0x78, 0x70, 0x62, 0x6b, 0x67, 0x65, 0x6b, 0x6a, 0x61, 0x68, 0x6a, 0x61, 0x67, 0x63, 0x73,
+ 0x62, 0x73, 0x6d, 0x62, 0x70, 0x72, 0x77, 0x67, 0x6c, 0x68, 0x67, 0x6b, 0x74, 0x79, 0x65, 0x79,
+ 0x78, 0x72, 0x6a, 0x75, 0x76, 0x6b, 0x66, 0x6e, 0x6e, 0x70, 0x71, 0x61, 0x6b, 0x64, 0x61, 0x77,
+ 0x72, 0x63, 0x63, 0x61, 0x64, 0x68, 0x65, 0x75, 0x6c, 0x61, 0x70, 0x75, 0x76, 0x68, 0x69, 0x64,
+ 0x71, 0x68, 0x73, 0x78, 0x71, 0x71, 0x6a, 0x6d, 0x79, 0x74, 0x71, 0x63, 0x78, 0x74, 0x6e, 0x6f,
+ 0x70, 0x6f, 0x74, 0x73, 0x70, 0x6e, 0x74, 0x73, 0x76, 0x75, 0x69, 0x76, 0x63, 0x6b, 0x69, 0x67,
+ 0x70, 0x78, 0x63, 0x67, 0x67, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x68, 0x69, 0x71, 0x6f, 0x6d, 0x6c,
+ 0x72, 0x68, 0x76, 0x78, 0x76, 0x73, 0x6d, 0x65, 0x6a, 0x69, 0x76, 0x66, 0x62, 0x63, 0x71, 0x73,
+ 0x73, 0x62, 0x6d, 0x66, 0x67, 0x68, 0x61, 0x75, 0x67, 0x6b, 0x70, 0x6e, 0x6d, 0x66, 0x61, 0x74,
+ 0x76, 0x67, 0x6a, 0x6d, 0x69, 0x6b, 0x75, 0x68, 0x6a, 0x65, 0x6f, 0x6f, 0x64, 0x70, 0x76, 0x67,
+ 0x69, 0x63, 0x70, 0x63, 0x64, 0x75, 0x6c, 0x67, 0x6d, 0x76, 0x67, 0x6a, 0x64, 0x62, 0x6b, 0x65,
+ 0x72, 0x71, 0x6c, 0x68, 0x6d, 0x78, 0x66, 0x73, 0x75, 0x6f, 0x72, 0x63, 0x77, 0x6b, 0x77, 0x71,
+ 0x68, 0x62, 0x6f, 0x6c, 0x77, 0x6c, 0x69, 0x6b, 0x6b, 0x6b, 0x76, 0x66, 0x76, 0x72, 0x6f, 0x67,
+ 0x63, 0x70, 0x66, 0x78, 0x6b, 0x76, 0x6a, 0x74, 0x62, 0x67, 0x61, 0x6c, 0x75, 0x63, 0x68, 0x76,
+ 0x6c, 0x6e, 0x75, 0x76, 0x63, 0x6a, 0x6f, 0x64, 0x75, 0x65, 0x61, 0x6d, 0x72, 0x63, 0x62, 0x69,
+ 0x76, 0x66, 0x6e, 0x6a, 0x6c, 0x63, 0x68, 0x68, 0x71, 0x69, 0x6b, 0x62, 0x66, 0x69, 0x63, 0x6f,
+ 0x6d, 0x6f, 0x70, 0x76, 0x77, 0x63, 0x69, 0x62, 0x76, 0x70, 0x6e, 0x76, 0x78, 0x69, 0x71, 0x73,
+ 0x75, 0x6b, 0x6d, 0x78, 0x61, 0x64, 0x78, 0x64, 0x6e, 0x76, 0x72, 0x66, 0x64, 0x72, 0x66, 0x67,
+ 0x6e, 0x66, 0x67, 0x78, 0x69, 0x6d, 0x62, 0x6e, 0x72, 0x63, 0x71, 0x6c, 0x67, 0x62, 0x6a, 0x63,
+ 0x70, 0x71, 0x6c, 0x71, 0x66, 0x66, 0x67, 0x76, 0x69, 0x6a, 0x65, 0x72, 0x64, 0x6e, 0x67, 0x6c,
+ 0x68, 0x6b, 0x6c, 0x65, 0x6b, 0x70, 0x6b, 0x6a, 0x66, 0x73, 0x70, 0x77, 0x71, 0x61, 0x78, 0x74,
+ 0x63, 0x6c, 0x75, 0x6a, 0x62, 0x77, 0x69, 0x6d, 0x6d, 0x6e, 0x65, 0x75, 0x71, 0x76, 0x66, 0x77,
+ 0x78, 0x78, 0x72, 0x72, 0x68, 0x79, 0x66, 0x68, 0x65, 0x6e, 0x6d, 0x68, 0x6b, 0x65, 0x70, 0x66,
+ 0x72, 0x65, 0x79, 0x65, 0x79, 0x73, 0x78, 0x62, 0x62, 0x69, 0x75, 0x63, 0x74, 0x6c, 0x72, 0x74,
+ 0x73, 0x71, 0x64, 0x6c, 0x75, 0x77, 0x6a, 0x6a, 0x68, 0x64, 0x68, 0x76, 0x71, 0x6d, 0x74, 0x6f,
+ 0x66, 0x75, 0x70, 0x71, 0x62, 0x64, 0x64, 0x74, 0x6b, 0x72, 0x70, 0x71, 0x6d, 0x79, 0x68, 0x61,
+ 0x64, 0x6c, 0x61, 0x75, 0x72, 0x79, 0x77, 0x69, 0x6d, 0x67, 0x64, 0x78, 0x6c, 0x6b, 0x63, 0x78,
+ 0x6a, 0x71, 0x63, 0x67, 0x6d, 0x68, 0x64, 0x61, 0x73, 0x6a, 0x73, 0x68, 0x74, 0x69, 0x6a, 0x72,
+ 0x64, 0x6c, 0x77, 0x70, 0x72, 0x6b, 0x68, 0x6c, 0x6d, 0x6d, 0x63, 0x63, 0x6c, 0x6f, 0x78, 0x63,
+ 0x61, 0x79, 0x6a, 0x64, 0x6a, 0x70, 0x72, 0x71, 0x70, 0x74, 0x66, 0x73, 0x6d, 0x79, 0x6d, 0x6a,
+ 0x62, 0x71, 0x6f, 0x79, 0x66, 0x69, 0x65, 0x78, 0x61, 0x73, 0x61, 0x6b, 0x78, 0x70, 0x78, 0x64,
+ 0x6e, 0x6d, 0x79, 0x6b, 0x77, 0x75, 0x78, 0x78, 0x68, 0x61, 0x6f, 0x6d, 0x78, 0x79, 0x6a, 0x77,
+ 0x6b, 0x77, 0x69, 0x64, 0x74, 0x6e, 0x63, 0x76, 0x67, 0x65, 0x73, 0x61, 0x72, 0x73, 0x78, 0x66,
+ 0x6b, 0x6c, 0x64, 0x6e, 0x78, 0x73, 0x73, 0x68, 0x6e, 0x73, 0x76, 0x62, 0x6e, 0x6d, 0x66, 0x6d,
+ 0x64, 0x79, 0x62, 0x6a, 0x6e, 0x72, 0x71, 0x64, 0x70, 0x79, 0x6a, 0x62, 0x64, 0x65, 0x61, 0x73,
+ 0x74, 0x6e, 0x63, 0x6b, 0x6a, 0x6f, 0x66, 0x68, 0x6e, 0x67, 0x68, 0x77, 0x73, 0x68, 0x61, 0x63,
+ 0x74, 0x73, 0x62, 0x6a, 0x69, 0x75, 0x72, 0x6d, 0x77, 0x6c, 0x6e, 0x72, 0x6f, 0x70, 0x6d, 0x76,
+ 0x71, 0x6e, 0x76, 0x66, 0x6e, 0x6e, 0x75, 0x70, 0x69, 0x72, 0x66, 0x61, 0x6f, 0x6d, 0x66, 0x75,
+ 0x65, 0x68, 0x69, 0x64, 0x78, 0x6c, 0x6f, 0x6b, 0x66, 0x72, 0x6a, 0x77, 0x64, 0x72, 0x72, 0x61,
+ 0x6f, 0x67, 0x61, 0x64, 0x6c, 0x76, 0x70, 0x6c, 0x66, 0x76, 0x79, 0x77, 0x6b, 0x6e, 0x6e, 0x67,
+ 0x76, 0x76, 0x75, 0x6a, 0x64, 0x70, 0x71, 0x63, 0x67, 0x6a, 0x6e, 0x63, 0x73, 0x76, 0x6e, 0x78,
+ 0x62, 0x6e, 0x63, 0x6f, 0x74, 0x65, 0x64, 0x6f, 0x6f, 0x62, 0x79, 0x75, 0x72, 0x69, 0x61, 0x78,
+ 0x72, 0x75, 0x6e, 0x6b, 0x64, 0x67, 0x6e, 0x72, 0x6f, 0x73, 0x68, 0x79, 0x68, 0x61, 0x6b, 0x6d,
+ 0x72, 0x64, 0x6f, 0x71, 0x72, 0x64, 0x6b, 0x71, 0x6c, 0x62, 0x68, 0x6b, 0x6a, 0x6d, 0x64, 0x6b,
+ 0x71, 0x76, 0x6c, 0x63, 0x72, 0x61, 0x73, 0x62, 0x6f, 0x6f, 0x6c, 0x69, 0x75, 0x64, 0x63, 0x70,
+ 0x6b, 0x6e, 0x6f, 0x6f, 0x6c, 0x65, 0x6f, 0x78, 0x6a, 0x74, 0x64, 0x79, 0x62, 0x64, 0x69, 0x79,
+ 0x63, 0x6c, 0x77, 0x6f, 0x74, 0x6a, 0x68, 0x61, 0x6a, 0x68, 0x74, 0x6a, 0x70, 0x68, 0x72, 0x68,
+ 0x63, 0x6f, 0x71, 0x77, 0x78, 0x76, 0x6a, 0x6e, 0x76, 0x73, 0x67, 0x69, 0x6f, 0x73, 0x68, 0x71,
+ 0x69, 0x6c, 0x73, 0x73, 0x79, 0x67, 0x79, 0x61, 0x64, 0x70, 0x61, 0x71, 0x68, 0x73, 0x6b, 0x65,
+ 0x64, 0x75, 0x76, 0x69, 0x73, 0x6e, 0x67, 0x77, 0x6a, 0x72, 0x62, 0x74, 0x76, 0x63, 0x6f, 0x73,
+ 0x64, 0x64, 0x62, 0x69, 0x72, 0x6b, 0x71, 0x63, 0x73, 0x73, 0x64, 0x69, 0x6e, 0x73, 0x64, 0x6d,
+ 0x63, 0x70, 0x67, 0x76, 0x61, 0x79, 0x6d, 0x73, 0x74, 0x6c, 0x79, 0x6d, 0x74, 0x68, 0x65, 0x77,
+ 0x63, 0x66, 0x77, 0x6e, 0x64, 0x69, 0x6a, 0x62, 0x65, 0x6e, 0x79, 0x6b, 0x6c, 0x6e, 0x79, 0x6e,
+ 0x65, 0x76, 0x6b, 0x6e, 0x62, 0x6a, 0x72, 0x73, 0x72, 0x76, 0x69, 0x71, 0x62, 0x76, 0x63, 0x69,
+ 0x6c, 0x63, 0x64, 0x70, 0x6d, 0x6b, 0x63, 0x6c, 0x61, 0x77, 0x71, 0x74, 0x61, 0x71, 0x6c, 0x65,
+ 0x65, 0x70, 0x70, 0x67, 0x6c, 0x77, 0x6e, 0x74, 0x64, 0x79, 0x62, 0x64, 0x63, 0x77, 0x79, 0x73,
+ 0x73, 0x66, 0x68, 0x70, 0x65, 0x61, 0x67, 0x72, 0x61, 0x6a, 0x68, 0x66, 0x62, 0x72, 0x6d, 0x6e,
+ 0x65, 0x78, 0x6b, 0x6b, 0x78, 0x74, 0x72, 0x6b, 0x71, 0x67, 0x6a, 0x69, 0x71, 0x65, 0x6d, 0x76,
+ 0x79, 0x69, 0x61, 0x65, 0x70, 0x68, 0x6e, 0x64, 0x6a, 0x77, 0x69, 0x6c, 0x6b, 0x6b, 0x63, 0x62,
+ 0x6d, 0x63, 0x76, 0x77, 0x67, 0x6e, 0x6b, 0x6d, 0x67, 0x71, 0x69, 0x78, 0x6e, 0x6d, 0x6b, 0x78,
+ 0x65, 0x6c, 0x61, 0x77, 0x6f, 0x64, 0x79, 0x62, 0x63, 0x61, 0x6b, 0x66, 0x72, 0x79, 0x69, 0x65,
+ 0x76, 0x77, 0x65, 0x6d, 0x6c, 0x69, 0x62, 0x6f, 0x79, 0x77, 0x6d, 0x78, 0x6d, 0x62, 0x6f, 0x78,
+ 0x68, 0x77, 0x6d, 0x70, 0x72, 0x6d, 0x63, 0x71, 0x79, 0x65, 0x61, 0x66, 0x78, 0x66, 0x63, 0x6e,
+ 0x69, 0x68, 0x73, 0x74, 0x6d, 0x6f, 0x76, 0x71, 0x64, 0x75, 0x77, 0x63, 0x6c, 0x63, 0x70, 0x61,
+ 0x6d, 0x77, 0x76, 0x78, 0x72, 0x71, 0x6f, 0x61, 0x74, 0x62, 0x64, 0x68, 0x64, 0x6a, 0x67, 0x75,
+ 0x71, 0x79, 0x66, 0x70, 0x64, 0x72, 0x62, 0x6b, 0x65, 0x65, 0x66, 0x79, 0x6d, 0x75, 0x70, 0x6a,
+ 0x61, 0x75, 0x67, 0x65, 0x70, 0x6c, 0x65, 0x61, 0x77, 0x72, 0x67, 0x73, 0x74, 0x68, 0x77, 0x70,
+ 0x65, 0x6c, 0x74, 0x75, 0x73, 0x74, 0x6d, 0x61, 0x64, 0x6f, 0x79, 0x78, 0x79, 0x62, 0x75, 0x6b,
+ 0x66, 0x79, 0x78, 0x75, 0x73, 0x71, 0x75, 0x76, 0x6a, 0x63, 0x73, 0x68, 0x65, 0x71, 0x6e, 0x62,
+ 0x75, 0x77, 0x74, 0x68, 0x70, 0x77, 0x6c, 0x66, 0x6d, 0x79, 0x65, 0x64, 0x66, 0x69, 0x70, 0x66,
+ 0x74, 0x66, 0x6c, 0x77, 0x71, 0x6f, 0x6d, 0x78, 0x6d, 0x6d, 0x70, 0x73, 0x77, 0x75, 0x79, 0x65,
+ 0x63, 0x6e, 0x76, 0x66, 0x63, 0x6d, 0x71, 0x65, 0x67, 0x74, 0x73, 0x6b, 0x6f, 0x70, 0x62, 0x74,
+ 0x78, 0x68, 0x6d, 0x67, 0x72, 0x66, 0x75, 0x77, 0x69, 0x68, 0x62, 0x70, 0x74, 0x75, 0x6f, 0x63,
+ 0x68, 0x70, 0x79, 0x6e, 0x77, 0x61, 0x68, 0x6d, 0x6d, 0x68, 0x74, 0x78, 0x76, 0x78, 0x76, 0x74,
+ 0x65, 0x75, 0x6f, 0x70, 0x65, 0x79, 0x6f, 0x73, 0x63, 0x61, 0x6b, 0x76, 0x6c, 0x74, 0x75, 0x75,
+ 0x68, 0x6f, 0x65, 0x6d, 0x74, 0x68, 0x63, 0x71, 0x67, 0x64, 0x72, 0x63, 0x67, 0x6b, 0x68, 0x74,
+ 0x69, 0x65, 0x6c, 0x73, 0x74, 0x63, 0x75, 0x69, 0x71, 0x65, 0x75, 0x67, 0x6b, 0x79, 0x79, 0x6f,
+ 0x75, 0x63, 0x6d, 0x66, 0x75, 0x6f, 0x6d, 0x6d, 0x73, 0x63, 0x6d, 0x73, 0x70, 0x71, 0x76, 0x74,
+ 0x71, 0x72, 0x77, 0x71, 0x70, 0x76, 0x65, 0x79, 0x72, 0x6d, 0x6d, 0x76, 0x78, 0x65, 0x77, 0x72,
+ 0x78, 0x72, 0x6c, 0x75, 0x76, 0x76, 0x72, 0x72, 0x77, 0x6b, 0x68, 0x6a, 0x73, 0x6c, 0x6f, 0x6d,
+ 0x79, 0x68, 0x75, 0x62, 0x6c, 0x6d, 0x69, 0x68, 0x78, 0x62, 0x65, 0x79, 0x72, 0x78, 0x6d, 0x6e,
+ 0x68, 0x69, 0x6d, 0x74, 0x63, 0x65, 0x66, 0x70, 0x77, 0x62, 0x6b, 0x64, 0x75, 0x6e, 0x72, 0x6a,
+ 0x76, 0x6e, 0x75, 0x6f, 0x6e, 0x61, 0x71, 0x72, 0x74, 0x76, 0x61, 0x61, 0x74, 0x66, 0x66, 0x61,
+ 0x78, 0x72, 0x70, 0x62, 0x65, 0x73, 0x79, 0x64, 0x75, 0x67, 0x75, 0x66, 0x71, 0x73, 0x73, 0x70,
+ 0x69, 0x73, 0x6d, 0x6c, 0x70, 0x64, 0x69, 0x6f, 0x74, 0x78, 0x70, 0x72, 0x78, 0x71, 0x73, 0x63,
+ 0x62, 0x70, 0x76, 0x6c, 0x6b, 0x62, 0x61, 0x76, 0x73, 0x67, 0x77, 0x75, 0x6a, 0x72, 0x65, 0x64,
+ 0x62, 0x79, 0x6e, 0x76, 0x6d, 0x65, 0x6c, 0x62, 0x67, 0x63, 0x62, 0x77, 0x72, 0x78, 0x65, 0x77,
+ 0x71, 0x72, 0x64, 0x75, 0x69, 0x77, 0x6c, 0x6f, 0x77, 0x61, 0x69, 0x6d, 0x69, 0x76, 0x63, 0x72,
+ 0x79, 0x79, 0x6a, 0x6f, 0x63, 0x72, 0x74, 0x62, 0x78, 0x77, 0x6d, 0x75, 0x62, 0x6c, 0x6b, 0x6f,
+ 0x74, 0x63, 0x73, 0x70, 0x74, 0x6d, 0x6a, 0x70, 0x6f, 0x6a, 0x6f, 0x66, 0x61, 0x66, 0x71, 0x75,
+ 0x74, 0x73, 0x6a, 0x6c, 0x6d, 0x79, 0x68, 0x71, 0x78, 0x75, 0x6d, 0x6f, 0x70, 0x74, 0x70, 0x76,
+ 0x71, 0x6a, 0x61, 0x62, 0x67, 0x69, 0x6d, 0x62, 0x74, 0x6b, 0x67, 0x73, 0x66, 0x6f, 0x6d, 0x6c,
+ 0x79, 0x69, 0x69, 0x6d, 0x67, 0x6d, 0x71, 0x66, 0x73, 0x66, 0x6e, 0x65, 0x6c, 0x75, 0x79, 0x68,
+ 0x6a, 0x74, 0x79, 0x70, 0x72, 0x73, 0x6b, 0x65, 0x6d, 0x6a, 0x6e, 0x6a, 0x76, 0x6d, 0x6d, 0x6a,
+ 0x6b, 0x68, 0x65, 0x6a, 0x68, 0x71, 0x6b, 0x63, 0x73, 0x6a, 0x78, 0x68, 0x6f, 0x76, 0x70, 0x75,
+ 0x73, 0x72, 0x78, 0x6f, 0x74, 0x6c, 0x6f, 0x6d, 0x6c, 0x74, 0x6b, 0x73, 0x71, 0x66, 0x6a, 0x64,
+ 0x6a, 0x68, 0x6b, 0x67, 0x72, 0x78, 0x76, 0x68, 0x70, 0x69, 0x6e, 0x75, 0x6d, 0x76, 0x6b, 0x62,
+ 0x6b, 0x61, 0x78, 0x6f, 0x77, 0x72, 0x75, 0x76, 0x6e, 0x69, 0x6f, 0x6c, 0x6d, 0x68, 0x6e, 0x6b,
+ 0x6d, 0x75, 0x6c, 0x75, 0x72, 0x75, 0x78, 0x6b, 0x73, 0x71, 0x6b, 0x76, 0x63, 0x63, 0x6f, 0x6b,
+ 0x75, 0x6a, 0x6a, 0x74, 0x6c, 0x78, 0x73, 0x74, 0x75, 0x71, 0x6e, 0x76, 0x77, 0x66, 0x6e, 0x61,
+ 0x66, 0x62, 0x63, 0x65, 0x72, 0x63, 0x67, 0x77, 0x62, 0x6a, 0x71, 0x75, 0x6a, 0x6f, 0x63, 0x71,
+ 0x77, 0x72, 0x74, 0x6e, 0x71, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x66, 0x77, 0x61, 0x68, 0x74, 0x70,
+ 0x64, 0x76, 0x62, 0x65, 0x64, 0x68, 0x63, 0x68, 0x77, 0x75, 0x6b, 0x79, 0x77, 0x70, 0x61, 0x63,
+ 0x6b, 0x64, 0x6a, 0x79, 0x72, 0x6f, 0x73, 0x62, 0x65, 0x69, 0x71, 0x6d, 0x66, 0x61, 0x73, 0x6e,
+ 0x6b, 0x63, 0x65, 0x77, 0x73, 0x72, 0x77, 0x62, 0x63, 0x75, 0x67, 0x62, 0x65, 0x62, 0x6b, 0x76,
+ 0x61, 0x71, 0x78, 0x76, 0x61, 0x6d, 0x70, 0x65, 0x6f, 0x68, 0x77, 0x64, 0x61, 0x6a, 0x6f, 0x67,
+ 0x6b, 0x67, 0x68, 0x75, 0x71, 0x66, 0x73, 0x69, 0x75, 0x62, 0x6a, 0x6c, 0x72, 0x71, 0x68, 0x69,
+ 0x61, 0x6a, 0x6a, 0x79, 0x71, 0x6c, 0x6d, 0x76, 0x6f, 0x68, 0x74, 0x77, 0x6a, 0x63, 0x62, 0x6d,
+ 0x6a, 0x6f, 0x74, 0x61, 0x6f, 0x6b, 0x69, 0x64, 0x72, 0x6b, 0x67, 0x75, 0x76, 0x77, 0x78, 0x61,
+ 0x6c, 0x70, 0x69, 0x6f, 0x69, 0x62, 0x61, 0x67, 0x72, 0x62, 0x67, 0x65, 0x67, 0x6e, 0x62, 0x6c,
+ 0x66, 0x69, 0x69, 0x6b, 0x6a, 0x6d, 0x6a, 0x70, 0x6d, 0x73, 0x6f, 0x77, 0x63, 0x75, 0x6c, 0x69,
+ 0x65, 0x6c, 0x79, 0x6d, 0x79, 0x71, 0x6d, 0x61, 0x74, 0x6b, 0x78, 0x75, 0x6b, 0x6c, 0x73, 0x63,
+ 0x6d, 0x6c, 0x6c, 0x78, 0x74, 0x6f, 0x6f, 0x67, 0x71, 0x67, 0x78, 0x71, 0x64, 0x65, 0x63, 0x6b,
+ 0x69, 0x74, 0x6d, 0x74, 0x6b, 0x6b, 0x78, 0x61, 0x74, 0x66, 0x75, 0x77, 0x66, 0x68, 0x69, 0x6a,
+ 0x66, 0x65, 0x79, 0x78, 0x65, 0x6a, 0x6e, 0x71, 0x76, 0x79, 0x69, 0x71, 0x64, 0x76, 0x77, 0x67,
+ 0x61, 0x6d, 0x6c, 0x71, 0x66, 0x6c, 0x67, 0x64, 0x65, 0x62, 0x66, 0x77, 0x6c, 0x65, 0x74, 0x72,
+ 0x62, 0x64, 0x61, 0x6c, 0x6e, 0x6c, 0x75, 0x6e, 0x64, 0x74, 0x78, 0x63, 0x62, 0x62, 0x68, 0x73,
+ 0x61, 0x67, 0x79, 0x6f, 0x67, 0x6c, 0x66, 0x62, 0x75, 0x72, 0x76, 0x6f, 0x62, 0x61, 0x66, 0x73,
+ 0x74, 0x75, 0x6c, 0x62, 0x69, 0x6a, 0x68, 0x6b, 0x75, 0x77, 0x6d, 0x72, 0x6e, 0x62, 0x77, 0x6a,
+ 0x64, 0x72, 0x74, 0x67, 0x76, 0x79, 0x6c, 0x65, 0x79, 0x78, 0x79, 0x68, 0x76, 0x75, 0x78, 0x69,
+ 0x61, 0x78, 0x6e, 0x77, 0x64, 0x69, 0x61, 0x69, 0x74, 0x70, 0x66, 0x78, 0x75, 0x64, 0x6a, 0x68,
+ 0x6f, 0x67, 0x6c, 0x6e, 0x74, 0x70, 0x75, 0x74, 0x75, 0x6a, 0x61, 0x6d, 0x78, 0x76, 0x63, 0x67,
+ 0x6f, 0x66, 0x6b, 0x71, 0x6d, 0x74, 0x6c, 0x66, 0x63, 0x62, 0x73, 0x6c, 0x74, 0x63, 0x6c, 0x6d,
+ 0x72, 0x73, 0x6c, 0x77, 0x75, 0x6a, 0x77, 0x66, 0x75, 0x75, 0x73, 0x71, 0x68, 0x6d, 0x6d, 0x61,
+ 0x6d, 0x6b, 0x63, 0x63, 0x6c, 0x6d, 0x77, 0x74, 0x6c, 0x69, 0x65, 0x68, 0x6d, 0x67, 0x63, 0x70,
+ 0x63, 0x61, 0x6a, 0x71, 0x72, 0x67, 0x6f, 0x78, 0x73, 0x62, 0x70, 0x6f, 0x6f, 0x65, 0x6f, 0x6f,
+ 0x73, 0x75, 0x6d, 0x6a, 0x75, 0x70, 0x71, 0x61, 0x61, 0x72, 0x6a, 0x66, 0x72, 0x72, 0x62, 0x61,
+ 0x67, 0x63, 0x73, 0x79, 0x6e, 0x71, 0x62, 0x6a, 0x63, 0x66, 0x76, 0x6c, 0x64, 0x64, 0x68, 0x77,
+ 0x66, 0x6b, 0x6c, 0x6e, 0x6b, 0x61, 0x73, 0x6e, 0x73, 0x63, 0x78, 0x72, 0x62, 0x75, 0x6b, 0x63,
+ 0x6e, 0x6b, 0x6e, 0x65, 0x6a, 0x62, 0x62, 0x77, 0x6a, 0x77, 0x6c, 0x62, 0x70, 0x67, 0x68, 0x72,
+ 0x73, 0x6d, 0x71, 0x63, 0x69, 0x74, 0x68, 0x6c, 0x78, 0x69, 0x78, 0x65, 0x75, 0x6d, 0x63, 0x6b,
+ 0x64, 0x75, 0x65, 0x76, 0x6c, 0x72, 0x6e, 0x63, 0x79, 0x6d, 0x63, 0x6d, 0x67, 0x70, 0x66, 0x6b,
+ 0x76, 0x6a, 0x6b, 0x62, 0x75, 0x69, 0x76, 0x6c, 0x62, 0x76, 0x66, 0x72, 0x61, 0x6a, 0x67, 0x71,
+ 0x64, 0x74, 0x74, 0x70, 0x6c, 0x63, 0x73, 0x6e, 0x71, 0x74, 0x6b, 0x6a, 0x64, 0x62, 0x69, 0x68,
+ 0x65, 0x71, 0x6a, 0x6b, 0x62, 0x73, 0x6c, 0x67, 0x6d, 0x68, 0x78, 0x6d, 0x73, 0x69, 0x65, 0x74,
+ 0x6c, 0x61, 0x66, 0x74, 0x65, 0x65, 0x62, 0x6a, 0x6a, 0x6d, 0x71, 0x61, 0x69, 0x74, 0x70, 0x6c,
+ 0x68, 0x65, 0x65, 0x6b, 0x69, 0x79, 0x72, 0x71, 0x71, 0x74, 0x6f, 0x6e, 0x6c, 0x62, 0x74, 0x63,
+ 0x64, 0x69, 0x66, 0x70, 0x76, 0x6d, 0x6b, 0x72, 0x6c, 0x6a, 0x77, 0x61, 0x74, 0x72, 0x62, 0x65,
+ 0x68, 0x6e, 0x75, 0x62, 0x71, 0x6e, 0x63, 0x63, 0x64, 0x64, 0x62, 0x69, 0x74, 0x6c, 0x6e, 0x70,
+ 0x78, 0x69, 0x63, 0x65, 0x6a, 0x6f, 0x78, 0x6c, 0x6a, 0x65, 0x6e, 0x6c, 0x6c, 0x65, 0x67, 0x6e,
+ 0x68, 0x62, 0x63, 0x65, 0x6c, 0x62, 0x6f, 0x6e, 0x6d, 0x61, 0x6c, 0x61, 0x76, 0x69, 0x71, 0x77,
+ 0x74, 0x6a, 0x74, 0x74, 0x6d, 0x6a, 0x6a, 0x77, 0x70, 0x76, 0x70, 0x75, 0x6c, 0x6f, 0x6c, 0x75,
+ 0x69, 0x61, 0x6c, 0x75, 0x62, 0x6f, 0x68, 0x67, 0x69, 0x67, 0x61, 0x6d, 0x72, 0x72, 0x63, 0x6b,
+ 0x63, 0x70, 0x6c, 0x70, 0x69, 0x71, 0x6e, 0x76, 0x6f, 0x77, 0x74, 0x64, 0x76, 0x67, 0x6f, 0x77,
+ 0x76, 0x77, 0x6e, 0x61, 0x72, 0x76, 0x74, 0x65, 0x63, 0x63, 0x73, 0x65, 0x6d, 0x67, 0x69, 0x65,
+ 0x74, 0x6e, 0x77, 0x63, 0x78, 0x77, 0x63, 0x76, 0x68, 0x65, 0x76, 0x73, 0x64, 0x6a, 0x68, 0x6a,
+ 0x65, 0x72, 0x75, 0x70, 0x61, 0x6b, 0x71, 0x68, 0x74, 0x6f, 0x78, 0x6d, 0x74, 0x62, 0x61, 0x71,
+ 0x64, 0x6b, 0x6a, 0x76, 0x79, 0x61, 0x6c, 0x77, 0x6a, 0x68, 0x78, 0x62, 0x70, 0x78, 0x78, 0x65,
+ 0x64, 0x74, 0x6f, 0x71, 0x69, 0x75, 0x70, 0x78, 0x71, 0x78, 0x66, 0x65, 0x74, 0x6d, 0x6a, 0x75,
+ 0x66, 0x76, 0x70, 0x75, 0x69, 0x6c, 0x73, 0x76, 0x79, 0x63, 0x62, 0x68, 0x74, 0x6e, 0x69, 0x72,
+ 0x76, 0x6b, 0x62, 0x6d, 0x67, 0x65, 0x79, 0x70, 0x79, 0x6d, 0x65, 0x70, 0x79, 0x6a, 0x62, 0x61,
+ 0x76, 0x64, 0x65, 0x75, 0x69, 0x6a, 0x76, 0x70, 0x6c, 0x69, 0x74, 0x6b, 0x65, 0x6d, 0x69, 0x75,
+ 0x77, 0x68, 0x6b, 0x6f, 0x72, 0x66, 0x68, 0x75, 0x73, 0x63, 0x77, 0x69, 0x69, 0x6b, 0x68, 0x67,
+ 0x66, 0x6f, 0x71, 0x69, 0x63, 0x77, 0x74, 0x61, 0x6b, 0x70, 0x62, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
+ 0x78, 0x6e, 0x66, 0x69, 0x78, 0x6f, 0x64, 0x77, 0x61, 0x6c, 0x70, 0x75, 0x62, 0x70, 0x6d, 0x76,
+ 0x79, 0x72, 0x66, 0x64, 0x76, 0x75, 0x6f, 0x6f, 0x71, 0x73, 0x75, 0x73, 0x77, 0x79, 0x71, 0x63,
+ 0x76, 0x78, 0x73, 0x6e, 0x6d, 0x70, 0x69, 0x78, 0x75, 0x6c, 0x6e, 0x76, 0x76, 0x79, 0x6d, 0x68,
+ 0x71, 0x76, 0x63, 0x77, 0x67, 0x6d, 0x6a, 0x64, 0x6f, 0x73, 0x64, 0x70, 0x68, 0x67, 0x6f, 0x61,
+ 0x67, 0x6d, 0x76, 0x6f, 0x6c, 0x74, 0x72, 0x74, 0x76, 0x62, 0x70, 0x61, 0x64, 0x6f, 0x73, 0x63,
+ 0x64, 0x74, 0x68, 0x68, 0x76, 0x79, 0x6a, 0x66, 0x78, 0x6a, 0x63, 0x68, 0x6a, 0x61, 0x73, 0x65,
+ 0x65, 0x6c, 0x6f, 0x61, 0x61, 0x71, 0x6b, 0x72, 0x6e, 0x71, 0x62, 0x6f, 0x66, 0x77, 0x72, 0x66,
+ 0x63, 0x75, 0x73, 0x65, 0x62, 0x66, 0x74, 0x69, 0x71, 0x66, 0x72, 0x67, 0x61, 0x6c, 0x68, 0x64,
+ 0x63, 0x66, 0x73, 0x78, 0x74, 0x63, 0x64, 0x71, 0x71, 0x75, 0x77, 0x6a, 0x71, 0x6a, 0x6e, 0x6b,
+ 0x77, 0x64, 0x66, 0x68, 0x69, 0x77, 0x77, 0x71, 0x62, 0x73, 0x64, 0x70, 0x71, 0x65, 0x63, 0x71,
+ 0x71, 0x70, 0x6e, 0x6a, 0x75, 0x6f, 0x70, 0x63, 0x67, 0x65, 0x72, 0x76, 0x77, 0x79, 0x72, 0x6b,
+ 0x68, 0x77, 0x75, 0x68, 0x6a, 0x66, 0x6f, 0x6c, 0x79, 0x76, 0x66, 0x65, 0x62, 0x72, 0x75, 0x62,
+ 0x69, 0x65, 0x77, 0x72, 0x77, 0x74, 0x69, 0x6f, 0x76, 0x6e, 0x76, 0x6a, 0x68, 0x76, 0x6f, 0x75,
+ 0x77, 0x6f, 0x75, 0x6c, 0x67, 0x74, 0x6d, 0x75, 0x79, 0x62, 0x6c, 0x6c, 0x66, 0x6e, 0x76, 0x62,
+ 0x6a, 0x71, 0x65, 0x66, 0x77, 0x62, 0x6b, 0x71, 0x61, 0x66, 0x71, 0x76, 0x6b, 0x6c, 0x72, 0x6c,
+ 0x63, 0x63, 0x68, 0x68, 0x6a, 0x69, 0x6a, 0x63, 0x75, 0x71, 0x68, 0x6b, 0x6d, 0x61, 0x76, 0x76,
+ 0x6e, 0x6a, 0x6a, 0x67, 0x68, 0x6f, 0x65, 0x67, 0x72, 0x6f, 0x70, 0x70, 0x6b, 0x63, 0x64, 0x75,
+ 0x63, 0x67, 0x65, 0x71, 0x69, 0x64, 0x66, 0x65, 0x6c, 0x72, 0x72, 0x66, 0x72, 0x74, 0x66, 0x75,
+ 0x65, 0x77, 0x6d, 0x69, 0x68, 0x6e, 0x72, 0x77, 0x6a, 0x61, 0x72, 0x67, 0x73, 0x76, 0x6b, 0x67,
+ 0x67, 0x6c, 0x75, 0x67, 0x61, 0x73, 0x78, 0x68, 0x72, 0x6b, 0x64, 0x69, 0x6d, 0x6c, 0x6e, 0x68,
+ 0x78, 0x76, 0x70, 0x67, 0x6c, 0x6f, 0x68, 0x63, 0x72, 0x76, 0x61, 0x69, 0x68, 0x71, 0x69, 0x6f,
+ 0x67, 0x6b, 0x61, 0x69, 0x6d, 0x73, 0x66, 0x6c, 0x64, 0x76, 0x71, 0x78, 0x79, 0x74, 0x62, 0x71,
+ 0x6b, 0x62, 0x71, 0x63, 0x69, 0x74, 0x61, 0x6a, 0x63, 0x75, 0x6f, 0x6a, 0x6b, 0x64, 0x61, 0x77,
+ 0x66, 0x61, 0x6c, 0x74, 0x71, 0x75, 0x61, 0x69, 0x6e, 0x69, 0x6a, 0x6b, 0x61, 0x65, 0x78, 0x68,
+ 0x62, 0x78, 0x75, 0x6c, 0x6b, 0x68, 0x79, 0x77, 0x6a, 0x76, 0x6f, 0x61, 0x63, 0x71, 0x65, 0x66,
+ 0x6e, 0x67, 0x73, 0x74, 0x62, 0x73, 0x73, 0x76, 0x78, 0x6b, 0x63, 0x78, 0x72, 0x6c, 0x6a, 0x67,
+ 0x69, 0x63, 0x78, 0x64, 0x63, 0x6d, 0x6a, 0x6f, 0x6d, 0x6d, 0x63, 0x74, 0x70, 0x64, 0x6d, 0x74,
+ 0x72, 0x77, 0x6a, 0x75, 0x73, 0x6c, 0x63, 0x68, 0x71, 0x71, 0x62, 0x75, 0x6d, 0x79, 0x70, 0x6f,
+ 0x77, 0x72, 0x62, 0x6a, 0x69, 0x6e, 0x76, 0x77, 0x61, 0x73, 0x71, 0x79, 0x78, 0x70, 0x78, 0x6e,
+ 0x64, 0x79, 0x78, 0x6e, 0x6e, 0x65, 0x78, 0x73, 0x68, 0x76, 0x79, 0x75, 0x65, 0x73, 0x65, 0x6c,
+ 0x72, 0x6d, 0x64, 0x76, 0x73, 0x76, 0x6e, 0x67, 0x73, 0x74, 0x6a, 0x62, 0x79, 0x79, 0x70, 0x64,
+ 0x71, 0x73, 0x6d, 0x64, 0x6a, 0x62, 0x66, 0x67, 0x70, 0x67, 0x74, 0x66, 0x6e, 0x78, 0x78, 0x6e,
+ 0x64, 0x6e, 0x64, 0x6f, 0x64, 0x6f, 0x73, 0x6d, 0x68, 0x79, 0x68, 0x67, 0x6f, 0x61, 0x66, 0x68,
+ 0x78, 0x73, 0x62, 0x6b, 0x61, 0x63, 0x6b, 0x6a, 0x76, 0x62, 0x66, 0x78, 0x79, 0x61, 0x76, 0x6c,
+ 0x64, 0x71, 0x72, 0x75, 0x66, 0x69, 0x6c, 0x64, 0x62, 0x6b, 0x63, 0x6d, 0x73, 0x72, 0x61, 0x77,
+ 0x69, 0x71, 0x73, 0x78, 0x63, 0x74, 0x74, 0x66, 0x71, 0x73, 0x63, 0x6c, 0x70, 0x66, 0x65, 0x68,
+ 0x6a, 0x72, 0x62, 0x70, 0x75, 0x65, 0x66, 0x70, 0x73, 0x72, 0x62, 0x69, 0x70, 0x6f, 0x78, 0x78,
+ 0x66, 0x78, 0x71, 0x63, 0x73, 0x6a, 0x70, 0x70, 0x6a, 0x74, 0x67, 0x6e, 0x79, 0x67, 0x75, 0x72,
+ 0x69, 0x74, 0x61, 0x73, 0x74, 0x68, 0x75, 0x70, 0x6f, 0x72, 0x63, 0x75, 0x70, 0x73, 0x64, 0x69,
+ 0x72, 0x75, 0x74, 0x63, 0x71, 0x75, 0x6f, 0x64, 0x74, 0x66, 0x63, 0x65, 0x68, 0x76, 0x74, 0x77,
+ 0x75, 0x69, 0x75, 0x63, 0x65, 0x61, 0x79, 0x77, 0x67, 0x77, 0x69, 0x6f, 0x6e, 0x74, 0x75, 0x75,
+ 0x74, 0x6c, 0x62, 0x6f, 0x6b, 0x78, 0x71, 0x66, 0x78, 0x62, 0x64, 0x64, 0x6b, 0x69, 0x77, 0x64,
+ 0x66, 0x70, 0x74, 0x73, 0x6a, 0x68, 0x74, 0x75, 0x65, 0x6e, 0x73, 0x70, 0x78, 0x6f, 0x74, 0x69,
+ 0x72, 0x76, 0x70, 0x72, 0x69, 0x68, 0x70, 0x6d, 0x66, 0x67, 0x74, 0x73, 0x6d, 0x66, 0x68, 0x73,
+ 0x6b, 0x6c, 0x66, 0x62, 0x66, 0x76, 0x66, 0x6a, 0x70, 0x76, 0x67, 0x72, 0x65, 0x78, 0x61, 0x6f,
+ 0x78, 0x68, 0x62, 0x61, 0x73, 0x64, 0x65, 0x6b, 0x73, 0x6a, 0x64, 0x61, 0x68, 0x67, 0x64, 0x78,
+ 0x6f, 0x6f, 0x61, 0x65, 0x78, 0x68, 0x64, 0x6f, 0x71, 0x72, 0x69, 0x66, 0x64, 0x71, 0x73, 0x61,
+ 0x78, 0x74, 0x69, 0x77, 0x6a, 0x74, 0x74, 0x79, 0x6f, 0x70, 0x78, 0x6b, 0x76, 0x6d, 0x79, 0x72,
+ 0x6e, 0x65, 0x74, 0x62, 0x6f, 0x67, 0x67, 0x6e, 0x6b, 0x74, 0x77, 0x73, 0x69, 0x75, 0x61, 0x64,
+ 0x6a, 0x79, 0x70, 0x70, 0x62, 0x78, 0x6c, 0x71, 0x61, 0x72, 0x6c, 0x65, 0x79, 0x61, 0x77, 0x6a,
+ 0x79, 0x6b, 0x6f, 0x66, 0x66, 0x79, 0x6a, 0x79, 0x6c, 0x62, 0x67, 0x6f, 0x64, 0x6a, 0x67, 0x61,
+ 0x72, 0x6a, 0x64, 0x72, 0x72, 0x77, 0x79, 0x61, 0x78, 0x77, 0x71, 0x70, 0x76, 0x67, 0x78, 0x78,
+ 0x63, 0x6b, 0x78, 0x64, 0x77, 0x6c, 0x73, 0x66, 0x6c, 0x63, 0x74, 0x6e, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x68, 0x64, 0x6c, 0x65, 0x6d, 0x64, 0x76, 0x71, 0x72, 0x6b, 0x66, 0x74, 0x71, 0x6d, 0x6b,
+ 0x74, 0x69, 0x75, 0x73, 0x67, 0x65, 0x6c, 0x73, 0x63, 0x6c, 0x72, 0x66, 0x78, 0x69, 0x67, 0x73,
+ 0x75, 0x6f, 0x6a, 0x75, 0x6b, 0x6e, 0x6f, 0x64, 0x6a, 0x66, 0x73, 0x69, 0x63, 0x66, 0x6e, 0x6b,
+ 0x68, 0x71, 0x69, 0x61, 0x6a, 0x63, 0x78, 0x6e, 0x6c, 0x6a, 0x78, 0x66, 0x71, 0x69, 0x6e, 0x62,
+ 0x78, 0x75, 0x72, 0x64, 0x73, 0x6f, 0x65, 0x78, 0x70, 0x73, 0x72, 0x72, 0x70, 0x78, 0x76, 0x6a,
+ 0x6b, 0x63, 0x75, 0x78, 0x76, 0x67, 0x6c, 0x66, 0x61, 0x6a, 0x71, 0x71, 0x6d, 0x6a, 0x6a, 0x62,
+ 0x65, 0x76, 0x73, 0x71, 0x77, 0x69, 0x63, 0x65, 0x74, 0x64, 0x79, 0x66, 0x6d, 0x66, 0x74, 0x68,
+ 0x62, 0x6d, 0x6d, 0x64, 0x78, 0x74, 0x76, 0x75, 0x78, 0x79, 0x66, 0x71, 0x6d, 0x6d, 0x6f, 0x78,
+ 0x61, 0x63, 0x64, 0x74, 0x71, 0x63, 0x65, 0x63, 0x68, 0x64, 0x78, 0x6b, 0x79, 0x6d, 0x71, 0x6f,
+ 0x61, 0x79, 0x72, 0x6e, 0x64, 0x71, 0x63, 0x63, 0x6f, 0x71, 0x73, 0x6d, 0x74, 0x71, 0x63, 0x64,
+ 0x74, 0x6f, 0x71, 0x78, 0x78, 0x6b, 0x68, 0x74, 0x6f, 0x63, 0x62, 0x6f, 0x78, 0x6e, 0x6f, 0x76,
+ 0x6a, 0x6b, 0x6c, 0x68, 0x68, 0x62, 0x62, 0x6a, 0x71, 0x67, 0x6f, 0x63, 0x66, 0x72, 0x63, 0x63,
+ 0x6f, 0x74, 0x6d, 0x72, 0x77, 0x71, 0x72, 0x6d, 0x65, 0x6c, 0x6b, 0x76, 0x70, 0x74, 0x72, 0x61,
+ 0x74, 0x72, 0x71, 0x76, 0x79, 0x6a, 0x77, 0x72, 0x65, 0x61, 0x6b, 0x74, 0x6d, 0x61, 0x67, 0x75,
+ 0x73, 0x73, 0x69, 0x67, 0x77, 0x66, 0x73, 0x72, 0x63, 0x6b, 0x66, 0x76, 0x6a, 0x69, 0x67, 0x61,
+ 0x74, 0x64, 0x66, 0x78, 0x64, 0x72, 0x62, 0x6a, 0x64, 0x65, 0x61, 0x62, 0x63, 0x72, 0x61, 0x66,
+ 0x68, 0x72, 0x78, 0x77, 0x73, 0x6a, 0x72, 0x66, 0x79, 0x6d, 0x70, 0x77, 0x78, 0x64, 0x66, 0x63,
+ 0x73, 0x68, 0x61, 0x75, 0x73, 0x77, 0x63, 0x79, 0x79, 0x78, 0x66, 0x6f, 0x6d, 0x79, 0x67, 0x6f,
+ 0x74, 0x61, 0x66, 0x78, 0x6f, 0x77, 0x6b, 0x77, 0x66, 0x6a, 0x63, 0x61, 0x75, 0x69, 0x77, 0x62,
+ 0x72, 0x77, 0x74, 0x66, 0x67, 0x6b, 0x63, 0x61, 0x71, 0x77, 0x63, 0x75, 0x67, 0x72, 0x67, 0x67,
+ 0x76, 0x73, 0x61, 0x72, 0x72, 0x61, 0x75, 0x6a, 0x68, 0x67, 0x75, 0x64, 0x79, 0x78, 0x6a, 0x61,
+ 0x73, 0x6c, 0x6f, 0x62, 0x64, 0x73, 0x69, 0x79, 0x71, 0x79, 0x65, 0x72, 0x79, 0x6c, 0x6c, 0x6f,
+ 0x71, 0x70, 0x66, 0x72, 0x77, 0x73, 0x62, 0x66, 0x66, 0x78, 0x79, 0x72, 0x71, 0x73, 0x64, 0x68,
+ 0x6b, 0x6e, 0x6f, 0x6e, 0x67, 0x73, 0x6c, 0x6a, 0x74, 0x74, 0x6a, 0x6a, 0x74, 0x64, 0x70, 0x62,
+ 0x79, 0x75, 0x61, 0x6d, 0x6b, 0x70, 0x70, 0x6c, 0x6a, 0x73, 0x62, 0x72, 0x72, 0x76, 0x72, 0x66,
+ 0x78, 0x66, 0x78, 0x78, 0x75, 0x70, 0x6d, 0x6a, 0x70, 0x66, 0x65, 0x6f, 0x72, 0x76, 0x6c, 0x67,
+ 0x76, 0x69, 0x73, 0x74, 0x76, 0x63, 0x76, 0x75, 0x70, 0x76, 0x69, 0x6f, 0x65, 0x70, 0x72, 0x6a,
+ 0x6b, 0x68, 0x6d, 0x6a, 0x71, 0x79, 0x72, 0x6a, 0x74, 0x68, 0x64, 0x72, 0x76, 0x65, 0x6e, 0x63,
+ 0x66, 0x6a, 0x61, 0x66, 0x71, 0x69, 0x62, 0x69, 0x76, 0x66, 0x71, 0x67, 0x73, 0x6f, 0x6c, 0x61,
+ 0x62, 0x63, 0x69, 0x6f, 0x64, 0x70, 0x6e, 0x66, 0x70, 0x68, 0x70, 0x6d, 0x77, 0x66, 0x74, 0x73,
+ 0x64, 0x64, 0x6f, 0x64, 0x68, 0x63, 0x79, 0x72, 0x70, 0x6c, 0x76, 0x64, 0x66, 0x74, 0x70, 0x6b,
+ 0x78, 0x6a, 0x63, 0x6a, 0x61, 0x69, 0x74, 0x61, 0x6b, 0x6a, 0x62, 0x63, 0x71, 0x6b, 0x6e, 0x6f,
+ 0x67, 0x77, 0x61, 0x64, 0x79, 0x73, 0x6c, 0x64, 0x64, 0x64, 0x74, 0x71, 0x6a, 0x73, 0x64, 0x6b,
+ 0x65, 0x6a, 0x79, 0x78, 0x70, 0x6e, 0x72, 0x71, 0x66, 0x79, 0x78, 0x77, 0x76, 0x61, 0x78, 0x69,
+ 0x6f, 0x76, 0x71, 0x70, 0x6b, 0x75, 0x69, 0x75, 0x72, 0x6f, 0x66, 0x65, 0x66, 0x69, 0x79, 0x67,
+ 0x71, 0x76, 0x76, 0x79, 0x71, 0x6b, 0x74, 0x6f, 0x77, 0x61, 0x77, 0x63, 0x71, 0x75, 0x72, 0x73,
+ 0x63, 0x6d, 0x72, 0x6e, 0x73, 0x66, 0x62, 0x70, 0x66, 0x79, 0x77, 0x73, 0x61, 0x69, 0x6a, 0x6a,
+ 0x6c, 0x69, 0x69, 0x69, 0x62, 0x63, 0x63, 0x76, 0x74, 0x6d, 0x6e, 0x72, 0x69, 0x6d, 0x67, 0x76,
+ 0x6a, 0x72, 0x73, 0x61, 0x61, 0x6f, 0x6f, 0x67, 0x78, 0x74, 0x76, 0x77, 0x62, 0x73, 0x64, 0x74,
+ 0x69, 0x76, 0x61, 0x69, 0x65, 0x70, 0x76, 0x73, 0x6f, 0x70, 0x77, 0x73, 0x75, 0x72, 0x68, 0x76,
+ 0x73, 0x62, 0x78, 0x74, 0x78, 0x74, 0x69, 0x71, 0x69, 0x77, 0x6c, 0x68, 0x6d, 0x6a, 0x65, 0x75,
+ 0x70, 0x79, 0x6c, 0x76, 0x62, 0x64, 0x68, 0x62, 0x70, 0x72, 0x6d, 0x65, 0x70, 0x67, 0x65, 0x73,
+ 0x6f, 0x77, 0x66, 0x63, 0x74, 0x77, 0x68, 0x6e, 0x74, 0x6a, 0x76, 0x70, 0x79, 0x77, 0x72, 0x67,
+ 0x65, 0x68, 0x71, 0x66, 0x70, 0x76, 0x67, 0x6e, 0x65, 0x68, 0x77, 0x61, 0x6c, 0x76, 0x76, 0x75,
+ 0x6f, 0x78, 0x77, 0x77, 0x6c, 0x65, 0x73, 0x76, 0x6b, 0x64, 0x70, 0x6f, 0x6a, 0x79, 0x6d, 0x73,
+ 0x6e, 0x64, 0x62, 0x6d, 0x79, 0x63, 0x72, 0x71, 0x68, 0x61, 0x74, 0x62, 0x64, 0x63, 0x72, 0x73,
+ 0x62, 0x79, 0x73, 0x66, 0x66, 0x6e, 0x79, 0x61, 0x70, 0x6b, 0x6a, 0x6e, 0x62, 0x79, 0x6b, 0x64,
+ 0x68, 0x65, 0x66, 0x62, 0x74, 0x63, 0x76, 0x71, 0x73, 0x77, 0x6d, 0x73, 0x71, 0x66, 0x64, 0x6d,
+ 0x71, 0x68, 0x68, 0x77, 0x74, 0x61, 0x79, 0x70, 0x74, 0x66, 0x65, 0x71, 0x74, 0x66, 0x63, 0x73,
+ 0x6c, 0x68, 0x64, 0x75, 0x72, 0x62, 0x72, 0x72, 0x6e, 0x71, 0x6e, 0x69, 0x75, 0x72, 0x71, 0x63,
+ 0x75, 0x65, 0x79, 0x73, 0x65, 0x62, 0x67, 0x62, 0x70, 0x61, 0x62, 0x74, 0x69, 0x6b, 0x6d, 0x6c,
+ 0x65, 0x6a, 0x78, 0x63, 0x72, 0x6f, 0x66, 0x62, 0x77, 0x62, 0x75, 0x69, 0x6f, 0x70, 0x6c, 0x68,
+ 0x6a, 0x78, 0x73, 0x61, 0x66, 0x79, 0x6f, 0x6e, 0x73, 0x67, 0x76, 0x79, 0x64, 0x62, 0x6f, 0x66,
+ 0x63, 0x6b, 0x6c, 0x75, 0x74, 0x77, 0x6a, 0x66, 0x75, 0x63, 0x77, 0x76, 0x6b, 0x6d, 0x69, 0x77,
+ 0x71, 0x61, 0x65, 0x72, 0x66, 0x6d, 0x70, 0x73, 0x68, 0x73, 0x74, 0x77, 0x6e, 0x73, 0x74, 0x76,
+ 0x63, 0x75, 0x72, 0x63, 0x66, 0x6d, 0x67, 0x72, 0x61, 0x69, 0x61, 0x6b, 0x69, 0x74, 0x77, 0x68,
+ 0x74, 0x6e, 0x73, 0x77, 0x66, 0x65, 0x62, 0x6a, 0x6d, 0x66, 0x71, 0x6c, 0x78, 0x79, 0x65, 0x66,
+ 0x62, 0x68, 0x72, 0x61, 0x6e, 0x6a, 0x79, 0x6e, 0x70, 0x79, 0x70, 0x68, 0x71, 0x77, 0x69, 0x6f,
+ 0x63, 0x66, 0x67, 0x77, 0x71, 0x6b, 0x6b, 0x62, 0x71, 0x67, 0x66, 0x6b, 0x65, 0x65, 0x78, 0x75,
+ 0x75, 0x74, 0x75, 0x71, 0x68, 0x72, 0x64, 0x6b, 0x6e, 0x6d, 0x69, 0x62, 0x64, 0x68, 0x73, 0x63,
+ 0x61, 0x74, 0x6e, 0x77, 0x6a, 0x6f, 0x77, 0x74, 0x63, 0x75, 0x6f, 0x70, 0x75, 0x61, 0x67, 0x68,
+ 0x68, 0x72, 0x6b, 0x73, 0x73, 0x66, 0x6f, 0x79, 0x64, 0x77, 0x68, 0x74, 0x71, 0x6d, 0x69, 0x73,
+ 0x71, 0x62, 0x76, 0x77, 0x68, 0x68, 0x79, 0x75, 0x6c, 0x75, 0x79, 0x6e, 0x78, 0x6f, 0x66, 0x71,
+ 0x65, 0x6a, 0x62, 0x67, 0x75, 0x74, 0x6f, 0x64, 0x78, 0x6a, 0x63, 0x73, 0x72, 0x63, 0x68, 0x6b,
+ 0x69, 0x70, 0x6c, 0x73, 0x65, 0x6d, 0x69, 0x63, 0x74, 0x77, 0x6c, 0x6b, 0x78, 0x65, 0x75, 0x6b,
+ 0x6a, 0x77, 0x73, 0x77, 0x77, 0x64, 0x61, 0x69, 0x75, 0x76, 0x77, 0x78, 0x61, 0x6e, 0x71, 0x78,
+ 0x65, 0x77, 0x76, 0x71, 0x70, 0x6d, 0x6d, 0x65, 0x69, 0x71, 0x65, 0x66, 0x74, 0x6c, 0x61, 0x66,
+ 0x6c, 0x62, 0x76, 0x73, 0x77, 0x66, 0x6a, 0x65, 0x79, 0x71, 0x6b, 0x67, 0x68, 0x6e, 0x72, 0x71,
+ 0x73, 0x71, 0x62, 0x71, 0x74, 0x6d, 0x75, 0x6e, 0x6e, 0x6f, 0x75, 0x73, 0x76, 0x6c, 0x68, 0x6e,
+ 0x79, 0x6e, 0x79, 0x74, 0x79, 0x79, 0x79, 0x74, 0x66, 0x77, 0x6d, 0x75, 0x6e, 0x67, 0x68, 0x68,
+ 0x6c, 0x76, 0x68, 0x65, 0x68, 0x6f, 0x76, 0x71, 0x69, 0x68, 0x6f, 0x6c, 0x66, 0x74, 0x6d, 0x73,
+ 0x70, 0x76, 0x61, 0x65, 0x73, 0x62, 0x79, 0x76, 0x6c, 0x69, 0x76, 0x76, 0x79, 0x6a, 0x77, 0x65,
+ 0x68, 0x6d, 0x61, 0x75, 0x6f, 0x6c, 0x66, 0x69, 0x70, 0x73, 0x62, 0x73, 0x6e, 0x72, 0x77, 0x6d,
+ 0x68, 0x67, 0x6d, 0x75, 0x74, 0x67, 0x69, 0x6e, 0x69, 0x65, 0x72, 0x63, 0x64, 0x75, 0x63, 0x63,
+ 0x6a, 0x62, 0x67, 0x74, 0x6b, 0x74, 0x77, 0x73, 0x77, 0x73, 0x65, 0x69, 0x71, 0x63, 0x63, 0x6f,
+ 0x64, 0x63, 0x79, 0x6c, 0x75, 0x74, 0x6b, 0x70, 0x78, 0x62, 0x69, 0x69, 0x6a, 0x72, 0x63, 0x64,
+ 0x62, 0x78, 0x6e, 0x74, 0x6d, 0x66, 0x73, 0x75, 0x77, 0x6c, 0x6a, 0x77, 0x78, 0x62, 0x67, 0x78,
+ 0x70, 0x64, 0x77, 0x6b, 0x6b, 0x77, 0x68, 0x75, 0x62, 0x67, 0x73, 0x76, 0x61, 0x61, 0x71, 0x63,
+ 0x75, 0x70, 0x6a, 0x78, 0x6d, 0x6a, 0x76, 0x72, 0x76, 0x63, 0x66, 0x6c, 0x6e, 0x77, 0x79, 0x6f,
+ 0x69, 0x65, 0x65, 0x65, 0x69, 0x6e, 0x71, 0x79, 0x64, 0x71, 0x72, 0x75, 0x6f, 0x70, 0x61, 0x65,
+ 0x75, 0x6f, 0x70, 0x6b, 0x78, 0x79, 0x6a, 0x6c, 0x78, 0x62, 0x6d, 0x6d, 0x64, 0x62, 0x6e, 0x68,
+ 0x79, 0x64, 0x79, 0x74, 0x61, 0x65, 0x6a, 0x68, 0x6f, 0x62, 0x62, 0x65, 0x73, 0x71, 0x6e, 0x77,
+ 0x76, 0x63, 0x62, 0x74, 0x74, 0x74, 0x6b, 0x6d, 0x79, 0x78, 0x69, 0x75, 0x69, 0x6f, 0x62, 0x6b,
+ 0x6a, 0x75, 0x77, 0x67, 0x71, 0x6d, 0x67, 0x70, 0x6d, 0x65, 0x6b, 0x70, 0x6d, 0x70, 0x74, 0x78,
+ 0x61, 0x68, 0x72, 0x6d, 0x78, 0x79, 0x6d, 0x77, 0x66, 0x68, 0x73, 0x73, 0x64, 0x71, 0x64, 0x64,
+ 0x6e, 0x72, 0x66, 0x74, 0x72, 0x65, 0x75, 0x79, 0x6c, 0x6c, 0x68, 0x72, 0x6c, 0x71, 0x6b, 0x6c,
+ 0x70, 0x61, 0x79, 0x76, 0x72, 0x6a, 0x65, 0x71, 0x72, 0x76, 0x71, 0x6e, 0x74, 0x75, 0x62, 0x64,
+ 0x67, 0x6c, 0x65, 0x78, 0x75, 0x67, 0x6f, 0x79, 0x64, 0x69, 0x66, 0x67, 0x6f, 0x64, 0x62, 0x72,
+ 0x73, 0x79, 0x70, 0x68, 0x63, 0x61, 0x64, 0x6a, 0x6f, 0x6d, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x6e,
+ 0x6f, 0x74, 0x61, 0x6e, 0x6c, 0x62, 0x6f, 0x64, 0x62, 0x76, 0x6c, 0x74, 0x68, 0x79, 0x70, 0x6a,
+ 0x68, 0x71, 0x71, 0x6b, 0x6c, 0x66, 0x67, 0x6e, 0x61, 0x68, 0x6a, 0x6f, 0x65, 0x77, 0x64, 0x70,
+ 0x6c, 0x65, 0x63, 0x66, 0x6b, 0x65, 0x63, 0x62, 0x79, 0x6f, 0x79, 0x63, 0x77, 0x70, 0x61, 0x67,
+ 0x65, 0x73, 0x67, 0x6e, 0x74, 0x6c, 0x67, 0x6e, 0x64, 0x71, 0x75, 0x61, 0x70, 0x70, 0x6c, 0x68,
+ 0x64, 0x65, 0x63, 0x6c, 0x6e, 0x68, 0x6c, 0x67, 0x70, 0x64, 0x63, 0x65, 0x76, 0x78, 0x78, 0x78,
+ 0x68, 0x65, 0x61, 0x79, 0x77, 0x6d, 0x72, 0x72, 0x6f, 0x79, 0x67, 0x78, 0x6d, 0x6d, 0x67, 0x71,
+ 0x75, 0x70, 0x72, 0x69, 0x68, 0x75, 0x6f, 0x70, 0x63, 0x66, 0x65, 0x69, 0x75, 0x64, 0x75, 0x72,
+ 0x6b, 0x67, 0x75, 0x6b, 0x67, 0x6f, 0x6d, 0x65, 0x6d, 0x63, 0x73, 0x79, 0x61, 0x6e, 0x65, 0x69,
+ 0x64, 0x6c, 0x73, 0x75, 0x69, 0x66, 0x79, 0x65, 0x61, 0x68, 0x76, 0x70, 0x62, 0x76, 0x78, 0x72,
+ 0x6b, 0x74, 0x73, 0x6b, 0x66, 0x63, 0x6c, 0x79, 0x68, 0x72, 0x78, 0x6a, 0x62, 0x70, 0x73, 0x6a,
+ 0x68, 0x73, 0x67, 0x67, 0x64, 0x70, 0x77, 0x69, 0x71, 0x65, 0x6f, 0x6c, 0x68, 0x61, 0x6f, 0x64,
+ 0x6c, 0x68, 0x6d, 0x64, 0x79, 0x74, 0x78, 0x63, 0x68, 0x62, 0x64, 0x61, 0x70, 0x78, 0x6e, 0x74,
+ 0x6b, 0x71, 0x67, 0x73, 0x71, 0x73, 0x62, 0x61, 0x64, 0x6a, 0x6c, 0x6e, 0x77, 0x70, 0x66, 0x65,
+ 0x6b, 0x64, 0x66, 0x6d, 0x6d, 0x6a, 0x69, 0x65, 0x65, 0x71, 0x66, 0x62, 0x6f, 0x74, 0x6c, 0x6e,
+ 0x65, 0x73, 0x75, 0x6f, 0x78, 0x62, 0x68, 0x72, 0x65, 0x6c, 0x6f, 0x6a, 0x70, 0x77, 0x64, 0x6b,
+ 0x6d, 0x73, 0x71, 0x68, 0x6b, 0x61, 0x6b, 0x69, 0x73, 0x65, 0x6d, 0x61, 0x70, 0x79, 0x6f, 0x76,
+ 0x62, 0x67, 0x78, 0x61, 0x6d, 0x63, 0x6f, 0x70, 0x66, 0x6e, 0x65, 0x65, 0x62, 0x62, 0x6b, 0x73,
+ 0x64, 0x6c, 0x78, 0x71, 0x62, 0x76, 0x65, 0x6e, 0x71, 0x73, 0x6e, 0x75, 0x6d, 0x71, 0x6e, 0x75,
+ 0x73, 0x6a, 0x74, 0x75, 0x6d, 0x68, 0x6f, 0x69, 0x6a, 0x73, 0x6b, 0x75, 0x70, 0x75, 0x69, 0x6a,
+ 0x71, 0x6a, 0x73, 0x71, 0x6c, 0x66, 0x70, 0x63, 0x75, 0x6d, 0x6f, 0x65, 0x71, 0x75, 0x77, 0x61,
+ 0x63, 0x78, 0x6f, 0x78, 0x6c, 0x69, 0x76, 0x6b, 0x65, 0x79, 0x67, 0x74, 0x77, 0x65, 0x64, 0x6f,
+ 0x74, 0x79, 0x6f, 0x74, 0x6e, 0x74, 0x66, 0x6e, 0x69, 0x70, 0x77, 0x68, 0x78, 0x73, 0x66, 0x6a,
+ 0x65, 0x6f, 0x6b, 0x64, 0x6c, 0x73, 0x75, 0x64, 0x64, 0x6b, 0x68, 0x62, 0x77, 0x72, 0x75, 0x76,
+ 0x75, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x64, 0x77, 0x62, 0x65, 0x66, 0x65, 0x6a, 0x71, 0x69, 0x71,
+ 0x78, 0x65, 0x77, 0x77, 0x79, 0x66, 0x67, 0x72, 0x6d, 0x6c, 0x68, 0x6a, 0x63, 0x79, 0x79, 0x72,
+ 0x73, 0x62, 0x62, 0x77, 0x6e, 0x6c, 0x79, 0x75, 0x6f, 0x78, 0x68, 0x6a, 0x64, 0x6c, 0x62, 0x67,
+ 0x65, 0x6c, 0x65, 0x64, 0x65, 0x70, 0x75, 0x68, 0x64, 0x72, 0x71, 0x62, 0x66, 0x74, 0x70, 0x77,
+ 0x63, 0x6d, 0x76, 0x63, 0x78, 0x64, 0x76, 0x75, 0x72, 0x6e, 0x65, 0x68, 0x71, 0x78, 0x73, 0x62,
+ 0x77, 0x6c, 0x6f, 0x63, 0x74, 0x64, 0x71, 0x62, 0x79, 0x78, 0x70, 0x6a, 0x69, 0x6c, 0x73, 0x68,
+ 0x6c, 0x6a, 0x6b, 0x6f, 0x73, 0x6a, 0x6e, 0x62, 0x73, 0x66, 0x6f, 0x67, 0x76, 0x74, 0x6c, 0x78,
+ 0x63, 0x6c, 0x76, 0x77, 0x6c, 0x6c, 0x74, 0x71, 0x6b, 0x66, 0x66, 0x63, 0x61, 0x68, 0x6c, 0x76,
+ 0x68, 0x72, 0x72, 0x6c, 0x6b, 0x61, 0x6f, 0x6c, 0x72, 0x6b, 0x6b, 0x78, 0x77, 0x78, 0x61, 0x68,
+ 0x63, 0x66, 0x6a, 0x68, 0x78, 0x74, 0x71, 0x68, 0x64, 0x69, 0x6d, 0x75, 0x72, 0x6d, 0x78, 0x6c,
+ 0x6a, 0x68, 0x61, 0x74, 0x69, 0x64, 0x67, 0x71, 0x62, 0x67, 0x6b, 0x6f, 0x6e, 0x63, 0x64, 0x73,
+ 0x72, 0x6c, 0x6c, 0x78, 0x64, 0x70, 0x76, 0x79, 0x69, 0x61, 0x76, 0x6d, 0x72, 0x65, 0x64, 0x75,
+ 0x64, 0x77, 0x70, 0x64, 0x71, 0x73, 0x77, 0x75, 0x61, 0x6b, 0x76, 0x6e, 0x62, 0x70, 0x75, 0x69,
+ 0x6e, 0x73, 0x74, 0x66, 0x6d, 0x6c, 0x6b, 0x70, 0x67, 0x66, 0x6f, 0x75, 0x65, 0x69, 0x78, 0x67,
+ 0x68, 0x78, 0x67, 0x6b, 0x6a, 0x68, 0x72, 0x71, 0x72, 0x71, 0x77, 0x6c, 0x74, 0x64, 0x79, 0x74,
+ 0x73, 0x6d, 0x73, 0x6f, 0x79, 0x62, 0x79, 0x71, 0x6c, 0x73, 0x73, 0x6a, 0x67, 0x6e, 0x72, 0x69,
+ 0x75, 0x71, 0x62, 0x72, 0x65, 0x63, 0x6b, 0x63, 0x79, 0x76, 0x74, 0x79, 0x79, 0x67, 0x77, 0x79,
+ 0x66, 0x64, 0x72, 0x73, 0x61, 0x6f, 0x62, 0x72, 0x76, 0x66, 0x62, 0x76, 0x73, 0x68, 0x67, 0x63,
+ 0x72, 0x64, 0x6b, 0x74, 0x6c, 0x6b, 0x78, 0x6f, 0x64, 0x6a, 0x69, 0x76, 0x67, 0x75, 0x73, 0x70,
+ 0x78, 0x73, 0x75, 0x67, 0x71, 0x71, 0x74, 0x65, 0x65, 0x6d, 0x70, 0x6e, 0x74, 0x64, 0x77, 0x68,
+ 0x78, 0x72, 0x6e, 0x71, 0x73, 0x70, 0x6f, 0x6a, 0x6a, 0x6f, 0x69, 0x70, 0x63, 0x6b, 0x71, 0x6e,
+ 0x77, 0x79, 0x6c, 0x6e, 0x73, 0x6f, 0x63, 0x71, 0x67, 0x65, 0x77, 0x6d, 0x75, 0x6d, 0x64, 0x67,
+ 0x64, 0x65, 0x73, 0x73, 0x64, 0x74, 0x61, 0x66, 0x73, 0x69, 0x69, 0x65, 0x68, 0x66, 0x6e, 0x67,
+ 0x69, 0x6d, 0x66, 0x65, 0x78, 0x79, 0x72, 0x61, 0x6b, 0x68, 0x64, 0x75, 0x67, 0x67, 0x71, 0x76,
+ 0x76, 0x75, 0x79, 0x71, 0x62, 0x76, 0x61, 0x77, 0x71, 0x76, 0x76, 0x67, 0x70, 0x69, 0x62, 0x6e,
+ 0x6e, 0x65, 0x65, 0x67, 0x75, 0x6a, 0x70, 0x70, 0x71, 0x68, 0x71, 0x6b, 0x61, 0x6c, 0x74, 0x6a,
+ 0x6b, 0x63, 0x70, 0x72, 0x6c, 0x73, 0x79, 0x77, 0x74, 0x67, 0x71, 0x72, 0x65, 0x61, 0x72, 0x67,
+ 0x66, 0x6d, 0x69, 0x65, 0x75, 0x61, 0x6d, 0x69, 0x77, 0x6c, 0x76, 0x71, 0x64, 0x6f, 0x79, 0x69,
+ 0x77, 0x6a, 0x68, 0x73, 0x65, 0x6a, 0x75, 0x6f, 0x75, 0x6a, 0x63, 0x67, 0x6c, 0x6d, 0x78, 0x74,
+ 0x74, 0x75, 0x72, 0x6a, 0x64, 0x74, 0x72, 0x75, 0x63, 0x6f, 0x72, 0x6f, 0x6b, 0x65, 0x64, 0x76,
+ 0x79, 0x6d, 0x78, 0x71, 0x61, 0x61, 0x79, 0x6d, 0x77, 0x79, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x72,
+ 0x74, 0x70, 0x64, 0x72, 0x6a, 0x78, 0x6c, 0x61, 0x6d, 0x71, 0x6b, 0x6a, 0x64, 0x70, 0x62, 0x6a,
+ 0x67, 0x69, 0x64, 0x6e, 0x6b, 0x62, 0x61, 0x6a, 0x74, 0x61, 0x69, 0x78, 0x75, 0x78, 0x72, 0x69,
+ 0x6d, 0x6e, 0x79, 0x63, 0x70, 0x68, 0x78, 0x6d, 0x79, 0x64, 0x63, 0x6e, 0x62, 0x6e, 0x65, 0x6f,
+ 0x6c, 0x79, 0x71, 0x70, 0x6a, 0x68, 0x70, 0x75, 0x6e, 0x64, 0x72, 0x79, 0x67, 0x77, 0x63, 0x68,
+ 0x72, 0x79, 0x65, 0x78, 0x64, 0x77, 0x6e, 0x6d, 0x62, 0x71, 0x79, 0x75, 0x6b, 0x63, 0x72, 0x71,
+ 0x65, 0x69, 0x6c, 0x68, 0x66, 0x62, 0x61, 0x79, 0x6c, 0x67, 0x61, 0x78, 0x76, 0x6c, 0x6d, 0x75,
+ 0x73, 0x6d, 0x6a, 0x6c, 0x6e, 0x66, 0x70, 0x61, 0x6f, 0x6e, 0x6b, 0x70, 0x69, 0x6b, 0x61, 0x75,
+ 0x6d, 0x73, 0x6b, 0x61, 0x63, 0x72, 0x73, 0x65, 0x6a, 0x77, 0x6b, 0x78, 0x68, 0x6e, 0x6f, 0x78,
+ 0x69, 0x6b, 0x67, 0x63, 0x73, 0x76, 0x6f, 0x67, 0x63, 0x71, 0x6d, 0x67, 0x62, 0x6e, 0x76, 0x71,
+ 0x71, 0x78, 0x78, 0x68, 0x78, 0x63, 0x65, 0x72, 0x6f, 0x6b, 0x75, 0x6a, 0x76, 0x6d, 0x79, 0x68,
+ 0x75, 0x78, 0x77, 0x75, 0x6d, 0x70, 0x6e, 0x71, 0x76, 0x77, 0x76, 0x76, 0x70, 0x77, 0x72, 0x61,
+ 0x6a, 0x62, 0x71, 0x67, 0x67, 0x73, 0x71, 0x75, 0x6a, 0x73, 0x66, 0x6e, 0x64, 0x76, 0x6a, 0x65,
+ 0x76, 0x66, 0x6e, 0x68, 0x67, 0x78, 0x79, 0x66, 0x65, 0x6d, 0x6d, 0x71, 0x73, 0x66, 0x63, 0x79,
+ 0x6c, 0x72, 0x6a, 0x6f, 0x78, 0x76, 0x6d, 0x78, 0x63, 0x6b, 0x67, 0x70, 0x78, 0x76, 0x61, 0x62,
+ 0x6e, 0x76, 0x75, 0x76, 0x74, 0x70, 0x75, 0x61, 0x77, 0x65, 0x75, 0x6b, 0x65, 0x74, 0x76, 0x78,
+ 0x66, 0x6f, 0x6a, 0x72, 0x72, 0x70, 0x66, 0x70, 0x63, 0x73, 0x78, 0x6e, 0x74, 0x77, 0x78, 0x68,
+ 0x61, 0x6a, 0x61, 0x75, 0x74, 0x75, 0x6b, 0x62, 0x6c, 0x6a, 0x72, 0x79, 0x73, 0x79, 0x6b, 0x6f,
+ 0x77, 0x6b, 0x75, 0x74, 0x76, 0x74, 0x64, 0x79, 0x64, 0x74, 0x64, 0x77, 0x71, 0x6b, 0x75, 0x72,
+ 0x66, 0x75, 0x61, 0x79, 0x78, 0x6a, 0x69, 0x6a, 0x68, 0x6a, 0x6c, 0x77, 0x68, 0x67, 0x77, 0x73,
+ 0x76, 0x6e, 0x65, 0x6d, 0x6f, 0x6a, 0x6e, 0x61, 0x6e, 0x6c, 0x74, 0x6b, 0x70, 0x70, 0x75, 0x66,
+ 0x70, 0x72, 0x76, 0x69, 0x77, 0x6b, 0x76, 0x68, 0x6e, 0x6b, 0x77, 0x64, 0x76, 0x61, 0x6b, 0x6d,
+ 0x77, 0x6f, 0x6a, 0x6a, 0x62, 0x6b, 0x79, 0x73, 0x6a, 0x6a, 0x76, 0x62, 0x64, 0x69, 0x6e, 0x6a,
+ 0x6e, 0x6a, 0x6b, 0x75, 0x6f, 0x61, 0x72, 0x6d, 0x79, 0x66, 0x71, 0x77, 0x6e, 0x72, 0x68, 0x74,
+ 0x69, 0x6d, 0x6b, 0x70, 0x65, 0x77, 0x65, 0x64, 0x66, 0x77, 0x6b, 0x71, 0x6c, 0x66, 0x70, 0x75,
+ 0x63, 0x63, 0x62, 0x65, 0x64, 0x65, 0x75, 0x6a, 0x6b, 0x67, 0x69, 0x64, 0x77, 0x71, 0x65, 0x70,
+ 0x78, 0x67, 0x63, 0x62, 0x79, 0x74, 0x75, 0x73, 0x72, 0x70, 0x63, 0x72, 0x77, 0x66, 0x6f, 0x73,
+ 0x66, 0x6e, 0x61, 0x69, 0x62, 0x75, 0x79, 0x71, 0x68, 0x77, 0x6f, 0x79, 0x67, 0x76, 0x62, 0x67,
+ 0x78, 0x63, 0x70, 0x67, 0x62, 0x69, 0x6b, 0x75, 0x78, 0x77, 0x6d, 0x71, 0x78, 0x63, 0x77, 0x78,
+ 0x61, 0x61, 0x75, 0x74, 0x6b, 0x77, 0x62, 0x71, 0x70, 0x74, 0x6d, 0x6e, 0x62, 0x71, 0x73, 0x71,
+ 0x78, 0x76, 0x79, 0x68, 0x61, 0x73, 0x78, 0x61, 0x77, 0x77, 0x62, 0x67, 0x77, 0x6b, 0x6d, 0x69,
+ 0x77, 0x6a, 0x6a, 0x74, 0x69, 0x64, 0x62, 0x70, 0x6c, 0x6e, 0x75, 0x71, 0x6e, 0x66, 0x72, 0x76,
+ 0x63, 0x63, 0x61, 0x6b, 0x74, 0x62, 0x68, 0x61, 0x70, 0x69, 0x75, 0x64, 0x6a, 0x6b, 0x68, 0x73,
+ 0x6f, 0x6b, 0x67, 0x63, 0x6a, 0x79, 0x79, 0x65, 0x78, 0x78, 0x78, 0x67, 0x73, 0x6d, 0x63, 0x79,
+ 0x6c, 0x75, 0x6e, 0x65, 0x79, 0x66, 0x6d, 0x63, 0x76, 0x6a, 0x6b, 0x67, 0x71, 0x67, 0x69, 0x61,
+ 0x77, 0x71, 0x75, 0x76, 0x76, 0x70, 0x74, 0x6d, 0x69, 0x67, 0x61, 0x66, 0x64, 0x6b, 0x73, 0x66,
+ 0x66, 0x70, 0x65, 0x77, 0x6d, 0x70, 0x6f, 0x61, 0x71, 0x6e, 0x76, 0x69, 0x6a, 0x68, 0x70, 0x71,
+ 0x70, 0x66, 0x6e, 0x6f, 0x70, 0x72, 0x64, 0x78, 0x6f, 0x75, 0x66, 0x69, 0x73, 0x68, 0x71, 0x77,
+ 0x6b, 0x69, 0x73, 0x66, 0x79, 0x6d, 0x64, 0x69, 0x74, 0x66, 0x73, 0x64, 0x6e, 0x63, 0x6b, 0x75,
+ 0x70, 0x70, 0x64, 0x72, 0x71, 0x79, 0x74, 0x71, 0x73, 0x6f, 0x6a, 0x72, 0x6d, 0x72, 0x78, 0x63,
+ 0x73, 0x61, 0x70, 0x6d, 0x69, 0x76, 0x73, 0x64, 0x6c, 0x73, 0x69, 0x61, 0x78, 0x61, 0x78, 0x78,
+ 0x67, 0x75, 0x6a, 0x78, 0x75, 0x6e, 0x6f, 0x61, 0x6e, 0x62, 0x75, 0x74, 0x77, 0x6a, 0x68, 0x70,
+ 0x67, 0x65, 0x72, 0x6c, 0x6e, 0x75, 0x79, 0x6a, 0x72, 0x6b, 0x6b, 0x63, 0x79, 0x77, 0x74, 0x73,
+ 0x6a, 0x6c, 0x6a, 0x69, 0x6b, 0x66, 0x6b, 0x61, 0x6a, 0x72, 0x66, 0x67, 0x73, 0x6c, 0x67, 0x6d,
+ 0x6c, 0x69, 0x6a, 0x6d, 0x6f, 0x64, 0x78, 0x6f, 0x6c, 0x75, 0x74, 0x6c, 0x74, 0x74, 0x64, 0x72,
+ 0x76, 0x6f, 0x62, 0x63, 0x77, 0x66, 0x69, 0x74, 0x70, 0x6e, 0x72, 0x75, 0x6c, 0x77, 0x61, 0x79,
+ 0x78, 0x72, 0x62, 0x75, 0x75, 0x6c, 0x6a, 0x6c, 0x69, 0x70, 0x76, 0x63, 0x6a, 0x74, 0x75, 0x6c,
+ 0x77, 0x6c, 0x75, 0x72, 0x68, 0x79, 0x6b, 0x62, 0x6a, 0x64, 0x63, 0x6b, 0x70, 0x6e, 0x6d, 0x6b,
+ 0x78, 0x6e, 0x77, 0x6c, 0x61, 0x6d, 0x66, 0x6e, 0x62, 0x77, 0x75, 0x76, 0x6c, 0x72, 0x73, 0x6c,
+ 0x61, 0x63, 0x77, 0x75, 0x64, 0x70, 0x76, 0x72, 0x6a, 0x63, 0x62, 0x6d, 0x72, 0x71, 0x62, 0x6e,
+ 0x76, 0x70, 0x66, 0x68, 0x63, 0x68, 0x69, 0x66, 0x6c, 0x72, 0x74, 0x6c, 0x78, 0x74, 0x64, 0x64,
+ 0x74, 0x63, 0x6a, 0x6d, 0x69, 0x6a, 0x74, 0x78, 0x66, 0x68, 0x63, 0x66, 0x76, 0x63, 0x74, 0x67,
+ 0x6e, 0x74, 0x69, 0x66, 0x69, 0x75, 0x63, 0x65, 0x64, 0x62, 0x6f, 0x71, 0x6c, 0x62, 0x79, 0x61,
+ 0x71, 0x6d, 0x76, 0x6e, 0x63, 0x62, 0x66, 0x61, 0x6d, 0x65, 0x74, 0x6e, 0x6a, 0x78, 0x61, 0x73,
+ 0x6e, 0x62, 0x78, 0x72, 0x65, 0x6b, 0x6a, 0x76, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x72, 0x6c, 0x61,
+ 0x64, 0x61, 0x64, 0x69, 0x61, 0x6f, 0x6f, 0x66, 0x6a, 0x70, 0x63, 0x6e, 0x77, 0x6b, 0x77, 0x63,
+ 0x74, 0x62, 0x79, 0x6e, 0x77, 0x6b, 0x70, 0x6d, 0x67, 0x72, 0x66, 0x71, 0x72, 0x6a, 0x6f, 0x73,
+ 0x77, 0x71, 0x77, 0x62, 0x75, 0x78, 0x6b, 0x6a, 0x63, 0x6b, 0x78, 0x67, 0x66, 0x66, 0x61, 0x77,
+ 0x79, 0x65, 0x66, 0x64, 0x73, 0x71, 0x72, 0x77, 0x73, 0x77, 0x77, 0x75, 0x79, 0x73, 0x75, 0x65,
+ 0x75, 0x65, 0x6d, 0x74, 0x75, 0x65, 0x74, 0x67, 0x69, 0x70, 0x6c, 0x6d, 0x74, 0x73, 0x68, 0x6f,
+ 0x61, 0x6c, 0x76, 0x65, 0x6d, 0x6c, 0x6d, 0x64, 0x64, 0x61, 0x73, 0x6f, 0x68, 0x77, 0x6e, 0x6c,
+ 0x6a, 0x77, 0x73, 0x77, 0x64, 0x69, 0x79, 0x6d, 0x75, 0x65, 0x6f, 0x6a, 0x72, 0x68, 0x75, 0x6f,
+ 0x6a, 0x77, 0x69, 0x6c, 0x61, 0x70, 0x69, 0x64, 0x78, 0x73, 0x6e, 0x70, 0x74, 0x75, 0x63, 0x71,
+ 0x77, 0x78, 0x73, 0x69, 0x66, 0x73, 0x73, 0x6a, 0x6e, 0x66, 0x65, 0x67, 0x70, 0x63, 0x6b, 0x6f,
+ 0x76, 0x77, 0x75, 0x74, 0x6d, 0x77, 0x74, 0x6c, 0x74, 0x69, 0x78, 0x6d, 0x6c, 0x75, 0x75, 0x66,
+ 0x76, 0x69, 0x71, 0x69, 0x73, 0x66, 0x78, 0x6e, 0x69, 0x78, 0x69, 0x69, 0x62, 0x71, 0x70, 0x69,
+ 0x70, 0x78, 0x64, 0x6d, 0x67, 0x70, 0x62, 0x67, 0x64, 0x65, 0x71, 0x77, 0x6b, 0x72, 0x6a, 0x71,
+ 0x70, 0x68, 0x70, 0x62, 0x71, 0x71, 0x74, 0x70, 0x73, 0x6d, 0x70, 0x64, 0x67, 0x76, 0x69, 0x74,
+ 0x70, 0x64, 0x72, 0x6a, 0x70, 0x6d, 0x66, 0x69, 0x74, 0x62, 0x66, 0x6a, 0x72, 0x69, 0x62, 0x74,
+ 0x71, 0x77, 0x6e, 0x72, 0x6b, 0x73, 0x66, 0x71, 0x74, 0x6a, 0x6c, 0x68, 0x6b, 0x6a, 0x68, 0x6c,
+ 0x6c, 0x69, 0x6d, 0x67, 0x76, 0x78, 0x61, 0x78, 0x6b, 0x6a, 0x65, 0x74, 0x75, 0x6d, 0x74, 0x71,
+ 0x79, 0x61, 0x6a, 0x75, 0x75, 0x6c, 0x74, 0x6f, 0x72, 0x6f, 0x74, 0x65, 0x75, 0x76, 0x62, 0x74,
+ 0x6f, 0x64, 0x62, 0x6a, 0x67, 0x62, 0x68, 0x6f, 0x6b, 0x75, 0x6a, 0x6c, 0x77, 0x69, 0x78, 0x66,
+ 0x6e, 0x68, 0x70, 0x66, 0x68, 0x77, 0x74, 0x67, 0x6b, 0x68, 0x63, 0x69, 0x76, 0x62, 0x6e, 0x69,
+ 0x64, 0x6d, 0x70, 0x63, 0x6a, 0x78, 0x67, 0x64, 0x69, 0x70, 0x65, 0x6c, 0x61, 0x67, 0x6a, 0x64,
+ 0x67, 0x61, 0x62, 0x76, 0x69, 0x77, 0x70, 0x70, 0x6a, 0x78, 0x70, 0x68, 0x78, 0x65, 0x64, 0x61,
+ 0x69, 0x6e, 0x76, 0x62, 0x64, 0x66, 0x6a, 0x79, 0x63, 0x78, 0x6b, 0x71, 0x78, 0x77, 0x6d, 0x6b,
+ 0x75, 0x72, 0x6f, 0x67, 0x75, 0x69, 0x72, 0x76, 0x66, 0x79, 0x63, 0x77, 0x74, 0x6d, 0x67, 0x6c,
+ 0x70, 0x75, 0x71, 0x78, 0x6f, 0x6d, 0x73, 0x75, 0x64, 0x78, 0x6b, 0x77, 0x6b, 0x77, 0x6f, 0x79,
+ 0x78, 0x6f, 0x73, 0x65, 0x62, 0x61, 0x79, 0x75, 0x74, 0x66, 0x71, 0x74, 0x6e, 0x6b, 0x68, 0x61,
+ 0x74, 0x62, 0x75, 0x65, 0x76, 0x78, 0x6b, 0x75, 0x6b, 0x69, 0x70, 0x74, 0x75, 0x74, 0x6e, 0x6b,
+ 0x68, 0x62, 0x61, 0x65, 0x6b, 0x77, 0x63, 0x66, 0x61, 0x62, 0x6a, 0x73, 0x68, 0x63, 0x61, 0x79,
+ 0x68, 0x70, 0x74, 0x70, 0x73, 0x6d, 0x67, 0x71, 0x6b, 0x73, 0x63, 0x6d, 0x61, 0x6b, 0x69, 0x69,
+ 0x72, 0x65, 0x61, 0x6e, 0x61, 0x78, 0x67, 0x6a, 0x73, 0x67, 0x6a, 0x6c, 0x66, 0x74, 0x6b, 0x75,
+ 0x6a, 0x6b, 0x66, 0x69, 0x68, 0x63, 0x73, 0x65, 0x6b, 0x62, 0x77, 0x68, 0x73, 0x72, 0x6a, 0x67,
+ 0x6a, 0x63, 0x6a, 0x70, 0x77, 0x72, 0x69, 0x69, 0x6d, 0x6b, 0x70, 0x62, 0x79, 0x74, 0x63, 0x74,
+ 0x6d, 0x6c, 0x79, 0x79, 0x76, 0x6e, 0x74, 0x79, 0x6b, 0x6a, 0x70, 0x6b, 0x6f, 0x78, 0x6f, 0x72,
+ 0x77, 0x69, 0x71, 0x62, 0x61, 0x6a, 0x72, 0x6b, 0x6e, 0x78, 0x6e, 0x65, 0x75, 0x61, 0x70, 0x70,
+ 0x71, 0x61, 0x66, 0x6d, 0x65, 0x6c, 0x75, 0x71, 0x70, 0x6e, 0x63, 0x6a, 0x69, 0x61, 0x62, 0x72,
+ 0x77, 0x79, 0x6c, 0x65, 0x70, 0x6d, 0x75, 0x78, 0x6f, 0x78, 0x6f, 0x73, 0x6c, 0x78, 0x61, 0x75,
+ 0x73, 0x63, 0x65, 0x6b, 0x78, 0x61, 0x6d, 0x67, 0x69, 0x71, 0x6e, 0x76, 0x6a, 0x70, 0x70, 0x6b,
+ 0x68, 0x78, 0x73, 0x75, 0x74, 0x70, 0x72, 0x70, 0x61, 0x67, 0x63, 0x78, 0x78, 0x61, 0x68, 0x76,
+ 0x77, 0x64, 0x6d, 0x66, 0x71, 0x69, 0x71, 0x61, 0x6a, 0x6f, 0x6c, 0x6c, 0x62, 0x6a, 0x77, 0x77,
+ 0x77, 0x70, 0x6b, 0x78, 0x68, 0x61, 0x67, 0x6d, 0x72, 0x6b, 0x73, 0x75, 0x70, 0x67, 0x70, 0x73,
+ 0x76, 0x6a, 0x74, 0x76, 0x79, 0x67, 0x6d, 0x61, 0x67, 0x73, 0x6c, 0x6a, 0x79, 0x64, 0x61, 0x76,
+ 0x68, 0x75, 0x76, 0x69, 0x75, 0x72, 0x6e, 0x63, 0x62, 0x63, 0x6c, 0x76, 0x78, 0x6b, 0x79, 0x78,
+ 0x79, 0x79, 0x68, 0x74, 0x6a, 0x71, 0x6c, 0x73, 0x69, 0x69, 0x6c, 0x61, 0x70, 0x74, 0x66, 0x65,
+ 0x71, 0x67, 0x75, 0x6b, 0x66, 0x69, 0x61, 0x72, 0x72, 0x6e, 0x6e, 0x6c, 0x64, 0x65, 0x79, 0x68,
+ 0x6d, 0x66, 0x63, 0x71, 0x6b, 0x73, 0x62, 0x6d, 0x68, 0x72, 0x70, 0x6c, 0x68, 0x64, 0x76, 0x61,
+ 0x77, 0x6a, 0x61, 0x6c, 0x6b, 0x69, 0x63, 0x77, 0x6f, 0x78, 0x63, 0x64, 0x62, 0x63, 0x74, 0x62,
+ 0x69, 0x79, 0x6f, 0x75, 0x6a, 0x6f, 0x73, 0x6a, 0x66, 0x62, 0x6a, 0x6a, 0x76, 0x65, 0x69, 0x6d,
+ 0x78, 0x68, 0x6d, 0x67, 0x78, 0x76, 0x78, 0x73, 0x6e, 0x6f, 0x72, 0x73, 0x72, 0x77, 0x6c, 0x64,
+ 0x6e, 0x74, 0x62, 0x65, 0x61, 0x64, 0x72, 0x75, 0x6a, 0x64, 0x63, 0x67, 0x61, 0x6f, 0x76, 0x76,
+ 0x68, 0x69, 0x73, 0x75, 0x6b, 0x6d, 0x65, 0x69, 0x6d, 0x6a, 0x77, 0x6e, 0x77, 0x67, 0x6a, 0x6b,
+ 0x6d, 0x77, 0x73, 0x62, 0x63, 0x73, 0x6c, 0x62, 0x71, 0x6c, 0x6f, 0x73, 0x6d, 0x61, 0x77, 0x76,
+ 0x64, 0x66, 0x79, 0x6a, 0x6f, 0x76, 0x67, 0x6c, 0x6c, 0x6f, 0x76, 0x72, 0x77, 0x72, 0x6d, 0x64,
+ 0x6b, 0x76, 0x74, 0x69, 0x62, 0x79, 0x63, 0x6b, 0x64, 0x6a, 0x63, 0x75, 0x6d, 0x76, 0x76, 0x62,
+ 0x69, 0x6b, 0x71, 0x65, 0x6c, 0x79, 0x6c, 0x76, 0x64, 0x70, 0x68, 0x6a, 0x6d, 0x75, 0x6e, 0x73,
+ 0x68, 0x77, 0x66, 0x6a, 0x73, 0x61, 0x72, 0x67, 0x6d, 0x63, 0x6a, 0x77, 0x64, 0x61, 0x6b, 0x68,
+ 0x6c, 0x6a, 0x64, 0x73, 0x68, 0x77, 0x67, 0x71, 0x74, 0x72, 0x72, 0x6a, 0x67, 0x66, 0x6d, 0x69,
+ 0x77, 0x72, 0x67, 0x75, 0x76, 0x65, 0x6e, 0x79, 0x6b, 0x71, 0x6c, 0x79, 0x6a, 0x64, 0x66, 0x6a,
+ 0x6f, 0x6c, 0x75, 0x71, 0x71, 0x6e, 0x75, 0x79, 0x68, 0x79, 0x63, 0x71, 0x69, 0x67, 0x69, 0x70,
+ 0x70, 0x67, 0x68, 0x71, 0x68, 0x76, 0x68, 0x78, 0x62, 0x6e, 0x79, 0x73, 0x77, 0x73, 0x66, 0x6c,
+ 0x63, 0x74, 0x71, 0x66, 0x70, 0x6b, 0x75, 0x6d, 0x79, 0x64, 0x77, 0x64, 0x6c, 0x68, 0x62, 0x79,
+ 0x78, 0x62, 0x6f, 0x76, 0x64, 0x62, 0x69, 0x62, 0x77, 0x61, 0x70, 0x67, 0x79, 0x73, 0x66, 0x6d,
+ 0x6d, 0x62, 0x77, 0x70, 0x66, 0x61, 0x79, 0x66, 0x67, 0x65, 0x78, 0x73, 0x65, 0x70, 0x79, 0x71,
+ 0x75, 0x71, 0x77, 0x77, 0x68, 0x6e, 0x6a, 0x6b, 0x72, 0x64, 0x68, 0x67, 0x64, 0x66, 0x6c, 0x62,
+ 0x75, 0x68, 0x64, 0x6f, 0x68, 0x73, 0x76, 0x61, 0x6e, 0x61, 0x77, 0x6f, 0x71, 0x62, 0x62, 0x65,
+ 0x6f, 0x68, 0x6b, 0x76, 0x6c, 0x67, 0x65, 0x6c, 0x74, 0x65, 0x79, 0x66, 0x72, 0x76, 0x74, 0x6f,
+ 0x63, 0x69, 0x6d, 0x6d, 0x78, 0x6c, 0x6d, 0x65, 0x6a, 0x63, 0x72, 0x65, 0x62, 0x6b, 0x66, 0x64,
+ 0x6d, 0x79, 0x72, 0x76, 0x74, 0x6d, 0x6a, 0x66, 0x67, 0x67, 0x61, 0x67, 0x6f, 0x64, 0x76, 0x79,
+ 0x76, 0x69, 0x6e, 0x66, 0x79, 0x73, 0x69, 0x65, 0x76, 0x6a, 0x6f, 0x66, 0x68, 0x64, 0x72, 0x6c,
+ 0x67, 0x6c, 0x79, 0x70, 0x6e, 0x72, 0x61, 0x69, 0x73, 0x6e, 0x65, 0x79, 0x67, 0x63, 0x6d, 0x74,
+ 0x67, 0x75, 0x6c, 0x6c, 0x71, 0x70, 0x62, 0x74, 0x61, 0x61, 0x64, 0x76, 0x69, 0x74, 0x6b, 0x76,
+ 0x76, 0x68, 0x62, 0x62, 0x66, 0x71, 0x6a, 0x72, 0x6a, 0x6b, 0x6c, 0x72, 0x6e, 0x74, 0x74, 0x77,
+ 0x61, 0x77, 0x69, 0x63, 0x6e, 0x6e, 0x68, 0x6c, 0x68, 0x61, 0x76, 0x61, 0x61, 0x75, 0x75, 0x6e,
+ 0x69, 0x70, 0x6d, 0x6c, 0x68, 0x73, 0x71, 0x63, 0x73, 0x61, 0x72, 0x75, 0x6f, 0x75, 0x67, 0x70,
+ 0x65, 0x69, 0x65, 0x77, 0x72, 0x79, 0x77, 0x73, 0x76, 0x6f, 0x77, 0x6a, 0x69, 0x72, 0x69, 0x71,
+ 0x79, 0x63, 0x63, 0x75, 0x6b, 0x69, 0x75, 0x62, 0x63, 0x75, 0x6d, 0x61, 0x6b, 0x6a, 0x63, 0x6f,
+ 0x77, 0x71, 0x69, 0x6b, 0x6e, 0x77, 0x75, 0x68, 0x78, 0x74, 0x70, 0x77, 0x68, 0x67, 0x73, 0x72,
+ 0x76, 0x61, 0x66, 0x68, 0x62, 0x61, 0x6a, 0x6e, 0x73, 0x77, 0x79, 0x61, 0x66, 0x74, 0x6a, 0x61,
+ 0x70, 0x6f, 0x66, 0x75, 0x65, 0x6a, 0x76, 0x66, 0x67, 0x75, 0x6c, 0x79, 0x6d, 0x79, 0x73, 0x79,
+ 0x66, 0x64, 0x61, 0x75, 0x79, 0x6d, 0x66, 0x6a, 0x78, 0x6a, 0x73, 0x76, 0x6a, 0x6a, 0x6d, 0x6f,
+ 0x67, 0x63, 0x66, 0x71, 0x67, 0x69, 0x73, 0x68, 0x6b, 0x6e, 0x6e, 0x6a, 0x70, 0x61, 0x68, 0x6a,
+ 0x77, 0x6d, 0x65, 0x69, 0x6b, 0x67, 0x6e, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x73, 0x6d, 0x75, 0x76,
+ 0x77, 0x6b, 0x6b, 0x72, 0x70, 0x61, 0x74, 0x6c, 0x76, 0x76, 0x76, 0x75, 0x70, 0x72, 0x77, 0x6c,
+ 0x6b, 0x64, 0x66, 0x6f, 0x64, 0x6e, 0x6f, 0x75, 0x64, 0x67, 0x63, 0x71, 0x64, 0x70, 0x79, 0x67,
+ 0x61, 0x70, 0x62, 0x72, 0x71, 0x6d, 0x66, 0x65, 0x6f, 0x66, 0x63, 0x77, 0x6b, 0x78, 0x70, 0x77,
+ 0x65, 0x71, 0x78, 0x66, 0x70, 0x64, 0x76, 0x63, 0x74, 0x6d, 0x62, 0x77, 0x6b, 0x70, 0x6d, 0x6a,
+ 0x64, 0x72, 0x65, 0x61, 0x6e, 0x75, 0x62, 0x6a, 0x66, 0x6d, 0x6f, 0x6e, 0x74, 0x6a, 0x79, 0x67,
+ 0x73, 0x6e, 0x64, 0x74, 0x72, 0x79, 0x64, 0x6a, 0x79, 0x61, 0x61, 0x64, 0x62, 0x66, 0x6f, 0x72,
+ 0x70, 0x72, 0x75, 0x66, 0x77, 0x6e, 0x62, 0x6c, 0x73, 0x64, 0x66, 0x71, 0x69, 0x79, 0x66, 0x74,
+ 0x64, 0x61, 0x70, 0x67, 0x62, 0x69, 0x72, 0x71, 0x61, 0x78, 0x74, 0x77, 0x6b, 0x6f, 0x62, 0x77,
+ 0x6c, 0x75, 0x67, 0x78, 0x67, 0x61, 0x64, 0x76, 0x68, 0x72, 0x68, 0x71, 0x64, 0x63, 0x70, 0x69,
+ 0x6f, 0x75, 0x6e, 0x76, 0x78, 0x6d, 0x69, 0x68, 0x78, 0x62, 0x63, 0x72, 0x65, 0x69, 0x63, 0x72,
+ 0x71, 0x75, 0x71, 0x69, 0x74, 0x65, 0x6b, 0x71, 0x79, 0x6e, 0x6e, 0x74, 0x63, 0x70, 0x71, 0x6a,
+ 0x6a, 0x6e, 0x73, 0x6a, 0x6e, 0x66, 0x63, 0x66, 0x66, 0x63, 0x6f, 0x76, 0x69, 0x78, 0x75, 0x68,
+ 0x65, 0x74, 0x65, 0x6b, 0x68, 0x68, 0x6c, 0x66, 0x79, 0x6d, 0x64, 0x78, 0x6a, 0x69, 0x71, 0x6b,
+ 0x6b, 0x61, 0x69, 0x73, 0x67, 0x78, 0x6d, 0x73, 0x78, 0x6b, 0x62, 0x75, 0x71, 0x76, 0x79, 0x6b,
+ 0x61, 0x73, 0x6d, 0x75, 0x66, 0x63, 0x6f, 0x6c, 0x65, 0x6a, 0x6f, 0x78, 0x63, 0x78, 0x75, 0x6b,
+ 0x78, 0x6e, 0x6c, 0x71, 0x70, 0x75, 0x68, 0x68, 0x6b, 0x78, 0x77, 0x69, 0x73, 0x6c, 0x67, 0x6b,
+ 0x6d, 0x6f, 0x6b, 0x72, 0x77, 0x78, 0x73, 0x68, 0x63, 0x68, 0x69, 0x62, 0x77, 0x72, 0x77, 0x77,
+ 0x73, 0x66, 0x77, 0x6e, 0x68, 0x6a, 0x68, 0x73, 0x6c, 0x6c, 0x6d, 0x73, 0x67, 0x63, 0x64, 0x6d,
+ 0x68, 0x73, 0x69, 0x66, 0x78, 0x64, 0x6e, 0x74, 0x69, 0x71, 0x6f, 0x62, 0x68, 0x6f, 0x76, 0x71,
+ 0x65, 0x63, 0x70, 0x61, 0x68, 0x67, 0x72, 0x79, 0x71, 0x6e, 0x69, 0x68, 0x74, 0x61, 0x6c, 0x6b,
+ 0x6c, 0x6f, 0x63, 0x70, 0x69, 0x71, 0x6d, 0x63, 0x64, 0x6b, 0x79, 0x67, 0x72, 0x75, 0x65, 0x6b,
+ 0x75, 0x61, 0x76, 0x63, 0x61, 0x6d, 0x6e, 0x6d, 0x70, 0x6a, 0x77, 0x66, 0x75, 0x66, 0x68, 0x6d,
+ 0x71, 0x66, 0x6d, 0x6e, 0x64, 0x63, 0x65, 0x6c, 0x72, 0x6f, 0x6f, 0x62, 0x78, 0x6d, 0x67, 0x62,
+ 0x6a, 0x6d, 0x75, 0x67, 0x63, 0x63, 0x61, 0x78, 0x66, 0x78, 0x66, 0x78, 0x6d, 0x66, 0x79, 0x78,
+ 0x72, 0x65, 0x65, 0x79, 0x61, 0x79, 0x70, 0x73, 0x72, 0x73, 0x77, 0x69, 0x62, 0x62, 0x71, 0x61,
+ 0x76, 0x66, 0x73, 0x66, 0x66, 0x62, 0x63, 0x62, 0x6f, 0x63, 0x64, 0x63, 0x6d, 0x6d, 0x75, 0x6d,
+ 0x6f, 0x68, 0x62, 0x62, 0x6e, 0x76, 0x65, 0x6b, 0x6a, 0x69, 0x70, 0x63, 0x75, 0x68, 0x6f, 0x6e,
+ 0x6d, 0x71, 0x66, 0x75, 0x64, 0x79, 0x74, 0x69, 0x6d, 0x6e, 0x78, 0x6c, 0x73, 0x69, 0x73, 0x79,
+ 0x67, 0x6b, 0x6a, 0x6e, 0x63, 0x6f, 0x79, 0x66, 0x62, 0x69, 0x63, 0x71, 0x6c, 0x73, 0x73, 0x63,
+ 0x6c, 0x77, 0x64, 0x6d, 0x67, 0x72, 0x64, 0x6b, 0x75, 0x72, 0x69, 0x70, 0x68, 0x70, 0x62, 0x62,
+ 0x64, 0x78, 0x69, 0x78, 0x6f, 0x67, 0x6e, 0x6c, 0x77, 0x6f, 0x78, 0x65, 0x70, 0x70, 0x6a, 0x68,
+ 0x6e, 0x71, 0x64, 0x61, 0x6d, 0x63, 0x75, 0x75, 0x6e, 0x79, 0x66, 0x63, 0x6e, 0x70, 0x6f, 0x74,
+ 0x77, 0x75, 0x63, 0x6e, 0x71, 0x78, 0x67, 0x74, 0x79, 0x66, 0x71, 0x6b, 0x64, 0x74, 0x6c, 0x79,
+ 0x62, 0x78, 0x6d, 0x67, 0x6b, 0x6a, 0x67, 0x61, 0x63, 0x61, 0x6e, 0x73, 0x61, 0x6b, 0x66, 0x69,
+ 0x74, 0x6e, 0x70, 0x66, 0x6a, 0x66, 0x64, 0x73, 0x66, 0x74, 0x6a, 0x74, 0x63, 0x72, 0x63, 0x73,
+ 0x71, 0x76, 0x6c, 0x64, 0x69, 0x74, 0x6b, 0x73, 0x6c, 0x6d, 0x75, 0x66, 0x62, 0x79, 0x6d, 0x62,
+ 0x70, 0x77, 0x6f, 0x63, 0x6b, 0x69, 0x6c, 0x78, 0x69, 0x78, 0x66, 0x65, 0x68, 0x62, 0x6f, 0x67,
+ 0x65, 0x61, 0x71, 0x6f, 0x69, 0x71, 0x75, 0x74, 0x75, 0x62, 0x69, 0x79, 0x76, 0x78, 0x6a, 0x66,
+ 0x79, 0x6e, 0x6e, 0x73, 0x62, 0x63, 0x77, 0x74, 0x75, 0x71, 0x6d, 0x6d, 0x6d, 0x74, 0x73, 0x6a,
+ 0x78, 0x74, 0x64, 0x6d, 0x79, 0x6c, 0x6f, 0x66, 0x6d, 0x61, 0x6f, 0x70, 0x71, 0x76, 0x79, 0x70,
+ 0x67, 0x71, 0x6d, 0x69, 0x62, 0x66, 0x75, 0x79, 0x71, 0x61, 0x69, 0x6b, 0x68, 0x66, 0x62, 0x66,
+ 0x6c, 0x73, 0x6e, 0x71, 0x62, 0x6c, 0x6e, 0x6d, 0x63, 0x66, 0x73, 0x6d, 0x61, 0x69, 0x75, 0x6a,
+ 0x64, 0x61, 0x72, 0x71, 0x75, 0x73, 0x6d, 0x71, 0x79, 0x68, 0x61, 0x77, 0x74, 0x64, 0x69, 0x6d,
+ 0x6d, 0x61, 0x72, 0x72, 0x76, 0x64, 0x66, 0x70, 0x64, 0x6c, 0x73, 0x6c, 0x75, 0x74, 0x73, 0x66,
+ 0x75, 0x67, 0x77, 0x6d, 0x6f, 0x77, 0x6e, 0x68, 0x67, 0x66, 0x68, 0x6f, 0x6b, 0x68, 0x62, 0x73,
+ 0x64, 0x79, 0x73, 0x75, 0x69, 0x6f, 0x77, 0x71, 0x68, 0x6e, 0x66, 0x78, 0x6f, 0x66, 0x73, 0x68,
+ 0x74, 0x76, 0x6d, 0x6d, 0x67, 0x6d, 0x6d, 0x73, 0x65, 0x66, 0x6b, 0x6d, 0x69, 0x70, 0x72, 0x70,
+ 0x70, 0x70, 0x64, 0x71, 0x75, 0x74, 0x61, 0x78, 0x77, 0x64, 0x6f, 0x79, 0x63, 0x69, 0x6b, 0x64,
+ 0x67, 0x78, 0x6d, 0x65, 0x63, 0x66, 0x73, 0x6d, 0x70, 0x64, 0x77, 0x75, 0x66, 0x61, 0x77, 0x70,
+ 0x63, 0x66, 0x75, 0x65, 0x78, 0x6c, 0x77, 0x73, 0x62, 0x77, 0x75, 0x67, 0x64, 0x72, 0x61, 0x6b,
+ 0x6c, 0x6a, 0x68, 0x79, 0x6a, 0x6b, 0x6b, 0x70, 0x67, 0x74, 0x6b, 0x71, 0x72, 0x65, 0x78, 0x71,
+ 0x74, 0x64, 0x79, 0x79, 0x61, 0x69, 0x68, 0x6c, 0x68, 0x64, 0x67, 0x74, 0x64, 0x67, 0x64, 0x73,
+ 0x6d, 0x6a, 0x6c, 0x61, 0x70, 0x6e, 0x72, 0x62, 0x6d, 0x66, 0x62, 0x77, 0x6e, 0x6e, 0x74, 0x79,
+ 0x62, 0x6d, 0x77, 0x76, 0x65, 0x6c, 0x72, 0x76, 0x79, 0x75, 0x76, 0x78, 0x6f, 0x77, 0x61, 0x75,
+ 0x6c, 0x75, 0x63, 0x65, 0x79, 0x77, 0x69, 0x73, 0x62, 0x71, 0x6a, 0x6e, 0x6b, 0x72, 0x6c, 0x76,
+ 0x6e, 0x62, 0x64, 0x75, 0x62, 0x6e, 0x75, 0x6c, 0x65, 0x78, 0x72, 0x6c, 0x6a, 0x66, 0x6f, 0x6e,
+ 0x74, 0x68, 0x77, 0x64, 0x68, 0x6d, 0x6b, 0x6b, 0x6b, 0x6e, 0x71, 0x76, 0x6f, 0x71, 0x66, 0x75,
+ 0x68, 0x70, 0x73, 0x75, 0x6d, 0x6c, 0x6e, 0x6b, 0x74, 0x75, 0x79, 0x66, 0x69, 0x61, 0x79, 0x61,
+ 0x6d, 0x66, 0x71, 0x67, 0x63, 0x73, 0x63, 0x75, 0x76, 0x68, 0x70, 0x67, 0x78, 0x6f, 0x6c, 0x68,
+ 0x6e, 0x75, 0x69, 0x69, 0x75, 0x78, 0x6f, 0x6b, 0x67, 0x76, 0x79, 0x73, 0x61, 0x72, 0x74, 0x62,
+ 0x64, 0x61, 0x63, 0x64, 0x63, 0x77, 0x6e, 0x6b, 0x78, 0x65, 0x70, 0x73, 0x6b, 0x64, 0x6a, 0x64,
+ 0x76, 0x6b, 0x74, 0x6e, 0x6b, 0x67, 0x64, 0x78, 0x65, 0x71, 0x68, 0x68, 0x74, 0x62, 0x79, 0x75,
+ 0x73, 0x6a, 0x69, 0x72, 0x64, 0x75, 0x61, 0x66, 0x70, 0x66, 0x71, 0x6d, 0x6b, 0x63, 0x62, 0x71,
+ 0x66, 0x65, 0x68, 0x78, 0x77, 0x65, 0x62, 0x78, 0x66, 0x72, 0x67, 0x78, 0x6d, 0x75, 0x6d, 0x75,
+ 0x6a, 0x72, 0x68, 0x6d, 0x71, 0x68, 0x63, 0x6e, 0x76, 0x63, 0x79, 0x66, 0x73, 0x69, 0x6e, 0x6f,
+ 0x70, 0x66, 0x70, 0x62, 0x66, 0x63, 0x6a, 0x61, 0x72, 0x69, 0x79, 0x69, 0x69, 0x74, 0x76, 0x61,
+ 0x61, 0x67, 0x68, 0x72, 0x62, 0x61, 0x64, 0x6b, 0x66, 0x6d, 0x6e, 0x79, 0x79, 0x67, 0x68, 0x79,
+ 0x76, 0x64, 0x65, 0x65, 0x61, 0x6b, 0x6a, 0x6f, 0x6d, 0x6e, 0x63, 0x68, 0x6e, 0x76, 0x71, 0x67,
+ 0x6b, 0x74, 0x70, 0x68, 0x73, 0x77, 0x6c, 0x76, 0x67, 0x6a, 0x77, 0x79, 0x77, 0x74, 0x68, 0x6a,
+ 0x70, 0x6e, 0x6f, 0x62, 0x6f, 0x62, 0x6c, 0x6d, 0x63, 0x6f, 0x77, 0x6e, 0x75, 0x6c, 0x6a, 0x61,
+ 0x68, 0x71, 0x76, 0x64, 0x78, 0x66, 0x66, 0x67, 0x65, 0x6e, 0x74, 0x6e, 0x77, 0x65, 0x6c, 0x67,
+ 0x62, 0x6e, 0x62, 0x6d, 0x6b, 0x69, 0x6c, 0x67, 0x74, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x6d, 0x71,
+ 0x6c, 0x65, 0x6d, 0x74, 0x62, 0x62, 0x6f, 0x64, 0x61, 0x61, 0x70, 0x6a, 0x79, 0x62, 0x62, 0x77,
+ 0x6d, 0x70, 0x79, 0x70, 0x67, 0x69, 0x76, 0x75, 0x66, 0x70, 0x6a, 0x6c, 0x62, 0x71, 0x61, 0x6b,
+ 0x77, 0x68, 0x6d, 0x6b, 0x73, 0x73, 0x68, 0x62, 0x70, 0x71, 0x71, 0x6b, 0x70, 0x68, 0x75, 0x63,
+ 0x77, 0x63, 0x76, 0x62, 0x6a, 0x66, 0x61, 0x77, 0x6f, 0x76, 0x64, 0x75, 0x72, 0x64, 0x6a, 0x70,
+ 0x75, 0x76, 0x65, 0x68, 0x68, 0x63, 0x6c, 0x6f, 0x78, 0x6b, 0x66, 0x68, 0x68, 0x71, 0x79, 0x74,
+ 0x76, 0x68, 0x77, 0x76, 0x79, 0x75, 0x6b, 0x79, 0x61, 0x64, 0x69, 0x6f, 0x6e, 0x68, 0x6d, 0x75,
+ 0x63, 0x71, 0x6b, 0x77, 0x65, 0x68, 0x72, 0x63, 0x64, 0x64, 0x6a, 0x62, 0x6e, 0x6d, 0x72, 0x77,
+ 0x74, 0x71, 0x67, 0x6d, 0x68, 0x61, 0x63, 0x65, 0x6c, 0x64, 0x64, 0x71, 0x75, 0x62, 0x67, 0x69,
+ 0x6d, 0x76, 0x68, 0x62, 0x71, 0x74, 0x65, 0x62, 0x61, 0x6f, 0x70, 0x78, 0x6f, 0x65, 0x78, 0x71,
+ 0x67, 0x77, 0x70, 0x66, 0x65, 0x72, 0x69, 0x79, 0x72, 0x66, 0x62, 0x79, 0x66, 0x62, 0x6d, 0x6b,
+ 0x6b, 0x6b, 0x78, 0x6a, 0x68, 0x66, 0x6f, 0x77, 0x70, 0x75, 0x70, 0x75, 0x78, 0x6d, 0x63, 0x74,
+ 0x61, 0x78, 0x77, 0x65, 0x63, 0x68, 0x6f, 0x6f, 0x74, 0x79, 0x73, 0x6a, 0x6b, 0x6b, 0x67, 0x70,
+ 0x74, 0x77, 0x76, 0x73, 0x75, 0x6a, 0x74, 0x66, 0x6a, 0x73, 0x64, 0x6f, 0x6c, 0x73, 0x6e, 0x75,
+ 0x77, 0x6d, 0x71, 0x71, 0x63, 0x6f, 0x62, 0x75, 0x62, 0x72, 0x72, 0x66, 0x71, 0x63, 0x71, 0x6f,
+ 0x67, 0x71, 0x65, 0x76, 0x71, 0x70, 0x6f, 0x79, 0x73, 0x69, 0x67, 0x64, 0x71, 0x68, 0x73, 0x79,
+ 0x74, 0x68, 0x70, 0x6d, 0x6e, 0x74, 0x6e, 0x79, 0x79, 0x62, 0x65, 0x6f, 0x74, 0x75, 0x61, 0x6b,
+ 0x69, 0x6e, 0x70, 0x6e, 0x70, 0x6b, 0x70, 0x72, 0x78, 0x63, 0x6c, 0x66, 0x67, 0x71, 0x6e, 0x72,
+ 0x65, 0x69, 0x72, 0x6e, 0x75, 0x71, 0x61, 0x6e, 0x64, 0x6b, 0x6a, 0x69, 0x63, 0x62, 0x76, 0x62,
+ 0x6f, 0x61, 0x74, 0x72, 0x66, 0x6b, 0x67, 0x63, 0x6d, 0x66, 0x73, 0x64, 0x65, 0x72, 0x65, 0x73,
+ 0x76, 0x70, 0x62, 0x62, 0x70, 0x71, 0x70, 0x66, 0x78, 0x62, 0x6b, 0x6f, 0x63, 0x6e, 0x74, 0x63,
+ 0x61, 0x63, 0x78, 0x74, 0x79, 0x6e, 0x77, 0x64, 0x6a, 0x73, 0x64, 0x72, 0x6a, 0x68, 0x6a, 0x68,
+ 0x64, 0x62, 0x67, 0x68, 0x6a, 0x75, 0x78, 0x74, 0x73, 0x70, 0x6d, 0x65, 0x75, 0x71, 0x61, 0x6f,
+ 0x6c, 0x78, 0x67, 0x79, 0x66, 0x71, 0x64, 0x75, 0x79, 0x75, 0x77, 0x67, 0x78, 0x69, 0x70, 0x70,
+ 0x74, 0x6d, 0x62, 0x70, 0x68, 0x71, 0x75, 0x6d, 0x67, 0x73, 0x79, 0x64, 0x74, 0x72, 0x67, 0x74,
+ 0x70, 0x6b, 0x67, 0x78, 0x66, 0x6c, 0x77, 0x71, 0x78, 0x6c, 0x75, 0x66, 0x69, 0x67, 0x69, 0x67,
+ 0x79, 0x6c, 0x70, 0x71, 0x6b, 0x6c, 0x67, 0x74, 0x79, 0x79, 0x78, 0x79, 0x63, 0x73, 0x79, 0x6c,
+ 0x62, 0x62, 0x78, 0x62, 0x74, 0x75, 0x61, 0x6d, 0x73, 0x6d, 0x61, 0x64, 0x78, 0x6d, 0x61, 0x6b,
+ 0x70, 0x77, 0x67, 0x63, 0x65, 0x67, 0x62, 0x78, 0x70, 0x6c, 0x6b, 0x63, 0x78, 0x63, 0x66, 0x79,
+ 0x62, 0x6c, 0x73, 0x65, 0x6c, 0x6d, 0x70, 0x67, 0x69, 0x67, 0x6d, 0x61, 0x79, 0x6b, 0x76, 0x66,
+ 0x66, 0x76, 0x6e, 0x62, 0x66, 0x61, 0x77, 0x67, 0x6c, 0x61, 0x6f, 0x61, 0x62, 0x75, 0x64, 0x72,
+ 0x77, 0x76, 0x6d, 0x65, 0x77, 0x65, 0x67, 0x73, 0x64, 0x73, 0x77, 0x68, 0x73, 0x69, 0x74, 0x6a,
+ 0x65, 0x62, 0x77, 0x73, 0x6a, 0x61, 0x64, 0x73, 0x6f, 0x65, 0x75, 0x76, 0x61, 0x63, 0x78, 0x64,
+ 0x69, 0x79, 0x6d, 0x68, 0x69, 0x65, 0x6b, 0x69, 0x6b, 0x62, 0x6d, 0x6d, 0x73, 0x6f, 0x61, 0x70,
+ 0x79, 0x67, 0x6d, 0x6c, 0x61, 0x72, 0x70, 0x79, 0x66, 0x78, 0x61, 0x66, 0x71, 0x74, 0x6c, 0x73,
+ 0x62, 0x70, 0x61, 0x72, 0x6e, 0x78, 0x77, 0x69, 0x77, 0x6a, 0x62, 0x75, 0x6f, 0x6d, 0x67, 0x79,
+ 0x71, 0x63, 0x61, 0x76, 0x77, 0x75, 0x6b, 0x65, 0x72, 0x72, 0x63, 0x65, 0x65, 0x64, 0x71, 0x74,
+ 0x78, 0x77, 0x6f, 0x70, 0x71, 0x63, 0x77, 0x63, 0x75, 0x66, 0x66, 0x6d, 0x67, 0x61, 0x76, 0x77,
+ 0x69, 0x64, 0x72, 0x6f, 0x73, 0x69, 0x6d, 0x69, 0x71, 0x74, 0x64, 0x71, 0x63, 0x72, 0x6d, 0x75,
+ 0x77, 0x63, 0x77, 0x67, 0x77, 0x64, 0x6d, 0x6f, 0x72, 0x6f, 0x6d, 0x6b, 0x62, 0x63, 0x70, 0x6f,
+ 0x72, 0x62, 0x61, 0x71, 0x65, 0x67, 0x64, 0x76, 0x78, 0x63, 0x66, 0x66, 0x71, 0x6f, 0x69, 0x73,
+ 0x6b, 0x72, 0x67, 0x6e, 0x77, 0x77, 0x79, 0x73, 0x69, 0x79, 0x6a, 0x6d, 0x72, 0x74, 0x79, 0x68,
+ 0x72, 0x6e, 0x70, 0x61, 0x6d, 0x67, 0x66, 0x74, 0x67, 0x6d, 0x73, 0x72, 0x79, 0x62, 0x6d, 0x6f,
+ 0x64, 0x63, 0x6f, 0x67, 0x70, 0x69, 0x79, 0x71, 0x68, 0x72, 0x79, 0x63, 0x6e, 0x77, 0x6b, 0x66,
+ 0x67, 0x73, 0x65, 0x71, 0x72, 0x68, 0x64, 0x73, 0x78, 0x6f, 0x64, 0x66, 0x78, 0x68, 0x66, 0x65,
+ 0x64, 0x78, 0x72, 0x61, 0x68, 0x74, 0x79, 0x6f, 0x6f, 0x6a, 0x63, 0x6a, 0x66, 0x6b, 0x64, 0x71,
+ 0x6f, 0x66, 0x6b, 0x67, 0x6c, 0x6c, 0x6c, 0x73, 0x61, 0x6f, 0x78, 0x73, 0x71, 0x62, 0x6e, 0x65,
+ 0x74, 0x67, 0x66, 0x6f, 0x6c, 0x77, 0x6a, 0x67, 0x6d, 0x61, 0x77, 0x77, 0x73, 0x73, 0x61, 0x6c,
+ 0x79, 0x6a, 0x78, 0x62, 0x74, 0x62, 0x62, 0x62, 0x70, 0x64, 0x6e, 0x6e, 0x62, 0x63, 0x79, 0x62,
+ 0x61, 0x6c, 0x74, 0x6e, 0x67, 0x77, 0x63, 0x75, 0x71, 0x61, 0x63, 0x67, 0x6f, 0x71, 0x63, 0x67,
+ 0x76, 0x73, 0x63, 0x66, 0x74, 0x68, 0x63, 0x6c, 0x66, 0x6a, 0x71, 0x69, 0x76, 0x6c, 0x72, 0x62,
+ 0x72, 0x67, 0x61, 0x6f, 0x72, 0x71, 0x64, 0x74, 0x64, 0x71, 0x6b, 0x68, 0x63, 0x6b, 0x67, 0x62,
+ 0x68, 0x6b, 0x79, 0x76, 0x70, 0x71, 0x76, 0x62, 0x72, 0x72, 0x74, 0x61, 0x6f, 0x67, 0x6f, 0x79,
+ 0x74, 0x69, 0x62, 0x72, 0x65, 0x61, 0x73, 0x61, 0x6b, 0x72, 0x6d, 0x73, 0x79, 0x78, 0x63, 0x62,
+ 0x70, 0x68, 0x63, 0x73, 0x77, 0x65, 0x61, 0x71, 0x79, 0x70, 0x71, 0x6a, 0x67, 0x70, 0x6c, 0x62,
+ 0x6f, 0x79, 0x62, 0x72, 0x73, 0x61, 0x75, 0x77, 0x74, 0x79, 0x70, 0x76, 0x64, 0x6c, 0x71, 0x61,
+ 0x67, 0x68, 0x76, 0x66, 0x71, 0x75, 0x72, 0x69, 0x71, 0x66, 0x6a, 0x78, 0x66, 0x6d, 0x6f, 0x75,
+ 0x6d, 0x6a, 0x61, 0x75, 0x6c, 0x6f, 0x71, 0x75, 0x6d, 0x70, 0x67, 0x79, 0x69, 0x61, 0x65, 0x72,
+ 0x6c, 0x6c, 0x71, 0x68, 0x72, 0x75, 0x65, 0x61, 0x77, 0x69, 0x73, 0x63, 0x6d, 0x75, 0x67, 0x69,
+ 0x76, 0x6b, 0x6e, 0x78, 0x70, 0x64, 0x6a, 0x6c, 0x67, 0x6e, 0x6e, 0x69, 0x77, 0x67, 0x67, 0x6e,
+ 0x66, 0x76, 0x61, 0x72, 0x6b, 0x61, 0x78, 0x74, 0x66, 0x76, 0x6e, 0x68, 0x6b, 0x66, 0x64, 0x66,
+ 0x72, 0x6b, 0x68, 0x74, 0x6f, 0x6b, 0x77, 0x73, 0x77, 0x6f, 0x6d, 0x70, 0x61, 0x76, 0x78, 0x76,
+ 0x6c, 0x71, 0x6f, 0x75, 0x79, 0x67, 0x68, 0x68, 0x68, 0x77, 0x68, 0x68, 0x62, 0x6d, 0x6d, 0x74,
+ 0x6f, 0x78, 0x6e, 0x62, 0x6e, 0x65, 0x77, 0x74, 0x79, 0x62, 0x67, 0x79, 0x65, 0x61, 0x72, 0x78,
+ 0x61, 0x73, 0x6e, 0x6e, 0x77, 0x6c, 0x6a, 0x77, 0x62, 0x70, 0x68, 0x64, 0x62, 0x79, 0x65, 0x73,
+ 0x6d, 0x77, 0x77, 0x78, 0x78, 0x69, 0x65, 0x6e, 0x75, 0x71, 0x79, 0x6b, 0x73, 0x6e, 0x75, 0x69,
+ 0x77, 0x69, 0x65, 0x75, 0x6f, 0x79, 0x79, 0x6c, 0x6b, 0x74, 0x74, 0x63, 0x67, 0x67, 0x75, 0x71,
+ 0x74, 0x73, 0x76, 0x69, 0x68, 0x6d, 0x67, 0x67, 0x74, 0x62, 0x69, 0x6d, 0x6b, 0x78, 0x77, 0x68,
+ 0x66, 0x61, 0x6a, 0x67, 0x6f, 0x65, 0x62, 0x69, 0x67, 0x64, 0x78, 0x6b, 0x75, 0x6a, 0x67, 0x72,
+ 0x76, 0x64, 0x6f, 0x72, 0x63, 0x63, 0x76, 0x66, 0x6b, 0x6c, 0x62, 0x64, 0x70, 0x63, 0x68, 0x70,
+ 0x74, 0x71, 0x61, 0x77, 0x6b, 0x78, 0x68, 0x6c, 0x77, 0x63, 0x76, 0x71, 0x6c, 0x69, 0x6a, 0x6c,
+ 0x70, 0x75, 0x78, 0x68, 0x62, 0x77, 0x61, 0x63, 0x77, 0x6c, 0x66, 0x77, 0x79, 0x64, 0x79, 0x79,
+ 0x76, 0x75, 0x77, 0x6e, 0x6d, 0x72, 0x66, 0x6b, 0x74, 0x6b, 0x62, 0x67, 0x76, 0x66, 0x74, 0x74,
+ 0x6d, 0x68, 0x6e, 0x6a, 0x77, 0x6c, 0x6c, 0x75, 0x6b, 0x65, 0x74, 0x6d, 0x62, 0x6c, 0x61, 0x70,
+ 0x75, 0x67, 0x6f, 0x67, 0x6d, 0x64, 0x76, 0x72, 0x6b, 0x6a, 0x65, 0x6e, 0x6c, 0x63, 0x71, 0x6b,
+ 0x6a, 0x64, 0x6f, 0x74, 0x67, 0x70, 0x69, 0x6e, 0x62, 0x72, 0x66, 0x65, 0x66, 0x65, 0x6e, 0x74,
+ 0x64, 0x6a, 0x78, 0x71, 0x61, 0x62, 0x61, 0x66, 0x6a, 0x63, 0x73, 0x66, 0x66, 0x79, 0x69, 0x6c,
+ 0x74, 0x6e, 0x78, 0x69, 0x6b, 0x6c, 0x62, 0x6f, 0x6c, 0x65, 0x61, 0x74, 0x65, 0x65, 0x66, 0x70,
+ 0x74, 0x71, 0x64, 0x67, 0x76, 0x69, 0x79, 0x6f, 0x6a, 0x74, 0x63, 0x62, 0x76, 0x71, 0x6a, 0x6e,
+ 0x78, 0x6b, 0x75, 0x67, 0x6c, 0x68, 0x63, 0x76, 0x63, 0x73, 0x6b, 0x6d, 0x69, 0x62, 0x72, 0x72,
+ 0x70, 0x64, 0x79, 0x79, 0x63, 0x6c, 0x70, 0x72, 0x6a, 0x79, 0x76, 0x74, 0x67, 0x6f, 0x6a, 0x67,
+ 0x68, 0x63, 0x69, 0x69, 0x78, 0x65, 0x77, 0x64, 0x6e, 0x6f, 0x78, 0x72, 0x68, 0x79, 0x68, 0x6f,
+ 0x6a, 0x62, 0x72, 0x75, 0x70, 0x6b, 0x75, 0x76, 0x6c, 0x69, 0x6b, 0x6d, 0x6d, 0x65, 0x73, 0x62,
+ 0x69, 0x68, 0x76, 0x78, 0x61, 0x6c, 0x6e, 0x61, 0x72, 0x72, 0x75, 0x75, 0x70, 0x78, 0x71, 0x79,
+ 0x74, 0x6e, 0x73, 0x6a, 0x75, 0x73, 0x76, 0x67, 0x6d, 0x6b, 0x68, 0x75, 0x77, 0x72, 0x79, 0x62,
+ 0x68, 0x6d, 0x77, 0x6b, 0x6b, 0x61, 0x73, 0x75, 0x6a, 0x74, 0x72, 0x66, 0x68, 0x63, 0x70, 0x79,
+ 0x69, 0x66, 0x6c, 0x67, 0x71, 0x72, 0x67, 0x76, 0x73, 0x63, 0x67, 0x76, 0x68, 0x6e, 0x63, 0x6e,
+ 0x75, 0x63, 0x69, 0x70, 0x6e, 0x78, 0x66, 0x66, 0x75, 0x6b, 0x67, 0x79, 0x6d, 0x6d, 0x70, 0x6e,
+ 0x6e, 0x6e, 0x6a, 0x69, 0x76, 0x63, 0x6c, 0x76, 0x75, 0x79, 0x61, 0x79, 0x74, 0x65, 0x71, 0x67,
+ 0x62, 0x64, 0x62, 0x67, 0x6f, 0x6b, 0x6c, 0x68, 0x61, 0x6d, 0x73, 0x74, 0x70, 0x6b, 0x62, 0x63,
+ 0x75, 0x66, 0x6a, 0x6a, 0x6a, 0x6a, 0x64, 0x76, 0x6b, 0x67, 0x73, 0x75, 0x69, 0x73, 0x6a, 0x6e,
+ 0x6a, 0x68, 0x6d, 0x72, 0x73, 0x71, 0x73, 0x75, 0x6a, 0x6d, 0x69, 0x75, 0x66, 0x71, 0x67, 0x75,
+ 0x73, 0x70, 0x71, 0x61, 0x69, 0x71, 0x74, 0x71, 0x65, 0x6b, 0x71, 0x6f, 0x77, 0x68, 0x6a, 0x77,
+ 0x74, 0x79, 0x6f, 0x74, 0x69, 0x62, 0x70, 0x70, 0x65, 0x6d, 0x6d, 0x77, 0x6d, 0x72, 0x62, 0x67,
+ 0x62, 0x74, 0x61, 0x76, 0x79, 0x6b, 0x79, 0x6d, 0x75, 0x71, 0x6e, 0x6e, 0x76, 0x78, 0x72, 0x70,
+ 0x62, 0x64, 0x70, 0x6c, 0x6e, 0x79, 0x6c, 0x68, 0x70, 0x65, 0x67, 0x6d, 0x65, 0x75, 0x76, 0x62,
+ 0x63, 0x70, 0x6b, 0x75, 0x66, 0x68, 0x68, 0x6f, 0x70, 0x66, 0x68, 0x73, 0x6e, 0x78, 0x75, 0x68,
+ 0x6a, 0x6e, 0x61, 0x78, 0x6e, 0x6b, 0x78, 0x6e, 0x71, 0x66, 0x75, 0x68, 0x62, 0x79, 0x6f, 0x6c,
+ 0x66, 0x67, 0x67, 0x6c, 0x6c, 0x75, 0x62, 0x79, 0x6b, 0x69, 0x71, 0x69, 0x75, 0x72, 0x71, 0x6a,
+ 0x70, 0x76, 0x70, 0x6d, 0x73, 0x70, 0x72, 0x61, 0x6b, 0x77, 0x78, 0x78, 0x6e, 0x73, 0x75, 0x75,
+ 0x6f, 0x68, 0x63, 0x6c, 0x75, 0x75, 0x6d, 0x72, 0x70, 0x75, 0x63, 0x63, 0x63, 0x6b, 0x6a, 0x79,
+ 0x6d, 0x61, 0x73, 0x62, 0x79, 0x75, 0x6b, 0x6d, 0x6a, 0x6f, 0x67, 0x61, 0x62, 0x6a, 0x6e, 0x71,
+ 0x69, 0x71, 0x6a, 0x6a, 0x61, 0x68, 0x68, 0x76, 0x61, 0x6a, 0x72, 0x6b, 0x76, 0x74, 0x6f, 0x61,
+ 0x70, 0x77, 0x6b, 0x67, 0x73, 0x75, 0x70, 0x62, 0x6d, 0x65, 0x67, 0x6e, 0x62, 0x79, 0x74, 0x74,
+ 0x72, 0x61, 0x6b, 0x76, 0x64, 0x69, 0x69, 0x69, 0x6c, 0x68, 0x78, 0x65, 0x70, 0x68, 0x79, 0x70,
+ 0x65, 0x72, 0x64, 0x68, 0x68, 0x6c, 0x61, 0x6f, 0x74, 0x62, 0x78, 0x6c, 0x72, 0x6c, 0x62, 0x75,
+ 0x6c, 0x64, 0x72, 0x64, 0x79, 0x6b, 0x72, 0x72, 0x62, 0x6d, 0x74, 0x72, 0x6b, 0x78, 0x6c, 0x75,
+ 0x78, 0x6a, 0x71, 0x67, 0x72, 0x79, 0x76, 0x71, 0x75, 0x6d, 0x75, 0x67, 0x6a, 0x63, 0x66, 0x6f,
+ 0x63, 0x66, 0x79, 0x74, 0x74, 0x70, 0x75, 0x6a, 0x6e, 0x64, 0x64, 0x6e, 0x68, 0x6e, 0x6e, 0x78,
+ 0x6b, 0x68, 0x72, 0x74, 0x6c, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x6c, 0x68, 0x78, 0x72, 0x66, 0x70,
+ 0x79, 0x6d, 0x61, 0x61, 0x76, 0x77, 0x64, 0x6e, 0x77, 0x74, 0x67, 0x6f, 0x74, 0x6f, 0x6b, 0x77,
+ 0x69, 0x79, 0x79, 0x73, 0x72, 0x71, 0x63, 0x62, 0x69, 0x78, 0x75, 0x72, 0x62, 0x71, 0x6f, 0x6e,
+ 0x66, 0x67, 0x6c, 0x72, 0x66, 0x6b, 0x61, 0x6e, 0x70, 0x70, 0x71, 0x65, 0x68, 0x71, 0x6e, 0x79,
+ 0x75, 0x75, 0x75, 0x67, 0x64, 0x67, 0x75, 0x74, 0x6a, 0x6b, 0x6a, 0x65, 0x71, 0x75, 0x73, 0x6d,
+ 0x77, 0x78, 0x72, 0x6e, 0x71, 0x61, 0x73, 0x63, 0x75, 0x78, 0x6c, 0x6c, 0x78, 0x6a, 0x75, 0x68,
+ 0x65, 0x71, 0x77, 0x6a, 0x76, 0x6d, 0x6d, 0x6e, 0x6a, 0x6e, 0x61, 0x65, 0x64, 0x72, 0x6c, 0x74,
+ 0x76, 0x63, 0x69, 0x65, 0x77, 0x70, 0x61, 0x69, 0x65, 0x74, 0x6d, 0x74, 0x68, 0x67, 0x6f, 0x65,
+ 0x62, 0x69, 0x63, 0x77, 0x77, 0x61, 0x75, 0x76, 0x69, 0x69, 0x77, 0x6f, 0x66, 0x6b, 0x77, 0x65,
+ 0x77, 0x6c, 0x6b, 0x69, 0x6a, 0x6a, 0x6c, 0x67, 0x76, 0x66, 0x74, 0x75, 0x72, 0x6c, 0x66, 0x6a,
+ 0x64, 0x67, 0x62, 0x73, 0x65, 0x75, 0x6e, 0x75, 0x6a, 0x6a, 0x69, 0x6a, 0x70, 0x63, 0x6f, 0x6c,
+ 0x69, 0x6b, 0x69, 0x6e, 0x79, 0x69, 0x70, 0x71, 0x66, 0x6b, 0x71, 0x71, 0x6e, 0x73, 0x66, 0x77,
+ 0x66, 0x73, 0x72, 0x77, 0x70, 0x74, 0x66, 0x71, 0x69, 0x6b, 0x74, 0x64, 0x61, 0x79, 0x65, 0x6e,
+ 0x6d, 0x6f, 0x6b, 0x78, 0x61, 0x70, 0x76, 0x74, 0x63, 0x78, 0x76, 0x63, 0x75, 0x67, 0x77, 0x62,
+ 0x63, 0x6a, 0x62, 0x79, 0x70, 0x61, 0x6e, 0x6c, 0x72, 0x6f, 0x6c, 0x6a, 0x63, 0x79, 0x75, 0x65,
+ 0x72, 0x72, 0x6e, 0x76, 0x64, 0x73, 0x77, 0x6b, 0x6d, 0x75, 0x67, 0x74, 0x75, 0x6f, 0x73, 0x67,
+ 0x6b, 0x61, 0x6a, 0x68, 0x69, 0x66, 0x6f, 0x64, 0x6f, 0x6e, 0x6a, 0x68, 0x61, 0x75, 0x79, 0x76,
+ 0x69, 0x70, 0x69, 0x6b, 0x6f, 0x71, 0x76, 0x6a, 0x77, 0x63, 0x68, 0x6f, 0x6e, 0x64, 0x71, 0x62,
+ 0x6d, 0x67, 0x76, 0x78, 0x70, 0x67, 0x6f, 0x67, 0x79, 0x78, 0x68, 0x73, 0x6a, 0x6f, 0x6b, 0x67,
+ 0x74, 0x6a, 0x77, 0x71, 0x77, 0x72, 0x70, 0x6c, 0x69, 0x78, 0x73, 0x72, 0x72, 0x71, 0x67, 0x6d,
+ 0x63, 0x6c, 0x6a, 0x72, 0x77, 0x61, 0x6b, 0x66, 0x63, 0x6c, 0x71, 0x78, 0x70, 0x6f, 0x70, 0x76,
+ 0x79, 0x61, 0x79, 0x6f, 0x6d, 0x61, 0x6b, 0x73, 0x6f, 0x6a, 0x64, 0x78, 0x6d, 0x6e, 0x67, 0x6e,
+ 0x74, 0x67, 0x74, 0x6a, 0x74, 0x6e, 0x79, 0x79, 0x6c, 0x63, 0x79, 0x6b, 0x6f, 0x6c, 0x69, 0x71,
+ 0x67, 0x75, 0x61, 0x62, 0x6b, 0x6d, 0x66, 0x71, 0x65, 0x79, 0x63, 0x6b, 0x79, 0x6e, 0x76, 0x6c,
+ 0x77, 0x75, 0x61, 0x66, 0x63, 0x65, 0x77, 0x75, 0x6a, 0x78, 0x75, 0x6f, 0x6a, 0x61, 0x61, 0x6d,
+ 0x69, 0x68, 0x77, 0x73, 0x79, 0x64, 0x77, 0x6f, 0x77, 0x76, 0x76, 0x6d, 0x69, 0x67, 0x78, 0x6f,
+ 0x76, 0x6a, 0x73, 0x65, 0x77, 0x6e, 0x70, 0x6a, 0x78, 0x75, 0x77, 0x79, 0x62, 0x6e, 0x76, 0x63,
+ 0x61, 0x76, 0x69, 0x6a, 0x68, 0x74, 0x78, 0x69, 0x78, 0x71, 0x6f, 0x6d, 0x76, 0x6f, 0x6d, 0x64,
+ 0x6d, 0x73, 0x6c, 0x65, 0x68, 0x6c, 0x71, 0x6d, 0x61, 0x66, 0x69, 0x78, 0x6e, 0x6b, 0x64, 0x79,
+ 0x62, 0x70, 0x6d, 0x66, 0x6d, 0x74, 0x6e, 0x70, 0x67, 0x6c, 0x6e, 0x68, 0x6c, 0x6a, 0x71, 0x79,
+ 0x66, 0x67, 0x68, 0x73, 0x6f, 0x72, 0x73, 0x6a, 0x77, 0x73, 0x65, 0x67, 0x62, 0x75, 0x69, 0x6d,
+ 0x71, 0x67, 0x73, 0x73, 0x72, 0x6b, 0x6e, 0x70, 0x6f, 0x68, 0x62, 0x72, 0x6c, 0x74, 0x66, 0x72,
+ 0x77, 0x72, 0x6d, 0x67, 0x75, 0x74, 0x77, 0x62, 0x72, 0x73, 0x68, 0x6e, 0x70, 0x65, 0x64, 0x6b,
+ 0x6f, 0x65, 0x76, 0x6c, 0x6f, 0x69, 0x72, 0x72, 0x77, 0x62, 0x63, 0x61, 0x6e, 0x6e, 0x72, 0x73,
+ 0x62, 0x6d, 0x6b, 0x69, 0x76, 0x6d, 0x71, 0x62, 0x61, 0x6b, 0x6a, 0x62, 0x6d, 0x79, 0x6d, 0x78,
+ 0x6a, 0x70, 0x68, 0x61, 0x65, 0x76, 0x66, 0x71, 0x64, 0x6c, 0x65, 0x77, 0x72, 0x66, 0x71, 0x6f,
+ 0x69, 0x71, 0x79, 0x70, 0x72, 0x6d, 0x6c, 0x69, 0x70, 0x69, 0x72, 0x64, 0x61, 0x79, 0x74, 0x6a,
+ 0x61, 0x79, 0x72, 0x6c, 0x74, 0x68, 0x66, 0x72, 0x72, 0x67, 0x63, 0x68, 0x6d, 0x77, 0x64, 0x71,
+ 0x71, 0x75, 0x69, 0x6d, 0x6e, 0x69, 0x72, 0x76, 0x6e, 0x6d, 0x72, 0x78, 0x75, 0x73, 0x66, 0x72,
+ 0x6b, 0x77, 0x6b, 0x77, 0x77, 0x72, 0x64, 0x77, 0x74, 0x76, 0x6e, 0x66, 0x62, 0x6e, 0x69, 0x77,
+ 0x62, 0x6f, 0x62, 0x73, 0x67, 0x79, 0x69, 0x63, 0x79, 0x63, 0x73, 0x64, 0x78, 0x76, 0x70, 0x6b,
+ 0x6f, 0x77, 0x72, 0x61, 0x6f, 0x78, 0x79, 0x67, 0x68, 0x76, 0x61, 0x6f, 0x63, 0x78, 0x6e, 0x6c,
+ 0x69, 0x73, 0x6c, 0x79, 0x74, 0x79, 0x67, 0x79, 0x79, 0x77, 0x77, 0x78, 0x65, 0x75, 0x6b, 0x65,
+ 0x65, 0x69, 0x77, 0x6c, 0x66, 0x65, 0x75, 0x68, 0x78, 0x66, 0x62, 0x6c, 0x75, 0x67, 0x67, 0x70,
+ 0x69, 0x6c, 0x79, 0x71, 0x77, 0x69, 0x6f, 0x79, 0x6d, 0x69, 0x6d, 0x79, 0x71, 0x66, 0x63, 0x75,
+ 0x69, 0x69, 0x76, 0x72, 0x75, 0x69, 0x6b, 0x64, 0x62, 0x70, 0x64, 0x78, 0x79, 0x69, 0x64, 0x72,
+ 0x68, 0x73, 0x6d, 0x68, 0x6c, 0x72, 0x66, 0x6a, 0x6a, 0x6f, 0x66, 0x67, 0x6d, 0x6f, 0x66, 0x74,
+ 0x62, 0x74, 0x70, 0x61, 0x71, 0x70, 0x70, 0x68, 0x77, 0x73, 0x78, 0x65, 0x61, 0x6f, 0x6e, 0x6c,
+ 0x61, 0x71, 0x6f, 0x66, 0x68, 0x76, 0x76, 0x66, 0x79, 0x70, 0x6c, 0x6d, 0x64, 0x6d, 0x76, 0x75,
+ 0x6d, 0x6e, 0x73, 0x6d, 0x67, 0x62, 0x78, 0x75, 0x6c, 0x6a, 0x6b, 0x74, 0x78, 0x63, 0x67, 0x65,
+ 0x62, 0x63, 0x66, 0x67, 0x6d, 0x77, 0x79, 0x66, 0x77, 0x72, 0x79, 0x6a, 0x64, 0x76, 0x78, 0x78,
+ 0x6b, 0x66, 0x6f, 0x6c, 0x6e, 0x6d, 0x6b, 0x73, 0x71, 0x6d, 0x68, 0x68, 0x74, 0x6a, 0x75, 0x6b,
+ 0x63, 0x77, 0x66, 0x6b, 0x6e, 0x69, 0x66, 0x75, 0x6e, 0x75, 0x6c, 0x67, 0x66, 0x6e, 0x6f, 0x73,
+ 0x6e, 0x73, 0x73, 0x77, 0x75, 0x71, 0x72, 0x76, 0x68, 0x64, 0x6a, 0x6c, 0x61, 0x66, 0x6b, 0x6d,
+ 0x6e, 0x63, 0x79, 0x69, 0x73, 0x6f, 0x64, 0x72, 0x61, 0x65, 0x6a, 0x67, 0x79, 0x6c, 0x6d, 0x61,
+ 0x62, 0x6e, 0x67, 0x6d, 0x77, 0x6a, 0x77, 0x72, 0x68, 0x68, 0x6f, 0x76, 0x77, 0x73, 0x76, 0x6f,
+ 0x6c, 0x66, 0x69, 0x75, 0x63, 0x74, 0x70, 0x74, 0x75, 0x6e, 0x66, 0x6d, 0x66, 0x69, 0x6b, 0x70,
+ 0x70, 0x74, 0x66, 0x6a, 0x6c, 0x6e, 0x65, 0x6b, 0x67, 0x71, 0x79, 0x76, 0x65, 0x64, 0x6a, 0x70,
+ 0x63, 0x71, 0x6f, 0x68, 0x76, 0x77, 0x65, 0x77, 0x78, 0x78, 0x65, 0x68, 0x69, 0x63, 0x66, 0x74,
+ 0x70, 0x65, 0x6e, 0x6a, 0x68, 0x6c, 0x70, 0x67, 0x6f, 0x62, 0x66, 0x79, 0x75, 0x71, 0x63, 0x6b,
+ 0x64, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x63, 0x6b, 0x78, 0x66, 0x63, 0x6f, 0x6f, 0x73, 0x67, 0x73,
+ 0x72, 0x67, 0x61, 0x66, 0x77, 0x6f, 0x78, 0x63, 0x6f, 0x64, 0x6d, 0x68, 0x70, 0x74, 0x70, 0x70,
+ 0x6c, 0x61, 0x63, 0x63, 0x78, 0x72, 0x68, 0x69, 0x76, 0x79, 0x74, 0x6c, 0x6c, 0x64, 0x72, 0x62,
+ 0x65, 0x73, 0x6e, 0x63, 0x70, 0x74, 0x6a, 0x74, 0x68, 0x6d, 0x62, 0x63, 0x74, 0x6b, 0x62, 0x6f,
+ 0x61, 0x72, 0x68, 0x62, 0x6c, 0x6e, 0x72, 0x71, 0x79, 0x76, 0x74, 0x67, 0x74, 0x70, 0x74, 0x67,
+ 0x77, 0x6e, 0x79, 0x70, 0x6f, 0x66, 0x72, 0x6b, 0x75, 0x6f, 0x64, 0x63, 0x65, 0x6f, 0x67, 0x66,
+ 0x68, 0x65, 0x75, 0x77, 0x64, 0x76, 0x61, 0x66, 0x69, 0x6a, 0x67, 0x62, 0x72, 0x71, 0x64, 0x79,
+ 0x72, 0x61, 0x73, 0x72, 0x75, 0x61, 0x65, 0x63, 0x77, 0x77, 0x77, 0x70, 0x65, 0x65, 0x62, 0x76,
+ 0x65, 0x68, 0x70, 0x77, 0x6c, 0x63, 0x63, 0x62, 0x6a, 0x68, 0x79, 0x64, 0x63, 0x73, 0x6f, 0x66,
+ 0x78, 0x66, 0x78, 0x63, 0x6f, 0x67, 0x74, 0x75, 0x69, 0x73, 0x77, 0x69, 0x6b, 0x78, 0x65, 0x70,
+ 0x6c, 0x66, 0x70, 0x77, 0x62, 0x69, 0x66, 0x74, 0x76, 0x75, 0x6e, 0x64, 0x71, 0x6f, 0x79, 0x62,
+ 0x67, 0x78, 0x77, 0x79, 0x74, 0x62, 0x73, 0x61, 0x6a, 0x67, 0x76, 0x6a, 0x6f, 0x62, 0x65, 0x75,
+ 0x75, 0x74, 0x68, 0x6a, 0x68, 0x6b, 0x65, 0x79, 0x73, 0x75, 0x6c, 0x69, 0x66, 0x68, 0x6c, 0x79,
+ 0x6f, 0x78, 0x75, 0x72, 0x72, 0x61, 0x6c, 0x68, 0x76, 0x64, 0x79, 0x6a, 0x6f, 0x69, 0x62, 0x75,
+ 0x6b, 0x73, 0x71, 0x74, 0x74, 0x79, 0x6d, 0x6d, 0x61, 0x6d, 0x72, 0x78, 0x72, 0x6e, 0x65, 0x61,
+ 0x75, 0x65, 0x65, 0x77, 0x62, 0x62, 0x69, 0x6c, 0x6d, 0x6a, 0x76, 0x78, 0x64, 0x76, 0x61, 0x63,
+ 0x69, 0x71, 0x6b, 0x6d, 0x62, 0x78, 0x6a, 0x6d, 0x6a, 0x78, 0x67, 0x68, 0x79, 0x79, 0x66, 0x71,
+ 0x6b, 0x73, 0x72, 0x76, 0x78, 0x74, 0x63, 0x72, 0x74, 0x76, 0x70, 0x79, 0x68, 0x79, 0x69, 0x62,
+ 0x61, 0x74, 0x6b, 0x79, 0x79, 0x6c, 0x78, 0x62, 0x65, 0x78, 0x71, 0x64, 0x75, 0x73, 0x68, 0x62,
+ 0x68, 0x74, 0x71, 0x70, 0x77, 0x61, 0x6a, 0x65, 0x79, 0x6d, 0x61, 0x68, 0x61, 0x75, 0x6e, 0x6e,
+ 0x6d, 0x73, 0x73, 0x65, 0x77, 0x66, 0x70, 0x78, 0x69, 0x65, 0x64, 0x71, 0x6f, 0x65, 0x62, 0x6e,
+ 0x72, 0x69, 0x61, 0x6e, 0x75, 0x76, 0x79, 0x68, 0x69, 0x64, 0x6e, 0x69, 0x63, 0x6d, 0x68, 0x65,
+ 0x62, 0x63, 0x68, 0x67, 0x79, 0x67, 0x67, 0x66, 0x63, 0x71, 0x69, 0x79, 0x69, 0x6a, 0x77, 0x76,
+ 0x64, 0x6c, 0x73, 0x71, 0x6b, 0x6b, 0x77, 0x63, 0x79, 0x78, 0x62, 0x76, 0x75, 0x73, 0x68, 0x65,
+ 0x64, 0x6b, 0x70, 0x78, 0x75, 0x71, 0x6e, 0x64, 0x70, 0x64, 0x6b, 0x6f, 0x77, 0x63, 0x77, 0x67,
+ 0x74, 0x66, 0x78, 0x73, 0x6c, 0x77, 0x62, 0x68, 0x76, 0x66, 0x70, 0x6e, 0x71, 0x66, 0x73, 0x6f,
+ 0x70, 0x65, 0x77, 0x63, 0x61, 0x71, 0x64, 0x6c, 0x72, 0x6a, 0x71, 0x68, 0x73, 0x66, 0x67, 0x62,
+ 0x70, 0x77, 0x69, 0x67, 0x6e, 0x78, 0x63, 0x75, 0x6a, 0x6b, 0x70, 0x69, 0x6c, 0x76, 0x64, 0x6f,
+ 0x76, 0x63, 0x75, 0x62, 0x77, 0x67, 0x6e, 0x67, 0x67, 0x6d, 0x63, 0x66, 0x6d, 0x67, 0x65, 0x6b,
+ 0x66, 0x73, 0x66, 0x75, 0x66, 0x73, 0x73, 0x6b, 0x6b, 0x78, 0x75, 0x65, 0x75, 0x76, 0x70, 0x76,
+ 0x61, 0x6e, 0x78, 0x66, 0x6c, 0x71, 0x66, 0x62, 0x71, 0x75, 0x79, 0x70, 0x74, 0x79, 0x65, 0x70,
+ 0x66, 0x79, 0x6f, 0x75, 0x70, 0x63, 0x76, 0x75, 0x62, 0x6c, 0x78, 0x68, 0x6f, 0x77, 0x75, 0x6e,
+ 0x79, 0x6d, 0x70, 0x64, 0x71, 0x63, 0x69, 0x75, 0x76, 0x68, 0x71, 0x75, 0x67, 0x79, 0x69, 0x69,
+ 0x77, 0x6d, 0x69, 0x6a, 0x68, 0x6a, 0x6d, 0x6a, 0x70, 0x6c, 0x73, 0x6d, 0x6c, 0x6c, 0x6f, 0x6c,
+ 0x69, 0x71, 0x73, 0x78, 0x6d, 0x61, 0x61, 0x73, 0x6f, 0x66, 0x66, 0x63, 0x71, 0x6b, 0x6d, 0x6f,
+ 0x75, 0x67, 0x71, 0x77, 0x67, 0x73, 0x76, 0x76, 0x6a, 0x76, 0x6c, 0x61, 0x67, 0x66, 0x6a, 0x69,
+ 0x73, 0x71, 0x69, 0x72, 0x72, 0x78, 0x63, 0x6b, 0x76, 0x6f, 0x6b, 0x78, 0x6b, 0x72, 0x6f, 0x6a,
+ 0x64, 0x64, 0x73, 0x74, 0x75, 0x64, 0x6f, 0x75, 0x77, 0x62, 0x64, 0x6a, 0x67, 0x6a, 0x73, 0x69,
+ 0x6a, 0x79, 0x63, 0x77, 0x75, 0x72, 0x75, 0x62, 0x68, 0x73, 0x6c, 0x69, 0x74, 0x64, 0x6d, 0x71,
+ 0x76, 0x76, 0x6e, 0x67, 0x67, 0x65, 0x71, 0x70, 0x6a, 0x79, 0x74, 0x75, 0x76, 0x74, 0x67, 0x79,
+ 0x62, 0x63, 0x68, 0x6c, 0x6c, 0x70, 0x67, 0x65, 0x64, 0x76, 0x72, 0x65, 0x76, 0x6b, 0x69, 0x71,
+ 0x73, 0x79, 0x78, 0x64, 0x70, 0x79, 0x61, 0x76, 0x61, 0x66, 0x74, 0x79, 0x6a, 0x6a, 0x6a, 0x78,
+ 0x6b, 0x6e, 0x74, 0x6f, 0x66, 0x67, 0x62, 0x76, 0x71, 0x65, 0x62, 0x64, 0x73, 0x6f, 0x63, 0x66,
+ 0x66, 0x6c, 0x63, 0x77, 0x6e, 0x72, 0x66, 0x72, 0x74, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x6b, 0x79,
+ 0x68, 0x67, 0x63, 0x62, 0x79, 0x6f, 0x73, 0x65, 0x62, 0x76, 0x67, 0x65, 0x77, 0x64, 0x6c, 0x61,
+ 0x62, 0x61, 0x64, 0x68, 0x79, 0x72, 0x70, 0x61, 0x77, 0x6d, 0x6a, 0x69, 0x70, 0x6f, 0x77, 0x77,
+ 0x77, 0x6f, 0x6d, 0x78, 0x76, 0x69, 0x69, 0x78, 0x74, 0x68, 0x68, 0x74, 0x6c, 0x6e, 0x66, 0x67,
+ 0x67, 0x64, 0x62, 0x6d, 0x63, 0x71, 0x75, 0x65, 0x75, 0x71, 0x6c, 0x75, 0x62, 0x66, 0x66, 0x61,
+ 0x69, 0x67, 0x79, 0x70, 0x6e, 0x6b, 0x6f, 0x71, 0x62, 0x77, 0x6c, 0x75, 0x67, 0x77, 0x78, 0x79,
+ 0x6e, 0x77, 0x6a, 0x71, 0x71, 0x77, 0x68, 0x72, 0x73, 0x77, 0x69, 0x75, 0x61, 0x62, 0x65, 0x71,
+ 0x6a, 0x78, 0x6e, 0x6c, 0x6c, 0x70, 0x76, 0x74, 0x69, 0x76, 0x61, 0x78, 0x66, 0x76, 0x6e, 0x62,
+ 0x64, 0x71, 0x71, 0x6c, 0x79, 0x65, 0x65, 0x6b, 0x71, 0x6a, 0x73, 0x6e, 0x6b, 0x76, 0x70, 0x79,
+ 0x77, 0x63, 0x63, 0x67, 0x75, 0x6e, 0x66, 0x74, 0x63, 0x76, 0x75, 0x6d, 0x66, 0x6c, 0x63, 0x6b,
+ 0x79, 0x6d, 0x72, 0x69, 0x67, 0x75, 0x65, 0x78, 0x70, 0x6c, 0x71, 0x69, 0x69, 0x76, 0x70, 0x75,
+ 0x6b, 0x6a, 0x64, 0x66, 0x71, 0x71, 0x6b, 0x6a, 0x66, 0x64, 0x79, 0x69, 0x6f, 0x74, 0x62, 0x6c,
+ 0x78, 0x61, 0x77, 0x65, 0x6e, 0x6f, 0x62, 0x70, 0x76, 0x6e, 0x73, 0x73, 0x74, 0x6d, 0x6d, 0x6a,
+ 0x68, 0x66, 0x6f, 0x78, 0x70, 0x76, 0x66, 0x6f, 0x6a, 0x76, 0x6a, 0x6b, 0x6c, 0x6f, 0x64, 0x77,
+ 0x78, 0x61, 0x68, 0x63, 0x72, 0x61, 0x69, 0x78, 0x69, 0x74, 0x78, 0x6c, 0x79, 0x61, 0x61, 0x67,
+ 0x72, 0x6a, 0x6f, 0x76, 0x6d, 0x6d, 0x6c, 0x78, 0x63, 0x63, 0x65, 0x69, 0x67, 0x78, 0x73, 0x76,
+ 0x78, 0x69, 0x6d, 0x70, 0x73, 0x76, 0x78, 0x71, 0x75, 0x65, 0x77, 0x69, 0x73, 0x77, 0x6f, 0x66,
+ 0x65, 0x6b, 0x61, 0x6a, 0x78, 0x70, 0x70, 0x71, 0x71, 0x79, 0x67, 0x79, 0x79, 0x6e, 0x64, 0x6c,
+ 0x6c, 0x6a, 0x6d, 0x6a, 0x79, 0x73, 0x63, 0x72, 0x6a, 0x72, 0x77, 0x6d, 0x74, 0x70, 0x77, 0x6e,
+ 0x77, 0x63, 0x6c, 0x71, 0x74, 0x75, 0x61, 0x79, 0x69, 0x71, 0x6a, 0x64, 0x62, 0x64, 0x68, 0x74,
+ 0x75, 0x75, 0x64, 0x6d, 0x6f, 0x66, 0x64, 0x69, 0x74, 0x73, 0x6c, 0x66, 0x77, 0x64, 0x73, 0x6e,
+ 0x63, 0x6e, 0x73, 0x6d, 0x67, 0x73, 0x76, 0x6b, 0x73, 0x6b, 0x77, 0x6c, 0x6f, 0x70, 0x69, 0x6f,
+ 0x6f, 0x62, 0x63, 0x6c, 0x70, 0x70, 0x6e, 0x61, 0x78, 0x6d, 0x63, 0x74, 0x67, 0x66, 0x79, 0x69,
+ 0x6c, 0x68, 0x71, 0x68, 0x68, 0x75, 0x79, 0x61, 0x62, 0x6b, 0x6b, 0x70, 0x6a, 0x69, 0x75, 0x6a,
+ 0x78, 0x65, 0x74, 0x62, 0x6d, 0x72, 0x6c, 0x74, 0x79, 0x74, 0x66, 0x63, 0x6d, 0x71, 0x68, 0x70,
+ 0x6c, 0x74, 0x79, 0x6e, 0x69, 0x75, 0x66, 0x6d, 0x69, 0x6f, 0x74, 0x68, 0x6c, 0x6b, 0x65, 0x72,
+ 0x69, 0x70, 0x68, 0x72, 0x65, 0x66, 0x6e, 0x72, 0x71, 0x66, 0x6f, 0x6e, 0x68, 0x62, 0x70, 0x6d,
+ 0x6e, 0x75, 0x77, 0x68, 0x74, 0x77, 0x74, 0x61, 0x76, 0x74, 0x79, 0x63, 0x6f, 0x63, 0x6c, 0x64,
+ 0x78, 0x69, 0x6f, 0x6d, 0x66, 0x78, 0x6c, 0x6c, 0x6f, 0x6a, 0x77, 0x6d, 0x74, 0x67, 0x63, 0x64,
+ 0x79, 0x64, 0x6d, 0x61, 0x66, 0x64, 0x79, 0x6a, 0x61, 0x68, 0x63, 0x6d, 0x61, 0x69, 0x70, 0x6e,
+ 0x61, 0x77, 0x6a, 0x64, 0x63, 0x64, 0x70, 0x6d, 0x75, 0x62, 0x6d, 0x74, 0x64, 0x6d, 0x74, 0x67,
+ 0x6d, 0x72, 0x77, 0x79, 0x6f, 0x76, 0x6f, 0x75, 0x62, 0x6b, 0x72, 0x77, 0x66, 0x6b, 0x6c, 0x73,
+ 0x6a, 0x76, 0x69, 0x68, 0x6f, 0x77, 0x62, 0x74, 0x71, 0x66, 0x74, 0x64, 0x63, 0x79, 0x6d, 0x72,
+ 0x76, 0x65, 0x69, 0x6f, 0x74, 0x73, 0x73, 0x6b, 0x68, 0x66, 0x64, 0x72, 0x70, 0x62, 0x73, 0x76,
+ 0x64, 0x6d, 0x70, 0x6c, 0x6d, 0x63, 0x73, 0x73, 0x72, 0x76, 0x69, 0x76, 0x62, 0x64, 0x72, 0x75,
+ 0x76, 0x6a, 0x62, 0x78, 0x69, 0x61, 0x73, 0x68, 0x6a, 0x67, 0x62, 0x6a, 0x64, 0x67, 0x77, 0x76,
+ 0x6c, 0x69, 0x77, 0x74, 0x72, 0x6f, 0x77, 0x66, 0x67, 0x62, 0x6e, 0x6e, 0x66, 0x70, 0x72, 0x64,
+ 0x71, 0x79, 0x66, 0x6f, 0x72, 0x65, 0x75, 0x64, 0x6b, 0x6f, 0x77, 0x73, 0x79, 0x75, 0x78, 0x6a,
+ 0x64, 0x6d, 0x79, 0x77, 0x6b, 0x62, 0x77, 0x62, 0x66, 0x67, 0x73, 0x68, 0x72, 0x62, 0x62, 0x67,
+ 0x6d, 0x6d, 0x6c, 0x78, 0x69, 0x6c, 0x75, 0x77, 0x6d, 0x69, 0x6c, 0x6b, 0x71, 0x64, 0x67, 0x65,
+ 0x73, 0x63, 0x6d, 0x6d, 0x63, 0x65, 0x6a, 0x71, 0x79, 0x6d, 0x63, 0x6e, 0x77, 0x64, 0x63, 0x71,
+ 0x67, 0x77, 0x68, 0x6d, 0x63, 0x61, 0x67, 0x6e, 0x63, 0x71, 0x6a, 0x72, 0x75, 0x61, 0x74, 0x69,
+ 0x6c, 0x76, 0x62, 0x65, 0x78, 0x74, 0x63, 0x67, 0x75, 0x6e, 0x71, 0x69, 0x68, 0x77, 0x69, 0x6b,
+ 0x69, 0x67, 0x70, 0x6c, 0x77, 0x6d, 0x6d, 0x6c, 0x77, 0x77, 0x6d, 0x66, 0x61, 0x6d, 0x66, 0x76,
+ 0x69, 0x62, 0x66, 0x6b, 0x64, 0x68, 0x75, 0x78, 0x67, 0x64, 0x61, 0x6b, 0x78, 0x6c, 0x78, 0x70,
+ 0x77, 0x61, 0x68, 0x65, 0x6d, 0x66, 0x6f, 0x77, 0x78, 0x6f, 0x61, 0x6e, 0x6e, 0x77, 0x67, 0x6b,
+ 0x62, 0x70, 0x6b, 0x69, 0x61, 0x67, 0x6a, 0x76, 0x68, 0x74, 0x61, 0x6d, 0x70, 0x69, 0x70, 0x6a,
+ 0x68, 0x71, 0x69, 0x63, 0x74, 0x6a, 0x6a, 0x73, 0x69, 0x64, 0x64, 0x75, 0x79, 0x6a, 0x65, 0x6d,
+ 0x71, 0x62, 0x68, 0x77, 0x74, 0x65, 0x68, 0x6b, 0x78, 0x69, 0x6e, 0x73, 0x65, 0x66, 0x6e, 0x64,
+ 0x6b, 0x79, 0x66, 0x61, 0x78, 0x63, 0x6a, 0x6b, 0x68, 0x76, 0x74, 0x6f, 0x72, 0x6d, 0x73, 0x6c,
+ 0x67, 0x6a, 0x72, 0x78, 0x75, 0x6f, 0x6d, 0x6e, 0x61, 0x6d, 0x66, 0x6b, 0x65, 0x72, 0x70, 0x62,
+ 0x67, 0x78, 0x64, 0x74, 0x77, 0x6c, 0x67, 0x78, 0x71, 0x75, 0x71, 0x62, 0x61, 0x6d, 0x79, 0x70,
+ 0x74, 0x71, 0x6e, 0x6a, 0x70, 0x61, 0x74, 0x6a, 0x6f, 0x6a, 0x78, 0x6d, 0x72, 0x61, 0x67, 0x76,
+ 0x71, 0x71, 0x75, 0x61, 0x62, 0x6d, 0x62, 0x67, 0x6a, 0x61, 0x73, 0x73, 0x70, 0x64, 0x69, 0x74,
+ 0x6e, 0x62, 0x72, 0x72, 0x6d, 0x78, 0x72, 0x76, 0x6a, 0x6b, 0x70, 0x6d, 0x72, 0x77, 0x61, 0x63,
+ 0x79, 0x64, 0x6b, 0x6e, 0x72, 0x6b, 0x61, 0x6e, 0x77, 0x70, 0x6f, 0x6e, 0x68, 0x6c, 0x71, 0x62,
+ 0x74, 0x64, 0x63, 0x67, 0x65, 0x67, 0x67, 0x6e, 0x6d, 0x66, 0x62, 0x72, 0x71, 0x74, 0x76, 0x79,
+ 0x70, 0x72, 0x6f, 0x6f, 0x63, 0x79, 0x72, 0x63, 0x73, 0x64, 0x68, 0x63, 0x73, 0x6c, 0x6f, 0x6c,
+ 0x66, 0x71, 0x6a, 0x75, 0x69, 0x6e, 0x67, 0x72, 0x74, 0x6a, 0x61, 0x73, 0x68, 0x69, 0x6d, 0x71,
+ 0x76, 0x73, 0x65, 0x6f, 0x63, 0x62, 0x71, 0x6d, 0x66, 0x71, 0x69, 0x61, 0x76, 0x6d, 0x76, 0x6f,
+ 0x64, 0x6c, 0x6c, 0x74, 0x6c, 0x74, 0x76, 0x6b, 0x63, 0x62, 0x79, 0x70, 0x6e, 0x64, 0x64, 0x6e,
+ 0x71, 0x73, 0x71, 0x6d, 0x71, 0x67, 0x6a, 0x69, 0x66, 0x76, 0x6e, 0x73, 0x6f, 0x77, 0x67, 0x6f,
+ 0x65, 0x71, 0x73, 0x70, 0x61, 0x6b, 0x67, 0x69, 0x6b, 0x75, 0x68, 0x6d, 0x6a, 0x6c, 0x61, 0x70,
+ 0x71, 0x74, 0x66, 0x63, 0x61, 0x67, 0x77, 0x77, 0x68, 0x61, 0x61, 0x6a, 0x74, 0x72, 0x6d, 0x75,
+ 0x75, 0x64, 0x69, 0x70, 0x64, 0x62, 0x6b, 0x75, 0x6c, 0x73, 0x68, 0x65, 0x6b, 0x74, 0x74, 0x6e,
+ 0x6c, 0x61, 0x6c, 0x74, 0x71, 0x6c, 0x79, 0x69, 0x65, 0x76, 0x74, 0x77, 0x76, 0x69, 0x66, 0x78,
+ 0x68, 0x79, 0x64, 0x79, 0x61, 0x76, 0x68, 0x70, 0x78, 0x73, 0x68, 0x6d, 0x6a, 0x67, 0x65, 0x76,
+ 0x73, 0x66, 0x6b, 0x6b, 0x70, 0x68, 0x64, 0x79, 0x76, 0x71, 0x77, 0x63, 0x65, 0x6a, 0x6e, 0x6f,
+ 0x6f, 0x75, 0x6e, 0x65, 0x6a, 0x64, 0x6c, 0x69, 0x6e, 0x70, 0x6f, 0x6d, 0x66, 0x76, 0x76, 0x66,
+ 0x62, 0x6c, 0x78, 0x71, 0x71, 0x73, 0x63, 0x6e, 0x72, 0x6d, 0x6a, 0x74, 0x67, 0x77, 0x78, 0x74,
+ 0x65, 0x66, 0x6d, 0x72, 0x73, 0x6f, 0x64, 0x6e, 0x66, 0x71, 0x61, 0x6c, 0x72, 0x77, 0x67, 0x71,
+ 0x6d, 0x74, 0x69, 0x72, 0x61, 0x6b, 0x78, 0x67, 0x78, 0x73, 0x70, 0x6b, 0x63, 0x76, 0x6f, 0x65,
+ 0x6b, 0x70, 0x74, 0x71, 0x75, 0x67, 0x78, 0x6c, 0x6b, 0x79, 0x76, 0x71, 0x63, 0x6b, 0x6a, 0x6e,
+ 0x61, 0x66, 0x6f, 0x69, 0x6f, 0x64, 0x6d, 0x6e, 0x61, 0x76, 0x69, 0x6f, 0x78, 0x68, 0x61, 0x74,
+ 0x6d, 0x68, 0x72, 0x64, 0x63, 0x79, 0x78, 0x76, 0x6c, 0x6e, 0x6f, 0x6a, 0x72, 0x6b, 0x6b, 0x70,
+ 0x65, 0x65, 0x6c, 0x63, 0x66, 0x73, 0x66, 0x68, 0x6e, 0x74, 0x6d, 0x64, 0x62, 0x6f, 0x6f, 0x67,
+ 0x79, 0x6d, 0x69, 0x61, 0x78, 0x6b, 0x6b, 0x61, 0x6e, 0x76, 0x6b, 0x70, 0x6e, 0x6d, 0x68, 0x67,
+ 0x72, 0x72, 0x69, 0x74, 0x78, 0x6a, 0x78, 0x75, 0x68, 0x71, 0x70, 0x6b, 0x6b, 0x64, 0x71, 0x63,
+ 0x65, 0x6c, 0x6c, 0x69, 0x72, 0x76, 0x70, 0x6f, 0x70, 0x6e, 0x73, 0x74, 0x6a, 0x61, 0x75, 0x63,
+ 0x61, 0x78, 0x6f, 0x79, 0x67, 0x65, 0x61, 0x69, 0x6d, 0x68, 0x6c, 0x62, 0x75, 0x79, 0x68, 0x79,
+ 0x65, 0x6a, 0x67, 0x6f, 0x68, 0x78, 0x70, 0x69, 0x62, 0x6b, 0x6b, 0x64, 0x77, 0x78, 0x6b, 0x76,
+ 0x69, 0x64, 0x75, 0x70, 0x72, 0x6e, 0x67, 0x73, 0x65, 0x73, 0x71, 0x73, 0x70, 0x78, 0x66, 0x6b,
+ 0x6e, 0x6b, 0x6a, 0x6f, 0x73, 0x6b, 0x70, 0x71, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x70, 0x6f, 0x6b,
+ 0x69, 0x75, 0x61, 0x70, 0x72, 0x65, 0x6a, 0x75, 0x61, 0x67, 0x75, 0x76, 0x65, 0x6d, 0x6c, 0x73,
+ 0x71, 0x6b, 0x73, 0x65, 0x79, 0x68, 0x70, 0x69, 0x6f, 0x69, 0x6c, 0x6b, 0x72, 0x64, 0x6c, 0x79,
+ 0x6b, 0x6a, 0x61, 0x6e, 0x6b, 0x6b, 0x74, 0x6f, 0x79, 0x64, 0x68, 0x68, 0x6c, 0x6f, 0x62, 0x70,
+ 0x6e, 0x79, 0x62, 0x62, 0x6b, 0x66, 0x6e, 0x6f, 0x70, 0x64, 0x66, 0x75, 0x73, 0x79, 0x62, 0x75,
+ 0x6d, 0x67, 0x73, 0x73, 0x6b, 0x71, 0x6e, 0x6d, 0x75, 0x62, 0x72, 0x6a, 0x6b, 0x63, 0x6d, 0x6c,
+ 0x6f, 0x6c, 0x74, 0x65, 0x71, 0x65, 0x6b, 0x77, 0x61, 0x66, 0x78, 0x6c, 0x78, 0x6d, 0x6e, 0x6d,
+ 0x6a, 0x71, 0x62, 0x64, 0x68, 0x67, 0x61, 0x6a, 0x68, 0x72, 0x72, 0x62, 0x6e, 0x6b, 0x71, 0x6d,
+ 0x66, 0x75, 0x6a, 0x78, 0x64, 0x66, 0x75, 0x74, 0x6f, 0x6a, 0x74, 0x73, 0x75, 0x62, 0x71, 0x6b,
+ 0x6d, 0x66, 0x65, 0x62, 0x64, 0x68, 0x79, 0x74, 0x6f, 0x75, 0x76, 0x66, 0x79, 0x70, 0x77, 0x65,
+ 0x78, 0x72, 0x65, 0x6e, 0x6a, 0x62, 0x61, 0x74, 0x65, 0x73, 0x68, 0x6e, 0x72, 0x75, 0x62, 0x64,
+ 0x69, 0x73, 0x65, 0x6b, 0x61, 0x6f, 0x6e, 0x71, 0x61, 0x6e, 0x64, 0x6e, 0x76, 0x73, 0x65, 0x63,
+ 0x6c, 0x6b, 0x6d, 0x77, 0x66, 0x6b, 0x72, 0x72, 0x78, 0x72, 0x6b, 0x6c, 0x69, 0x69, 0x79, 0x76,
+ 0x6c, 0x78, 0x75, 0x73, 0x65, 0x6d, 0x6e, 0x74, 0x68, 0x65, 0x76, 0x70, 0x64, 0x6b, 0x77, 0x6c,
+ 0x77, 0x6d, 0x66, 0x79, 0x64, 0x64, 0x6f, 0x74, 0x62, 0x6d, 0x6b, 0x61, 0x78, 0x6a, 0x73, 0x63,
+ 0x6e, 0x6a, 0x62, 0x73, 0x72, 0x6e, 0x6c, 0x62, 0x75, 0x79, 0x62, 0x66, 0x69, 0x72, 0x6c, 0x67,
+ 0x78, 0x61, 0x76, 0x62, 0x6e, 0x66, 0x63, 0x69, 0x68, 0x73, 0x78, 0x78, 0x74, 0x61, 0x78, 0x71,
+ 0x6e, 0x70, 0x75, 0x77, 0x63, 0x64, 0x61, 0x6f, 0x75, 0x68, 0x76, 0x71, 0x64, 0x72, 0x74, 0x75,
+ 0x6c, 0x73, 0x72, 0x75, 0x63, 0x61, 0x74, 0x61, 0x77, 0x74, 0x64, 0x65, 0x74, 0x68, 0x74, 0x78,
+ 0x63, 0x65, 0x79, 0x75, 0x6e, 0x6d, 0x78, 0x77, 0x67, 0x65, 0x6e, 0x74, 0x66, 0x76, 0x6c, 0x78,
+ 0x66, 0x76, 0x62, 0x76, 0x68, 0x65, 0x6f, 0x73, 0x78, 0x76, 0x69, 0x6c, 0x63, 0x76, 0x70, 0x79,
+ 0x6d, 0x75, 0x6e, 0x69, 0x61, 0x66, 0x65, 0x78, 0x6d, 0x68, 0x62, 0x73, 0x66, 0x73, 0x6b, 0x6c,
+ 0x77, 0x64, 0x6c, 0x72, 0x78, 0x75, 0x6c, 0x63, 0x6a, 0x64, 0x68, 0x79, 0x6f, 0x74, 0x65, 0x6d,
+ 0x65, 0x70, 0x6d, 0x70, 0x68, 0x65, 0x65, 0x65, 0x6c, 0x74, 0x6b, 0x69, 0x74, 0x6a, 0x71, 0x64,
+ 0x72, 0x74, 0x6e, 0x74, 0x75, 0x6a, 0x72, 0x73, 0x6b, 0x69, 0x6c, 0x71, 0x78, 0x77, 0x75, 0x6c,
+ 0x69, 0x62, 0x6e, 0x6a, 0x68, 0x73, 0x77, 0x77, 0x63, 0x66, 0x6a, 0x78, 0x6d, 0x77, 0x6e, 0x62,
+ 0x6c, 0x76, 0x66, 0x66, 0x75, 0x65, 0x6e, 0x69, 0x79, 0x75, 0x72, 0x6d, 0x70, 0x71, 0x65, 0x67,
+ 0x74, 0x75, 0x6a, 0x6d, 0x66, 0x64, 0x72, 0x66, 0x77, 0x70, 0x76, 0x75, 0x73, 0x75, 0x6c, 0x73,
+ 0x75, 0x6b, 0x71, 0x6f, 0x79, 0x6a, 0x62, 0x78, 0x69, 0x62, 0x72, 0x61, 0x6a, 0x69, 0x67, 0x71,
+ 0x66, 0x71, 0x6a, 0x63, 0x62, 0x79, 0x78, 0x69, 0x61, 0x63, 0x6a, 0x62, 0x6a, 0x63, 0x68, 0x79,
+ 0x68, 0x69, 0x63, 0x73, 0x65, 0x67, 0x70, 0x74, 0x72, 0x6f, 0x79, 0x75, 0x65, 0x73, 0x6a, 0x76,
+ 0x6f, 0x64, 0x66, 0x72, 0x6d, 0x71, 0x66, 0x6d, 0x62, 0x6a, 0x68, 0x74, 0x67, 0x68, 0x6e, 0x6b,
+ 0x67, 0x6c, 0x6e, 0x72, 0x79, 0x6e, 0x6b, 0x6a, 0x66, 0x6a, 0x65, 0x69, 0x75, 0x6a, 0x6e, 0x6a,
+ 0x61, 0x62, 0x79, 0x6c, 0x65, 0x74, 0x63, 0x76, 0x63, 0x6a, 0x68, 0x76, 0x6d, 0x69, 0x66, 0x68,
+ 0x67, 0x71, 0x66, 0x68, 0x66, 0x75, 0x76, 0x6e, 0x74, 0x70, 0x63, 0x67, 0x73, 0x68, 0x67, 0x61,
+ 0x74, 0x66, 0x65, 0x6e, 0x6b, 0x6c, 0x64, 0x6b, 0x70, 0x70, 0x75, 0x78, 0x76, 0x68, 0x74, 0x69,
+ 0x76, 0x77, 0x6e, 0x6c, 0x77, 0x76, 0x66, 0x6a, 0x61, 0x6a, 0x63, 0x6a, 0x6e, 0x73, 0x67, 0x73,
+ 0x6b, 0x6e, 0x75, 0x6a, 0x6a, 0x70, 0x73, 0x66, 0x78, 0x79, 0x61, 0x79, 0x6a, 0x6e, 0x76, 0x74,
+ 0x65, 0x74, 0x79, 0x6d, 0x6e, 0x79, 0x75, 0x76, 0x72, 0x6f, 0x6a, 0x67, 0x61, 0x66, 0x6a, 0x72,
+ 0x65, 0x79, 0x61, 0x6b, 0x65, 0x6b, 0x67, 0x77, 0x6a, 0x69, 0x73, 0x64, 0x6e, 0x6a, 0x68, 0x68,
+ 0x73, 0x76, 0x70, 0x78, 0x79, 0x6c, 0x71, 0x78, 0x6b, 0x77, 0x6b, 0x72, 0x70, 0x64, 0x75, 0x64,
+ 0x6a, 0x63, 0x69, 0x79, 0x70, 0x64, 0x72, 0x78, 0x72, 0x70, 0x79, 0x6a, 0x61, 0x6f, 0x73, 0x64,
+ 0x76, 0x79, 0x77, 0x71, 0x69, 0x75, 0x69, 0x6a, 0x77, 0x65, 0x77, 0x72, 0x6b, 0x61, 0x65, 0x78,
+ 0x65, 0x64, 0x66, 0x62, 0x6f, 0x72, 0x74, 0x72, 0x73, 0x71, 0x64, 0x77, 0x76, 0x70, 0x6c, 0x73,
+ 0x6e, 0x68, 0x6a, 0x62, 0x6d, 0x6f, 0x70, 0x78, 0x64, 0x6e, 0x65, 0x6f, 0x6d, 0x65, 0x64, 0x71,
+ 0x70, 0x6c, 0x6c, 0x6d, 0x6e, 0x71, 0x65, 0x66, 0x63, 0x74, 0x62, 0x71, 0x6a, 0x6c, 0x6b, 0x70,
+ 0x6a, 0x65, 0x6e, 0x6c, 0x6c, 0x69, 0x6e, 0x61, 0x79, 0x73, 0x78, 0x72, 0x65, 0x69, 0x78, 0x62,
+ 0x6f, 0x79, 0x6e, 0x63, 0x74, 0x6c, 0x65, 0x66, 0x72, 0x6d, 0x6a, 0x6c, 0x6b, 0x62, 0x6b, 0x6c,
+ 0x70, 0x65, 0x64, 0x70, 0x67, 0x71, 0x6c, 0x77, 0x6b, 0x66, 0x62, 0x77, 0x72, 0x6d, 0x6a, 0x62,
+ 0x61, 0x62, 0x65, 0x71, 0x78, 0x67, 0x62, 0x66, 0x69, 0x70, 0x79, 0x6b, 0x66, 0x6f, 0x62, 0x6c,
+ 0x61, 0x73, 0x61, 0x6c, 0x64, 0x62, 0x61, 0x70, 0x61, 0x63, 0x73, 0x6d, 0x76, 0x79, 0x68, 0x66,
+ 0x66, 0x6f, 0x66, 0x6f, 0x6a, 0x63, 0x61, 0x66, 0x75, 0x75, 0x61, 0x72, 0x61, 0x6a, 0x71, 0x6b,
+ 0x62, 0x65, 0x6c, 0x62, 0x62, 0x74, 0x69, 0x75, 0x69, 0x6f, 0x77, 0x65, 0x73, 0x72, 0x63, 0x72,
+ 0x62, 0x78, 0x75, 0x75, 0x6d, 0x70, 0x6c, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x61, 0x61, 0x6d, 0x79,
+ 0x65, 0x71, 0x68, 0x6a, 0x6e, 0x6e, 0x6d, 0x69, 0x68, 0x75, 0x65, 0x6a, 0x72, 0x61, 0x77, 0x75,
+ 0x69, 0x73, 0x76, 0x6b, 0x64, 0x77, 0x75, 0x61, 0x68, 0x74, 0x65, 0x63, 0x69, 0x6e, 0x62, 0x6f,
+ 0x63, 0x79, 0x65, 0x65, 0x6e, 0x66, 0x6f, 0x6c, 0x61, 0x78, 0x69, 0x68, 0x73, 0x62, 0x61, 0x6f,
+ 0x65, 0x75, 0x70, 0x6a, 0x68, 0x72, 0x6e, 0x66, 0x62, 0x6a, 0x74, 0x61, 0x63, 0x72, 0x6e, 0x65,
+ 0x65, 0x67, 0x71, 0x79, 0x61, 0x63, 0x72, 0x6f, 0x77, 0x77, 0x77, 0x74, 0x67, 0x77, 0x68, 0x6c,
+ 0x72, 0x64, 0x61, 0x77, 0x64, 0x62, 0x6f, 0x63, 0x67, 0x77, 0x6a, 0x6b, 0x71, 0x78, 0x75, 0x6b,
+ 0x6a, 0x77, 0x76, 0x75, 0x6e, 0x6d, 0x61, 0x67, 0x74, 0x6c, 0x62, 0x75, 0x78, 0x63, 0x6a, 0x71,
+ 0x6f, 0x6f, 0x77, 0x6f, 0x74, 0x6c, 0x66, 0x73, 0x78, 0x75, 0x78, 0x62, 0x66, 0x62, 0x70, 0x78,
+ 0x65, 0x6a, 0x77, 0x71, 0x6c, 0x71, 0x65, 0x79, 0x79, 0x70, 0x6b, 0x70, 0x79, 0x6b, 0x78, 0x69,
+ 0x6d, 0x65, 0x6c, 0x66, 0x78, 0x73, 0x6e, 0x6a, 0x72, 0x77, 0x69, 0x76, 0x68, 0x78, 0x6b, 0x71,
+ 0x70, 0x76, 0x63, 0x61, 0x62, 0x79, 0x6f, 0x64, 0x6e, 0x72, 0x65, 0x72, 0x69, 0x69, 0x64, 0x65,
+ 0x6b, 0x71, 0x68, 0x6b, 0x6d, 0x6c, 0x6c, 0x62, 0x72, 0x71, 0x72, 0x65, 0x61, 0x69, 0x65, 0x71,
+ 0x6c, 0x6d, 0x66, 0x69, 0x67, 0x71, 0x6d, 0x6d, 0x71, 0x69, 0x6f, 0x69, 0x68, 0x6d, 0x61, 0x74,
+ 0x6f, 0x65, 0x6c, 0x68, 0x67, 0x79, 0x6b, 0x63, 0x69, 0x78, 0x61, 0x6f, 0x65, 0x6c, 0x79, 0x64,
+ 0x68, 0x66, 0x72, 0x6a, 0x6d, 0x64, 0x6d, 0x71, 0x6c, 0x64, 0x6b, 0x6d, 0x64, 0x74, 0x64, 0x62,
+ 0x72, 0x64, 0x67, 0x71, 0x68, 0x61, 0x76, 0x70, 0x69, 0x61, 0x73, 0x6a, 0x72, 0x62, 0x66, 0x75,
+ 0x64, 0x77, 0x70, 0x6e, 0x72, 0x6f, 0x76, 0x6f, 0x68, 0x73, 0x6c, 0x6a, 0x61, 0x78, 0x72, 0x73,
+ 0x70, 0x76, 0x76, 0x6b, 0x75, 0x71, 0x65, 0x6d, 0x6a, 0x67, 0x73, 0x64, 0x62, 0x68, 0x79, 0x74,
+ 0x71, 0x70, 0x66, 0x77, 0x6e, 0x72, 0x65, 0x75, 0x72, 0x74, 0x76, 0x72, 0x78, 0x64, 0x73, 0x68,
+ 0x71, 0x65, 0x74, 0x67, 0x6b, 0x67, 0x64, 0x61, 0x64, 0x63, 0x6e, 0x71, 0x6e, 0x74, 0x76, 0x67,
+ 0x6b, 0x6c, 0x6c, 0x63, 0x6e, 0x70, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x64, 0x71, 0x66, 0x78,
+ 0x66, 0x6c, 0x62, 0x76, 0x75, 0x69, 0x65, 0x70, 0x75, 0x76, 0x77, 0x6e, 0x78, 0x6a, 0x6f, 0x6e,
+ 0x67, 0x73, 0x6b, 0x61, 0x6b, 0x6a, 0x6a, 0x72, 0x6f, 0x69, 0x77, 0x61, 0x61, 0x75, 0x6f, 0x71,
+ 0x76, 0x76, 0x70, 0x71, 0x75, 0x63, 0x75, 0x6d, 0x6e, 0x74, 0x68, 0x63, 0x64, 0x6c, 0x65, 0x74,
+ 0x77, 0x64, 0x72, 0x67, 0x73, 0x72, 0x65, 0x61, 0x64, 0x70, 0x71, 0x76, 0x6a, 0x79, 0x6f, 0x6e,
+ 0x61, 0x71, 0x63, 0x76, 0x78, 0x71, 0x63, 0x77, 0x66, 0x75, 0x78, 0x67, 0x6e, 0x66, 0x6c, 0x61,
+ 0x6d, 0x78, 0x79, 0x61, 0x67, 0x6c, 0x79, 0x6e, 0x68, 0x6d, 0x66, 0x67, 0x61, 0x75, 0x76, 0x73,
+ 0x6c, 0x6c, 0x76, 0x6c, 0x6f, 0x61, 0x71, 0x61, 0x73, 0x72, 0x69, 0x6e, 0x64, 0x78, 0x73, 0x79,
+ 0x64, 0x63, 0x69, 0x61, 0x71, 0x65, 0x74, 0x65, 0x6b, 0x6d, 0x75, 0x74, 0x61, 0x64, 0x77, 0x65,
+ 0x78, 0x71, 0x64, 0x65, 0x6b, 0x71, 0x6d, 0x6b, 0x70, 0x6c, 0x62, 0x68, 0x64, 0x62, 0x63, 0x62,
+ 0x6c, 0x70, 0x6b, 0x72, 0x78, 0x62, 0x79, 0x68, 0x6e, 0x79, 0x6a, 0x65, 0x6d, 0x69, 0x64, 0x79,
+ 0x6d, 0x6a, 0x69, 0x6d, 0x79, 0x69, 0x6e, 0x76, 0x6d, 0x74, 0x71, 0x79, 0x74, 0x6a, 0x70, 0x6f,
+ 0x79, 0x6c, 0x6e, 0x71, 0x63, 0x77, 0x76, 0x64, 0x66, 0x6c, 0x74, 0x64, 0x68, 0x65, 0x75, 0x72,
+ 0x73, 0x6a, 0x65, 0x67, 0x73, 0x69, 0x6b, 0x6f, 0x64, 0x62, 0x61, 0x74, 0x70, 0x73, 0x6f, 0x6a,
+ 0x6a, 0x6b, 0x6a, 0x67, 0x6a, 0x66, 0x72, 0x6e, 0x73, 0x62, 0x65, 0x77, 0x6a, 0x70, 0x65, 0x69,
+ 0x6a, 0x70, 0x61, 0x62, 0x6f, 0x63, 0x62, 0x77, 0x77, 0x6b, 0x65, 0x75, 0x63, 0x65, 0x61, 0x65,
+ 0x68, 0x66, 0x67, 0x75, 0x61, 0x67, 0x79, 0x71, 0x6a, 0x66, 0x65, 0x73, 0x6c, 0x64, 0x70, 0x65,
+ 0x68, 0x6e, 0x71, 0x6b, 0x74, 0x67, 0x77, 0x75, 0x6a, 0x79, 0x74, 0x70, 0x79, 0x6d, 0x62, 0x6e,
+ 0x71, 0x75, 0x72, 0x62, 0x6b, 0x6d, 0x68, 0x71, 0x78, 0x66, 0x67, 0x75, 0x6a, 0x6f, 0x6f, 0x67,
+ 0x6b, 0x62, 0x67, 0x63, 0x63, 0x71, 0x62, 0x79, 0x68, 0x6e, 0x77, 0x70, 0x68, 0x73, 0x64, 0x76,
+ 0x65, 0x75, 0x6e, 0x66, 0x6a, 0x75, 0x61, 0x63, 0x6b, 0x73, 0x62, 0x61, 0x71, 0x69, 0x6b, 0x70,
+ 0x6c, 0x76, 0x79, 0x77, 0x6c, 0x6c, 0x64, 0x6e, 0x6d, 0x79, 0x71, 0x62, 0x70, 0x6f, 0x6e, 0x6d,
+ 0x73, 0x74, 0x69, 0x76, 0x6f, 0x74, 0x71, 0x73, 0x78, 0x6c, 0x61, 0x70, 0x78, 0x78, 0x6f, 0x67,
+ 0x68, 0x70, 0x72, 0x64, 0x71, 0x6d, 0x6a, 0x67, 0x75, 0x61, 0x78, 0x71, 0x74, 0x73, 0x63, 0x6c,
+ 0x65, 0x66, 0x6c, 0x72, 0x6d, 0x77, 0x6f, 0x62, 0x77, 0x70, 0x68, 0x68, 0x72, 0x72, 0x66, 0x73,
+ 0x74, 0x79, 0x69, 0x79, 0x65, 0x79, 0x68, 0x67, 0x69, 0x71, 0x74, 0x75, 0x65, 0x6c, 0x62, 0x61,
+ 0x63, 0x64, 0x74, 0x63, 0x76, 0x77, 0x79, 0x65, 0x73, 0x63, 0x73, 0x6c, 0x6d, 0x6b, 0x6a, 0x78,
+ 0x6c, 0x67, 0x76, 0x64, 0x68, 0x73, 0x63, 0x78, 0x66, 0x73, 0x78, 0x67, 0x75, 0x70, 0x63, 0x63,
+ 0x61, 0x77, 0x6f, 0x68, 0x6d, 0x6d, 0x74, 0x6a, 0x66, 0x62, 0x61, 0x76, 0x62, 0x63, 0x6c, 0x6f,
+ 0x68, 0x6f, 0x74, 0x69, 0x6a, 0x6b, 0x6c, 0x70, 0x78, 0x6a, 0x75, 0x63, 0x67, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x63, 0x69, 0x64, 0x66, 0x72, 0x69, 0x6d, 0x6d, 0x6a, 0x74, 0x74, 0x62, 0x6a, 0x61, 0x67,
+ 0x79, 0x6f, 0x78, 0x62, 0x6e, 0x63, 0x64, 0x74, 0x71, 0x75, 0x6d, 0x73, 0x72, 0x67, 0x6f, 0x72,
+ 0x6d, 0x70, 0x6f, 0x6c, 0x6b, 0x66, 0x69, 0x79, 0x63, 0x79, 0x79, 0x63, 0x66, 0x70, 0x6c, 0x61,
+ 0x6a, 0x61, 0x62, 0x76, 0x70, 0x71, 0x73, 0x71, 0x72, 0x63, 0x77, 0x78, 0x62, 0x6b, 0x75, 0x68,
+ 0x69, 0x75, 0x6b, 0x77, 0x71, 0x6e, 0x61, 0x6e, 0x62, 0x79, 0x6d, 0x61, 0x6e, 0x63, 0x6a, 0x74,
+ 0x77, 0x6d, 0x78, 0x64, 0x75, 0x6b, 0x79, 0x79, 0x73, 0x62, 0x67, 0x62, 0x75, 0x6f, 0x72, 0x67,
+ 0x6f, 0x76, 0x73, 0x68, 0x66, 0x6f, 0x78, 0x72, 0x69, 0x64, 0x61, 0x66, 0x69, 0x6c, 0x6a, 0x71,
+ 0x73, 0x75, 0x6a, 0x69, 0x71, 0x64, 0x6c, 0x61, 0x72, 0x67, 0x71, 0x76, 0x6d, 0x64, 0x74, 0x72,
+ 0x78, 0x67, 0x6d, 0x70, 0x65, 0x79, 0x61, 0x6d, 0x79, 0x6a, 0x68, 0x67, 0x6c, 0x64, 0x70, 0x74,
+ 0x62, 0x70, 0x6c, 0x6d, 0x72, 0x6e, 0x72, 0x68, 0x70, 0x78, 0x6f, 0x6e, 0x6b, 0x72, 0x66, 0x77,
+ 0x72, 0x61, 0x64, 0x68, 0x6b, 0x61, 0x6a, 0x6d, 0x77, 0x6c, 0x63, 0x78, 0x6c, 0x65, 0x63, 0x74,
+ 0x78, 0x76, 0x68, 0x61, 0x6f, 0x6b, 0x64, 0x66, 0x73, 0x72, 0x73, 0x66, 0x65, 0x67, 0x6d, 0x70,
+ 0x69, 0x63, 0x75, 0x6a, 0x79, 0x6e, 0x6d, 0x79, 0x63, 0x71, 0x61, 0x75, 0x76, 0x79, 0x73, 0x66,
+ 0x76, 0x61, 0x6c, 0x71, 0x69, 0x71, 0x6b, 0x66, 0x65, 0x78, 0x78, 0x63, 0x6d, 0x78, 0x6c, 0x6e,
+ 0x6e, 0x79, 0x69, 0x6a, 0x6f, 0x75, 0x64, 0x6c, 0x65, 0x71, 0x68, 0x78, 0x67, 0x73, 0x76, 0x75,
+ 0x70, 0x6c, 0x6d, 0x6f, 0x6c, 0x64, 0x67, 0x73, 0x74, 0x73, 0x6b, 0x6b, 0x78, 0x68, 0x77, 0x62,
+ 0x74, 0x79, 0x72, 0x6f, 0x6f, 0x76, 0x73, 0x74, 0x76, 0x67, 0x6d, 0x6c, 0x71, 0x75, 0x61, 0x6a,
+ 0x69, 0x77, 0x6d, 0x68, 0x72, 0x72, 0x6b, 0x6b, 0x69, 0x6c, 0x6c, 0x63, 0x68, 0x62, 0x76, 0x6c,
+ 0x78, 0x69, 0x79, 0x62, 0x79, 0x68, 0x71, 0x62, 0x70, 0x72, 0x6e, 0x66, 0x71, 0x71, 0x67, 0x63,
+ 0x69, 0x67, 0x70, 0x6b, 0x76, 0x69, 0x79, 0x78, 0x73, 0x74, 0x69, 0x67, 0x6a, 0x78, 0x63, 0x66,
+ 0x65, 0x65, 0x64, 0x65, 0x72, 0x6d, 0x67, 0x72, 0x64, 0x70, 0x73, 0x66, 0x6e, 0x73, 0x70, 0x65,
+ 0x6e, 0x61, 0x68, 0x66, 0x66, 0x6a, 0x75, 0x6a, 0x65, 0x66, 0x70, 0x65, 0x66, 0x6b, 0x77, 0x63,
+ 0x62, 0x6c, 0x61, 0x73, 0x77, 0x70, 0x65, 0x72, 0x61, 0x69, 0x76, 0x62, 0x6b, 0x72, 0x64, 0x69,
+ 0x61, 0x62, 0x6d, 0x79, 0x64, 0x76, 0x61, 0x76, 0x69, 0x72, 0x64, 0x73, 0x76, 0x66, 0x67, 0x6b,
+ 0x71, 0x75, 0x6b, 0x75, 0x76, 0x68, 0x77, 0x63, 0x79, 0x76, 0x6c, 0x69, 0x67, 0x69, 0x76, 0x6d,
+ 0x79, 0x6c, 0x62, 0x65, 0x66, 0x76, 0x73, 0x78, 0x73, 0x75, 0x6e, 0x68, 0x6e, 0x61, 0x79, 0x75,
+ 0x6f, 0x77, 0x71, 0x6d, 0x62, 0x74, 0x63, 0x67, 0x66, 0x68, 0x63, 0x79, 0x72, 0x75, 0x6a, 0x78,
+ 0x79, 0x75, 0x66, 0x75, 0x6e, 0x61, 0x6a, 0x6b, 0x72, 0x75, 0x6f, 0x6f, 0x6f, 0x75, 0x78, 0x70,
+ 0x71, 0x68, 0x76, 0x67, 0x65, 0x63, 0x78, 0x78, 0x6d, 0x73, 0x63, 0x63, 0x74, 0x67, 0x71, 0x6c,
+ 0x75, 0x78, 0x74, 0x6e, 0x66, 0x62, 0x6e, 0x63, 0x78, 0x75, 0x64, 0x78, 0x69, 0x67, 0x6b, 0x64,
+ 0x6d, 0x6f, 0x79, 0x74, 0x61, 0x6f, 0x64, 0x67, 0x71, 0x77, 0x62, 0x63, 0x6d, 0x64, 0x76, 0x67,
+ 0x63, 0x79, 0x75, 0x74, 0x6f, 0x68, 0x73, 0x77, 0x69, 0x76, 0x6e, 0x68, 0x6a, 0x6a, 0x79, 0x75,
+ 0x65, 0x68, 0x69, 0x6e, 0x6d, 0x6a, 0x69, 0x68, 0x65, 0x6c, 0x77, 0x62, 0x61, 0x6b, 0x6d, 0x77,
+ 0x78, 0x70, 0x6a, 0x73, 0x79, 0x6b, 0x63, 0x6e, 0x79, 0x6d, 0x65, 0x72, 0x75, 0x66, 0x6c, 0x79,
+ 0x6f, 0x6e, 0x77, 0x69, 0x6d, 0x72, 0x70, 0x74, 0x75, 0x62, 0x75, 0x72, 0x6c, 0x65, 0x77, 0x66,
+ 0x78, 0x62, 0x70, 0x70, 0x6c, 0x6e, 0x69, 0x78, 0x73, 0x79, 0x71, 0x6b, 0x6d, 0x75, 0x62, 0x76,
+ 0x63, 0x65, 0x6d, 0x67, 0x6c, 0x66, 0x74, 0x6d, 0x6b, 0x72, 0x66, 0x6e, 0x61, 0x6f, 0x6d, 0x61,
+ 0x67, 0x6b, 0x66, 0x71, 0x71, 0x6a, 0x6e, 0x72, 0x78, 0x64, 0x71, 0x73, 0x63, 0x79, 0x74, 0x72,
+ 0x74, 0x70, 0x77, 0x66, 0x73, 0x79, 0x71, 0x65, 0x6a, 0x73, 0x66, 0x66, 0x67, 0x63, 0x65, 0x6f,
+ 0x71, 0x6c, 0x73, 0x79, 0x73, 0x67, 0x78, 0x74, 0x61, 0x6f, 0x6a, 0x75, 0x74, 0x70, 0x76, 0x78,
+ 0x69, 0x6b, 0x75, 0x78, 0x6f, 0x68, 0x6c, 0x65, 0x65, 0x61, 0x71, 0x65, 0x66, 0x67, 0x66, 0x74,
+ 0x79, 0x6c, 0x62, 0x74, 0x69, 0x66, 0x79, 0x61, 0x79, 0x77, 0x64, 0x69, 0x70, 0x67, 0x73, 0x6b,
+ 0x75, 0x74, 0x6c, 0x68, 0x74, 0x62, 0x64, 0x66, 0x76, 0x6a, 0x69, 0x72, 0x68, 0x73, 0x65, 0x63,
+ 0x74, 0x61, 0x6e, 0x6c, 0x63, 0x64, 0x67, 0x6d, 0x68, 0x75, 0x63, 0x6a, 0x68, 0x64, 0x79, 0x61,
+ 0x6b, 0x77, 0x6a, 0x66, 0x75, 0x79, 0x6b, 0x65, 0x6f, 0x77, 0x75, 0x62, 0x72, 0x77, 0x75, 0x73,
+ 0x64, 0x69, 0x79, 0x61, 0x73, 0x67, 0x6b, 0x68, 0x62, 0x75, 0x69, 0x6f, 0x6a, 0x64, 0x77, 0x68,
+ 0x65, 0x65, 0x66, 0x61, 0x61, 0x68, 0x72, 0x6a, 0x69, 0x6b, 0x77, 0x71, 0x78, 0x6c, 0x6b, 0x74,
+ 0x76, 0x6c, 0x6a, 0x70, 0x63, 0x6f, 0x71, 0x65, 0x65, 0x6f, 0x69, 0x71, 0x62, 0x73, 0x6c, 0x75,
+ 0x6a, 0x74, 0x61, 0x78, 0x6d, 0x62, 0x6e, 0x6e, 0x79, 0x69, 0x68, 0x69, 0x77, 0x6c, 0x6a, 0x6c,
+ 0x75, 0x62, 0x6e, 0x66, 0x67, 0x66, 0x62, 0x71, 0x75, 0x69, 0x66, 0x6c, 0x68, 0x75, 0x71, 0x61,
+ 0x78, 0x62, 0x69, 0x64, 0x6c, 0x66, 0x70, 0x61, 0x64, 0x61, 0x70, 0x63, 0x6b, 0x6d, 0x78, 0x62,
+ 0x6f, 0x65, 0x71, 0x73, 0x75, 0x66, 0x6a, 0x69, 0x6b, 0x64, 0x63, 0x6f, 0x63, 0x6d, 0x69, 0x73,
+ 0x79, 0x77, 0x69, 0x68, 0x66, 0x70, 0x76, 0x6e, 0x6f, 0x64, 0x72, 0x79, 0x70, 0x66, 0x6f, 0x63,
+ 0x79, 0x64, 0x76, 0x69, 0x70, 0x6e, 0x65, 0x67, 0x73, 0x6c, 0x63, 0x6f, 0x77, 0x67, 0x63, 0x6a,
+ 0x69, 0x6a, 0x6f, 0x74, 0x61, 0x68, 0x6b, 0x63, 0x6c, 0x63, 0x71, 0x68, 0x61, 0x70, 0x70, 0x64,
+ 0x62, 0x6b, 0x68, 0x75, 0x63, 0x6e, 0x78, 0x68, 0x61, 0x71, 0x6e, 0x77, 0x6f, 0x6b, 0x78, 0x78,
+ 0x74, 0x73, 0x70, 0x77, 0x66, 0x61, 0x6b, 0x77, 0x6c, 0x63, 0x76, 0x65, 0x79, 0x73, 0x76, 0x79,
+ 0x68, 0x78, 0x6b, 0x73, 0x61, 0x71, 0x77, 0x70, 0x61, 0x6b, 0x79, 0x73, 0x6b, 0x6c, 0x77, 0x63,
+ 0x69, 0x62, 0x77, 0x64, 0x68, 0x6c, 0x68, 0x79, 0x62, 0x72, 0x6a, 0x62, 0x72, 0x62, 0x61, 0x6b,
+ 0x71, 0x63, 0x63, 0x66, 0x62, 0x6a, 0x75, 0x65, 0x79, 0x6e, 0x65, 0x63, 0x6a, 0x75, 0x63, 0x79,
+ 0x74, 0x69, 0x69, 0x76, 0x78, 0x66, 0x68, 0x6c, 0x67, 0x77, 0x65, 0x6f, 0x76, 0x75, 0x61, 0x63,
+ 0x71, 0x73, 0x76, 0x64, 0x69, 0x64, 0x75, 0x64, 0x71, 0x62, 0x74, 0x6f, 0x74, 0x63, 0x66, 0x63,
+ 0x74, 0x70, 0x6d, 0x66, 0x6f, 0x6f, 0x6b, 0x79, 0x74, 0x61, 0x70, 0x76, 0x6d, 0x6f, 0x70, 0x64,
+ 0x74, 0x68, 0x6a, 0x6e, 0x6b, 0x70, 0x68, 0x64, 0x72, 0x72, 0x75, 0x63, 0x6e, 0x69, 0x69, 0x61,
+ 0x6f, 0x64, 0x76, 0x6a, 0x69, 0x6e, 0x79, 0x70, 0x61, 0x64, 0x76, 0x69, 0x6d, 0x6b, 0x67, 0x6a,
+ 0x6e, 0x67, 0x63, 0x6f, 0x78, 0x79, 0x73, 0x64, 0x77, 0x77, 0x72, 0x73, 0x61, 0x65, 0x6a, 0x65,
+ 0x6d, 0x62, 0x6b, 0x69, 0x78, 0x6d, 0x78, 0x74, 0x62, 0x75, 0x79, 0x6b, 0x66, 0x65, 0x73, 0x64,
+ 0x67, 0x72, 0x61, 0x64, 0x6c, 0x64, 0x61, 0x6f, 0x74, 0x64, 0x68, 0x73, 0x69, 0x6d, 0x69, 0x6f,
+ 0x6f, 0x71, 0x70, 0x77, 0x75, 0x77, 0x71, 0x73, 0x75, 0x63, 0x79, 0x74, 0x6f, 0x79, 0x6f, 0x63,
+ 0x64, 0x72, 0x78, 0x70, 0x63, 0x65, 0x62, 0x6e, 0x65, 0x64, 0x70, 0x6a, 0x6f, 0x63, 0x70, 0x6b,
+ 0x67, 0x73, 0x78, 0x61, 0x77, 0x70, 0x6e, 0x70, 0x78, 0x71, 0x61, 0x63, 0x6d, 0x68, 0x6a, 0x63,
+ 0x74, 0x73, 0x62, 0x75, 0x74, 0x75, 0x6c, 0x6b, 0x6c, 0x76, 0x6b, 0x65, 0x6a, 0x77, 0x77, 0x64,
+ 0x77, 0x6f, 0x6f, 0x70, 0x61, 0x74, 0x65, 0x74, 0x6d, 0x6f, 0x68, 0x77, 0x6b, 0x63, 0x65, 0x62,
+ 0x6a, 0x74, 0x6b, 0x78, 0x6d, 0x76, 0x63, 0x73, 0x6c, 0x68, 0x6f, 0x6d, 0x6c, 0x78, 0x79, 0x6b,
+ 0x74, 0x74, 0x66, 0x69, 0x76, 0x70, 0x6f, 0x6f, 0x78, 0x61, 0x6d, 0x68, 0x77, 0x75, 0x74, 0x79,
+ 0x68, 0x6f, 0x63, 0x64, 0x6b, 0x72, 0x65, 0x68, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x6d, 0x72, 0x75,
+ 0x74, 0x75, 0x6d, 0x6f, 0x6f, 0x65, 0x68, 0x64, 0x6a, 0x74, 0x64, 0x68, 0x62, 0x70, 0x6d, 0x68,
+ 0x73, 0x6d, 0x65, 0x76, 0x75, 0x64, 0x65, 0x66, 0x77, 0x69, 0x71, 0x6a, 0x64, 0x6b, 0x73, 0x6f,
+ 0x70, 0x6c, 0x6e, 0x6c, 0x6a, 0x66, 0x67, 0x78, 0x61, 0x79, 0x68, 0x63, 0x70, 0x79, 0x68, 0x6c,
+ 0x70, 0x6c, 0x76, 0x64, 0x68, 0x76, 0x67, 0x73, 0x65, 0x62, 0x65, 0x71, 0x71, 0x79, 0x72, 0x77,
+ 0x65, 0x77, 0x79, 0x78, 0x72, 0x75, 0x6e, 0x71, 0x68, 0x72, 0x64, 0x66, 0x64, 0x63, 0x68, 0x79,
+ 0x66, 0x70, 0x67, 0x64, 0x75, 0x62, 0x79, 0x66, 0x62, 0x74, 0x6b, 0x72, 0x6a, 0x70, 0x78, 0x61,
+ 0x70, 0x6e, 0x76, 0x63, 0x6a, 0x62, 0x76, 0x69, 0x68, 0x77, 0x6c, 0x73, 0x62, 0x68, 0x65, 0x6f,
+ 0x6f, 0x6c, 0x65, 0x6e, 0x66, 0x6a, 0x71, 0x69, 0x71, 0x66, 0x63, 0x6e, 0x63, 0x6d, 0x67, 0x78,
+ 0x72, 0x72, 0x66, 0x79, 0x66, 0x75, 0x72, 0x73, 0x69, 0x76, 0x75, 0x75, 0x69, 0x64, 0x6f, 0x6e,
+ 0x67, 0x67, 0x78, 0x72, 0x72, 0x71, 0x71, 0x69, 0x61, 0x66, 0x6f, 0x6d, 0x67, 0x68, 0x63, 0x62,
+ 0x6b, 0x6b, 0x6c, 0x61, 0x63, 0x65, 0x62, 0x76, 0x72, 0x74, 0x6c, 0x78, 0x68, 0x64, 0x71, 0x78,
+ 0x76, 0x6c, 0x72, 0x71, 0x73, 0x79, 0x66, 0x67, 0x72, 0x76, 0x6c, 0x75, 0x68, 0x6b, 0x70, 0x6e,
+ 0x63, 0x62, 0x6d, 0x67, 0x64, 0x70, 0x68, 0x68, 0x6c, 0x65, 0x77, 0x61, 0x75, 0x71, 0x61, 0x6d,
+ 0x6d, 0x67, 0x6c, 0x69, 0x63, 0x6a, 0x6d, 0x62, 0x6a, 0x77, 0x73, 0x6d, 0x65, 0x6f, 0x73, 0x76,
+ 0x69, 0x6f, 0x70, 0x66, 0x64, 0x73, 0x69, 0x72, 0x71, 0x6a, 0x79, 0x74, 0x6e, 0x65, 0x6a, 0x6b,
+ 0x63, 0x76, 0x66, 0x66, 0x72, 0x6e, 0x79, 0x6d, 0x6b, 0x6d, 0x75, 0x76, 0x68, 0x70, 0x6a, 0x78,
+ 0x6a, 0x6c, 0x78, 0x68, 0x77, 0x66, 0x75, 0x62, 0x6c, 0x75, 0x79, 0x69, 0x6a, 0x6e, 0x78, 0x74,
+ 0x6c, 0x6e, 0x65, 0x78, 0x64, 0x6f, 0x78, 0x68, 0x76, 0x6c, 0x75, 0x73, 0x62, 0x62, 0x79, 0x68,
+ 0x6d, 0x77, 0x75, 0x6d, 0x67, 0x6c, 0x68, 0x72, 0x72, 0x65, 0x62, 0x68, 0x67, 0x61, 0x74, 0x63,
+ 0x72, 0x6c, 0x63, 0x65, 0x74, 0x78, 0x6a, 0x68, 0x71, 0x71, 0x6b, 0x64, 0x79, 0x77, 0x73, 0x68,
+ 0x69, 0x76, 0x64, 0x62, 0x77, 0x75, 0x78, 0x76, 0x67, 0x65, 0x75, 0x67, 0x75, 0x68, 0x6e, 0x64,
+ 0x79, 0x67, 0x77, 0x6f, 0x68, 0x70, 0x69, 0x64, 0x61, 0x62, 0x69, 0x76, 0x64, 0x78, 0x71, 0x6e,
+ 0x6e, 0x69, 0x61, 0x68, 0x75, 0x76, 0x6c, 0x74, 0x68, 0x63, 0x6d, 0x79, 0x73, 0x69, 0x61, 0x64,
+ 0x6c, 0x69, 0x74, 0x75, 0x69, 0x70, 0x69, 0x73, 0x74, 0x6c, 0x62, 0x6c, 0x6e, 0x6c, 0x6f, 0x73,
+ 0x61, 0x63, 0x6e, 0x66, 0x72, 0x63, 0x65, 0x71, 0x62, 0x68, 0x77, 0x74, 0x6d, 0x62, 0x62, 0x75,
+ 0x69, 0x75, 0x63, 0x63, 0x6b, 0x68, 0x70, 0x71, 0x63, 0x6f, 0x65, 0x70, 0x6a, 0x78, 0x73, 0x61,
+ 0x68, 0x75, 0x71, 0x66, 0x6a, 0x77, 0x6b, 0x6a, 0x76, 0x79, 0x61, 0x64, 0x6a, 0x6f, 0x68, 0x72,
+ 0x78, 0x64, 0x75, 0x6f, 0x64, 0x64, 0x6d, 0x65, 0x75, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x65, 0x6d,
+ 0x6d, 0x72, 0x65, 0x62, 0x70, 0x64, 0x64, 0x69, 0x6b, 0x6a, 0x63, 0x78, 0x6c, 0x74, 0x61, 0x68,
+ 0x61, 0x6e, 0x6c, 0x75, 0x73, 0x62, 0x62, 0x6e, 0x71, 0x62, 0x6a, 0x78, 0x65, 0x63, 0x61, 0x65,
+ 0x75, 0x73, 0x66, 0x61, 0x6b, 0x74, 0x6b, 0x65, 0x6f, 0x69, 0x63, 0x6e, 0x6f, 0x69, 0x6b, 0x62,
+ 0x65, 0x73, 0x64, 0x64, 0x6d, 0x72, 0x76, 0x66, 0x76, 0x76, 0x72, 0x6d, 0x67, 0x6a, 0x65, 0x6d,
+ 0x73, 0x65, 0x67, 0x79, 0x79, 0x63, 0x62, 0x6a, 0x6e, 0x68, 0x71, 0x65, 0x69, 0x6a, 0x6e, 0x63,
+ 0x76, 0x72, 0x6c, 0x67, 0x6f, 0x6d, 0x64, 0x74, 0x61, 0x76, 0x6d, 0x76, 0x6d, 0x75, 0x64, 0x6a,
+ 0x64, 0x63, 0x75, 0x70, 0x64, 0x6a, 0x68, 0x72, 0x65, 0x64, 0x79, 0x79, 0x72, 0x6a, 0x6d, 0x77,
+ 0x75, 0x64, 0x6f, 0x72, 0x63, 0x65, 0x74, 0x72, 0x6e, 0x67, 0x66, 0x70, 0x63, 0x6d, 0x64, 0x65,
+ 0x63, 0x72, 0x69, 0x6a, 0x74, 0x68, 0x77, 0x74, 0x72, 0x79, 0x77, 0x71, 0x6c, 0x6d, 0x79, 0x6f,
+ 0x62, 0x79, 0x72, 0x61, 0x62, 0x6a, 0x77, 0x76, 0x61, 0x6d, 0x74, 0x65, 0x73, 0x69, 0x6f, 0x74,
+ 0x76, 0x68, 0x71, 0x76, 0x67, 0x76, 0x62, 0x6f, 0x63, 0x6f, 0x6c, 0x77, 0x69, 0x6a, 0x62, 0x64,
+ 0x62, 0x74, 0x6a, 0x69, 0x71, 0x6a, 0x6c, 0x78, 0x67, 0x78, 0x67, 0x65, 0x65, 0x70, 0x79, 0x65,
+ 0x71, 0x75, 0x6c, 0x6e, 0x6b, 0x68, 0x6e, 0x62, 0x78, 0x65, 0x76, 0x69, 0x6d, 0x66, 0x6a, 0x70,
+ 0x6c, 0x63, 0x68, 0x75, 0x6a, 0x6c, 0x73, 0x75, 0x68, 0x6d, 0x63, 0x73, 0x68, 0x6e, 0x64, 0x6f,
+ 0x61, 0x65, 0x75, 0x6a, 0x75, 0x62, 0x76, 0x79, 0x6d, 0x64, 0x71, 0x76, 0x78, 0x62, 0x6a, 0x75,
+ 0x6c, 0x6b, 0x77, 0x79, 0x6a, 0x6a, 0x77, 0x74, 0x6a, 0x72, 0x73, 0x66, 0x72, 0x63, 0x69, 0x75,
+ 0x62, 0x70, 0x77, 0x6e, 0x62, 0x65, 0x71, 0x71, 0x6c, 0x76, 0x6b, 0x73, 0x6e, 0x64, 0x74, 0x75,
+ 0x62, 0x79, 0x71, 0x77, 0x75, 0x73, 0x6f, 0x76, 0x6d, 0x6a, 0x73, 0x72, 0x62, 0x6d, 0x69, 0x73,
+ 0x6a, 0x6a, 0x6d, 0x6f, 0x73, 0x63, 0x72, 0x75, 0x6c, 0x6c, 0x68, 0x67, 0x70, 0x74, 0x61, 0x73,
+ 0x78, 0x6d, 0x63, 0x6c, 0x6d, 0x74, 0x71, 0x65, 0x67, 0x74, 0x75, 0x6a, 0x6f, 0x6a, 0x6e, 0x64,
+ 0x6d, 0x68, 0x63, 0x77, 0x79, 0x6a, 0x65, 0x6b, 0x62, 0x6a, 0x66, 0x6b, 0x76, 0x73, 0x74, 0x75,
+ 0x6b, 0x6a, 0x79, 0x69, 0x6d, 0x6c, 0x79, 0x6a, 0x79, 0x79, 0x79, 0x78, 0x71, 0x75, 0x76, 0x67,
+ 0x70, 0x64, 0x69, 0x74, 0x70, 0x61, 0x62, 0x79, 0x65, 0x6e, 0x65, 0x6e, 0x77, 0x63, 0x67, 0x73,
+ 0x79, 0x77, 0x6a, 0x73, 0x78, 0x61, 0x62, 0x76, 0x74, 0x6f, 0x6b, 0x6f, 0x75, 0x6b, 0x6f, 0x67,
+ 0x6b, 0x67, 0x76, 0x65, 0x6b, 0x70, 0x6d, 0x6a, 0x61, 0x70, 0x6c, 0x67, 0x61, 0x64, 0x74, 0x63,
+ 0x63, 0x6b, 0x73, 0x74, 0x68, 0x6d, 0x6d, 0x69, 0x79, 0x65, 0x67, 0x71, 0x6e, 0x66, 0x6b, 0x72,
+ 0x69, 0x75, 0x6f, 0x65, 0x61, 0x75, 0x71, 0x67, 0x63, 0x70, 0x6b, 0x63, 0x76, 0x73, 0x64, 0x6a,
+ 0x6e, 0x61, 0x75, 0x72, 0x68, 0x6d, 0x62, 0x68, 0x65, 0x67, 0x79, 0x63, 0x6f, 0x66, 0x68, 0x68,
+ 0x66, 0x61, 0x79, 0x68, 0x62, 0x74, 0x70, 0x69, 0x6e, 0x6a, 0x78, 0x65, 0x6c, 0x77, 0x79, 0x73,
+ 0x69, 0x62, 0x71, 0x63, 0x62, 0x73, 0x65, 0x65, 0x6b, 0x6f, 0x6f, 0x71, 0x6b, 0x62, 0x6f, 0x68,
+ 0x75, 0x66, 0x78, 0x62, 0x73, 0x6d, 0x62, 0x71, 0x64, 0x6a, 0x6c, 0x63, 0x76, 0x66, 0x66, 0x69,
+ 0x62, 0x6c, 0x6a, 0x69, 0x78, 0x78, 0x65, 0x68, 0x73, 0x70, 0x65, 0x6f, 0x67, 0x73, 0x79, 0x69,
+ 0x67, 0x71, 0x70, 0x62, 0x6e, 0x65, 0x63, 0x62, 0x69, 0x70, 0x6e, 0x6f, 0x70, 0x77, 0x64, 0x62,
+ 0x6e, 0x6e, 0x69, 0x66, 0x61, 0x61, 0x79, 0x62, 0x68, 0x72, 0x6a, 0x75, 0x6c, 0x6f, 0x73, 0x75,
+ 0x6a, 0x73, 0x62, 0x6e, 0x6f, 0x65, 0x65, 0x62, 0x6d, 0x6a, 0x71, 0x6c, 0x6c, 0x77, 0x71, 0x6c,
+ 0x69, 0x6a, 0x6c, 0x74, 0x6a, 0x6b, 0x73, 0x78, 0x72, 0x6e, 0x68, 0x64, 0x69, 0x63, 0x73, 0x78,
+ 0x77, 0x6b, 0x6a, 0x6b, 0x78, 0x72, 0x61, 0x6d, 0x6b, 0x64, 0x71, 0x75, 0x6d, 0x65, 0x71, 0x71,
+ 0x6b, 0x61, 0x72, 0x71, 0x6e, 0x76, 0x77, 0x6e, 0x61, 0x66, 0x6d, 0x66, 0x68, 0x6a, 0x64, 0x6a,
+ 0x66, 0x75, 0x66, 0x6c, 0x76, 0x64, 0x73, 0x6a, 0x6c, 0x63, 0x6d, 0x71, 0x67, 0x73, 0x74, 0x76,
+ 0x74, 0x66, 0x70, 0x75, 0x6c, 0x6c, 0x63, 0x74, 0x70, 0x68, 0x6e, 0x78, 0x61, 0x73, 0x6a, 0x6f,
+ 0x67, 0x78, 0x6d, 0x79, 0x62, 0x68, 0x6f, 0x66, 0x64, 0x78, 0x61, 0x64, 0x6d, 0x61, 0x74, 0x72,
+ 0x61, 0x77, 0x6a, 0x79, 0x75, 0x66, 0x6f, 0x6c, 0x79, 0x78, 0x76, 0x76, 0x72, 0x69, 0x6f, 0x79,
+ 0x65, 0x62, 0x65, 0x73, 0x6e, 0x61, 0x6e, 0x71, 0x63, 0x68, 0x6e, 0x76, 0x64, 0x6f, 0x69, 0x62,
+ 0x6e, 0x6c, 0x6e, 0x79, 0x71, 0x79, 0x73, 0x72, 0x71, 0x65, 0x6b, 0x65, 0x65, 0x76, 0x65, 0x6d,
+ 0x72, 0x69, 0x77, 0x71, 0x70, 0x78, 0x78, 0x6b, 0x64, 0x70, 0x77, 0x79, 0x73, 0x75, 0x74, 0x62,
+ 0x79, 0x6b, 0x79, 0x70, 0x71, 0x70, 0x76, 0x6f, 0x71, 0x71, 0x66, 0x62, 0x6d, 0x6b, 0x74, 0x73,
+ 0x6c, 0x78, 0x77, 0x68, 0x6d, 0x6d, 0x65, 0x75, 0x74, 0x64, 0x71, 0x72, 0x6c, 0x64, 0x6f, 0x68,
+ 0x63, 0x75, 0x78, 0x68, 0x6f, 0x71, 0x61, 0x68, 0x78, 0x78, 0x73, 0x62, 0x74, 0x70, 0x61, 0x6e,
+ 0x68, 0x72, 0x71, 0x6c, 0x72, 0x71, 0x70, 0x77, 0x68, 0x6b, 0x64, 0x63, 0x6c, 0x76, 0x79, 0x70,
+ 0x76, 0x6f, 0x6a, 0x65, 0x67, 0x74, 0x73, 0x6a, 0x72, 0x64, 0x6a, 0x77, 0x6b, 0x61, 0x62, 0x69,
+ 0x70, 0x75, 0x6c, 0x6f, 0x6e, 0x6e, 0x6b, 0x76, 0x66, 0x71, 0x74, 0x62, 0x71, 0x75, 0x6c, 0x70,
+ 0x71, 0x70, 0x73, 0x61, 0x6a, 0x61, 0x79, 0x78, 0x74, 0x77, 0x77, 0x61, 0x76, 0x6f, 0x6c, 0x75,
+ 0x68, 0x63, 0x72, 0x77, 0x64, 0x64, 0x77, 0x6e, 0x67, 0x6a, 0x6c, 0x74, 0x73, 0x78, 0x72, 0x6a,
+ 0x6e, 0x69, 0x70, 0x79, 0x64, 0x62, 0x63, 0x76, 0x65, 0x76, 0x6d, 0x6f, 0x6b, 0x79, 0x63, 0x78,
+ 0x68, 0x70, 0x6e, 0x75, 0x64, 0x77, 0x64, 0x6e, 0x6c, 0x6e, 0x64, 0x78, 0x72, 0x73, 0x70, 0x74,
+ 0x72, 0x69, 0x76, 0x74, 0x61, 0x77, 0x64, 0x72, 0x65, 0x67, 0x6e, 0x68, 0x68, 0x67, 0x64, 0x6c,
+ 0x6f, 0x6e, 0x6f, 0x76, 0x71, 0x6c, 0x6f, 0x67, 0x75, 0x77, 0x6a, 0x72, 0x6d, 0x72, 0x70, 0x79,
+ 0x75, 0x72, 0x70, 0x78, 0x6f, 0x6e, 0x65, 0x6d, 0x74, 0x72, 0x6a, 0x62, 0x63, 0x76, 0x69, 0x68,
+ 0x77, 0x78, 0x6c, 0x72, 0x77, 0x63, 0x75, 0x6a, 0x6b, 0x74, 0x73, 0x64, 0x64, 0x70, 0x74, 0x61,
+ 0x6e, 0x6e, 0x62, 0x77, 0x76, 0x71, 0x78, 0x6a, 0x71, 0x76, 0x74, 0x61, 0x64, 0x75, 0x75, 0x63,
+ 0x61, 0x71, 0x78, 0x67, 0x77, 0x71, 0x66, 0x6e, 0x6c, 0x73, 0x61, 0x6e, 0x77, 0x62, 0x71, 0x63,
+ 0x6b, 0x6f, 0x6c, 0x6c, 0x6d, 0x77, 0x6a, 0x78, 0x77, 0x64, 0x6d, 0x70, 0x6a, 0x6c, 0x73, 0x78,
+ 0x68, 0x76, 0x6e, 0x6d, 0x6c, 0x70, 0x6f, 0x65, 0x64, 0x74, 0x61, 0x61, 0x73, 0x66, 0x76, 0x6a,
+ 0x62, 0x71, 0x6f, 0x6f, 0x73, 0x68, 0x79, 0x6b, 0x6b, 0x70, 0x73, 0x62, 0x75, 0x6a, 0x74, 0x73,
+ 0x75, 0x65, 0x6b, 0x6b, 0x6e, 0x74, 0x75, 0x75, 0x70, 0x6b, 0x70, 0x78, 0x77, 0x64, 0x65, 0x75,
+ 0x6f, 0x75, 0x73, 0x64, 0x66, 0x72, 0x67, 0x65, 0x77, 0x68, 0x66, 0x6b, 0x62, 0x6d, 0x62, 0x67,
+ 0x6d, 0x70, 0x66, 0x67, 0x62, 0x71, 0x68, 0x6f, 0x78, 0x68, 0x76, 0x67, 0x6d, 0x61, 0x63, 0x67,
+ 0x6b, 0x75, 0x65, 0x65, 0x67, 0x63, 0x78, 0x61, 0x6e, 0x78, 0x66, 0x71, 0x75, 0x6d, 0x66, 0x73,
+ 0x69, 0x75, 0x62, 0x78, 0x70, 0x70, 0x73, 0x75, 0x67, 0x67, 0x79, 0x63, 0x63, 0x62, 0x67, 0x72,
+ 0x69, 0x62, 0x6e, 0x78, 0x73, 0x77, 0x78, 0x79, 0x64, 0x70, 0x64, 0x66, 0x72, 0x66, 0x63, 0x6e,
+ 0x74, 0x70, 0x62, 0x77, 0x6e, 0x61, 0x77, 0x64, 0x79, 0x69, 0x74, 0x70, 0x63, 0x62, 0x6e, 0x6d,
+ 0x72, 0x71, 0x6c, 0x6b, 0x6f, 0x6d, 0x67, 0x69, 0x68, 0x6f, 0x79, 0x6a, 0x73, 0x67, 0x70, 0x65,
+ 0x62, 0x6a, 0x75, 0x6f, 0x67, 0x67, 0x68, 0x6d, 0x6f, 0x64, 0x64, 0x70, 0x69, 0x70, 0x64, 0x62,
+ 0x67, 0x76, 0x64, 0x62, 0x6e, 0x71, 0x63, 0x66, 0x65, 0x70, 0x68, 0x78, 0x6a, 0x66, 0x6f, 0x68,
+ 0x70, 0x78, 0x67, 0x70, 0x6e, 0x63, 0x6c, 0x67, 0x61, 0x62, 0x67, 0x69, 0x77, 0x73, 0x61, 0x77,
+ 0x6f, 0x6d, 0x6b, 0x6f, 0x67, 0x68, 0x66, 0x77, 0x6c, 0x71, 0x63, 0x72, 0x74, 0x79, 0x77, 0x65,
+ 0x66, 0x6d, 0x77, 0x77, 0x74, 0x71, 0x63, 0x78, 0x64, 0x69, 0x79, 0x74, 0x71, 0x74, 0x74, 0x71,
+ 0x75, 0x78, 0x62, 0x6b, 0x78, 0x6d, 0x6c, 0x70, 0x71, 0x71, 0x78, 0x70, 0x69, 0x75, 0x6b, 0x6b,
+ 0x63, 0x6e, 0x72, 0x68, 0x67, 0x6c, 0x71, 0x69, 0x6c, 0x75, 0x70, 0x6a, 0x61, 0x6d, 0x66, 0x76,
+ 0x73, 0x6a, 0x74, 0x65, 0x67, 0x66, 0x71, 0x6f, 0x68, 0x6f, 0x6e, 0x71, 0x78, 0x72, 0x6b, 0x75,
+ 0x79, 0x77, 0x6c, 0x66, 0x61, 0x6b, 0x75, 0x65, 0x6d, 0x62, 0x68, 0x6c, 0x70, 0x72, 0x73, 0x72,
+ 0x63, 0x64, 0x67, 0x69, 0x67, 0x72, 0x64, 0x6b, 0x61, 0x77, 0x62, 0x6f, 0x79, 0x6a, 0x68, 0x64,
+ 0x65, 0x6e, 0x76, 0x72, 0x68, 0x6e, 0x6e, 0x77, 0x62, 0x68, 0x71, 0x62, 0x66, 0x69, 0x73, 0x64,
+ 0x6f, 0x78, 0x6c, 0x6a, 0x77, 0x6b, 0x62, 0x76, 0x71, 0x70, 0x71, 0x70, 0x6e, 0x6e, 0x76, 0x68,
+ 0x6a, 0x6c, 0x6b, 0x63, 0x71, 0x79, 0x68, 0x6a, 0x78, 0x6e, 0x74, 0x66, 0x73, 0x65, 0x6d, 0x77,
+ 0x6a, 0x67, 0x79, 0x67, 0x64, 0x71, 0x79, 0x62, 0x75, 0x68, 0x65, 0x74, 0x6c, 0x6a, 0x72, 0x6b,
+ 0x79, 0x73, 0x71, 0x74, 0x6b, 0x63, 0x70, 0x70, 0x69, 0x65, 0x69, 0x76, 0x79, 0x69, 0x6b, 0x70,
+ 0x6a, 0x76, 0x6a, 0x65, 0x63, 0x73, 0x70, 0x69, 0x6a, 0x6c, 0x71, 0x63, 0x72, 0x6a, 0x73, 0x79,
+ 0x62, 0x65, 0x69, 0x79, 0x63, 0x6c, 0x75, 0x65, 0x6d, 0x6e, 0x73, 0x63, 0x66, 0x71, 0x75, 0x61,
+ 0x72, 0x79, 0x75, 0x6f, 0x61, 0x61, 0x6c, 0x68, 0x6e, 0x77, 0x66, 0x74, 0x6f, 0x70, 0x75, 0x65,
+ 0x66, 0x66, 0x65, 0x78, 0x72, 0x62, 0x67, 0x6d, 0x70, 0x69, 0x64, 0x65, 0x68, 0x63, 0x67, 0x72,
+ 0x79, 0x6c, 0x67, 0x64, 0x6d, 0x68, 0x70, 0x6a, 0x73, 0x6b, 0x62, 0x6e, 0x62, 0x66, 0x76, 0x77,
+ 0x69, 0x6a, 0x6e, 0x61, 0x74, 0x6d, 0x73, 0x72, 0x6f, 0x79, 0x6b, 0x68, 0x71, 0x62, 0x79, 0x6e,
+ 0x62, 0x6b, 0x67, 0x79, 0x77, 0x6e, 0x64, 0x6d, 0x66, 0x64, 0x73, 0x6f, 0x77, 0x70, 0x65, 0x6b,
+ 0x65, 0x71, 0x65, 0x6d, 0x69, 0x67, 0x6c, 0x75, 0x72, 0x66, 0x77, 0x61, 0x76, 0x6e, 0x69, 0x74,
+ 0x61, 0x72, 0x74, 0x62, 0x68, 0x67, 0x72, 0x6a, 0x65, 0x77, 0x6e, 0x75, 0x75, 0x79, 0x6a, 0x6d,
+ 0x6f, 0x75, 0x65, 0x6c, 0x6f, 0x6a, 0x71, 0x74, 0x74, 0x6f, 0x6e, 0x71, 0x71, 0x79, 0x62, 0x74,
+ 0x77, 0x6a, 0x61, 0x75, 0x74, 0x74, 0x6d, 0x61, 0x6f, 0x77, 0x77, 0x76, 0x71, 0x64, 0x79, 0x73,
+ 0x61, 0x69, 0x66, 0x6a, 0x61, 0x61, 0x64, 0x62, 0x74, 0x77, 0x6a, 0x6d, 0x6e, 0x6f, 0x68, 0x78,
+ 0x75, 0x6d, 0x6c, 0x70, 0x77, 0x74, 0x71, 0x78, 0x77, 0x75, 0x6c, 0x62, 0x72, 0x73, 0x63, 0x6c,
+ 0x74, 0x63, 0x64, 0x64, 0x73, 0x68, 0x74, 0x64, 0x6b, 0x63, 0x72, 0x76, 0x6d, 0x68, 0x74, 0x6c,
+ 0x6a, 0x79, 0x70, 0x68, 0x6b, 0x6c, 0x68, 0x61, 0x77, 0x69, 0x71, 0x64, 0x66, 0x64, 0x61, 0x76,
+ 0x61, 0x6a, 0x68, 0x75, 0x73, 0x6e, 0x70, 0x68, 0x77, 0x69, 0x67, 0x6d, 0x67, 0x6e, 0x73, 0x79,
+ 0x6a, 0x70, 0x77, 0x65, 0x6f, 0x68, 0x67, 0x6a, 0x66, 0x64, 0x6c, 0x6e, 0x6e, 0x72, 0x67, 0x70,
+ 0x76, 0x78, 0x68, 0x6d, 0x69, 0x66, 0x76, 0x77, 0x77, 0x66, 0x63, 0x74, 0x74, 0x68, 0x62, 0x65,
+ 0x74, 0x65, 0x74, 0x65, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x64, 0x6a, 0x72, 0x75, 0x72, 0x6c, 0x79,
+ 0x61, 0x77, 0x66, 0x62, 0x71, 0x63, 0x71, 0x66, 0x6f, 0x65, 0x6d, 0x62, 0x61, 0x66, 0x73, 0x71,
+ 0x77, 0x75, 0x62, 0x75, 0x63, 0x75, 0x68, 0x6a, 0x79, 0x68, 0x68, 0x77, 0x64, 0x79, 0x67, 0x67,
+ 0x64, 0x76, 0x69, 0x78, 0x6b, 0x6b, 0x64, 0x61, 0x64, 0x61, 0x76, 0x64, 0x6f, 0x75, 0x71, 0x6b,
+ 0x72, 0x70, 0x72, 0x6b, 0x66, 0x6c, 0x68, 0x73, 0x66, 0x62, 0x73, 0x75, 0x66, 0x79, 0x79, 0x71,
+ 0x69, 0x73, 0x6b, 0x70, 0x65, 0x67, 0x79, 0x66, 0x65, 0x69, 0x65, 0x6a, 0x64, 0x73, 0x79, 0x74,
+ 0x65, 0x64, 0x76, 0x77, 0x68, 0x74, 0x72, 0x79, 0x68, 0x62, 0x66, 0x74, 0x67, 0x78, 0x70, 0x6d,
+ 0x68, 0x70, 0x6d, 0x6b, 0x73, 0x74, 0x75, 0x76, 0x64, 0x6f, 0x6f, 0x79, 0x76, 0x68, 0x67, 0x6c,
+ 0x78, 0x72, 0x64, 0x67, 0x63, 0x65, 0x70, 0x6f, 0x61, 0x77, 0x62, 0x6b, 0x61, 0x64, 0x77, 0x6c,
+ 0x70, 0x74, 0x64, 0x70, 0x6d, 0x6e, 0x71, 0x68, 0x79, 0x73, 0x72, 0x6c, 0x72, 0x70, 0x77, 0x62,
+ 0x68, 0x61, 0x6d, 0x68, 0x6a, 0x73, 0x75, 0x6e, 0x74, 0x61, 0x61, 0x6f, 0x75, 0x67, 0x79, 0x77,
+ 0x67, 0x6d, 0x61, 0x70, 0x64, 0x76, 0x69, 0x67, 0x70, 0x79, 0x6f, 0x78, 0x70, 0x66, 0x76, 0x6a,
+ 0x6e, 0x69, 0x63, 0x66, 0x73, 0x69, 0x78, 0x6e, 0x66, 0x64, 0x62, 0x62, 0x66, 0x75, 0x61, 0x61,
+ 0x6c, 0x63, 0x79, 0x6b, 0x63, 0x6c, 0x6e, 0x64, 0x6e, 0x6a, 0x68, 0x66, 0x64, 0x63, 0x61, 0x75,
+ 0x79, 0x65, 0x67, 0x6b, 0x6a, 0x76, 0x75, 0x74, 0x6c, 0x63, 0x78, 0x63, 0x78, 0x61, 0x70, 0x66,
+ 0x64, 0x76, 0x71, 0x73, 0x73, 0x6f, 0x65, 0x64, 0x6f, 0x73, 0x75, 0x79, 0x72, 0x70, 0x65, 0x6f,
+ 0x65, 0x64, 0x70, 0x62, 0x6f, 0x73, 0x69, 0x72, 0x62, 0x76, 0x67, 0x67, 0x62, 0x77, 0x70, 0x67,
+ 0x6a, 0x78, 0x65, 0x70, 0x64, 0x73, 0x6d, 0x79, 0x77, 0x71, 0x68, 0x6b, 0x79, 0x76, 0x69, 0x65,
+ 0x75, 0x66, 0x75, 0x76, 0x6b, 0x6b, 0x71, 0x6d, 0x6b, 0x6e, 0x67, 0x6b, 0x6f, 0x71, 0x77, 0x6f,
+ 0x61, 0x70, 0x61, 0x62, 0x64, 0x75, 0x63, 0x6c, 0x77, 0x65, 0x6a, 0x77, 0x62, 0x69, 0x64, 0x6a,
+ 0x71, 0x77, 0x65, 0x66, 0x6c, 0x6f, 0x67, 0x6c, 0x62, 0x74, 0x79, 0x69, 0x62, 0x79, 0x6c, 0x6f,
+ 0x71, 0x6e, 0x6c, 0x68, 0x66, 0x6e, 0x66, 0x76, 0x62, 0x6a, 0x75, 0x73, 0x6c, 0x67, 0x68, 0x73,
+ 0x77, 0x73, 0x76, 0x77, 0x6f, 0x79, 0x74, 0x6c, 0x75, 0x71, 0x78, 0x6e, 0x63, 0x62, 0x72, 0x64,
+ 0x74, 0x74, 0x73, 0x70, 0x68, 0x66, 0x76, 0x75, 0x77, 0x6e, 0x6b, 0x76, 0x73, 0x73, 0x70, 0x63,
+ 0x6a, 0x78, 0x77, 0x77, 0x70, 0x6e, 0x63, 0x6c, 0x77, 0x78, 0x6e, 0x72, 0x6a, 0x6d, 0x62, 0x6e,
+ 0x6d, 0x75, 0x72, 0x63, 0x64, 0x78, 0x62, 0x66, 0x71, 0x67, 0x72, 0x6b, 0x62, 0x73, 0x75, 0x70,
+ 0x74, 0x6d, 0x67, 0x70, 0x63, 0x69, 0x69, 0x76, 0x68, 0x74, 0x70, 0x62, 0x75, 0x6a, 0x72, 0x75,
+ 0x79, 0x6e, 0x77, 0x68, 0x63, 0x65, 0x71, 0x68, 0x66, 0x66, 0x79, 0x6b, 0x6b, 0x6b, 0x62, 0x74,
+ 0x68, 0x63, 0x69, 0x6d, 0x6f, 0x71, 0x78, 0x66, 0x6e, 0x64, 0x70, 0x78, 0x6e, 0x67, 0x66, 0x62,
+ 0x66, 0x6f, 0x79, 0x6d, 0x6a, 0x77, 0x6e, 0x6b, 0x78, 0x64, 0x65, 0x64, 0x6e, 0x64, 0x68, 0x73,
+ 0x75, 0x72, 0x70, 0x71, 0x67, 0x63, 0x76, 0x61, 0x6a, 0x61, 0x66, 0x6f, 0x79, 0x74, 0x68, 0x6a,
+ 0x70, 0x77, 0x75, 0x70, 0x69, 0x76, 0x70, 0x72, 0x76, 0x61, 0x78, 0x6b, 0x62, 0x69, 0x68, 0x77,
+ 0x6c, 0x6a, 0x78, 0x65, 0x71, 0x66, 0x61, 0x6a, 0x74, 0x72, 0x66, 0x72, 0x6e, 0x6c, 0x74, 0x73,
+ 0x69, 0x68, 0x6f, 0x75, 0x71, 0x6b, 0x6b, 0x69, 0x70, 0x71, 0x73, 0x74, 0x61, 0x71, 0x69, 0x77,
+ 0x64, 0x6d, 0x6c, 0x77, 0x6f, 0x76, 0x76, 0x6f, 0x62, 0x63, 0x74, 0x73, 0x73, 0x74, 0x62, 0x67,
+ 0x6f, 0x71, 0x6f, 0x70, 0x73, 0x65, 0x6a, 0x71, 0x75, 0x62, 0x63, 0x79, 0x65, 0x61, 0x64, 0x75,
+ 0x79, 0x69, 0x74, 0x64, 0x6a, 0x61, 0x6a, 0x77, 0x63, 0x6c, 0x67, 0x69, 0x6e, 0x62, 0x73, 0x68,
+ 0x65, 0x64, 0x72, 0x62, 0x6f, 0x68, 0x73, 0x66, 0x6b, 0x76, 0x79, 0x6a, 0x73, 0x6b, 0x70, 0x69,
+ 0x62, 0x62, 0x71, 0x76, 0x6f, 0x6d, 0x6e, 0x6f, 0x71, 0x66, 0x6f, 0x70, 0x75, 0x73, 0x67, 0x72,
+ 0x64, 0x78, 0x6a, 0x79, 0x76, 0x68, 0x73, 0x65, 0x65, 0x71, 0x6f, 0x66, 0x6e, 0x61, 0x62, 0x72,
+ 0x70, 0x62, 0x65, 0x6b, 0x66, 0x73, 0x73, 0x76, 0x6e, 0x69, 0x79, 0x66, 0x69, 0x6f, 0x73, 0x6e,
+ 0x77, 0x72, 0x79, 0x65, 0x69, 0x73, 0x78, 0x61, 0x68, 0x6e, 0x6c, 0x79, 0x70, 0x64, 0x75, 0x6b,
+ 0x70, 0x73, 0x77, 0x75, 0x65, 0x79, 0x77, 0x73, 0x61, 0x72, 0x6b, 0x69, 0x79, 0x74, 0x63, 0x6a,
+ 0x64, 0x68, 0x63, 0x69, 0x61, 0x6a, 0x76, 0x74, 0x63, 0x6a, 0x79, 0x75, 0x6d, 0x70, 0x67, 0x6d,
+ 0x64, 0x64, 0x78, 0x61, 0x6d, 0x6d, 0x72, 0x78, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x65, 0x63, 0x72,
+ 0x74, 0x70, 0x70, 0x71, 0x6a, 0x72, 0x67, 0x69, 0x75, 0x6e, 0x6c, 0x61, 0x74, 0x66, 0x79, 0x63,
+ 0x66, 0x67, 0x75, 0x75, 0x79, 0x75, 0x79, 0x63, 0x72, 0x6c, 0x6f, 0x73, 0x77, 0x71, 0x78, 0x74,
+ 0x72, 0x75, 0x6f, 0x6b, 0x71, 0x67, 0x65, 0x63, 0x77, 0x78, 0x6b, 0x6c, 0x69, 0x6d, 0x6a, 0x78,
+ 0x63, 0x6c, 0x69, 0x6d, 0x6a, 0x6f, 0x61, 0x62, 0x64, 0x67, 0x61, 0x6b, 0x6b, 0x6d, 0x71, 0x61,
+ 0x64, 0x6b, 0x6f, 0x6d, 0x72, 0x65, 0x6d, 0x66, 0x65, 0x77, 0x78, 0x78, 0x6c, 0x63, 0x65, 0x76,
+ 0x71, 0x67, 0x73, 0x61, 0x69, 0x68, 0x71, 0x76, 0x6c, 0x72, 0x72, 0x63, 0x69, 0x6b, 0x78, 0x6a,
+ 0x6e, 0x79, 0x62, 0x64, 0x6c, 0x63, 0x6c, 0x72, 0x62, 0x6f, 0x72, 0x68, 0x79, 0x62, 0x70, 0x67,
+ 0x64, 0x73, 0x6e, 0x67, 0x6c, 0x69, 0x77, 0x76, 0x68, 0x70, 0x6f, 0x66, 0x63, 0x67, 0x72, 0x72,
+ 0x6c, 0x69, 0x61, 0x75, 0x61, 0x62, 0x6e, 0x75, 0x69, 0x61, 0x77, 0x77, 0x65, 0x6a, 0x6b, 0x74,
+ 0x61, 0x61, 0x6d, 0x6f, 0x77, 0x79, 0x72, 0x78, 0x73, 0x77, 0x6b, 0x63, 0x6d, 0x6b, 0x66, 0x71,
+ 0x78, 0x64, 0x64, 0x76, 0x79, 0x6f, 0x63, 0x62, 0x74, 0x69, 0x65, 0x6f, 0x65, 0x62, 0x74, 0x6f,
+ 0x74, 0x77, 0x78, 0x64, 0x64, 0x63, 0x61, 0x65, 0x76, 0x70, 0x63, 0x71, 0x62, 0x66, 0x66, 0x75,
+ 0x76, 0x78, 0x63, 0x65, 0x68, 0x6d, 0x6e, 0x61, 0x72, 0x74, 0x67, 0x77, 0x71, 0x71, 0x67, 0x66,
+ 0x6c, 0x67, 0x73, 0x65, 0x76, 0x72, 0x74, 0x67, 0x68, 0x75, 0x76, 0x70, 0x66, 0x68, 0x78, 0x6e,
+ 0x6d, 0x62, 0x6b, 0x77, 0x6c, 0x61, 0x65, 0x6a, 0x6d, 0x6c, 0x6f, 0x6c, 0x66, 0x6f, 0x76, 0x73,
+ 0x76, 0x6e, 0x72, 0x63, 0x62, 0x63, 0x72, 0x74, 0x6f, 0x63, 0x6a, 0x72, 0x6b, 0x63, 0x6d, 0x6d,
+ 0x62, 0x78, 0x72, 0x71, 0x71, 0x70, 0x6a, 0x6d, 0x65, 0x6e, 0x6e, 0x79, 0x74, 0x66, 0x74, 0x64,
+ 0x73, 0x64, 0x73, 0x74, 0x79, 0x66, 0x77, 0x61, 0x6a, 0x71, 0x68, 0x72, 0x73, 0x73, 0x77, 0x71,
+ 0x67, 0x61, 0x6e, 0x68, 0x75, 0x69, 0x61, 0x66, 0x67, 0x73, 0x73, 0x6e, 0x6b, 0x70, 0x61, 0x69,
+ 0x61, 0x63, 0x6e, 0x67, 0x62, 0x78, 0x6e, 0x6f, 0x74, 0x67, 0x61, 0x74, 0x68, 0x74, 0x63, 0x68,
+ 0x64, 0x68, 0x6a, 0x63, 0x79, 0x65, 0x78, 0x6c, 0x78, 0x6a, 0x62, 0x73, 0x71, 0x65, 0x65, 0x66,
+ 0x6a, 0x62, 0x75, 0x6b, 0x70, 0x66, 0x68, 0x73, 0x72, 0x6d, 0x71, 0x79, 0x6e, 0x71, 0x6f, 0x65,
+ 0x66, 0x6b, 0x72, 0x78, 0x75, 0x6c, 0x74, 0x6a, 0x62, 0x62, 0x74, 0x78, 0x61, 0x62, 0x75, 0x65,
+ 0x6d, 0x67, 0x64, 0x67, 0x78, 0x65, 0x68, 0x6e, 0x75, 0x6d, 0x78, 0x77, 0x61, 0x6a, 0x79, 0x72,
+ 0x63, 0x6a, 0x6b, 0x66, 0x6b, 0x75, 0x69, 0x73, 0x64, 0x73, 0x65, 0x64, 0x6a, 0x6c, 0x62, 0x6e,
+ 0x66, 0x6f, 0x63, 0x78, 0x67, 0x63, 0x76, 0x76, 0x6f, 0x78, 0x61, 0x67, 0x77, 0x73, 0x72, 0x77,
+ 0x63, 0x6e, 0x62, 0x79, 0x79, 0x73, 0x6c, 0x65, 0x66, 0x75, 0x6d, 0x68, 0x72, 0x61, 0x70, 0x6f,
+ 0x76, 0x6f, 0x68, 0x62, 0x65, 0x6f, 0x70, 0x6b, 0x6c, 0x65, 0x69, 0x66, 0x73, 0x71, 0x66, 0x71,
+ 0x74, 0x78, 0x76, 0x65, 0x75, 0x66, 0x78, 0x6b, 0x63, 0x78, 0x70, 0x6b, 0x74, 0x67, 0x78, 0x75,
+ 0x65, 0x6c, 0x6c, 0x75, 0x75, 0x75, 0x6b, 0x68, 0x6f, 0x63, 0x64, 0x64, 0x67, 0x61, 0x63, 0x61,
+ 0x6d, 0x65, 0x79, 0x76, 0x69, 0x70, 0x74, 0x71, 0x62, 0x68, 0x67, 0x65, 0x6f, 0x77, 0x74, 0x63,
+ 0x64, 0x65, 0x67, 0x78, 0x62, 0x66, 0x65, 0x6d, 0x6a, 0x6c, 0x62, 0x6c, 0x6a, 0x75, 0x61, 0x67,
+ 0x71, 0x6e, 0x6e, 0x70, 0x72, 0x6c, 0x78, 0x77, 0x69, 0x6d, 0x66, 0x69, 0x6e, 0x6b, 0x63, 0x76,
+ 0x72, 0x70, 0x67, 0x79, 0x64, 0x6e, 0x68, 0x73, 0x75, 0x73, 0x6d, 0x62, 0x6b, 0x6d, 0x69, 0x70,
+ 0x6b, 0x6f, 0x65, 0x6f, 0x78, 0x6d, 0x69, 0x79, 0x63, 0x71, 0x68, 0x72, 0x75, 0x6d, 0x67, 0x71,
+ 0x6d, 0x76, 0x6f, 0x6f, 0x69, 0x62, 0x78, 0x64, 0x69, 0x6f, 0x65, 0x66, 0x66, 0x63, 0x62, 0x6c,
+ 0x6b, 0x74, 0x79, 0x6f, 0x6f, 0x79, 0x70, 0x73, 0x79, 0x75, 0x67, 0x72, 0x79, 0x72, 0x63, 0x73,
+ 0x6c, 0x64, 0x72, 0x6f, 0x62, 0x6b, 0x62, 0x6f, 0x77, 0x76, 0x6c, 0x62, 0x6c, 0x70, 0x66, 0x67,
+ 0x6f, 0x63, 0x79, 0x66, 0x69, 0x67, 0x65, 0x79, 0x6e, 0x64, 0x6e, 0x69, 0x6d, 0x71, 0x67, 0x6f,
+ 0x69, 0x74, 0x67, 0x65, 0x72, 0x63, 0x6f, 0x64, 0x68, 0x73, 0x79, 0x79, 0x6d, 0x6d, 0x69, 0x67,
+ 0x77, 0x6d, 0x64, 0x70, 0x74, 0x6c, 0x75, 0x79, 0x74, 0x62, 0x62, 0x6a, 0x63, 0x65, 0x61, 0x79,
+ 0x62, 0x65, 0x68, 0x65, 0x6b, 0x73, 0x78, 0x73, 0x68, 0x6a, 0x79, 0x71, 0x71, 0x6e, 0x62, 0x61,
+ 0x6b, 0x73, 0x6a, 0x65, 0x6b, 0x74, 0x68, 0x74, 0x66, 0x6b, 0x6f, 0x76, 0x73, 0x6e, 0x65, 0x6d,
+ 0x6e, 0x78, 0x72, 0x69, 0x69, 0x76, 0x6e, 0x72, 0x71, 0x61, 0x61, 0x6c, 0x73, 0x63, 0x74, 0x6e,
+ 0x64, 0x71, 0x6d, 0x6b, 0x62, 0x62, 0x63, 0x6c, 0x61, 0x61, 0x65, 0x78, 0x64, 0x71, 0x78, 0x68,
+ 0x6b, 0x76, 0x72, 0x79, 0x70, 0x65, 0x6e, 0x6a, 0x65, 0x77, 0x63, 0x61, 0x64, 0x66, 0x71, 0x61,
+ 0x76, 0x72, 0x75, 0x68, 0x69, 0x63, 0x75, 0x71, 0x71, 0x6f, 0x75, 0x77, 0x76, 0x61, 0x6b, 0x73,
+ 0x6e, 0x77, 0x70, 0x71, 0x64, 0x62, 0x61, 0x76, 0x65, 0x65, 0x6f, 0x71, 0x76, 0x77, 0x72, 0x73,
+ 0x64, 0x73, 0x61, 0x74, 0x75, 0x75, 0x6c, 0x6f, 0x74, 0x64, 0x6d, 0x6c, 0x6a, 0x72, 0x66, 0x68,
+ 0x6f, 0x75, 0x63, 0x71, 0x73, 0x73, 0x6e, 0x6b, 0x75, 0x67, 0x74, 0x6e, 0x6b, 0x6f, 0x73, 0x73,
+ 0x6a, 0x61, 0x70, 0x66, 0x66, 0x61, 0x68, 0x70, 0x76, 0x6c, 0x70, 0x64, 0x72, 0x63, 0x61, 0x6e,
+ 0x77, 0x74, 0x73, 0x6d, 0x73, 0x67, 0x6e, 0x63, 0x61, 0x73, 0x63, 0x73, 0x6d, 0x76, 0x6a, 0x6d,
+ 0x6e, 0x6d, 0x6a, 0x66, 0x71, 0x6e, 0x66, 0x6c, 0x66, 0x72, 0x77, 0x6a, 0x79, 0x67, 0x67, 0x62,
+ 0x69, 0x72, 0x71, 0x66, 0x66, 0x69, 0x61, 0x6d, 0x75, 0x77, 0x66, 0x74, 0x6e, 0x6d, 0x67, 0x75,
+ 0x74, 0x63, 0x65, 0x75, 0x6d, 0x6f, 0x73, 0x61, 0x74, 0x6a, 0x66, 0x64, 0x70, 0x69, 0x61, 0x6e,
+ 0x61, 0x64, 0x77, 0x75, 0x63, 0x76, 0x6c, 0x66, 0x79, 0x77, 0x74, 0x79, 0x64, 0x6f, 0x65, 0x66,
+ 0x62, 0x71, 0x6a, 0x6c, 0x6a, 0x67, 0x69, 0x68, 0x61, 0x66, 0x75, 0x78, 0x73, 0x65, 0x6c, 0x61,
+ 0x6b, 0x74, 0x68, 0x75, 0x70, 0x70, 0x6a, 0x67, 0x73, 0x71, 0x63, 0x68, 0x65, 0x64, 0x77, 0x66,
+ 0x66, 0x6a, 0x71, 0x77, 0x72, 0x62, 0x73, 0x6d, 0x62, 0x73, 0x65, 0x6c, 0x75, 0x6f, 0x67, 0x6a,
+ 0x61, 0x79, 0x6b, 0x70, 0x61, 0x71, 0x61, 0x64, 0x6f, 0x63, 0x6a, 0x75, 0x63, 0x6e, 0x79, 0x78,
+ 0x71, 0x69, 0x73, 0x68, 0x6a, 0x6b, 0x66, 0x6f, 0x68, 0x66, 0x62, 0x65, 0x75, 0x70, 0x70, 0x68,
+ 0x61, 0x68, 0x76, 0x78, 0x6d, 0x64, 0x6c, 0x64, 0x63, 0x73, 0x72, 0x76, 0x65, 0x62, 0x66, 0x6b,
+ 0x66, 0x78, 0x62, 0x75, 0x6b, 0x69, 0x77, 0x76, 0x64, 0x77, 0x71, 0x72, 0x71, 0x6d, 0x78, 0x6a,
+ 0x6b, 0x73, 0x79, 0x64, 0x6b, 0x6a, 0x76, 0x73, 0x76, 0x6c, 0x78, 0x6d, 0x6c, 0x6a, 0x78, 0x79,
+ 0x75, 0x76, 0x75, 0x79, 0x67, 0x77, 0x6d, 0x76, 0x72, 0x79, 0x6a, 0x62, 0x6f, 0x78, 0x69, 0x73,
+ 0x73, 0x61, 0x70, 0x66, 0x67, 0x79, 0x61, 0x67, 0x62, 0x6d, 0x65, 0x6a, 0x72, 0x77, 0x66, 0x65,
+ 0x62, 0x63, 0x71, 0x6d, 0x6d, 0x62, 0x62, 0x6d, 0x75, 0x6a, 0x6e, 0x64, 0x79, 0x70, 0x67, 0x68,
+ 0x70, 0x65, 0x71, 0x77, 0x75, 0x77, 0x6e, 0x67, 0x6e, 0x61, 0x6c, 0x6e, 0x64, 0x64, 0x70, 0x70,
+ 0x6f, 0x6a, 0x62, 0x70, 0x62, 0x69, 0x6d, 0x62, 0x70, 0x6b, 0x63, 0x6c, 0x68, 0x79, 0x6b, 0x68,
+ 0x71, 0x6e, 0x71, 0x63, 0x6e, 0x62, 0x6c, 0x71, 0x76, 0x6a, 0x6b, 0x67, 0x66, 0x6a, 0x73, 0x79,
+ 0x6a, 0x73, 0x67, 0x77, 0x6c, 0x61, 0x6c, 0x6c, 0x6a, 0x63, 0x76, 0x6b, 0x64, 0x78, 0x61, 0x70,
+ 0x63, 0x6d, 0x6a, 0x75, 0x6f, 0x74, 0x65, 0x63, 0x6b, 0x68, 0x67, 0x73, 0x73, 0x63, 0x71, 0x63,
+ 0x73, 0x68, 0x68, 0x74, 0x76, 0x6e, 0x6e, 0x6c, 0x66, 0x61, 0x68, 0x71, 0x6c, 0x6e, 0x76, 0x79,
+ 0x77, 0x76, 0x61, 0x66, 0x69, 0x6d, 0x65, 0x6a, 0x70, 0x6a, 0x79, 0x79, 0x6a, 0x74, 0x65, 0x65,
+ 0x66, 0x6b, 0x77, 0x77, 0x68, 0x72, 0x62, 0x79, 0x6d, 0x78, 0x71, 0x67, 0x78, 0x74, 0x63, 0x79,
+ 0x74, 0x65, 0x69, 0x64, 0x64, 0x71, 0x68, 0x74, 0x65, 0x6d, 0x6a, 0x64, 0x78, 0x6c, 0x61, 0x76,
+ 0x73, 0x6c, 0x75, 0x73, 0x63, 0x64, 0x68, 0x76, 0x67, 0x6e, 0x78, 0x78, 0x68, 0x75, 0x69, 0x65,
+ 0x6a, 0x65, 0x75, 0x78, 0x77, 0x69, 0x71, 0x6a, 0x70, 0x6a, 0x70, 0x6b, 0x70, 0x72, 0x72, 0x63,
+ 0x64, 0x74, 0x79, 0x65, 0x63, 0x68, 0x79, 0x62, 0x63, 0x61, 0x79, 0x6e, 0x77, 0x79, 0x61, 0x65,
+ 0x66, 0x71, 0x6e, 0x79, 0x78, 0x69, 0x73, 0x6c, 0x67, 0x6f, 0x66, 0x75, 0x63, 0x63, 0x65, 0x78,
+ 0x66, 0x6a, 0x70, 0x67, 0x62, 0x63, 0x69, 0x77, 0x63, 0x76, 0x6d, 0x79, 0x68, 0x71, 0x77, 0x74,
+ 0x6f, 0x6c, 0x6b, 0x71, 0x6c, 0x67, 0x6e, 0x70, 0x71, 0x78, 0x66, 0x69, 0x73, 0x6e, 0x72, 0x69,
+ 0x77, 0x71, 0x65, 0x6f, 0x6f, 0x6b, 0x69, 0x66, 0x6e, 0x61, 0x6d, 0x73, 0x61, 0x70, 0x70, 0x6b,
+ 0x67, 0x76, 0x72, 0x66, 0x64, 0x71, 0x65, 0x71, 0x64, 0x61, 0x79, 0x64, 0x6b, 0x6a, 0x6c, 0x6c,
+ 0x69, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x6e, 0x74, 0x79, 0x63, 0x63, 0x6d, 0x65, 0x6b, 0x68, 0x6c,
+ 0x71, 0x76, 0x62, 0x78, 0x63, 0x77, 0x75, 0x69, 0x79, 0x6f, 0x76, 0x66, 0x77, 0x6e, 0x6c, 0x78,
+ 0x6e, 0x78, 0x73, 0x6a, 0x74, 0x62, 0x68, 0x66, 0x65, 0x68, 0x61, 0x6d, 0x77, 0x72, 0x69, 0x6f,
+ 0x6b, 0x6f, 0x65, 0x71, 0x6b, 0x79, 0x74, 0x77, 0x61, 0x74, 0x69, 0x74, 0x6a, 0x75, 0x70, 0x76,
+ 0x6a, 0x77, 0x70, 0x73, 0x77, 0x72, 0x75, 0x71, 0x76, 0x68, 0x73, 0x73, 0x6b, 0x6c, 0x63, 0x69,
+ 0x6b, 0x75, 0x67, 0x74, 0x6e, 0x79, 0x76, 0x67, 0x71, 0x78, 0x64, 0x6a, 0x79, 0x68, 0x6e, 0x65,
+ 0x65, 0x62, 0x76, 0x6f, 0x6d, 0x75, 0x74, 0x73, 0x6a, 0x68, 0x71, 0x67, 0x6b, 0x64, 0x62, 0x74,
+ 0x64, 0x62, 0x77, 0x65, 0x69, 0x61, 0x78, 0x68, 0x72, 0x70, 0x6d, 0x69, 0x6b, 0x70, 0x6e, 0x62,
+ 0x69, 0x74, 0x62, 0x6b, 0x6d, 0x64, 0x78, 0x70, 0x76, 0x74, 0x6a, 0x6a, 0x63, 0x65, 0x77, 0x78,
+ 0x72, 0x71, 0x63, 0x6d, 0x6c, 0x69, 0x63, 0x68, 0x71, 0x62, 0x62, 0x6f, 0x79, 0x75, 0x75, 0x73,
+ 0x71, 0x6c, 0x77, 0x6d, 0x62, 0x75, 0x67, 0x66, 0x61, 0x6e, 0x73, 0x73, 0x62, 0x67, 0x63, 0x72,
+ 0x66, 0x6e, 0x63, 0x71, 0x78, 0x6d, 0x68, 0x67, 0x79, 0x79, 0x76, 0x74, 0x6d, 0x74, 0x74, 0x73,
+ 0x79, 0x76, 0x6d, 0x79, 0x71, 0x72, 0x70, 0x65, 0x69, 0x66, 0x73, 0x74, 0x77, 0x6d, 0x74, 0x6e,
+ 0x6f, 0x72, 0x71, 0x62, 0x67, 0x74, 0x62, 0x77, 0x6f, 0x6b, 0x6c, 0x66, 0x65, 0x63, 0x75, 0x6e,
+ 0x66, 0x6b, 0x75, 0x69, 0x61, 0x79, 0x6c, 0x74, 0x77, 0x6b, 0x73, 0x78, 0x70, 0x6f, 0x69, 0x72,
+ 0x72, 0x74, 0x69, 0x6f, 0x62, 0x66, 0x61, 0x6c, 0x72, 0x6b, 0x63, 0x6c, 0x73, 0x67, 0x6e, 0x6b,
+ 0x74, 0x70, 0x70, 0x73, 0x6d, 0x6a, 0x73, 0x67, 0x71, 0x70, 0x77, 0x71, 0x69, 0x6a, 0x79, 0x71,
+ 0x75, 0x70, 0x67, 0x62, 0x72, 0x61, 0x79, 0x64, 0x73, 0x73, 0x66, 0x78, 0x64, 0x76, 0x69, 0x79,
+ 0x6f, 0x62, 0x6a, 0x6d, 0x76, 0x69, 0x66, 0x6b, 0x69, 0x6c, 0x6a, 0x69, 0x75, 0x71, 0x66, 0x77,
+ 0x69, 0x75, 0x76, 0x6b, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x6b, 0x79, 0x74, 0x61, 0x66, 0x64, 0x64,
+ 0x65, 0x66, 0x73, 0x64, 0x76, 0x74, 0x6f, 0x63, 0x6c, 0x6c, 0x66, 0x75, 0x69, 0x6a, 0x79, 0x72,
+ 0x70, 0x64, 0x61, 0x74, 0x75, 0x74, 0x62, 0x61, 0x72, 0x6a, 0x73, 0x6f, 0x77, 0x65, 0x67, 0x71,
+ 0x64, 0x66, 0x69, 0x69, 0x63, 0x6a, 0x66, 0x73, 0x76, 0x70, 0x63, 0x69, 0x75, 0x64, 0x78, 0x62,
+ 0x79, 0x67, 0x79, 0x6b, 0x78, 0x6f, 0x63, 0x6d, 0x69, 0x61, 0x67, 0x6f, 0x77, 0x64, 0x70, 0x66,
+ 0x6b, 0x73, 0x76, 0x6c, 0x6a, 0x64, 0x69, 0x6f, 0x68, 0x75, 0x72, 0x67, 0x6c, 0x64, 0x73, 0x71,
+ 0x67, 0x6f, 0x69, 0x6c, 0x63, 0x6a, 0x62, 0x71, 0x70, 0x78, 0x73, 0x66, 0x66, 0x6e, 0x79, 0x62,
+ 0x6c, 0x77, 0x65, 0x6f, 0x6e, 0x70, 0x61, 0x76, 0x67, 0x77, 0x6d, 0x71, 0x73, 0x70, 0x75, 0x62,
+ 0x62, 0x68, 0x78, 0x6f, 0x62, 0x76, 0x61, 0x65, 0x61, 0x6a, 0x70, 0x65, 0x70, 0x70, 0x73, 0x75,
+ 0x62, 0x73, 0x61, 0x72, 0x64, 0x6c, 0x76, 0x6e, 0x6b, 0x74, 0x67, 0x6a, 0x6b, 0x66, 0x65, 0x6c,
+ 0x62, 0x65, 0x79, 0x6b, 0x69, 0x78, 0x61, 0x72, 0x73, 0x62, 0x6c, 0x70, 0x64, 0x78, 0x71, 0x78,
+ 0x68, 0x6c, 0x63, 0x72, 0x66, 0x78, 0x73, 0x79, 0x68, 0x73, 0x62, 0x68, 0x68, 0x6f, 0x67, 0x63,
+ 0x73, 0x6d, 0x6d, 0x75, 0x64, 0x63, 0x79, 0x74, 0x68, 0x74, 0x79, 0x69, 0x6b, 0x63, 0x63, 0x67,
+ 0x6a, 0x76, 0x75, 0x6b, 0x63, 0x77, 0x79, 0x64, 0x76, 0x74, 0x68, 0x6c, 0x76, 0x77, 0x68, 0x70,
+ 0x69, 0x6f, 0x73, 0x66, 0x76, 0x62, 0x76, 0x64, 0x73, 0x65, 0x64, 0x61, 0x70, 0x67, 0x6e, 0x6b,
+ 0x72, 0x70, 0x6e, 0x70, 0x79, 0x75, 0x6e, 0x6f, 0x6c, 0x67, 0x6d, 0x78, 0x61, 0x62, 0x6c, 0x6e,
+ 0x79, 0x75, 0x75, 0x66, 0x74, 0x65, 0x61, 0x74, 0x69, 0x6d, 0x78, 0x64, 0x63, 0x68, 0x70, 0x61,
+ 0x73, 0x67, 0x65, 0x6e, 0x70, 0x74, 0x67, 0x66, 0x69, 0x62, 0x68, 0x6d, 0x73, 0x64, 0x6f, 0x67,
+ 0x6c, 0x6a, 0x67, 0x71, 0x6c, 0x72, 0x6e, 0x6a, 0x6b, 0x6f, 0x73, 0x73, 0x70, 0x77, 0x66, 0x61,
+ 0x6b, 0x70, 0x65, 0x6e, 0x6e, 0x76, 0x6a, 0x6e, 0x63, 0x75, 0x77, 0x6f, 0x72, 0x62, 0x78, 0x6a,
+ 0x6a, 0x6e, 0x6e, 0x66, 0x69, 0x66, 0x68, 0x76, 0x61, 0x74, 0x6f, 0x76, 0x6c, 0x63, 0x68, 0x66,
+ 0x65, 0x72, 0x71, 0x65, 0x63, 0x69, 0x75, 0x73, 0x6b, 0x64, 0x6b, 0x79, 0x68, 0x62, 0x79, 0x65,
+ 0x74, 0x61, 0x70, 0x76, 0x79, 0x70, 0x74, 0x75, 0x79, 0x62, 0x6e, 0x6c, 0x70, 0x6d, 0x69, 0x72,
+ 0x77, 0x71, 0x6b, 0x63, 0x71, 0x61, 0x6c, 0x72, 0x67, 0x6d, 0x6b, 0x74, 0x6e, 0x71, 0x75, 0x66,
+ 0x6b, 0x6b, 0x65, 0x77, 0x65, 0x73, 0x72, 0x63, 0x67, 0x6b, 0x6a, 0x61, 0x69, 0x73, 0x61, 0x64,
+ 0x6d, 0x77, 0x74, 0x76, 0x65, 0x63, 0x61, 0x6e, 0x62, 0x68, 0x6e, 0x70, 0x62, 0x64, 0x6e, 0x79,
+ 0x69, 0x79, 0x68, 0x75, 0x6c, 0x79, 0x71, 0x78, 0x62, 0x64, 0x62, 0x78, 0x79, 0x66, 0x70, 0x65,
+ 0x70, 0x68, 0x67, 0x6d, 0x67, 0x6b, 0x62, 0x65, 0x63, 0x68, 0x67, 0x63, 0x6b, 0x78, 0x6a, 0x63,
+ 0x73, 0x63, 0x62, 0x6c, 0x61, 0x6b, 0x68, 0x68, 0x6e, 0x61, 0x70, 0x73, 0x69, 0x74, 0x63, 0x68,
+ 0x64, 0x66, 0x69, 0x62, 0x6e, 0x6c, 0x63, 0x72, 0x63, 0x62, 0x62, 0x74, 0x67, 0x67, 0x61, 0x62,
+ 0x67, 0x72, 0x75, 0x6a, 0x74, 0x78, 0x62, 0x66, 0x67, 0x63, 0x69, 0x73, 0x75, 0x68, 0x76, 0x6c,
+ 0x66, 0x65, 0x69, 0x66, 0x6e, 0x6e, 0x68, 0x72, 0x72, 0x65, 0x62, 0x71, 0x63, 0x72, 0x71, 0x69,
+ 0x63, 0x66, 0x74, 0x77, 0x65, 0x67, 0x6b, 0x70, 0x6c, 0x63, 0x6d, 0x68, 0x68, 0x71, 0x70, 0x72,
+ 0x76, 0x72, 0x77, 0x61, 0x72, 0x73, 0x75, 0x68, 0x6c, 0x73, 0x6f, 0x61, 0x6e, 0x76, 0x6e, 0x66,
+ 0x70, 0x61, 0x67, 0x6c, 0x6b, 0x61, 0x75, 0x67, 0x64, 0x76, 0x6d, 0x6f, 0x77, 0x78, 0x72, 0x70,
+ 0x6e, 0x6e, 0x6d, 0x77, 0x67, 0x71, 0x75, 0x78, 0x74, 0x71, 0x73, 0x76, 0x67, 0x61, 0x79, 0x70,
+ 0x6d, 0x71, 0x73, 0x76, 0x6c, 0x6f, 0x6d, 0x77, 0x65, 0x65, 0x76, 0x68, 0x66, 0x6a, 0x76, 0x72,
+ 0x76, 0x71, 0x64, 0x67, 0x74, 0x6d, 0x62, 0x6b, 0x64, 0x6d, 0x74, 0x78, 0x6f, 0x73, 0x62, 0x76,
+ 0x78, 0x79, 0x74, 0x6b, 0x75, 0x77, 0x6a, 0x77, 0x66, 0x65, 0x74, 0x74, 0x79, 0x6e, 0x64, 0x6d,
+ 0x6e, 0x67, 0x64, 0x62, 0x6b, 0x77, 0x72, 0x69, 0x61, 0x70, 0x67, 0x64, 0x64, 0x77, 0x6b, 0x63,
+ 0x70, 0x74, 0x67, 0x73, 0x69, 0x72, 0x79, 0x65, 0x64, 0x6e, 0x71, 0x68, 0x71, 0x79, 0x6e, 0x6e,
+ 0x63, 0x64, 0x6a, 0x70, 0x62, 0x73, 0x76, 0x72, 0x71, 0x71, 0x74, 0x67, 0x6a, 0x62, 0x70, 0x63,
+ 0x69, 0x61, 0x6f, 0x6d, 0x63, 0x67, 0x74, 0x74, 0x70, 0x72, 0x6c, 0x6d, 0x6a, 0x69, 0x64, 0x79,
+ 0x77, 0x6d, 0x77, 0x75, 0x6b, 0x68, 0x6e, 0x70, 0x71, 0x64, 0x66, 0x6f, 0x72, 0x78, 0x78, 0x69,
+ 0x6e, 0x75, 0x6b, 0x74, 0x65, 0x6c, 0x65, 0x79, 0x73, 0x73, 0x65, 0x66, 0x6f, 0x72, 0x75, 0x69,
+ 0x77, 0x61, 0x75, 0x74, 0x63, 0x68, 0x73, 0x6c, 0x73, 0x78, 0x6d, 0x6b, 0x66, 0x64, 0x75, 0x76,
+ 0x6f, 0x6f, 0x79, 0x78, 0x68, 0x6d, 0x6b, 0x6f, 0x6a, 0x72, 0x6e, 0x62, 0x63, 0x67, 0x79, 0x78,
+ 0x6b, 0x77, 0x75, 0x71, 0x69, 0x6e, 0x70, 0x73, 0x61, 0x68, 0x66, 0x63, 0x6a, 0x66, 0x75, 0x74,
+ 0x63, 0x6d, 0x72, 0x71, 0x64, 0x77, 0x77, 0x69, 0x68, 0x70, 0x79, 0x6c, 0x72, 0x62, 0x78, 0x69,
+ 0x6a, 0x61, 0x6d, 0x75, 0x6d, 0x67, 0x61, 0x78, 0x61, 0x6e, 0x73, 0x6b, 0x6c, 0x74, 0x67, 0x61,
+ 0x71, 0x68, 0x62, 0x6c, 0x62, 0x77, 0x75, 0x65, 0x6c, 0x76, 0x73, 0x64, 0x68, 0x73, 0x68, 0x79,
+ 0x78, 0x6a, 0x73, 0x6a, 0x72, 0x65, 0x77, 0x68, 0x64, 0x76, 0x75, 0x6d, 0x6d, 0x64, 0x75, 0x78,
+ 0x78, 0x76, 0x61, 0x63, 0x6f, 0x78, 0x69, 0x76, 0x61, 0x66, 0x62, 0x64, 0x72, 0x6c, 0x70, 0x79,
+ 0x6e, 0x63, 0x6a, 0x70, 0x6f, 0x73, 0x63, 0x76, 0x6d, 0x76, 0x77, 0x6f, 0x78, 0x63, 0x67, 0x6c,
+ 0x6b, 0x77, 0x71, 0x79, 0x70, 0x6a, 0x78, 0x71, 0x69, 0x69, 0x76, 0x6c, 0x6d, 0x66, 0x6f, 0x66,
+ 0x68, 0x64, 0x61, 0x71, 0x61, 0x74, 0x68, 0x72, 0x75, 0x6e, 0x6d, 0x73, 0x69, 0x71, 0x71, 0x6b,
+ 0x79, 0x76, 0x74, 0x67, 0x63, 0x64, 0x70, 0x69, 0x65, 0x75, 0x72, 0x6e, 0x6c, 0x76, 0x63, 0x72,
+ 0x6d, 0x6e, 0x73, 0x6d, 0x75, 0x6c, 0x73, 0x75, 0x61, 0x64, 0x69, 0x72, 0x75, 0x66, 0x71, 0x6b,
+ 0x77, 0x6b, 0x64, 0x62, 0x68, 0x61, 0x6e, 0x67, 0x6d, 0x68, 0x66, 0x72, 0x69, 0x6b, 0x61, 0x78,
+ 0x70, 0x6d, 0x74, 0x6d, 0x6c, 0x75, 0x63, 0x72, 0x71, 0x71, 0x70, 0x78, 0x62, 0x6a, 0x69, 0x70,
+ 0x70, 0x68, 0x6a, 0x6e, 0x6e, 0x74, 0x69, 0x62, 0x64, 0x69, 0x66, 0x62, 0x78, 0x6b, 0x6c, 0x64,
+ 0x66, 0x79, 0x6a, 0x77, 0x77, 0x79, 0x70, 0x6e, 0x76, 0x78, 0x6a, 0x70, 0x69, 0x63, 0x62, 0x72,
+ 0x6f, 0x64, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x67, 0x74, 0x65, 0x77, 0x6c, 0x6e, 0x72, 0x64, 0x6d,
+ 0x62, 0x68, 0x72, 0x66, 0x67, 0x63, 0x69, 0x74, 0x69, 0x6c, 0x75, 0x72, 0x6c, 0x63, 0x73, 0x71,
+ 0x66, 0x6f, 0x79, 0x67, 0x73, 0x6b, 0x6f, 0x6c, 0x65, 0x72, 0x78, 0x72, 0x76, 0x6a, 0x6f, 0x6e,
+ 0x66, 0x6e, 0x75, 0x6c, 0x6b, 0x75, 0x72, 0x74, 0x6a, 0x6d, 0x67, 0x67, 0x67, 0x74, 0x61, 0x6c,
+ 0x64, 0x64, 0x71, 0x79, 0x62, 0x72, 0x65, 0x6d, 0x62, 0x6c, 0x6f, 0x76, 0x63, 0x71, 0x68, 0x79,
+ 0x6e, 0x78, 0x6c, 0x63, 0x79, 0x61, 0x69, 0x65, 0x68, 0x68, 0x64, 0x66, 0x66, 0x6d, 0x6a, 0x79,
+ 0x62, 0x71, 0x78, 0x70, 0x77, 0x78, 0x73, 0x69, 0x61, 0x6e, 0x66, 0x6e, 0x77, 0x72, 0x63, 0x68,
+ 0x75, 0x6e, 0x75, 0x64, 0x67, 0x6e, 0x69, 0x6a, 0x73, 0x71, 0x6f, 0x71, 0x62, 0x73, 0x6b, 0x65,
+ 0x74, 0x73, 0x73, 0x70, 0x66, 0x66, 0x75, 0x6e, 0x70, 0x76, 0x77, 0x71, 0x78, 0x6d, 0x71, 0x78,
+ 0x6b, 0x6e, 0x62, 0x6d, 0x6f, 0x69, 0x66, 0x61, 0x6f, 0x63, 0x6e, 0x6d, 0x79, 0x77, 0x65, 0x73,
+ 0x62, 0x73, 0x77, 0x61, 0x65, 0x61, 0x79, 0x76, 0x61, 0x78, 0x62, 0x77, 0x77, 0x70, 0x77, 0x77,
+ 0x77, 0x6b, 0x63, 0x6e, 0x70, 0x76, 0x6e, 0x79, 0x72, 0x68, 0x70, 0x6f, 0x62, 0x74, 0x65, 0x78,
+ 0x6d, 0x76, 0x6a, 0x63, 0x6a, 0x66, 0x6d, 0x77, 0x6c, 0x73, 0x66, 0x6c, 0x78, 0x6e, 0x6d, 0x6b,
+ 0x6f, 0x67, 0x6a, 0x67, 0x71, 0x79, 0x75, 0x77, 0x76, 0x74, 0x6d, 0x6e, 0x68, 0x66, 0x79, 0x69,
+ 0x68, 0x75, 0x64, 0x70, 0x6c, 0x74, 0x77, 0x70, 0x6f, 0x74, 0x71, 0x6f, 0x67, 0x6e, 0x6d, 0x6f,
+ 0x76, 0x62, 0x64, 0x74, 0x65, 0x74, 0x63, 0x66, 0x67, 0x6d, 0x75, 0x63, 0x79, 0x69, 0x65, 0x73,
+ 0x6c, 0x73, 0x65, 0x62, 0x6d, 0x78, 0x68, 0x66, 0x77, 0x66, 0x74, 0x71, 0x6b, 0x65, 0x6d, 0x72,
+ 0x63, 0x73, 0x61, 0x66, 0x6e, 0x71, 0x67, 0x64, 0x73, 0x64, 0x77, 0x6a, 0x78, 0x6f, 0x66, 0x63,
+ 0x61, 0x65, 0x63, 0x76, 0x63, 0x6d, 0x71, 0x6d, 0x79, 0x79, 0x65, 0x61, 0x79, 0x66, 0x6f, 0x64,
+ 0x6b, 0x74, 0x6c, 0x75, 0x70, 0x63, 0x66, 0x77, 0x74, 0x65, 0x70, 0x75, 0x78, 0x63, 0x66, 0x6f,
+ 0x6e, 0x62, 0x6f, 0x62, 0x79, 0x73, 0x65, 0x72, 0x66, 0x6a, 0x63, 0x79, 0x66, 0x73, 0x73, 0x63,
+ 0x62, 0x69, 0x76, 0x63, 0x6e, 0x6b, 0x74, 0x76, 0x75, 0x73, 0x71, 0x74, 0x6e, 0x6e, 0x73, 0x68,
+ 0x65, 0x61, 0x6c, 0x68, 0x67, 0x65, 0x79, 0x6c, 0x79, 0x63, 0x6b, 0x77, 0x62, 0x75, 0x6d, 0x73,
+ 0x6f, 0x70, 0x69, 0x68, 0x68, 0x77, 0x6c, 0x63, 0x63, 0x6e, 0x78, 0x76, 0x69, 0x76, 0x75, 0x74,
+ 0x70, 0x64, 0x6a, 0x70, 0x6c, 0x67, 0x6f, 0x62, 0x6c, 0x73, 0x6e, 0x6e, 0x78, 0x66, 0x74, 0x65,
+ 0x6b, 0x70, 0x79, 0x72, 0x73, 0x69, 0x6c, 0x72, 0x61, 0x6c, 0x78, 0x6e, 0x63, 0x72, 0x72, 0x76,
+ 0x64, 0x68, 0x78, 0x78, 0x70, 0x79, 0x61, 0x76, 0x70, 0x6f, 0x73, 0x6c, 0x69, 0x72, 0x63, 0x72,
+ 0x72, 0x78, 0x62, 0x69, 0x61, 0x73, 0x67, 0x66, 0x64, 0x6b, 0x67, 0x66, 0x79, 0x6c, 0x61, 0x6b,
+ 0x72, 0x6d, 0x6a, 0x74, 0x70, 0x72, 0x69, 0x6b, 0x71, 0x74, 0x6e, 0x6a, 0x79, 0x69, 0x78, 0x73,
+ 0x73, 0x77, 0x68, 0x68, 0x73, 0x6b, 0x78, 0x72, 0x66, 0x6a, 0x68, 0x61, 0x67, 0x67, 0x68, 0x76,
+ 0x6a, 0x71, 0x62, 0x62, 0x68, 0x6b, 0x68, 0x66, 0x76, 0x6b, 0x6e, 0x78, 0x65, 0x77, 0x62, 0x70,
+ 0x72, 0x76, 0x76, 0x6b, 0x76, 0x64, 0x6c, 0x68, 0x79, 0x6f, 0x68, 0x69, 0x6a, 0x66, 0x67, 0x73,
+ 0x63, 0x61, 0x6b, 0x69, 0x74, 0x64, 0x62, 0x6a, 0x76, 0x74, 0x6b, 0x79, 0x77, 0x65, 0x69, 0x65,
+ 0x6b, 0x77, 0x72, 0x74, 0x6f, 0x74, 0x62, 0x64, 0x78, 0x6f, 0x62, 0x73, 0x6b, 0x69, 0x79, 0x64,
+ 0x76, 0x78, 0x71, 0x6a, 0x79, 0x61, 0x77, 0x64, 0x66, 0x77, 0x63, 0x77, 0x77, 0x6b, 0x69, 0x68,
+ 0x6e, 0x6d, 0x67, 0x6a, 0x74, 0x6c, 0x76, 0x68, 0x68, 0x72, 0x61, 0x77, 0x71, 0x64, 0x61, 0x65,
+ 0x6b, 0x72, 0x65, 0x61, 0x6f, 0x64, 0x6d, 0x71, 0x76, 0x68, 0x66, 0x78, 0x71, 0x6c, 0x6e, 0x68,
+ 0x75, 0x74, 0x75, 0x6f, 0x73, 0x70, 0x70, 0x66, 0x76, 0x6d, 0x73, 0x66, 0x6a, 0x75, 0x63, 0x77,
+ 0x69, 0x66, 0x67, 0x76, 0x6b, 0x68, 0x65, 0x63, 0x68, 0x73, 0x6f, 0x75, 0x64, 0x79, 0x6d, 0x6f,
+ 0x6e, 0x79, 0x6a, 0x78, 0x64, 0x69, 0x71, 0x70, 0x6d, 0x78, 0x70, 0x61, 0x63, 0x77, 0x66, 0x79,
+ 0x6c, 0x79, 0x66, 0x70, 0x73, 0x77, 0x65, 0x6b, 0x72, 0x6d, 0x70, 0x65, 0x74, 0x61, 0x68, 0x66,
+ 0x63, 0x79, 0x79, 0x6b, 0x76, 0x76, 0x79, 0x78, 0x6f, 0x76, 0x67, 0x79, 0x77, 0x77, 0x62, 0x77,
+ 0x69, 0x78, 0x74, 0x65, 0x65, 0x6f, 0x6a, 0x6d, 0x76, 0x6d, 0x75, 0x66, 0x6d, 0x6e, 0x70, 0x79,
+ 0x6e, 0x6c, 0x79, 0x67, 0x72, 0x6b, 0x6c, 0x64, 0x76, 0x6c, 0x6e, 0x65, 0x69, 0x64, 0x76, 0x6d,
+ 0x68, 0x79, 0x6b, 0x74, 0x63, 0x6a, 0x63, 0x66, 0x65, 0x74, 0x69, 0x74, 0x71, 0x6e, 0x62, 0x66,
+ 0x78, 0x75, 0x62, 0x74, 0x73, 0x66, 0x61, 0x77, 0x6d, 0x6c, 0x62, 0x77, 0x61, 0x68, 0x6e, 0x76,
+ 0x66, 0x6a, 0x68, 0x73, 0x63, 0x6e, 0x74, 0x64, 0x6a, 0x75, 0x68, 0x63, 0x6f, 0x73, 0x77, 0x6a,
+ 0x68, 0x75, 0x72, 0x69, 0x79, 0x69, 0x65, 0x64, 0x6d, 0x6a, 0x67, 0x70, 0x79, 0x68, 0x6f, 0x6c,
+ 0x6d, 0x75, 0x6f, 0x73, 0x68, 0x68, 0x6f, 0x73, 0x70, 0x6c, 0x65, 0x67, 0x6b, 0x6e, 0x74, 0x69,
+ 0x63, 0x72, 0x6f, 0x63, 0x68, 0x6b, 0x79, 0x74, 0x77, 0x79, 0x76, 0x63, 0x71, 0x6b, 0x6d, 0x6d,
+ 0x6f, 0x69, 0x6c, 0x77, 0x6c, 0x6c, 0x62, 0x6b, 0x71, 0x6c, 0x74, 0x67, 0x6c, 0x64, 0x6b, 0x69,
+ 0x62, 0x73, 0x78, 0x66, 0x68, 0x77, 0x64, 0x74, 0x73, 0x73, 0x71, 0x74, 0x64, 0x6b, 0x6a, 0x69,
+ 0x74, 0x6c, 0x61, 0x72, 0x74, 0x6e, 0x6a, 0x6c, 0x68, 0x78, 0x77, 0x6b, 0x67, 0x6f, 0x6f, 0x6a,
+ 0x61, 0x65, 0x6a, 0x78, 0x67, 0x61, 0x77, 0x72, 0x6e, 0x62, 0x6d, 0x6c, 0x6e, 0x67, 0x69, 0x66,
+ 0x6e, 0x6a, 0x76, 0x69, 0x77, 0x72, 0x79, 0x6f, 0x63, 0x69, 0x6e, 0x67, 0x72, 0x6a, 0x76, 0x69,
+ 0x75, 0x78, 0x6d, 0x6e, 0x65, 0x64, 0x67, 0x70, 0x6a, 0x6d, 0x74, 0x78, 0x79, 0x73, 0x72, 0x70,
+ 0x72, 0x61, 0x6f, 0x77, 0x61, 0x79, 0x6f, 0x72, 0x6b, 0x61, 0x61, 0x75, 0x77, 0x69, 0x63, 0x6d,
+ 0x68, 0x78, 0x74, 0x65, 0x74, 0x67, 0x63, 0x77, 0x63, 0x71, 0x72, 0x72, 0x71, 0x6c, 0x64, 0x79,
+ 0x78, 0x6b, 0x63, 0x75, 0x70, 0x64, 0x6f, 0x6f, 0x6c, 0x76, 0x6f, 0x71, 0x75, 0x6d, 0x63, 0x74,
+ 0x71, 0x63, 0x76, 0x6e, 0x75, 0x63, 0x65, 0x69, 0x67, 0x76, 0x64, 0x61, 0x6d, 0x72, 0x6a, 0x6b,
+ 0x64, 0x70, 0x68, 0x75, 0x67, 0x6c, 0x77, 0x71, 0x61, 0x70, 0x6e, 0x67, 0x73, 0x70, 0x6e, 0x68,
+ 0x73, 0x73, 0x69, 0x65, 0x6a, 0x66, 0x68, 0x67, 0x72, 0x73, 0x72, 0x69, 0x6a, 0x77, 0x6b, 0x78,
+ 0x6a, 0x61, 0x73, 0x66, 0x66, 0x65, 0x6b, 0x65, 0x6b, 0x61, 0x68, 0x6b, 0x6a, 0x73, 0x79, 0x6f,
+ 0x64, 0x78, 0x72, 0x76, 0x6c, 0x67, 0x61, 0x70, 0x79, 0x77, 0x77, 0x6c, 0x69, 0x67, 0x71, 0x76,
+ 0x79, 0x61, 0x72, 0x62, 0x74, 0x64, 0x69, 0x64, 0x6f, 0x78, 0x6d, 0x6f, 0x67, 0x67, 0x6c, 0x78,
+ 0x74, 0x62, 0x64, 0x72, 0x63, 0x70, 0x64, 0x71, 0x79, 0x6f, 0x63, 0x74, 0x6c, 0x69, 0x6c, 0x6d,
+ 0x77, 0x6b, 0x69, 0x63, 0x72, 0x70, 0x70, 0x71, 0x75, 0x73, 0x68, 0x68, 0x6c, 0x79, 0x6c, 0x67,
+ 0x69, 0x65, 0x79, 0x76, 0x6b, 0x74, 0x6f, 0x6d, 0x61, 0x79, 0x79, 0x6a, 0x78, 0x6e, 0x78, 0x62,
+ 0x78, 0x65, 0x61, 0x70, 0x68, 0x75, 0x78, 0x6d, 0x77, 0x77, 0x70, 0x6e, 0x75, 0x67, 0x65, 0x66,
+ 0x6f, 0x6c, 0x65, 0x77, 0x61, 0x6b, 0x69, 0x68, 0x73, 0x61, 0x74, 0x62, 0x6c, 0x6a, 0x65, 0x6d,
+ 0x74, 0x77, 0x77, 0x64, 0x67, 0x6f, 0x76, 0x78, 0x62, 0x6b, 0x71, 0x6d, 0x67, 0x62, 0x75, 0x61,
+ 0x79, 0x78, 0x64, 0x65, 0x65, 0x72, 0x62, 0x64, 0x75, 0x61, 0x66, 0x6e, 0x66, 0x6c, 0x70, 0x73,
+ 0x70, 0x68, 0x6d, 0x79, 0x63, 0x71, 0x64, 0x70, 0x6d, 0x75, 0x78, 0x61, 0x6b, 0x61, 0x62, 0x67,
+ 0x65, 0x62, 0x64, 0x66, 0x64, 0x6a, 0x76, 0x67, 0x68, 0x78, 0x6e, 0x6d, 0x65, 0x79, 0x73, 0x64,
+ 0x63, 0x68, 0x72, 0x71, 0x6a, 0x6f, 0x71, 0x61, 0x77, 0x6a, 0x6b, 0x69, 0x62, 0x73, 0x63, 0x69,
+ 0x73, 0x63, 0x64, 0x6c, 0x67, 0x71, 0x69, 0x75, 0x71, 0x63, 0x64, 0x6b, 0x61, 0x6e, 0x73, 0x6c,
+ 0x65, 0x66, 0x64, 0x6e, 0x6a, 0x74, 0x6b, 0x78, 0x69, 0x6a, 0x64, 0x6a, 0x77, 0x70, 0x70, 0x67,
+ 0x6d, 0x72, 0x6b, 0x65, 0x79, 0x64, 0x61, 0x6f, 0x64, 0x70, 0x73, 0x63, 0x72, 0x6d, 0x68, 0x73,
+ 0x74, 0x6e, 0x6f, 0x72, 0x61, 0x73, 0x61, 0x72, 0x71, 0x72, 0x61, 0x75, 0x77, 0x67, 0x70, 0x68,
+ 0x70, 0x66, 0x61, 0x72, 0x67, 0x73, 0x69, 0x67, 0x6b, 0x71, 0x73, 0x64, 0x77, 0x61, 0x67, 0x70,
+ 0x69, 0x71, 0x72, 0x77, 0x77, 0x68, 0x71, 0x63, 0x6a, 0x61, 0x70, 0x63, 0x70, 0x62, 0x71, 0x68,
+ 0x63, 0x78, 0x71, 0x62, 0x70, 0x79, 0x74, 0x76, 0x76, 0x62, 0x74, 0x63, 0x76, 0x68, 0x62, 0x73,
+ 0x72, 0x64, 0x64, 0x66, 0x71, 0x65, 0x6d, 0x72, 0x63, 0x77, 0x6a, 0x77, 0x6a, 0x76, 0x62, 0x6c,
+ 0x77, 0x65, 0x61, 0x63, 0x6b, 0x62, 0x79, 0x71, 0x75, 0x6b, 0x68, 0x74, 0x6f, 0x66, 0x74, 0x65,
+ 0x72, 0x76, 0x74, 0x6f, 0x71, 0x71, 0x6e, 0x65, 0x63, 0x78, 0x6d, 0x74, 0x74, 0x66, 0x71, 0x78,
+ 0x77, 0x68, 0x75, 0x74, 0x72, 0x62, 0x73, 0x6c, 0x65, 0x71, 0x66, 0x6c, 0x6d, 0x75, 0x66, 0x62,
+ 0x71, 0x78, 0x68, 0x63, 0x65, 0x6f, 0x72, 0x63, 0x69, 0x74, 0x68, 0x74, 0x72, 0x64, 0x62, 0x68,
+ 0x63, 0x65, 0x74, 0x78, 0x67, 0x74, 0x61, 0x74, 0x75, 0x69, 0x6f, 0x6b, 0x78, 0x6c, 0x72, 0x79,
+ 0x77, 0x69, 0x63, 0x6a, 0x6c, 0x75, 0x62, 0x62, 0x77, 0x77, 0x64, 0x77, 0x72, 0x62, 0x79, 0x74,
+ 0x73, 0x6e, 0x75, 0x69, 0x68, 0x64, 0x71, 0x68, 0x6f, 0x65, 0x64, 0x77, 0x63, 0x6f, 0x75, 0x74,
+ 0x6a, 0x74, 0x75, 0x64, 0x62, 0x68, 0x62, 0x76, 0x75, 0x75, 0x66, 0x63, 0x69, 0x62, 0x79, 0x62,
+ 0x6d, 0x70, 0x75, 0x71, 0x74, 0x76, 0x71, 0x63, 0x68, 0x6f, 0x69, 0x77, 0x6b, 0x6b, 0x61, 0x6b,
+ 0x79, 0x74, 0x77, 0x6c, 0x65, 0x67, 0x6b, 0x71, 0x61, 0x76, 0x66, 0x77, 0x69, 0x72, 0x79, 0x66,
+ 0x6e, 0x63, 0x75, 0x70, 0x73, 0x6f, 0x6c, 0x72, 0x70, 0x69, 0x6e, 0x71, 0x61, 0x77, 0x6b, 0x61,
+ 0x6c, 0x70, 0x69, 0x74, 0x72, 0x6a, 0x75, 0x74, 0x78, 0x6e, 0x6a, 0x71, 0x6b, 0x6a, 0x67, 0x71,
+ 0x78, 0x61, 0x6d, 0x77, 0x6b, 0x6d, 0x68, 0x77, 0x79, 0x66, 0x77, 0x6a, 0x75, 0x76, 0x70, 0x62,
+ 0x67, 0x64, 0x64, 0x73, 0x6e, 0x72, 0x71, 0x69, 0x65, 0x6e, 0x66, 0x64, 0x70, 0x65, 0x73, 0x67,
+ 0x6e, 0x74, 0x6e, 0x6e, 0x62, 0x78, 0x68, 0x71, 0x64, 0x62, 0x68, 0x6e, 0x77, 0x6c, 0x6c, 0x74,
+ 0x66, 0x76, 0x72, 0x65, 0x62, 0x79, 0x6d, 0x72, 0x79, 0x78, 0x63, 0x62, 0x64, 0x77, 0x6a, 0x67,
+ 0x6b, 0x6b, 0x79, 0x65, 0x6d, 0x67, 0x6a, 0x72, 0x79, 0x78, 0x63, 0x64, 0x68, 0x66, 0x75, 0x6e,
+ 0x63, 0x6d, 0x70, 0x66, 0x69, 0x64, 0x64, 0x76, 0x74, 0x62, 0x69, 0x66, 0x6f, 0x75, 0x6d, 0x67,
+ 0x63, 0x79, 0x74, 0x61, 0x72, 0x70, 0x6a, 0x73, 0x65, 0x71, 0x71, 0x62, 0x75, 0x75, 0x65, 0x69,
+ 0x6d, 0x6d, 0x72, 0x64, 0x62, 0x64, 0x72, 0x63, 0x74, 0x6e, 0x74, 0x72, 0x6f, 0x79, 0x67, 0x79,
+ 0x78, 0x6c, 0x64, 0x63, 0x6b, 0x74, 0x76, 0x78, 0x70, 0x64, 0x6c, 0x64, 0x72, 0x66, 0x62, 0x71,
+ 0x69, 0x62, 0x61, 0x6a, 0x6f, 0x73, 0x6d, 0x77, 0x6d, 0x65, 0x6b, 0x71, 0x64, 0x6c, 0x63, 0x61,
+ 0x6c, 0x71, 0x65, 0x6c, 0x78, 0x74, 0x76, 0x67, 0x74, 0x69, 0x64, 0x76, 0x72, 0x74, 0x75, 0x68,
+ 0x68, 0x63, 0x77, 0x65, 0x64, 0x67, 0x70, 0x70, 0x70, 0x61, 0x6b, 0x65, 0x68, 0x67, 0x6d, 0x72,
+ 0x6e, 0x6a, 0x64, 0x68, 0x62, 0x69, 0x61, 0x76, 0x6b, 0x71, 0x64, 0x74, 0x66, 0x67, 0x6c, 0x71,
+ 0x6b, 0x76, 0x61, 0x77, 0x79, 0x75, 0x76, 0x6c, 0x71, 0x62, 0x6e, 0x61, 0x6b, 0x6d, 0x6e, 0x6d,
+ 0x77, 0x72, 0x75, 0x66, 0x72, 0x6b, 0x62, 0x6b, 0x62, 0x73, 0x75, 0x75, 0x71, 0x61, 0x67, 0x66,
+ 0x79, 0x70, 0x6c, 0x6c, 0x6b, 0x62, 0x63, 0x66, 0x6a, 0x74, 0x76, 0x6d, 0x70, 0x71, 0x6a, 0x62,
+ 0x68, 0x75, 0x72, 0x75, 0x62, 0x74, 0x70, 0x6b, 0x66, 0x61, 0x67, 0x6e, 0x61, 0x6f, 0x6f, 0x68,
+ 0x77, 0x6a, 0x72, 0x78, 0x79, 0x73, 0x71, 0x78, 0x75, 0x61, 0x78, 0x63, 0x6a, 0x75, 0x73, 0x73,
+ 0x62, 0x70, 0x71, 0x75, 0x63, 0x6e, 0x66, 0x72, 0x74, 0x79, 0x6f, 0x6f, 0x73, 0x6a, 0x75, 0x62,
+ 0x65, 0x79, 0x79, 0x79, 0x75, 0x6f, 0x79, 0x78, 0x6e, 0x77, 0x75, 0x62, 0x66, 0x6e, 0x67, 0x73,
+ 0x63, 0x68, 0x65, 0x74, 0x68, 0x75, 0x6a, 0x78, 0x69, 0x6f, 0x70, 0x62, 0x78, 0x65, 0x68, 0x63,
+ 0x75, 0x78, 0x62, 0x6d, 0x73, 0x6a, 0x68, 0x6a, 0x76, 0x6f, 0x76, 0x6a, 0x6c, 0x73, 0x62, 0x6e,
+ 0x64, 0x68, 0x6f, 0x63, 0x63, 0x67, 0x72, 0x78, 0x64, 0x71, 0x74, 0x76, 0x71, 0x62, 0x66, 0x79,
+ 0x70, 0x61, 0x78, 0x61, 0x77, 0x64, 0x69, 0x79, 0x6e, 0x64, 0x78, 0x66, 0x68, 0x75, 0x65, 0x76,
+ 0x70, 0x6a, 0x67, 0x6a, 0x63, 0x67, 0x61, 0x74, 0x67, 0x77, 0x6c, 0x78, 0x78, 0x75, 0x73, 0x64,
+ 0x69, 0x64, 0x69, 0x6f, 0x72, 0x62, 0x70, 0x65, 0x77, 0x74, 0x74, 0x66, 0x76, 0x6e, 0x74, 0x6b,
+ 0x74, 0x77, 0x76, 0x67, 0x62, 0x75, 0x68, 0x62, 0x66, 0x78, 0x77, 0x72, 0x6a, 0x66, 0x6a, 0x64,
+ 0x6b, 0x6c, 0x70, 0x78, 0x63, 0x77, 0x6b, 0x63, 0x62, 0x77, 0x77, 0x77, 0x65, 0x66, 0x73, 0x64,
+ 0x62, 0x76, 0x6b, 0x73, 0x75, 0x6a, 0x70, 0x63, 0x72, 0x6b, 0x71, 0x73, 0x66, 0x61, 0x70, 0x6c,
+ 0x76, 0x64, 0x61, 0x74, 0x69, 0x76, 0x79, 0x6a, 0x76, 0x75, 0x65, 0x6e, 0x67, 0x63, 0x6e, 0x75,
+ 0x65, 0x74, 0x6c, 0x75, 0x78, 0x6a, 0x6b, 0x78, 0x66, 0x68, 0x71, 0x6e, 0x76, 0x68, 0x68, 0x6c,
+ 0x70, 0x6c, 0x66, 0x64, 0x65, 0x72, 0x6f, 0x73, 0x74, 0x75, 0x6f, 0x6b, 0x6c, 0x6b, 0x6b, 0x6a,
+ 0x74, 0x71, 0x66, 0x63, 0x70, 0x69, 0x6e, 0x61, 0x6d, 0x64, 0x6b, 0x73, 0x74, 0x75, 0x6f, 0x6f,
+ 0x61, 0x61, 0x65, 0x6d, 0x73, 0x67, 0x71, 0x77, 0x65, 0x70, 0x73, 0x67, 0x6d, 0x6d, 0x6d, 0x62,
+ 0x74, 0x6c, 0x68, 0x65, 0x68, 0x74, 0x65, 0x76, 0x62, 0x6f, 0x70, 0x6c, 0x74, 0x68, 0x76, 0x6e,
+ 0x6c, 0x68, 0x6b, 0x76, 0x68, 0x68, 0x78, 0x72, 0x79, 0x6a, 0x67, 0x64, 0x71, 0x71, 0x6a, 0x77,
+ 0x6a, 0x74, 0x74, 0x64, 0x72, 0x69, 0x74, 0x74, 0x66, 0x61, 0x71, 0x70, 0x64, 0x68, 0x69, 0x6b,
+ 0x6b, 0x6e, 0x6b, 0x6a, 0x61, 0x70, 0x73, 0x68, 0x70, 0x62, 0x65, 0x77, 0x77, 0x62, 0x64, 0x73,
+ 0x6a, 0x6e, 0x79, 0x76, 0x78, 0x68, 0x6d, 0x6a, 0x6c, 0x76, 0x61, 0x71, 0x73, 0x73, 0x69, 0x78,
+ 0x69, 0x6c, 0x65, 0x73, 0x6f, 0x6d, 0x67, 0x6a, 0x64, 0x6b, 0x6e, 0x73, 0x69, 0x72, 0x64, 0x65,
+ 0x77, 0x71, 0x79, 0x72, 0x67, 0x71, 0x68, 0x6a, 0x63, 0x74, 0x75, 0x70, 0x79, 0x75, 0x73, 0x78,
+ 0x6d, 0x64, 0x76, 0x63, 0x67, 0x64, 0x69, 0x6b, 0x77, 0x73, 0x6a, 0x6d, 0x6b, 0x6d, 0x77, 0x68,
+ 0x62, 0x6c, 0x67, 0x76, 0x6a, 0x6f, 0x64, 0x6a, 0x76, 0x64, 0x61, 0x6a, 0x74, 0x77, 0x68, 0x69,
+ 0x6a, 0x6d, 0x6d, 0x6b, 0x67, 0x6f, 0x76, 0x73, 0x6e, 0x6f, 0x70, 0x67, 0x65, 0x77, 0x68, 0x63,
+ 0x64, 0x75, 0x64, 0x63, 0x79, 0x62, 0x77, 0x78, 0x64, 0x70, 0x6b, 0x70, 0x70, 0x69, 0x75, 0x62,
+ 0x73, 0x76, 0x76, 0x72, 0x67, 0x6f, 0x73, 0x6e, 0x70, 0x74, 0x70, 0x6d, 0x78, 0x79, 0x77, 0x6e,
+ 0x75, 0x77, 0x70, 0x76, 0x6f, 0x62, 0x73, 0x64, 0x78, 0x6f, 0x66, 0x64, 0x67, 0x69, 0x68, 0x79,
+ 0x70, 0x71, 0x72, 0x72, 0x6a, 0x73, 0x6d, 0x78, 0x61, 0x64, 0x67, 0x70, 0x72, 0x77, 0x79, 0x65,
+ 0x6e, 0x72, 0x62, 0x64, 0x74, 0x62, 0x72, 0x72, 0x78, 0x76, 0x79, 0x76, 0x68, 0x63, 0x64, 0x62,
+ 0x67, 0x70, 0x6e, 0x76, 0x6d, 0x6f, 0x74, 0x72, 0x76, 0x61, 0x64, 0x72, 0x74, 0x79, 0x67, 0x64,
+ 0x6f, 0x74, 0x69, 0x68, 0x78, 0x79, 0x65, 0x69, 0x69, 0x64, 0x6c, 0x63, 0x69, 0x62, 0x63, 0x61,
+ 0x64, 0x68, 0x78, 0x65, 0x64, 0x67, 0x6d, 0x6e, 0x6e, 0x67, 0x71, 0x6e, 0x65, 0x76, 0x76, 0x64,
+ 0x6a, 0x66, 0x65, 0x70, 0x77, 0x79, 0x67, 0x74, 0x74, 0x67, 0x6c, 0x69, 0x65, 0x72, 0x6e, 0x70,
+ 0x62, 0x6b, 0x78, 0x65, 0x68, 0x74, 0x70, 0x6a, 0x64, 0x6d, 0x6e, 0x79, 0x76, 0x73, 0x70, 0x72,
+ 0x6f, 0x67, 0x62, 0x75, 0x63, 0x64, 0x74, 0x74, 0x71, 0x78, 0x73, 0x79, 0x69, 0x79, 0x62, 0x66,
+ 0x64, 0x75, 0x6f, 0x76, 0x68, 0x75, 0x72, 0x75, 0x66, 0x71, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x67,
+ 0x61, 0x69, 0x6b, 0x69, 0x77, 0x68, 0x72, 0x69, 0x6b, 0x70, 0x72, 0x6b, 0x75, 0x66, 0x64, 0x79,
+ 0x6b, 0x65, 0x71, 0x69, 0x61, 0x67, 0x6c, 0x75, 0x64, 0x70, 0x75, 0x72, 0x68, 0x65, 0x73, 0x69,
+ 0x67, 0x65, 0x6d, 0x71, 0x66, 0x79, 0x61, 0x64, 0x70, 0x65, 0x73, 0x6a, 0x76, 0x76, 0x79, 0x71,
+ 0x71, 0x6e, 0x76, 0x64, 0x63, 0x6b, 0x73, 0x70, 0x6a, 0x6d, 0x65, 0x73, 0x74, 0x63, 0x6a, 0x6f,
+ 0x73, 0x75, 0x76, 0x72, 0x67, 0x67, 0x61, 0x72, 0x66, 0x70, 0x77, 0x79, 0x78, 0x6a, 0x76, 0x64,
+ 0x61, 0x6c, 0x6f, 0x79, 0x73, 0x62, 0x69, 0x62, 0x64, 0x6e, 0x62, 0x69, 0x67, 0x6d, 0x6e, 0x6b,
+ 0x78, 0x66, 0x64, 0x61, 0x6d, 0x79, 0x63, 0x77, 0x66, 0x61, 0x64, 0x72, 0x6a, 0x6e, 0x74, 0x78,
+ 0x62, 0x63, 0x6f, 0x6f, 0x66, 0x6d, 0x6b, 0x63, 0x64, 0x76, 0x6d, 0x67, 0x75, 0x65, 0x68, 0x6b,
+ 0x68, 0x63, 0x77, 0x6a, 0x70, 0x77, 0x6b, 0x67, 0x61, 0x79, 0x6b, 0x62, 0x79, 0x74, 0x68, 0x73,
+ 0x71, 0x78, 0x75, 0x75, 0x63, 0x64, 0x6f, 0x6a, 0x6b, 0x61, 0x73, 0x75, 0x6e, 0x71, 0x63, 0x68,
+ 0x71, 0x6a, 0x78, 0x6b, 0x68, 0x70, 0x68, 0x73, 0x72, 0x65, 0x73, 0x64, 0x70, 0x75, 0x64, 0x78,
+ 0x78, 0x78, 0x66, 0x76, 0x6f, 0x71, 0x65, 0x63, 0x61, 0x76, 0x74, 0x64, 0x78, 0x6b, 0x72, 0x61,
+ 0x6e, 0x64, 0x6e, 0x65, 0x78, 0x77, 0x63, 0x65, 0x6c, 0x6d, 0x77, 0x6c, 0x79, 0x6b, 0x65, 0x74,
+ 0x78, 0x62, 0x73, 0x66, 0x76, 0x62, 0x79, 0x76, 0x61, 0x79, 0x70, 0x69, 0x64, 0x66, 0x6b, 0x68,
+ 0x72, 0x62, 0x74, 0x6d, 0x6b, 0x75, 0x73, 0x67, 0x68, 0x78, 0x61, 0x6e, 0x62, 0x6f, 0x75, 0x65,
+ 0x72, 0x65, 0x78, 0x75, 0x69, 0x67, 0x70, 0x6e, 0x73, 0x61, 0x71, 0x61, 0x65, 0x77, 0x69, 0x6c,
+ 0x6a, 0x73, 0x62, 0x73, 0x66, 0x6b, 0x6f, 0x71, 0x73, 0x71, 0x62, 0x73, 0x78, 0x6d, 0x77, 0x67,
+ 0x78, 0x75, 0x66, 0x6c, 0x77, 0x6e, 0x65, 0x6c, 0x63, 0x79, 0x77, 0x6f, 0x70, 0x6f, 0x65, 0x75,
+ 0x72, 0x6f, 0x67, 0x63, 0x6c, 0x69, 0x75, 0x70, 0x69, 0x70, 0x6e, 0x67, 0x74, 0x72, 0x72, 0x72,
+ 0x6e, 0x69, 0x70, 0x74, 0x71, 0x68, 0x70, 0x76, 0x6b, 0x62, 0x68, 0x6f, 0x77, 0x66, 0x6b, 0x64,
+ 0x75, 0x66, 0x67, 0x79, 0x65, 0x65, 0x78, 0x61, 0x70, 0x67, 0x77, 0x76, 0x67, 0x6d, 0x64, 0x65,
+ 0x69, 0x62, 0x73, 0x65, 0x67, 0x70, 0x74, 0x6e, 0x67, 0x78, 0x75, 0x69, 0x6a, 0x68, 0x6d, 0x65,
+ 0x78, 0x62, 0x6d, 0x6d, 0x62, 0x68, 0x65, 0x6c, 0x6e, 0x67, 0x6b, 0x61, 0x78, 0x6f, 0x62, 0x79,
+ 0x64, 0x78, 0x6f, 0x65, 0x74, 0x72, 0x73, 0x6d, 0x6c, 0x61, 0x76, 0x77, 0x68, 0x64, 0x71, 0x77,
+ 0x71, 0x79, 0x77, 0x69, 0x6c, 0x66, 0x75, 0x65, 0x79, 0x72, 0x70, 0x75, 0x6b, 0x61, 0x64, 0x6c,
+ 0x67, 0x6e, 0x6e, 0x72, 0x75, 0x68, 0x68, 0x71, 0x62, 0x65, 0x72, 0x72, 0x73, 0x68, 0x71, 0x6a,
+ 0x78, 0x74, 0x74, 0x6a, 0x6a, 0x6f, 0x6d, 0x67, 0x62, 0x61, 0x6b, 0x6a, 0x66, 0x6a, 0x66, 0x6e,
+ 0x75, 0x64, 0x64, 0x74, 0x6e, 0x64, 0x62, 0x6f, 0x71, 0x6d, 0x74, 0x6c, 0x6b, 0x72, 0x6e, 0x68,
+ 0x6a, 0x70, 0x6d, 0x72, 0x66, 0x64, 0x6d, 0x71, 0x6b, 0x66, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x68,
+ 0x78, 0x6c, 0x61, 0x66, 0x79, 0x6e, 0x71, 0x62, 0x75, 0x63, 0x6f, 0x79, 0x6b, 0x78, 0x65, 0x6f,
+ 0x73, 0x70, 0x64, 0x69, 0x74, 0x6d, 0x6d, 0x67, 0x6a, 0x75, 0x79, 0x71, 0x72, 0x67, 0x6f, 0x72,
+ 0x61, 0x6c, 0x79, 0x77, 0x72, 0x67, 0x66, 0x76, 0x77, 0x66, 0x71, 0x79, 0x79, 0x77, 0x74, 0x73,
+ 0x61, 0x63, 0x77, 0x79, 0x6f, 0x6b, 0x70, 0x78, 0x79, 0x62, 0x72, 0x63, 0x78, 0x66, 0x74, 0x65,
+ 0x79, 0x66, 0x63, 0x6a, 0x69, 0x6c, 0x64, 0x62, 0x62, 0x78, 0x64, 0x68, 0x72, 0x73, 0x64, 0x62,
+ 0x67, 0x76, 0x6a, 0x75, 0x74, 0x70, 0x79, 0x6e, 0x6e, 0x64, 0x63, 0x79, 0x63, 0x75, 0x73, 0x67,
+ 0x77, 0x77, 0x6a, 0x69, 0x76, 0x74, 0x75, 0x69, 0x76, 0x71, 0x62, 0x6f, 0x68, 0x70, 0x78, 0x69,
+ 0x75, 0x74, 0x76, 0x66, 0x6c, 0x77, 0x69, 0x66, 0x6b, 0x6b, 0x70, 0x6b, 0x6d, 0x6b, 0x79, 0x76,
+ 0x69, 0x73, 0x64, 0x69, 0x74, 0x64, 0x6b, 0x72, 0x6c, 0x78, 0x64, 0x68, 0x66, 0x63, 0x75, 0x72,
+ 0x6d, 0x6f, 0x68, 0x6e, 0x61, 0x76, 0x72, 0x79, 0x69, 0x67, 0x76, 0x69, 0x70, 0x6e, 0x6a, 0x6c,
+ 0x64, 0x79, 0x68, 0x65, 0x79, 0x63, 0x74, 0x6b, 0x64, 0x72, 0x62, 0x6e, 0x77, 0x6e, 0x6d, 0x62,
+ 0x78, 0x72, 0x77, 0x6c, 0x67, 0x74, 0x6a, 0x75, 0x78, 0x69, 0x65, 0x63, 0x6e, 0x72, 0x70, 0x70,
+ 0x69, 0x69, 0x62, 0x77, 0x61, 0x63, 0x77, 0x62, 0x6e, 0x61, 0x79, 0x66, 0x77, 0x73, 0x75, 0x71,
+ 0x6b, 0x6d, 0x68, 0x74, 0x69, 0x65, 0x6a, 0x67, 0x6c, 0x71, 0x77, 0x6d, 0x68, 0x78, 0x66, 0x68,
+ 0x68, 0x75, 0x6c, 0x65, 0x6e, 0x6e, 0x77, 0x6d, 0x79, 0x62, 0x6c, 0x76, 0x62, 0x6c, 0x69, 0x61,
+ 0x76, 0x68, 0x68, 0x78, 0x6d, 0x68, 0x6d, 0x75, 0x67, 0x6d, 0x68, 0x64, 0x63, 0x68, 0x6e, 0x6d,
+ 0x68, 0x63, 0x76, 0x74, 0x78, 0x78, 0x66, 0x61, 0x64, 0x6a, 0x69, 0x6e, 0x74, 0x66, 0x63, 0x62,
+ 0x68, 0x78, 0x74, 0x73, 0x72, 0x78, 0x6b, 0x76, 0x79, 0x61, 0x65, 0x61, 0x68, 0x6d, 0x73, 0x76,
+ 0x74, 0x69, 0x6c, 0x70, 0x6a, 0x6b, 0x77, 0x72, 0x67, 0x79, 0x65, 0x6c, 0x6a, 0x77, 0x66, 0x73,
+ 0x66, 0x67, 0x71, 0x6f, 0x67, 0x61, 0x68, 0x6c, 0x73, 0x6e, 0x72, 0x67, 0x75, 0x6e, 0x78, 0x79,
+ 0x72, 0x62, 0x78, 0x63, 0x6d, 0x69, 0x79, 0x62, 0x63, 0x70, 0x64, 0x70, 0x67, 0x78, 0x79, 0x6d,
+ 0x76, 0x65, 0x6e, 0x68, 0x70, 0x65, 0x6c, 0x73, 0x71, 0x63, 0x6a, 0x75, 0x75, 0x6f, 0x66, 0x77,
+ 0x72, 0x6d, 0x6b, 0x71, 0x72, 0x6a, 0x6b, 0x71, 0x74, 0x75, 0x64, 0x64, 0x6e, 0x6e, 0x6f, 0x63,
+ 0x74, 0x72, 0x76, 0x76, 0x77, 0x67, 0x65, 0x6a, 0x69, 0x78, 0x63, 0x6a, 0x68, 0x68, 0x63, 0x74,
+ 0x6f, 0x70, 0x61, 0x73, 0x6e, 0x61, 0x64, 0x62, 0x6b, 0x65, 0x61, 0x74, 0x77, 0x62, 0x74, 0x6d,
+ 0x66, 0x62, 0x6e, 0x76, 0x6e, 0x6c, 0x67, 0x78, 0x67, 0x6b, 0x77, 0x64, 0x67, 0x69, 0x73, 0x79,
+ 0x67, 0x77, 0x6e, 0x6f, 0x68, 0x73, 0x6c, 0x69, 0x68, 0x75, 0x65, 0x6c, 0x77, 0x74, 0x77, 0x78,
+ 0x62, 0x62, 0x68, 0x6d, 0x6f, 0x77, 0x74, 0x68, 0x78, 0x67, 0x71, 0x79, 0x62, 0x74, 0x6d, 0x66,
+ 0x78, 0x64, 0x69, 0x6f, 0x65, 0x70, 0x6b, 0x62, 0x68, 0x6c, 0x62, 0x69, 0x63, 0x75, 0x6e, 0x68,
+ 0x79, 0x73, 0x78, 0x6f, 0x6b, 0x78, 0x65, 0x79, 0x71, 0x6e, 0x6a, 0x67, 0x73, 0x62, 0x73, 0x6b,
+ 0x6f, 0x68, 0x6e, 0x65, 0x6e, 0x78, 0x62, 0x61, 0x62, 0x78, 0x6e, 0x71, 0x72, 0x6a, 0x71, 0x67,
+ 0x70, 0x79, 0x67, 0x6e, 0x69, 0x75, 0x74, 0x71, 0x70, 0x78, 0x66, 0x70, 0x61, 0x6f, 0x6a, 0x70,
+ 0x61, 0x76, 0x74, 0x76, 0x67, 0x74, 0x79, 0x65, 0x67, 0x6b, 0x64, 0x78, 0x70, 0x63, 0x66, 0x79,
+ 0x74, 0x65, 0x74, 0x6f, 0x62, 0x79, 0x77, 0x74, 0x77, 0x71, 0x63, 0x61, 0x73, 0x78, 0x6c, 0x77,
+ 0x6c, 0x71, 0x67, 0x62, 0x79, 0x69, 0x67, 0x6a, 0x6f, 0x6c, 0x68, 0x67, 0x64, 0x76, 0x66, 0x73,
+ 0x77, 0x6d, 0x76, 0x65, 0x62, 0x68, 0x68, 0x76, 0x61, 0x68, 0x6d, 0x75, 0x6e, 0x61, 0x76, 0x00, 0x00
+};
+
+
+int main()
+{
+ const int ntests = 10;
+ size_t bufsize = sizeof(buf) - 1;
+ int i;
+ size_t bufsizes[ntests];
+ char old;
+
+ for (i = ntests-1; i >= 0; --i)
+ {
+ bufsizes[i] = bufsize;
+ bufsize /= 2;
+ }
+
+ printf("\n\n");
+ printf("Testing pathological pattern '.+nonexisting.+' to force worst-case asymptotic performance: \n");
+
+ for (i = 0; i < ntests; ++i)
+ {
+ old = buf[bufsizes[i]];
+ buf[bufsizes[i]] = 0;
+
+ printf(" matching on %lu bytes of test input: ", bufsizes[i]);
+ fflush(stdout);
+ printf("%d \n", re_match(".+nonexisting.+", buf));
+
+ buf[bufsizes[i]] = old;
+ }
+
+ printf("\n\n");
+
+ return 0;
+}
+
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c
new file mode 100644
index 0000000000..42eddbe704
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_print.c
@@ -0,0 +1,21 @@
+/*
+ This program prints out a verbose explanation of a given regular expression.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 2)
+ {
+ re_print(re_compile(argv[1]));
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> \n", argv[0]);
+ }
+ return -2;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c
new file mode 100644
index 0000000000..5a8baede6c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand.c
@@ -0,0 +1,28 @@
+/*
+ This program tries to match a given regular expression with text given as input to stdin.
+ If the text is a match for the pattern, the program returns 0.
+ If the text doesn't match the pattern, the program returns -2.
+
+ This program is used in random testing to test a lot of random text and regex together.
+ See ./scripts/regex_test.py and the Makefile for this project for the gritty details.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 3)
+ {
+ int m = re_match(argv[1], argv[2]);
+ if (m != -1)
+ return 0;
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> <TEXT> \n", argv[0]);
+ }
+ return -2;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c
new file mode 100644
index 0000000000..192ce5c608
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/lib/tiny_regex_c/tests/test_rand_neg.c
@@ -0,0 +1,29 @@
+/*
+ Negative version of test_rand.c -- returns true if no match
+
+ This program tries to match a given regular expression with text given as input to stdin.
+ If the text is NOT a match for the pattern, the program returns 0.
+ If the text does match the pattern, the program returns -2.
+
+ This program is used in random testing to test a lot of random text and regex together.
+ See ./scripts/regex_test_neg.py and the Makefile for this project for the gritty details.
+*/
+
+#include <stdio.h>
+#include "re.h"
+
+
+int main(int argc, char** argv)
+{
+ if (argc == 3)
+ {
+ int m = re_match(argv[1], argv[2]);
+ if (m == -1)
+ return 0;
+ }
+ else
+ {
+ printf("\nUsage: %s <PATTERN> <TEXT> \n", argv[0]);
+ }
+ return -2;
+}
diff --git a/erts/lib_src/yielding_c_fun/main_target.mk b/erts/lib_src/yielding_c_fun/main_target.mk
new file mode 100644
index 0000000000..de37c19897
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/main_target.mk
@@ -0,0 +1,26 @@
+# This file is included by GNUmakefile
+
+YCF_INCLUDE_DIRS = \
+ -I$(YCF_SOURCE_DIR) \
+ -I$(YCF_SOURCE_DIR)/lib/tiny_regex_c \
+ -I$(YCF_SOURCE_DIR)/lib/simple_c_gc
+
+YCF_HEADERS = $(sort $(shell find $(YCF_SOURCE_DIR) -name '*.h'))
+
+YCF_EXTRA_SOURCES = \
+ $(YCF_SOURCE_DIR)/lib/tiny_regex_c/re.c \
+ $(YCF_SOURCE_DIR)/lib/simple_c_gc/simple_c_gc.c
+
+YCF_SOURCES = $(sort $(wildcard $(YCF_SOURCE_DIR)/*.c) $(YCF_EXTRA_SOURCES))
+
+YCF_OBJECTS = $(patsubst $(YCF_SOURCE_DIR)/%.c, $(YCF_SOURCE_DIR)/%.o, $(YCF_SOURCES))
+
+YCF_CFLAGS = $(filter-out -Wstrict-prototypes -Wdeclaration-after-statement -Wmissing-prototypes,$(CFLAGS))
+
+YCF_EXECUTABLE = $(YCF_SOURCE_DIR)/bin/yielding_c_fun.bin$(EXE_SUFFIX)
+
+$(YCF_EXECUTABLE): $(YCF_OBJECTS)
+ $(V_LD) $(YCF_CFLAGS) $(LDFLAGS) $(YCF_OBJECTS) -o $@
+
+$(YCF_SOURCE_DIR)/%.o: $(YCF_SOURCE_DIR)/%.c $(YCF_HEADERS)
+ $(V_CC) $(YCF_CFLAGS) $(LDFLAGS) $(YCF_INCLUDE_DIRS) -c $< -o $@
diff --git a/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c
new file mode 100644
index 0000000000..3fa85ba061
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c
@@ -0,0 +1,124 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int rec_inc(int to_inc, int nr_of_incs){
+ int res;
+ printf("nr_of_incs %d\n", nr_of_incs);
+ if(nr_of_incs == 0) {
+ return to_inc;
+ } else {
+ res = rec_inc(to_inc + 1, nr_of_incs -1);
+ return res;
+ }
+}
+
+
+int fun(){
+ int i;
+ int v1;
+ int v2 = 13;
+ int v3 = 7;
+ int v4 = 5;
+ int v5 = 2;
+ printf("REC CALL START\n");
+ v1 = rec_inc(42, 100); /* v1 == 142 */
+ printf("REC CALL END\n");
+ printf("FOR LOOP START\n");
+ for(i = 0; i < 100; i++){
+ v2 = v2 + 1;
+ } /* v2 == 113 */
+ printf("FOR LOOP END\n");
+ printf("WHILE LOOP START\n");
+ i = 0;
+ while(i < 100){
+ v3 = v3 + 1;
+ i++;
+ } /* v3 == 107 */
+ printf("WHILE LOOP END\n");
+ printf("DO WHILE LOOP START\n");
+ i = 0;
+ do {
+ v4 = v4 + 1;
+ i++;
+ } while (i < 100); /* v4 == 105 */
+ printf("DO WHILE LOOP END\n");
+ printf("GOTO START\n");
+ i = 0;
+ my_label:
+ v5 = v5 + 1;
+ i++;
+ if (i < 100) goto my_label; /* v5 == 102 */
+ printf("GOTO END\n");
+ return v1 + v2 + v3 + v4 + v5; /* 142 + 113 + 107 + 105 + 102 == 569 */
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 10;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+ printf("REDS LEFT WHEN READY %ld\n", nr_of_reductions);
+#else
+ ret = fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 569){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out
new file mode 100644
index 0000000000..d6962791d0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/auto_yield.c.out
@@ -0,0 +1,173 @@
+REC CALL START
+nr_of_incs 100
+nr_of_incs 99
+nr_of_incs 98
+nr_of_incs 97
+nr_of_incs 96
+nr_of_incs 95
+nr_of_incs 94
+nr_of_incs 93
+nr_of_incs 92
+TRAPPED 0
+nr_of_incs 91
+nr_of_incs 90
+nr_of_incs 89
+nr_of_incs 88
+nr_of_incs 87
+nr_of_incs 86
+nr_of_incs 85
+nr_of_incs 84
+nr_of_incs 83
+nr_of_incs 82
+TRAPPED 0
+nr_of_incs 81
+nr_of_incs 80
+nr_of_incs 79
+nr_of_incs 78
+nr_of_incs 77
+nr_of_incs 76
+nr_of_incs 75
+nr_of_incs 74
+nr_of_incs 73
+nr_of_incs 72
+TRAPPED 0
+nr_of_incs 71
+nr_of_incs 70
+nr_of_incs 69
+nr_of_incs 68
+nr_of_incs 67
+nr_of_incs 66
+nr_of_incs 65
+nr_of_incs 64
+nr_of_incs 63
+nr_of_incs 62
+TRAPPED 0
+nr_of_incs 61
+nr_of_incs 60
+nr_of_incs 59
+nr_of_incs 58
+nr_of_incs 57
+nr_of_incs 56
+nr_of_incs 55
+nr_of_incs 54
+nr_of_incs 53
+nr_of_incs 52
+TRAPPED 0
+nr_of_incs 51
+nr_of_incs 50
+nr_of_incs 49
+nr_of_incs 48
+nr_of_incs 47
+nr_of_incs 46
+nr_of_incs 45
+nr_of_incs 44
+nr_of_incs 43
+nr_of_incs 42
+TRAPPED 0
+nr_of_incs 41
+nr_of_incs 40
+nr_of_incs 39
+nr_of_incs 38
+nr_of_incs 37
+nr_of_incs 36
+nr_of_incs 35
+nr_of_incs 34
+nr_of_incs 33
+nr_of_incs 32
+TRAPPED 0
+nr_of_incs 31
+nr_of_incs 30
+nr_of_incs 29
+nr_of_incs 28
+nr_of_incs 27
+nr_of_incs 26
+nr_of_incs 25
+nr_of_incs 24
+nr_of_incs 23
+nr_of_incs 22
+TRAPPED 0
+nr_of_incs 21
+nr_of_incs 20
+nr_of_incs 19
+nr_of_incs 18
+nr_of_incs 17
+nr_of_incs 16
+nr_of_incs 15
+nr_of_incs 14
+nr_of_incs 13
+nr_of_incs 12
+TRAPPED 0
+nr_of_incs 11
+nr_of_incs 10
+nr_of_incs 9
+nr_of_incs 8
+nr_of_incs 7
+nr_of_incs 6
+nr_of_incs 5
+nr_of_incs 4
+nr_of_incs 3
+nr_of_incs 2
+TRAPPED 0
+nr_of_incs 1
+nr_of_incs 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+REC CALL END
+FOR LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+FOR LOOP END
+WHILE LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+WHILE LOOP END
+DO WHILE LOOP START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+DO WHILE LOOP END
+GOTO START
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+TRAPPED 0
+GOTO END
+REDS LEFT WHEN READY 8
+RETURNED 569
diff --git a/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c
new file mode 100644
index 0000000000..683acda063
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(const char x){
+ const int y = x + 1; /* y == 2*/
+ int const *ptr = NULL;
+ YCF_YIELD();
+ if(ptr != NULL){
+ printf("ERROR\n");
+ }
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 2){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out
new file mode 100644
index 0000000000..37d51cb983
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/const_defenition.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 2
diff --git a/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c
new file mode 100644
index 0000000000..fcab3dbe5e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c
@@ -0,0 +1,85 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+
+int fun(int x){
+ int i;
+ int count = 0;
+ for(i = 0; i < 100; i++){
+ count = count + 2;
+ YCF_CONSUME_REDS(10);
+ }
+ return count + x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 201){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out
new file mode 100644
index 0000000000..32b93f33f6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/consume_reds.c.out
@@ -0,0 +1,11 @@
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+Number of yields 9
+RETURNED 201
diff --git a/erts/lib_src/yielding_c_fun/test/examples/control_statements.c b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c
new file mode 100644
index 0000000000..3abac5e57d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c
@@ -0,0 +1,289 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ YCF_YIELD();
+ if(x == 1){
+ /*empty control statement*/
+ } else {
+ /* another empty*/
+ }
+ if(x == 1){
+
+ } else {
+
+ }
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_1 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_2 %d %d\n", x, y);
+ }else printf("Hej\n");
+ YCF_YIELD();
+ if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_3 %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_4 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else{
+ int y = 4;
+ YCF_YIELD();
+ printf("s_5 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ while(x == 1) {
+ int y = 4;
+ x = 2;
+ YCF_YIELD();
+ printf("s_6 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ do x++; while(x == 1);
+ YCF_YIELD();
+ printf("s_7 %d\n", x);
+ YCF_YIELD();
+ x = 1;
+ do {
+ int y = 4;
+ YCF_YIELD();
+ printf("s_8 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ } while(x == 2);
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ while(x==1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_9 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ }
+ YCF_YIELD();
+ x = 2;
+ for(;;){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_10 %d %d\n", x, y);
+ YCF_YIELD();
+ break;
+ }
+ YCF_YIELD();
+ for(x = 0;x < 10;x++){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_11 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 42;
+ YCF_YIELD();
+ switch(x){
+ int y;
+ case 42:
+ y = 4;
+ YCF_YIELD();
+ printf("s_12 %d %d\n", x, y);
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ {
+ x = 1;
+ YCF_YIELD();
+ if(x == 1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_1 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 1) switch(1){
+ int y;
+ case 1:;
+ y = 4;
+ YCF_YIELD();
+ printf("s_2 %d %d\n", x, y);
+ }else printf("Hej\n");
+ YCF_YIELD();
+ if(x == 1) switch(1){
+ int y;
+ case 1:;
+ y = 4;
+ YCF_YIELD();
+ printf("s_3 %d %d\n", x, y);
+ }else if(x == 1){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(x == 1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_4 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ if(x == 2){
+ int y = 4;
+ printf("Hej %d %d\n", x, y);
+ }else if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_5 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ while(x == 1) if(1) {
+ int y = 4;
+ x = 2;
+ YCF_YIELD();
+ printf("s_6 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ do if(1) x++; while(x == 1);
+ YCF_YIELD();
+ printf("s_7 %d\n", x);
+ YCF_YIELD();
+ x = 1;
+ do if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_8 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ } while(x == 2);
+ YCF_YIELD();
+ x = 1;
+ YCF_YIELD();
+ while(x==1) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_9 %d %d\n", x, y);
+ YCF_YIELD();
+ x++;
+ }
+ YCF_YIELD();
+ x = 2;
+ for(;;) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_10 %d %d\n", x, y);
+ YCF_YIELD();
+ break;
+ }
+ YCF_YIELD();
+ for(x = 0;x < 10;x++) if(1){
+ int y = 4;
+ YCF_YIELD();
+ printf("s_11 %d %d\n", x, y);
+ }
+ YCF_YIELD();
+ x = 42;
+ YCF_YIELD();
+ switch(x){
+ int y;
+ case 42:
+ y = 4;
+ YCF_YIELD();
+ printf("s_12 %d %d\n", x, y);
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ }
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_trapps = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ nr_of_trapps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+ nr_of_trapps = 86;
+#endif
+ printf("RETURNED %d %d\n", ret, nr_of_trapps);
+ if(ret != 42){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out
new file mode 100644
index 0000000000..1beeae592e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/control_statements.c.out
@@ -0,0 +1,45 @@
+s_1 1 4
+s_2 1 4
+s_3 1 4
+s_4 1 4
+s_5 1 4
+s_6 2 4
+s_7 2
+s_8 1 4
+s_8 2 4
+s_9 1 4
+s_10 2 4
+s_11 0 4
+s_11 1 4
+s_11 2 4
+s_11 3 4
+s_11 4 4
+s_11 5 4
+s_11 6 4
+s_11 7 4
+s_11 8 4
+s_11 9 4
+s_12 42 4
+s_1 1 4
+s_2 1 4
+s_3 1 4
+s_4 1 4
+s_5 1 4
+s_6 2 4
+s_7 2
+s_8 1 4
+s_8 2 4
+s_9 1 4
+s_10 2 4
+s_11 0 4
+s_11 1 4
+s_11 2 4
+s_11 3 4
+s_11 4 4
+s_11 5 4
+s_11 6 4
+s_11 7 4
+s_11 8 4
+s_11 9 4
+s_12 42 4
+RETURNED 42 86
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c
new file mode 100644
index 0000000000..739cd9702a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c
@@ -0,0 +1,129 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int fun(char x){
+ int y = 10;
+ /*special_code_start:ON_SAVE_YIELD_STATE*/
+ if(0){
+ printf("y=%d\n", y);
+ y = 42;
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RETURN*/
+ if(0){
+ printf("I returned y=%d\n", y);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_DESTROY_STATE_OR_RETURN*/
+ if(0){
+ printf("I got destroyed or returned y=%d\n", y);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ x = 9;
+ }
+ /*special_code_end*/
+ if(0){
+ /*special_code_start:ON_SAVE_YIELD_STATE*/
+ if(0){
+ int z = 10;
+ printf("y=%d z=%d\n", y, z);
+ }
+ /*special_code_end*/
+ }
+ if(y != 10 || x != 1){
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ printf("y=%d x=%d\n", y, x);
+ x = x*2;
+ printf("y=%d x=%d\n", y, x);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_RESTORE_YIELD_STATE*/
+ if(0){
+ printf("y=%d x=%d\n", y, x);
+ x = x/2;
+ printf("y=%d x=%d\n", y, x);
+ }
+ /*special_code_end*/
+ printf("ERROR BEFORE YIELD\n");
+ exit(1);
+ }
+ YCF_YIELD();
+ if(y != 42 || x != 9){
+ printf("ERROR AFTER YIELD\n");
+ exit(1);
+ }
+ printf("SUCCESS\n");
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 9){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out
new file mode 100644
index 0000000000..639fd64b46
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state.c.out
@@ -0,0 +1,11 @@
+y=10
+y=42 z=10
+TRAPPED
+y=42 x=9
+y=42 x=18
+y=42 x=18
+y=42 x=9
+SUCCESS
+I returned y=42
+I got destroyed or returned y=42
+RETURNED 9
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c
new file mode 100644
index 0000000000..28e1512ece
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c
@@ -0,0 +1,131 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+#ifndef YCF_YIELDING_C_FUN_HELPERS
+
+#define ON_SAVE_YIELD_STATE
+#define ON_RESTORE_YIELD_STATE
+#define ON_DESTROY_STATE
+#define ON_RETURN
+#define ON_DESTROY_STATE_OR_RETURN
+#define YCF_SPECIAL_CODE_START(PARAM) \
+ /*special_code_start:PARAM*/ \
+ if(0){
+#define YCF_SPECIAL_CODE_END() \
+ } \
+ /*special_code_end*/
+
+#endif
+
+int fun(char x){
+ int y = 10;
+ YCF_SPECIAL_CODE_START(ON_SAVE_YIELD_STATE);
+ printf("y=%d\n", y);
+ y = 42;
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RETURN);
+ printf("I returned y=%d\n", y);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_DESTROY_STATE_OR_RETURN);
+ printf("I got destroyed or returned y=%d\n", y);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ x = 9;
+ YCF_SPECIAL_CODE_END();
+ if(0){
+ YCF_SPECIAL_CODE_START(ON_SAVE_YIELD_STATE); {
+ int z = 10;
+ printf("y=%d z=%d\n", y, z);
+ } YCF_SPECIAL_CODE_END();
+ }
+ if(y != 10 || x != 1){
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ printf("y=%d x=%d\n", y, x);
+ x = x*2;
+ printf("y=%d x=%d\n", y, x);
+ YCF_SPECIAL_CODE_END();
+ YCF_SPECIAL_CODE_START(ON_RESTORE_YIELD_STATE);
+ printf("y=%d x=%d\n", y, x);
+ x = x/2;
+ printf("y=%d x=%d\n", y, x);
+ YCF_SPECIAL_CODE_END();
+ printf("ERROR BEFORE YIELD\n");
+ exit(1);
+ }
+ YCF_YIELD();
+ if(y != 42 || x != 9){
+ printf("ERROR AFTER YIELD\n");
+ exit(1);
+ }
+ printf("SUCCESS\n");
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 9){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out
new file mode 100644
index 0000000000..639fd64b46
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/custom_code_save_restore_yield_state_alt_syntax.c.out
@@ -0,0 +1,11 @@
+y=10
+y=42 z=10
+TRAPPED
+y=42 x=9
+y=42 x=18
+y=42 x=18
+y=42 x=9
+SUCCESS
+I returned y=42
+I got destroyed or returned y=42
+RETURNED 9
diff --git a/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c
new file mode 100644
index 0000000000..76d500de24
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int fun(){
+ int array[1];
+ int* ptr;
+ array[0] = 1;
+ ptr = &array[0];
+ (void)ptr;
+ YCF_YIELD();
+ array[0] = 1;
+ return array[0];
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 1){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out
new file mode 100644
index 0000000000..7c3cfb5078
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/debug_ptr_to_stack.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations.c b/erts/lib_src/yielding_c_fun/test/examples/declarations.c
new file mode 100644
index 0000000000..718471c73f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations.c
@@ -0,0 +1,91 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+unsigned long fun(char x, char* y, char **z, unsigned long a, unsigned long* b, unsigned long** c){
+ unsigned int f = 3 + 3;
+ x = x + 1;
+ y = y + 1;
+ z = z + 1;
+ a = a + 1;
+ b = b + 1;
+ c = c + 1;
+ YCF_YIELD();
+ f = f - 6;
+ x = x + 1;
+ y = y + 1;
+ z = z + 1;
+ a = a + 1;
+ b = b + 1;
+ c = c + 1;
+ return (unsigned long)((long)(x + y) + ((z + a) + ((long)b + (long)c))) + f;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1,(void*)1,(void*)1,1,(void*)1,(void*)1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1,(void*)1,(void*)1,1,(void*)1,(void*)1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != fun(1,(void*)1,(void*)1,1,(void*)1,(void*)1)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out b/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out
new file mode 100644
index 0000000000..d96b566da5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 361
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c
new file mode 100644
index 0000000000..a8d2b02f02
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c
@@ -0,0 +1,83 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(){
+ int y = 0;
+ for(int x = 0; x < 10; x++){
+ y++;
+ for(int x = 0; x < 10; x++){
+ y++;
+ }
+ }
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 1;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 110){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out
new file mode 100644
index 0000000000..88a9c3bbd1
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/declarations_in_for_loops.c.out
@@ -0,0 +1,211 @@
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 110
diff --git a/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c
new file mode 100644
index 0000000000..aedaf74fb0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c
@@ -0,0 +1,92 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void fun(int level){
+ void* my_mem = malloc(1000);
+ /*special_code_start:ON_DESTROY_STATE*/
+ if(0){
+ printf("FREE AT LEVEL %d\n", level);
+ free(my_mem);
+ }
+ /*special_code_end*/
+ /*special_code_start:ON_DESTROY_STATE_OR_RETURN*/
+ if(0){
+ printf("I got destroyed or returned %d\n", level);
+ }
+ /*special_code_end*/
+ printf("LEVEL %d\n", level);
+ if (level == 10) {
+ YCF_YIELD();
+ printf("SHOULD NOT BE PRINTED 1\n");
+ return;
+ }else {
+ fun(level + 1);
+ printf("SHOULD NOT BE PRINTED 2\n");
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+ do {
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ fun_ycf_gen_destroy(wb);
+ printf("DESTROYED\n");
+ wb = NULL;
+ break;
+ }
+ } while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out
new file mode 100644
index 0000000000..bdf7893e32
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/destroy_while_yielded.c.out
@@ -0,0 +1,33 @@
+LEVEL 1
+LEVEL 2
+LEVEL 3
+LEVEL 4
+LEVEL 5
+LEVEL 6
+LEVEL 7
+LEVEL 8
+LEVEL 9
+LEVEL 10
+TRAPPED
+I got destroyed or returned 1
+FREE AT LEVEL 1
+I got destroyed or returned 2
+FREE AT LEVEL 2
+I got destroyed or returned 3
+FREE AT LEVEL 3
+I got destroyed or returned 4
+FREE AT LEVEL 4
+I got destroyed or returned 5
+FREE AT LEVEL 5
+I got destroyed or returned 6
+FREE AT LEVEL 6
+I got destroyed or returned 7
+FREE AT LEVEL 7
+I got destroyed or returned 8
+FREE AT LEVEL 8
+I got destroyed or returned 9
+FREE AT LEVEL 9
+I got destroyed or returned 10
+FREE AT LEVEL 10
+DESTROYED
+RETURNED
diff --git a/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c
new file mode 100644
index 0000000000..a99fed5753
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ int y;
+ y = 1;
+ int z = 1;
+ YCF_YIELD();
+ x = x + y + z; /* x == 4*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/in_code_var_declaration.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c
new file mode 100644
index 0000000000..87024abe39
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c
@@ -0,0 +1,93 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ long y = x + 1; /* y == 2*/
+ YCF_YIELD();
+ {
+ int x = 10;
+ YCF_YIELD();
+ y = y + x; /* y == 12*/
+ YCF_YIELD();
+ {
+ long x = 30;
+ YCF_YIELD();
+ y = y + x; /* y == 42*/
+ YCF_YIELD();
+ }
+ YCF_YIELD();
+ y = y + x; /* y == 52*/
+ }
+ YCF_YIELD();
+ y = y + x; /* y == 53*/
+ YCF_YIELD();
+ return y;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 53){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out
new file mode 100644
index 0000000000..bf653730af
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/multi_scope_yield.c.out
@@ -0,0 +1,9 @@
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 53
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c
new file mode 100644
index 0000000000..239de1a334
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c
@@ -0,0 +1,91 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+
+int sub_fun(int x){
+ int i;
+ int count = 0;
+ for(i = 0; i < 100; i++){
+ count = count + 2;
+ YCF_CONSUME_REDS(10);
+ }
+ return count + x;
+}
+
+int fun(int x){
+ int ret;
+ ret = sub_fun(x);
+ return ret;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 201){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out
new file mode 100644
index 0000000000..32b93f33f6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_call_consume_reds.c.out
@@ -0,0 +1,11 @@
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+TRAPPED -9
+Number of yields 9
+RETURNED 201
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c
new file mode 100644
index 0000000000..b87685e23d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c
@@ -0,0 +1,104 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(){
+ int x;
+ int y;
+ int z;
+ int outer = 0;
+ int inner = 0;
+ for(x = 0; x < 2; x++){
+ for(y = 0; y < 2; y++){ /* 2 times */
+ for(z = 0; z < 2; z++){ /* 4 times */
+ YCF_YIELD(); /* 8 times */
+ outer++;
+ printf("outer %d: x=%d y=%d z=%d\n", outer, x, y, z);
+ {
+ int x;
+ int y;
+ int z;
+ for(x = 0; x < 2; x++){ /* 8 times */
+ for(y = 0; y < 2; y++){ /* 16 times */
+ for(z = 0; z < 2; z++){ /* 32 times */
+ YCF_YIELD(); /* 64 times */
+ inner++;
+ printf("inner %d: x=%d y=%d z=%d\n", inner, x, y, z);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return inner + outer;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_traps = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ nr_of_traps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun();
+#endif
+ printf("NR OF TRAPS %d\n", nr_of_traps);
+ printf("RETURNED %d\n", ret);
+ if(ret != 72 || nr_of_traps != ret){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out
new file mode 100644
index 0000000000..b48664e112
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/nested_loop_yield.c.out
@@ -0,0 +1,74 @@
+outer 1: x=0 y=0 z=0
+inner 1: x=0 y=0 z=0
+inner 2: x=0 y=0 z=1
+inner 3: x=0 y=1 z=0
+inner 4: x=0 y=1 z=1
+inner 5: x=1 y=0 z=0
+inner 6: x=1 y=0 z=1
+inner 7: x=1 y=1 z=0
+inner 8: x=1 y=1 z=1
+outer 2: x=0 y=0 z=1
+inner 9: x=0 y=0 z=0
+inner 10: x=0 y=0 z=1
+inner 11: x=0 y=1 z=0
+inner 12: x=0 y=1 z=1
+inner 13: x=1 y=0 z=0
+inner 14: x=1 y=0 z=1
+inner 15: x=1 y=1 z=0
+inner 16: x=1 y=1 z=1
+outer 3: x=0 y=1 z=0
+inner 17: x=0 y=0 z=0
+inner 18: x=0 y=0 z=1
+inner 19: x=0 y=1 z=0
+inner 20: x=0 y=1 z=1
+inner 21: x=1 y=0 z=0
+inner 22: x=1 y=0 z=1
+inner 23: x=1 y=1 z=0
+inner 24: x=1 y=1 z=1
+outer 4: x=0 y=1 z=1
+inner 25: x=0 y=0 z=0
+inner 26: x=0 y=0 z=1
+inner 27: x=0 y=1 z=0
+inner 28: x=0 y=1 z=1
+inner 29: x=1 y=0 z=0
+inner 30: x=1 y=0 z=1
+inner 31: x=1 y=1 z=0
+inner 32: x=1 y=1 z=1
+outer 5: x=1 y=0 z=0
+inner 33: x=0 y=0 z=0
+inner 34: x=0 y=0 z=1
+inner 35: x=0 y=1 z=0
+inner 36: x=0 y=1 z=1
+inner 37: x=1 y=0 z=0
+inner 38: x=1 y=0 z=1
+inner 39: x=1 y=1 z=0
+inner 40: x=1 y=1 z=1
+outer 6: x=1 y=0 z=1
+inner 41: x=0 y=0 z=0
+inner 42: x=0 y=0 z=1
+inner 43: x=0 y=1 z=0
+inner 44: x=0 y=1 z=1
+inner 45: x=1 y=0 z=0
+inner 46: x=1 y=0 z=1
+inner 47: x=1 y=1 z=0
+inner 48: x=1 y=1 z=1
+outer 7: x=1 y=1 z=0
+inner 49: x=0 y=0 z=0
+inner 50: x=0 y=0 z=1
+inner 51: x=0 y=1 z=0
+inner 52: x=0 y=1 z=1
+inner 53: x=1 y=0 z=0
+inner 54: x=1 y=0 z=1
+inner 55: x=1 y=1 z=0
+inner 56: x=1 y=1 z=1
+outer 8: x=1 y=1 z=1
+inner 57: x=0 y=0 z=0
+inner 58: x=0 y=0 z=1
+inner 59: x=0 y=1 z=0
+inner 60: x=0 y=1 z=1
+inner 61: x=1 y=0 z=0
+inner 62: x=1 y=0 z=1
+inner 63: x=1 y=1 z=0
+inner 64: x=1 y=1 z=1
+NR OF TRAPS 72
+RETURNED 72
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore
new file mode 100644
index 0000000000..b5e8b97bac
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/.gitignore
@@ -0,0 +1,25 @@
+.rebar3
+_*
+.eunit
+*.o
+*.beam
+*.plt
+*.swp
+*.swo
+.erlang.cookie
+ebin
+log
+erl_crash.dump
+.rebar
+logs
+_build
+.idea
+*.iml
+rebar3.crashdump
+*~
+c_src/compile_commands.json
+c_src/compile_commands_old.json
+c_src/gen_yielding_sha_256.c
+c_src/gen_yielding_sha_256.h
+priv/
+rebar.lock
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE
new file mode 100644
index 0000000000..66232832b9
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/LICENSE
@@ -0,0 +1,191 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2019, Kjell Winblad <kjellwinblad@gmail.com>.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile
new file mode 100644
index 0000000000..8bae3dc26a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/Makefile
@@ -0,0 +1,34 @@
+
+ifeq ($(wildcard rebar3),rebar3)
+REBAR3 = $(CURDIR)/rebar3
+endif
+
+REBAR3 ?= $(shell test -e `which rebar3` 2>/dev/null && which rebar3 || echo "./rebar3")
+
+
+.PHONY: deps test build
+
+all: build test docs
+
+build: $(REBAR3)
+ @$(REBAR3) compile
+
+deps:
+ @$(REBAR3) get-deps
+
+clean:
+ @$(REBAR3) clean
+
+distclean: clean
+ @$(REBAR3) delete-deps
+
+docs:
+ @$(REBAR3) edoc
+
+
+test:
+ @$(REBAR3) ct
+
+
+release: test
+ @$(REBAR3) release
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md
new file mode 100644
index 0000000000..ba579880b3
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/README.md
@@ -0,0 +1,23 @@
+sha256_nif
+=====
+
+An OTP library that illustrates how one can implement a yielding
+Erlang NIF API with the help of Yielding C Fun.
+
+Build
+-----
+
+ $ rebar3 compile
+
+or
+
+ $ make
+
+Test
+----
+
+ $ rebar3 ct
+
+or
+
+ $ make test \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile
new file mode 100644
index 0000000000..dde621c591
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/Makefile
@@ -0,0 +1,83 @@
+# Based on c_src.mk from erlang.mk by Loic Hoguin <essen@ninenines.eu>
+
+CURDIR := $(shell pwd)
+BASEDIR := $(abspath $(CURDIR)/..)
+
+PROJECT ?= $(notdir $(BASEDIR))
+PROJECT := $(strip $(PROJECT))
+
+ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).")
+ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).")
+ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).")
+
+C_SRC_DIR = $(CURDIR)
+C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so
+
+# System type and C compiler/flags.
+
+UNAME_SYS := $(shell uname -s)
+ifeq ($(UNAME_SYS), Darwin)
+ CC ?= cc
+ CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall
+ CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
+ LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
+else ifeq ($(UNAME_SYS), FreeBSD)
+ CC ?= cc
+ CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+ CXXFLAGS ?= -O3 -finline-functions -Wall
+else ifeq ($(UNAME_SYS), Linux)
+ CC ?= gcc
+ CFLAGS ?= -O3 -std=c99 -g -Wall -Wno-missing-prototypes -Wno-unused-function
+#-O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
+ CXXFLAGS ?= -O3 -finline-functions -Wall
+endif
+
+EXTRACFLAGSYCFCODE=-Wno-unused-function
+
+CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
+
+LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei
+LDFLAGS += -shared
+
+# Verbosity.
+
+c_verbose_0 = @echo " C " $(?F);
+c_verbose = $(c_verbose_$(V))
+
+cpp_verbose_0 = @echo " CPP " $(?F);
+cpp_verbose = $(cpp_verbose_$(V))
+
+link_verbose_0 = @echo " LD " $(@F);
+link_verbose = $(link_verbose_$(V))
+
+SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \) | grep -v "./sha-2/sha-256_orginal.c" | grep -v "./sha-2/sha-256.c" | grep -v "./gen_yielding_sha_256.c" | grep -v "./sha-2/test.c")
+OBJECTS = gen_yielding_sha_256.o $(addsuffix .o, $(basename $(SOURCES)))
+
+COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
+COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
+
+$(C_SRC_OUTPUT): $(OBJECTS)
+ @mkdir -p $(BASEDIR)/priv/
+ $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)
+
+gen_yielding_sha_256.c: sha-2/sha-256.c
+ ../../../../bin/yielding_c_fun -use_gc -yield -debug -f calc_sha_256 -f calc_chunk -header_file_name gen_yielding_sha_256.h "sha-2/sha-256.c" > gen_yielding_sha_256.c
+
+gen_yielding_sha_256.o: gen_yielding_sha_256.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGSYCFCODE) $(CPPFLAGS) -c gen_yielding_sha_256.c
+
+%.o: %.c
+ $(COMPILE_C) $(OUTPUT_OPTION) $<
+
+%.o: %.cc
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.C
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+%.o: %.cpp
+ $(COMPILE_CPP) $(OUTPUT_OPTION) $<
+
+clean:
+ @rm -f gen_yielding_sha_256.c gen_yielding_sha_256.h $(C_SRC_OUTPUT) $(OBJECTS)
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore
new file mode 100644
index 0000000000..6587b2f0fc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.o
+test
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml
new file mode 100644
index 0000000000..ffec9e605d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/.travis.yml
@@ -0,0 +1,2 @@
+language: c
+script: make
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION
new file mode 100644
index 0000000000..28e48c7b6f
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/GIT_VERSION
@@ -0,0 +1,265 @@
+origin https://github.com/amosnier/sha-2.git (fetch)
+origin https://github.com/amosnier/sha-2.git (push)
+commit ff76937294694ec347819d81a1f580187fb34246
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:24:58 2018 +0200
+
+ Update README.md
+
+commit ad933ac6959f8507c349b2c60f02b9336f5ade10
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:24:18 2018 +0200
+
+ Update README.md
+
+commit 92e7db3c188a2c7a971564b2fdac2d38c7b329e3
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:22:45 2018 +0200
+
+ Update README.md
+
+commit 6d500e221aa29f9a3701833f1f31ccabba9c58f7
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 23:22:00 2018 +0200
+
+ Update README.md
+
+ Added section about reference implementation.
+
+commit 924fba5c17abac8e6b90eef99b509f2cf759d223
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 22:02:09 2018 +0200
+
+ Replaced some for loops by memset
+
+commit 9583456b329abe8bfbaaedd0aca76fc3eb31e0b8
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 19:16:41 2018 +0200
+
+ Implemented workaround for ld issue with large BSS
+
+ The large arrays are now allocated on heap instead of in BSS. This
+ actually feels like a pretty stupid linker limitation, and the
+ workaround leads to code that is less readable. But it only impacts
+ the test program.
+
+ The issue was reported with GCC 7.3.0 and compiling for 64-bit arch on
+ MSYS2's MinGW compiler. GCC 7.3.0 for 64-bit on Linux (my own machine
+ and Travis) seems perfectly OK with a large BSS segment (around 3 GB
+ is what the test program uses for test data).
+
+ Reported issue:
+
+ $ make
+ cc -Wall -Wextra -Wpedantic -c -o test.o test.c
+ cc -Wall -Wextra -Wpedantic -c -o sha-256.o sha-256.c
+ cc test.o sha-256.o -o test
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o: in function `__tmainCRTStartup':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:251:(.text+0x1cd): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_Sleep' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs01360.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:278:(.text+0x255): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_SetUnhandledExceptionFilter' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs01346.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:286:(.text+0x283): relocation truncated to fit: R_X86_64_PC32 against symbol `__mingw_winmain_hInstance' defined in COMMON section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:312:(.text+0x2f3): relocation truncated to fit: R_X86_64_PC32 against symbol `__mingw_winmain_lpCmdLine' defined in COMMON section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:238:(.text+0x475): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetStartupInfoA' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00721.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-gccmain.o): in function `__main':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:74:(.text+0xb2): relocation truncated to fit: R_X86_64_PC32 against `.bss'
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:76:(.text+0xc2): relocation truncated to fit: R_X86_64_PC32 against `.bss'
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-charmax.o): in function `my_lconv_init':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/charmax.c:19:(.text+0x3): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp___lconv_init' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmsvcrt.a(dkxcbs00090.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-gs_support.o): in function `__security_init_cookie':
+ C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:62:(.text+0x47): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetSystemTimeAsFileTime' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00746.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:70:(.text+0x52): relocation truncated to fit: R_X86_64_PC32 against symbol `__imp_GetCurrentProcessId' defined in .idata$5 section in C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/lib/../lib/libkernel32.a(dkhchs00536.o)
+ C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gs_support.c:71:(.text+0x5b): additional relocation overflows omitted from the output
+ collect2.exe: error: ld returned 1 exit status
+ make: *** [<builtin>: test] Error 1```
+
+commit 23b53726f3c5c283112110468ff1c9b4981260e9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Sat Jul 28 16:53:38 2018 +0200
+
+ Added explicit cast to correct printf() related bug
+
+commit f03474231fc0e86c66cbe2a74a9d9f6589dd96f9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:41:17 2017 +0100
+
+ Activated testing of long messages
+
+commit a222d72880c8b9b2b22155d34adfc72e49e511c5
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:11:41 2017 +0100
+
+ Minor README change
+
+commit 98a0bf165c558dde77211ff09cc6b0e1c30badc7
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:08:48 2017 +0100
+
+ Minor README change
+
+commit 688b14926c42b61dbfb61f3046594afb058dad31
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:07:54 2017 +0100
+
+ Added Travis comment
+
+commit d6b49c65bdaa54120d20401e15368cb462f8a122
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 23:00:29 2017 +0100
+
+ Fixed minor warnings
+
+commit a6c25e2cdf96eeb522199c67e895f0791e297fc9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:53:19 2017 +0100
+
+ Included test in make
+
+commit 538cfe97fa7ad217875196b4e5ce8ddfc1d88f7a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:34:57 2017 +0100
+
+ Travis icon again
+
+commit 903e46163b6d6258b3d5e5b5de60f134749e2f10
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:33:50 2017 +0100
+
+ Work on Travis icon
+
+commit d87b78db4bfa08ddceecb3837943b7886d2f4f88
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:31:02 2017 +0100
+
+ Added Travis link
+
+commit 19d1e8a568af53c9788ed6fc2ecc9fcb34b17037
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 22:24:03 2017 +0100
+
+ Started adding Travis
+
+commit 8f83e8dedf169858efd366fe2237ec31e8f3c3ec
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:54:31 2017 +0100
+
+ Minor README change
+
+commit 369c47af630d6f3dc6cf817e402ef070a588d5a9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:52:36 2017 +0100
+
+ Minor README change
+
+commit b177afbe06965bec656f0cf437f449f747f6575e
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:45:25 2017 +0100
+
+ Improved formatting
+
+commit 8fd2b04fed6b5bcaad04e66cb2a3158b61b5ce1e
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:44:04 2017 +0100
+
+ Improved formatting
+
+commit 96988951cc63d2305ce7612ebad5456bfca444c9
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:40:56 2017 +0100
+
+ Improved formatting
+
+commit 37500f461fbc5e0553ec4ea2f5ec449126ac63c0
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:37:39 2017 +0100
+
+ Formatting improvement
+
+commit 6f35ae7007f0f9b54e5798d6a63ffbd1f9f9d59c
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 21:34:20 2017 +0100
+
+ Added extensive testing
+
+commit cba84edcb52079236c0c32c591a74ec89155fc62
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Fri Dec 15 19:37:47 2017 +0100
+
+ Made changes according to review comments from StackExchange CODE REVIEW
+
+commit d7214be72713f020f2dffbac01a13442921cabce
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Thu Dec 14 22:45:59 2017 +0100
+
+ Added comment about code review
+
+commit da12ae4e94725e87052da5c0191103de0d817ed5
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Thu Dec 14 20:11:09 2017 +0100
+
+ Tests at compile time instead of runtime
+
+commit b21a7b552969cbf8a2c702f617e15750fe1b2b2b
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:03:01 2017 +0100
+
+ Added comment
+
+commit 85e93b4a79b7ed9f20077021b489c3a1cbd6a251
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:01:19 2017 +0100
+
+ Typo
+
+commit ccc3d23b29eeb4c1ace83ae93f415d7a3d57ed45
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 12:00:03 2017 +0100
+
+ Added comment.
+
+commit 047f309134372f826027ac383667c18ce7a2062f
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:29:58 2017 +0100
+
+ Added comment about testing
+
+commit 5833df68d9de2f1cb1cc3db57608eba25559a9bf
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:27:39 2017 +0100
+
+ Removed temporary code
+
+commit f3f5e1b4532a40e37fc9b880f348a525c4a4165a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:23:44 2017 +0100
+
+ Added some notes
+
+commit 097f4b536db64d29fbc5f54d052ba795db68f00f
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:18:17 2017 +0100
+
+ Typo
+
+commit 73684be1627a05d4dbd585d2cc926e6b91275dc4
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:14:32 2017 +0100
+
+ Corrected typo
+
+commit 489f5e66b63ed90ed3de197237e608e101253efb
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 11:12:47 2017 +0100
+
+ Completed the implementation, including testing, and fixed bugs
+
+commit d676207600a94c5d94ef6f6188cc1c907f06b42a
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Wed Dec 13 00:52:28 2017 +0100
+
+ Started a SHA-256 implementation. Buggy so far.
+
+commit 3433d4ef5ccb136f0b889610cac3f752776ea525
+Author: Alain Mosnier <alain@wanamoon.net>
+Date: Tue Dec 12 20:48:09 2017 +0100
+
+ Initial commit
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE
new file mode 100644
index 0000000000..cf1ab25da0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile
new file mode 100644
index 0000000000..53bcbab93e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -Wall -Wextra -Wpedantic
+
+.PHONY: all
+all: test
+ ./test
+
+test: test.o sha-256.o
+
+.PHONY: clean
+clean:
+ rm test *.o
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md
new file mode 100644
index 0000000000..167a69d08c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/README.md
@@ -0,0 +1,97 @@
+# sha-2 [![Build Status](https://travis-ci.org/amosnier/sha-2.svg?branch=master)](https://travis-ci.org/amosnier/sha-2)
+
+## Contents
+
+SHA-2 algorithm implementations.
+
+At the moment, only SHA-256 is implemented.
+
+## Design criteria
+
+- Easy to test, include in any project, compile and link.
+
+- ANSI C with as little specific C99 as possible (e.g. extended
+ integer types are used, but not bool).
+
+- Portable. Makes no assumptions on the target system's endianess or
+ word size.
+
+- The SHA-256 implementation is a straightforward implementation of
+ the algorithm specified on
+ [Wikipedia](https://en.wikipedia.org/wiki/SHA-2).
+
+## Notes
+
+The Makefile is as minimal as possible. No effort was put into making
+it general. Its purpose is mainly to ease testing for the developer's
+host machine. The actual implementation is however extremely easy to
+include in any project, may it use GNU make or any other build tool.
+
+## Code review
+
+This code has been reviewed at [Stack Exchange CODE
+REVIEW](https://codereview.stackexchange.com/questions/182812/self-contained-sha-256-implementation-in-c),
+and the implementation has been improved accordingly.
+
+## Testing
+
+Testing is continuously performed on Travis CI (see above).
+
+Apart from that, the implementation has been successfully tested on an x86-64 machine
+under Linux as well as on a 16-bit DSP. On the x86-64 machine, all the
+available NIST test vectors where successfully tested ([SHA-256
+examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf)
+and [SHA-2 Additional
+examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA2_Additional.pdf),
+plus a few others).
+
+In particular:
+
+```
+Input Message: "abc"
+Message Digest is BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD
+```
+
+```
+Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+Message Digest is 248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1
+```
+
+```
+SHA-256 Test Data
+#1) 1 byte 0xbd
+68325720 aabd7c82 f30f554b 313d0570 c95accbb 7dc4b5aa e11204c0 8ffe732b
+#2) 4 bytes 0xc98c8e55
+7abc22c0 ae5af26c e93dbb94 433a0e0b 2e119d01 4f8e7f65 bd56c61c cccd9504
+#3) 55 bytes of zeros
+02779466 cdec1638 11d07881 5c633f21 90141308 1449002f 24aa3e80 f0b88ef7
+#4) 56 bytes of zeros
+d4817aa5 497628e7 c77e6b60 6107042b bba31308 88c5f47a 375e6179 be789fbb
+#5) 57 bytes of zeros
+65a16cb7 861335d5 ace3c607 18b5052e 44660726 da4cd13b b745381b 235a1785
+#6) 64 bytes of zeros
+f5a5fd42 d16a2030 2798ef6e d309979b 43003d23 20d9f0e8 ea9831a9 2759fb4b
+#7) 1000 bytes of zeros
+541b3e9d aa09b20b f85fa273 e5cbd3e8 0185aa4e c298e765 db87742b 70138a53
+#8) 1000 bytes of 0x41 ‘A’
+c2e68682 3489ced2 017f6059 b8b23931 8b6364f6 dcd835d0 a519105a 1eadd6e4
+#9) 1005 bytes of 0x55 ‘U’
+f4d62dde c0f3dd90 ea1380fa 16a5ff8d c4c54b21 740650f2 4afc4120 903552b0
+#10) 1000000 bytes of zeros
+d29751f2 649b32ff 572b5e0a 9f541ea6 60a50f94 ff0beedf b0b692b9 24cc8025
+#11) 0x20000000 (536870912) bytes of 0x5a ‘Z’
+15a1868c 12cc5395 1e182344 277447cd 0979536b adcc512a d24c67e9 b2d4f3dd
+#12) 0x41000000 (1090519040) bytes of zeros
+461c19a9 3bd4344f 9215f5ec 64357090 342bc66b 15a14831 7d276e31 cbc20b53
+#13) 0x6000003e (1610612798) bytes of 0x42 ‘B’
+c23ce8a7 895f4b21 ec0daf37 920ac0a2 62a22004 5a03eb2d fed48ef9 b05aabea
+```
+
+## License
+
+This repository is made available in the public domain. See [LICENSE
+FILE](LICENSE).
+
+## Reference implementation
+
+I had missed that when I made this implementation but [RFC 6234, chapter 8](https://tools.ietf.org/html/rfc6234#section-8) actually includes a reference implementation in C that is (at least in ambition) broader in scope than this one. I have however neither compiled nor tested it.
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c
new file mode 100644
index 0000000000..4338531d7e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.c
@@ -0,0 +1,224 @@
+#include <stdint.h>
+#include <string.h>
+
+#ifdef YCF_YIELD_CODE_GENERATED
+#include "sha-2/sha-256.h"
+#else
+#include "sha-256.h"
+#endif
+
+#define CHUNK_SIZE 64
+#define TOTAL_LEN_LEN 8
+
+/*
+ * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
+ */
+
+/*
+ * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
+ * When useful for clarification, portions of the pseudo-code are reproduced here too.
+ */
+
+/*
+ * Initialize array of round constants:
+ * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
+ */
+static const uint32_t k[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+struct buffer_state {
+ const uint8_t * p;
+ size_t len;
+ size_t total_len;
+ int single_one_delivered; /* bool */
+ int total_len_delivered; /* bool */
+};
+
+static inline uint32_t right_rot(uint32_t value, unsigned int count)
+{
+ /*
+ * Defined behaviour in standard C for all count where 0 < count < 32,
+ * which is what we need here.
+ */
+ return value >> count | value << (32 - count);
+}
+
+static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
+{
+ state->p = input;
+ state->len = len;
+ state->total_len = len;
+ state->single_one_delivered = 0;
+ state->total_len_delivered = 0;
+}
+
+/* Return value: bool */
+static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state)
+{
+ size_t space_in_chunk;
+
+ if (state->total_len_delivered) {
+ return 0;
+ }
+
+ if (state->len >= CHUNK_SIZE) {
+ memcpy(chunk, state->p, CHUNK_SIZE);
+ state->p += CHUNK_SIZE;
+ state->len -= CHUNK_SIZE;
+ return 1;
+ }
+
+ memcpy(chunk, state->p, state->len);
+ chunk += state->len;
+ space_in_chunk = CHUNK_SIZE - state->len;
+ state->p += state->len;
+ state->len = 0;
+
+ /* If we are here, space_in_chunk is one at minimum. */
+ if (!state->single_one_delivered) {
+ *chunk++ = 0x80;
+ space_in_chunk -= 1;
+ state->single_one_delivered = 1;
+ }
+
+ /*
+ * Now:
+ * - either there is enough space left for the total length, and we can conclude,
+ * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
+ * In the latter case, we will conclude at the next invokation of this function.
+ */
+ if (space_in_chunk >= TOTAL_LEN_LEN) {
+ const size_t left = space_in_chunk - TOTAL_LEN_LEN;
+ size_t len = state->total_len;
+ int i;
+ memset(chunk, 0x00, left);
+ chunk += left;
+
+ /* Storing of len * 8 as a big endian 64-bit without overflow. */
+ chunk[7] = (uint8_t) (len << 3);
+ len >>= 5;
+ for (i = 6; i >= 0; i--) {
+ chunk[i] = (uint8_t) len;
+ len >>= 8;
+ }
+ state->total_len_delivered = 1;
+ } else {
+ memset(chunk, 0x00, space_in_chunk);
+ }
+
+ return 1;
+}
+
+#define YCF_STACK_ALLOC(X) malloc(X)
+
+
+/*
+ * Limitations:
+ * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
+ * for large data sizes.
+ * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
+ * for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
+ * In particular, the len parameter is a number of bytes.
+ */
+void calc_sha_256(uint8_t hash[32], const void * input, size_t len)
+{
+ /*
+ * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
+ * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
+ * Note 3: The compression function uses 8 working variables, a through h
+ * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
+ * and when parsing message block data from bytes to words, for example,
+ * the first word of the input message "abc" after padding is 0x61626380
+ */
+
+ /*
+ * Initialize hash values:
+ * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
+ */
+ uint32_t h[8];
+ int i;
+ int j;
+
+ /* 512-bit chunks is what we will operate on. */
+ uint8_t* chunk = YCF_STACK_ALLOC(64);
+ struct buffer_state state;
+ h[0] = 0x6a09e667;
+ h[1] = 0xbb67ae85;
+ h[2] = 0x3c6ef372;
+ h[3] = 0xa54ff53a;
+ h[4] = 0x510e527f;
+ h[5] = 0x9b05688c;
+ h[6] = 0x1f83d9ab;
+ h[7] = 0x5be0cd19;
+ init_buf_state(&state, input, len);
+
+ while (calc_chunk(chunk, &state)) {
+ uint32_t ah[8];
+
+ /*
+ * create a 64-entry message schedule array w[0..63] of 32-bit words
+ * (The initial values in w[0..63] don't matter, so many implementations zero them here)
+ * copy chunk into first 16 words w[0..15] of the message schedule array
+ */
+ uint32_t w[64];
+ const uint8_t *p = chunk;
+
+ memset(w, 0x00, sizeof w);
+ for (i = 0; i < 16; i++) {
+ w[i] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
+ (uint32_t) p[2] << 8 | (uint32_t) p[3];
+ p += 4;
+ }
+
+ /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
+ for (i = 16; i < 64; i++) {
+ const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3);
+ const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10);
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1;
+ }
+
+ /* Initialize working variables to current hash value: */
+ for (i = 0; i < 8; i++)
+ ah[i] = h[i];
+
+ /* Compression function main loop: */
+ for (i = 0; i < 64; i++) {
+ const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
+ const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
+ const uint32_t temp1 = ah[7] + s1 + ch + k[i] + w[i];
+ const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
+ const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
+ const uint32_t temp2 = s0 + maj;
+
+ ah[7] = ah[6];
+ ah[6] = ah[5];
+ ah[5] = ah[4];
+ ah[4] = ah[3] + temp1;
+ ah[3] = ah[2];
+ ah[2] = ah[1];
+ ah[1] = ah[0];
+ ah[0] = temp1 + temp2;
+ }
+
+ /* Add the compressed chunk to the current hash value: */
+ for (i = 0; i < 8; i++)
+ h[i] += ah[i];
+ }
+
+ /* Produce the final hash value (big-endian): */
+ for (i = 0, j = 0; i < 8; i++)
+ {
+ hash[j++] = (uint8_t) (h[i] >> 24);
+ hash[j++] = (uint8_t) (h[i] >> 16);
+ hash[j++] = (uint8_t) (h[i] >> 8);
+ hash[j++] = (uint8_t) h[i];
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h
new file mode 100644
index 0000000000..2cce60bb7b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+void calc_sha_256(uint8_t hash[], const void *input, size_t len);
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c
new file mode 100644
index 0000000000..53d6ff2e2a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/sha-256_orginal.c
@@ -0,0 +1,210 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "sha-256.h"
+
+#define CHUNK_SIZE 64
+#define TOTAL_LEN_LEN 8
+
+/*
+ * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
+ */
+
+/*
+ * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
+ * When useful for clarification, portions of the pseudo-code are reproduced here too.
+ */
+
+/*
+ * Initialize array of round constants:
+ * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
+ */
+static const uint32_t k[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+struct buffer_state {
+ const uint8_t * p;
+ size_t len;
+ size_t total_len;
+ int single_one_delivered; /* bool */
+ int total_len_delivered; /* bool */
+};
+
+static inline uint32_t right_rot(uint32_t value, unsigned int count)
+{
+ /*
+ * Defined behaviour in standard C for all count where 0 < count < 32,
+ * which is what we need here.
+ */
+ return value >> count | value << (32 - count);
+}
+
+static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
+{
+ state->p = input;
+ state->len = len;
+ state->total_len = len;
+ state->single_one_delivered = 0;
+ state->total_len_delivered = 0;
+}
+
+/* Return value: bool */
+static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state)
+{
+ size_t space_in_chunk;
+
+ if (state->total_len_delivered) {
+ return 0;
+ }
+
+ if (state->len >= CHUNK_SIZE) {
+ memcpy(chunk, state->p, CHUNK_SIZE);
+ state->p += CHUNK_SIZE;
+ state->len -= CHUNK_SIZE;
+ return 1;
+ }
+
+ memcpy(chunk, state->p, state->len);
+ chunk += state->len;
+ space_in_chunk = CHUNK_SIZE - state->len;
+ state->p += state->len;
+ state->len = 0;
+
+ /* If we are here, space_in_chunk is one at minimum. */
+ if (!state->single_one_delivered) {
+ *chunk++ = 0x80;
+ space_in_chunk -= 1;
+ state->single_one_delivered = 1;
+ }
+
+ /*
+ * Now:
+ * - either there is enough space left for the total length, and we can conclude,
+ * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
+ * In the latter case, we will conclude at the next invokation of this function.
+ */
+ if (space_in_chunk >= TOTAL_LEN_LEN) {
+ const size_t left = space_in_chunk - TOTAL_LEN_LEN;
+ size_t len = state->total_len;
+ int i;
+ memset(chunk, 0x00, left);
+ chunk += left;
+
+ /* Storing of len * 8 as a big endian 64-bit without overflow. */
+ chunk[7] = (uint8_t) (len << 3);
+ len >>= 5;
+ for (i = 6; i >= 0; i--) {
+ chunk[i] = (uint8_t) len;
+ len >>= 8;
+ }
+ state->total_len_delivered = 1;
+ } else {
+ memset(chunk, 0x00, space_in_chunk);
+ }
+
+ return 1;
+}
+
+/*
+ * Limitations:
+ * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
+ * for large data sizes.
+ * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
+ * for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
+ * In particular, the len parameter is a number of bytes.
+ */
+void calc_sha_256(uint8_t hash[32], const void * input, size_t len)
+{
+ /*
+ * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
+ * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
+ * Note 3: The compression function uses 8 working variables, a through h
+ * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
+ * and when parsing message block data from bytes to words, for example,
+ * the first word of the input message "abc" after padding is 0x61626380
+ */
+
+ /*
+ * Initialize hash values:
+ * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
+ */
+ uint32_t h[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
+ int i, j;
+
+ /* 512-bit chunks is what we will operate on. */
+ uint8_t chunk[64];
+
+ struct buffer_state state;
+
+ init_buf_state(&state, input, len);
+
+ while (calc_chunk(chunk, &state)) {
+ uint32_t ah[8];
+
+ /*
+ * create a 64-entry message schedule array w[0..63] of 32-bit words
+ * (The initial values in w[0..63] don't matter, so many implementations zero them here)
+ * copy chunk into first 16 words w[0..15] of the message schedule array
+ */
+ uint32_t w[64];
+ const uint8_t *p = chunk;
+
+ memset(w, 0x00, sizeof w);
+ for (i = 0; i < 16; i++) {
+ w[i] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
+ (uint32_t) p[2] << 8 | (uint32_t) p[3];
+ p += 4;
+ }
+
+ /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
+ for (i = 16; i < 64; i++) {
+ const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3);
+ const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10);
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1;
+ }
+
+ /* Initialize working variables to current hash value: */
+ for (i = 0; i < 8; i++)
+ ah[i] = h[i];
+
+ /* Compression function main loop: */
+ for (i = 0; i < 64; i++) {
+ const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
+ const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
+ const uint32_t temp1 = ah[7] + s1 + ch + k[i] + w[i];
+ const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
+ const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
+ const uint32_t temp2 = s0 + maj;
+
+ ah[7] = ah[6];
+ ah[6] = ah[5];
+ ah[5] = ah[4];
+ ah[4] = ah[3] + temp1;
+ ah[3] = ah[2];
+ ah[2] = ah[1];
+ ah[1] = ah[0];
+ ah[0] = temp1 + temp2;
+ }
+
+ /* Add the compressed chunk to the current hash value: */
+ for (i = 0; i < 8; i++)
+ h[i] += ah[i];
+ }
+
+ /* Produce the final hash value (big-endian): */
+ for (i = 0, j = 0; i < 8; i++)
+ {
+ hash[j++] = (uint8_t) (h[i] >> 24);
+ hash[j++] = (uint8_t) (h[i] >> 16);
+ hash[j++] = (uint8_t) (h[i] >> 8);
+ hash[j++] = (uint8_t) h[i];
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c
new file mode 100644
index 0000000000..5b5031c374
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha-2/test.c
@@ -0,0 +1,238 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sha-256.h"
+
+struct string_vector {
+ const char *input;
+ const char *output;
+};
+
+static const struct string_vector STRING_VECTORS[] = {
+ {
+ "",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ },
+ {
+ "abc",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ "a8ae6e6ee929abea3afcfc5258c8ccd6f85273e0d4626d26c7279f3250f77c8e"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde",
+ "057ee79ece0b9a849552ab8d3c335fe9a5f1c46ef5f1d9b190c295728628299c"
+ },
+ {
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0",
+ "2a6ad82f3620d3ebe9d678c812ae12312699d673240d5be8fac0910a70000d93"
+ },
+ {
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+ },
+ {
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"
+ "ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
+ }
+};
+
+#define LARGE_MESSAGES 1
+
+static uint8_t data1[] = { 0xbd };
+static uint8_t data2[] = { 0xc9, 0x8c, 0x8e, 0x55 };
+static uint8_t data7[1000];
+static uint8_t data8[1000];
+static uint8_t data9[1005];
+#if LARGE_MESSAGES
+#define SIZEOF_DATA11 536870912
+#define SIZEOF_DATA12 1090519040
+#define SIZEOF_DATA13 1610612798
+static uint8_t * data11;
+static uint8_t * data12;
+static uint8_t * data13;
+#endif
+
+struct vector {
+ const uint8_t *input;
+ size_t input_len;
+ const char *output;
+};
+
+static struct vector vectors[] = {
+ {
+ data1,
+ sizeof data1,
+ "68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b"
+ },
+ {
+ data2,
+ sizeof data2,
+ "7abc22c0ae5af26ce93dbb94433a0e0b2e119d014f8e7f65bd56c61ccccd9504"
+ },
+ {
+ data7,
+ 55,
+ "02779466cdec163811d078815c633f21901413081449002f24aa3e80f0b88ef7"
+ },
+ {
+ data7,
+ 56,
+ "d4817aa5497628e7c77e6b606107042bbba3130888c5f47a375e6179be789fbb"
+ },
+ {
+ data7,
+ 57,
+ "65a16cb7861335d5ace3c60718b5052e44660726da4cd13bb745381b235a1785"
+ },
+ {
+ data7,
+ 64,
+ "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b"
+ },
+ {
+ data7,
+ sizeof data7,
+ "541b3e9daa09b20bf85fa273e5cbd3e80185aa4ec298e765db87742b70138a53"
+ },
+ {
+ data8,
+ sizeof data8,
+ "c2e686823489ced2017f6059b8b239318b6364f6dcd835d0a519105a1eadd6e4"
+ },
+ {
+ data9,
+ sizeof data9,
+ "f4d62ddec0f3dd90ea1380fa16a5ff8dc4c54b21740650f24afc4120903552b0"
+ }
+#if LARGE_MESSAGES
+ ,
+ {
+ NULL,
+ 1000000,
+ "d29751f2649b32ff572b5e0a9f541ea660a50f94ff0beedfb0b692b924cc8025"
+ },
+ {
+ NULL,
+ SIZEOF_DATA11,
+ "15a1868c12cc53951e182344277447cd0979536badcc512ad24c67e9b2d4f3dd"
+ },
+ {
+ NULL,
+ SIZEOF_DATA12,
+ "461c19a93bd4344f9215f5ec64357090342bc66b15a148317d276e31cbc20b53"
+ },
+ {
+ NULL,
+ SIZEOF_DATA13,
+ "c23ce8a7895f4b21ec0daf37920ac0a262a220045a03eb2dfed48ef9b05aabea"
+ }
+#endif
+};
+
+static void construct_binary_messages(void)
+{
+ memset(data7, 0x00, sizeof data7);
+ memset(data8, 0x41, sizeof data8);
+ memset(data9, 0x55, sizeof data9);
+#if LARGE_MESSAGES
+ /*
+ * Heap allocation as a workaround for some linkers not liking
+ * large BSS segments.
+ */
+ data11 = malloc(SIZEOF_DATA11);
+ data12 = malloc(SIZEOF_DATA12);
+ data13 = malloc(SIZEOF_DATA13);
+ memset(data11, 0x5a, SIZEOF_DATA11);
+ memset(data12, 0x00, SIZEOF_DATA12);
+ memset(data13, 0x42, SIZEOF_DATA13);
+ vectors[9].input = data12;
+ vectors[10].input = data11;
+ vectors[11].input = data12;
+ vectors[12].input = data13;
+#endif
+}
+
+static void destruct_binary_messages(void)
+{
+#if LARGE_MESSAGES
+ free(data11);
+ free(data12);
+ free(data13);
+#endif
+}
+
+static void hash_to_string(char string[65], const uint8_t hash[32])
+{
+ size_t i;
+ for (i = 0; i < 32; i++) {
+ string += sprintf(string, "%02x", hash[i]);
+ }
+}
+
+static int string_test(const char input[], const char output[])
+{
+ uint8_t hash[32];
+ char hash_string[65];
+ calc_sha_256(hash, input, strlen(input));
+ hash_to_string(hash_string, hash);
+ printf("input: %s\n", input);
+ printf("hash : %s\n", hash_string);
+ if (strcmp(output, hash_string)) {
+ printf("FAILURE!\n\n");
+ return 1;
+ } else {
+ printf("SUCCESS!\n\n");
+ return 0;
+ }
+}
+
+/*
+ * Limitation:
+ * - The variable input_len will be truncated to its LONG_BIT least
+ * significant bits in the print output. This will never be a problem
+ * for values that in practice are less than 2^32 - 1. Rationale: ANSI
+ * C-compatibility and keeping it simple.
+ */
+static int test(const uint8_t * input, size_t input_len, const char output[])
+{
+ uint8_t hash[32];
+ char hash_string[65];
+ calc_sha_256(hash, input, input_len);
+ hash_to_string(hash_string, hash);
+ printf("input starts with 0x%02x, length %lu\n", *input, (unsigned long) input_len);
+ printf("hash : %s\n", hash_string);
+ if (strcmp(output, hash_string)) {
+ printf("FAILURE!\n\n");
+ return 1;
+ } else {
+ printf("SUCCESS!\n\n");
+ return 0;
+ }
+}
+
+int main(void)
+{
+ size_t i;
+ for (i = 0; i < (sizeof STRING_VECTORS / sizeof (struct string_vector)); i++) {
+ const struct string_vector *vector = &STRING_VECTORS[i];
+ if (string_test(vector->input, vector->output))
+ return 1;
+ }
+ construct_binary_messages();
+ for (i = 0; i < (sizeof vectors / sizeof (struct vector)); i++) {
+ const struct vector *vector = &vectors[i];
+ if (test(vector->input, vector->input_len, vector->output))
+ {
+ destruct_binary_messages();
+ return 1;
+ }
+ }
+ destruct_binary_messages();
+ return 0;
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c
new file mode 100644
index 0000000000..6ad22c1dcd
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/c_src/sha256_nif.c
@@ -0,0 +1,173 @@
+
+#include "erl_nif.h"
+
+#include "sha-2/sha-256.h"
+struct buffer_state;
+#include "gen_yielding_sha_256.h"
+
+
+
+
+ERL_NIF_TERM
+mk_atom(ErlNifEnv* env, const char* atom)
+{
+ ERL_NIF_TERM ret;
+
+ if(!enif_make_existing_atom(env, atom, &ret, ERL_NIF_LATIN1))
+ {
+ return enif_make_atom(env, atom);
+ }
+
+ return ret;
+}
+
+ERL_NIF_TERM
+mk_error(ErlNifEnv* env, const char* mesg)
+{
+ return enif_make_tuple2(env, mk_atom(env, "error"), mk_atom(env, mesg));
+}
+
+
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return enif_alloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ enif_free(data);
+}
+
+typedef struct {
+ void* continuation;
+ ErlNifEnv* work_env;
+ ErlNifBinary out_bin;
+} sha256continuation;
+
+/* #define DEBUG */
+#ifdef DEBUG
+# define DEBUG_PRINT(x) printf x
+long nr_of_yields = 0;
+#else
+# define DEBUG_PRINT(x) do {} while (0)
+#endif
+
+void sha256continuation_ErlNifResourceDtor(ErlNifEnv* caller_env, void* obj){
+ sha256continuation* continuation = obj;
+ if(continuation->continuation != NULL){
+ calc_sha_256_ycf_gen_destroy(continuation->continuation);
+ enif_release_binary(&continuation->out_bin);
+ enif_free_env(continuation->work_env);
+ continuation->continuation = NULL;
+ DEBUG_PRINT(("DESTOY\n"));
+ }
+ DEBUG_PRINT(("DEALLOC\n"));
+}
+
+#define REDUCTIONS_UNTIL_YCF_YIELD() (200*300*8)
+
+static ERL_NIF_TERM
+sha256_nif_cont_after_yield(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifResourceType* res_type = (ErlNifResourceType*)enif_priv_data(env);
+ long nr_of_reductions = REDUCTIONS_UNTIL_YCF_YIELD();
+ sha256continuation* continuation;
+ enif_get_resource(env,
+ argv[0],
+ res_type,
+ (void**)&continuation);
+ calc_sha_256_ycf_gen_continue(&nr_of_reductions,
+ &continuation->continuation,
+ NULL);
+ if(YCF_IS_YIELDED(continuation->continuation)){
+#ifdef DEBUG
+ nr_of_yields++;
+#endif
+ return enif_schedule_nif(env, "sha256_nif_cont_after_yield", 0, sha256_nif_cont_after_yield, 1, argv);
+ }
+ DEBUG_PRINT(("NUMBER OF YCF_YIELD()S %ld \n", nr_of_yields));
+ enif_free_env(continuation->work_env);
+ ErlNifBinary out_bin = continuation->out_bin;
+ return enif_make_binary(env, &out_bin);
+}
+
+static ERL_NIF_TERM
+sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ /* ErlNifEnv* msg_env; */
+ ErlNifResourceType* res_type = (ErlNifResourceType*)enif_priv_data(env);
+ ErlNifEnv *work_env = enif_alloc_env();
+ void* wb = NULL;
+ long nr_of_reductions;
+ ERL_NIF_TERM newargv[1];
+ if(argc != 1 || !enif_is_binary(env, argv[0]))
+ {
+ return enif_make_badarg(env);
+ }
+ /* Copy the input binary to the work environemnt so it will be kept when we are yielding */
+ ERL_NIF_TERM input_bin_term = enif_make_copy(work_env, argv[0]);
+ ErlNifBinary input_bin;
+ enif_inspect_binary(work_env, input_bin_term, &input_bin);
+ ErlNifBinary out_bin;
+ enif_alloc_binary(256/8, &out_bin);
+ nr_of_reductions = REDUCTIONS_UNTIL_YCF_YIELD();
+#ifdef DEBUG
+ nr_of_yields = 0;
+#endif
+ calc_sha_256_ycf_gen_yielding(&nr_of_reductions,
+ &wb,
+ NULL,
+ allocator,
+ freer,
+ NULL,
+ 64,
+ NULL,
+ out_bin.data,
+ input_bin.data,
+ input_bin.size);
+ if(YCF_IS_YIELDED(wb)){
+ DEBUG_PRINT(("TRAPPED FIRST CALL %ld \n", nr_of_reductions));
+ sha256continuation* continuation =
+ enif_alloc_resource(res_type,
+ sizeof(sha256continuation));
+ continuation->work_env = work_env;
+ continuation->continuation = wb;
+ continuation->out_bin = out_bin;
+ newargv[0] = enif_make_resource(env, continuation);
+ enif_release_resource(continuation);
+ return enif_schedule_nif(env, "sha256_nif_cont_after_yield", 0, sha256_nif_cont_after_yield, 1, newargv);
+ }
+ enif_free_env(work_env);
+ return enif_make_binary(env, &out_bin);
+}
+
+static ErlNifFunc nif_funcs[] = {
+ {"sha256", 1, sha256_nif}
+};
+
+static int
+nifload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = enif_open_resource_type(env,
+ NULL,
+ "sha256_nif",
+ sha256continuation_ErlNifResourceDtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ return 0;
+}
+
+static int
+nifupgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = enif_open_resource_type(env,
+ NULL,
+ "sha256_nif",
+ sha256continuation_ErlNifResourceDtor,
+ ERL_NIF_RT_TAKEOVER,
+ NULL);
+ return 0;
+}
+
+ERL_NIF_INIT(sha256_nif, nif_funcs, nifload, NULL, nifupgrade, NULL);
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config
new file mode 100644
index 0000000000..d20b1db59d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/rebar.config
@@ -0,0 +1,12 @@
+{erl_opts, [debug_info]}.
+{deps, []}.
+
+{erl_opts, [debug_info]}.
+{deps, []}.
+
+{pre_hooks,
+ [{"(linux|darwin|solaris)", compile, "make -C c_src"},
+ {"(freebsd)", compile, "gmake -C c_src"}]}.
+{post_hooks,
+ [{"(linux|darwin|solaris)", clean, "make -C c_src clean"},
+ {"(freebsd)", clean, "gmake -C c_src clean"}]}. \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src
new file mode 100644
index 0000000000..603a5092bb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.app.src
@@ -0,0 +1,14 @@
+{application, sha256_nif,
+ [{description, "An OTP library"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {licenses, ["Apache 2.0"]},
+ {links, []}
+ ]}.
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl
new file mode 100644
index 0000000000..d613f2c5aa
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/src/sha256_nif.erl
@@ -0,0 +1,27 @@
+-module(sha256_nif).
+
+-export([sha256/1]).
+-on_load(init/0).
+
+-define(APPNAME, sha256_nif).
+-define(LIBNAME, sha256_erlang_nif).
+
+sha256(_) ->
+ not_loaded(?LINE).
+
+init() ->
+ SoName = case code:priv_dir(?APPNAME) of
+ {error, bad_name} ->
+ case filelib:is_dir(filename:join(["..", priv])) of
+ true ->
+ filename:join(["..", priv, ?LIBNAME]);
+ _ ->
+ filename:join([priv, ?LIBNAME])
+ end;
+ Dir ->
+ filename:join(Dir, ?LIBNAME)
+ end,
+ erlang:load_nif(SoName, 0).
+
+not_loaded(Line) ->
+ exit({not_loaded, [{module, ?MODULE}, {line, Line}]}).
diff --git a/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl
new file mode 100644
index 0000000000..7d0a42ce84
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/sha256_erlang_nif/test/basic_SUITE.erl
@@ -0,0 +1,32 @@
+-module(basic_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-export([all/0]).
+-export([test1/1, test2/1]).
+
+all() ->
+ [test1, test2].
+
+bin_to_hex_string(Bin)->
+ lists:flatten(io_lib:format("~s", [lists:flatten([io_lib:format("~2.16.0B",[X]) || <<X:8>> <= Bin ])])).
+
+foreach_up_to_helper(UpTo, UpTo, Fun) ->
+ ok;
+foreach_up_to_helper(UpTo, Current, Fun) ->
+ Fun(Current),
+ foreach_up_to_helper(UpTo, Current + 1, Fun).
+
+foreach_up_to(UpTo, Fun) ->
+ foreach_up_to_helper(UpTo, 1, Fun).
+
+test1(_Config) ->
+ foreach_up_to(15, fun(V) ->
+ In = erlang:list_to_binary(lists:flatten(lists:duplicate(1024 bsl V, "h"))),
+ {CryptoHashTime, Expect} = timer:tc(crypto, hash, [sha256,In]),
+ {MyTime, Expect} = timer:tc(sha256_nif, sha256, [In]),
+ io:format("Size: ~p My Time: ~p Crypto Hash Time: ~p", [1024 bsl V, MyTime/1000000, CryptoHashTime/1000000])
+ end),
+ ok.
+
+test2(_Config) ->
+ Expect = crypto:hash(sha256, "hej"),
+ Expect = sha256_nif:sha256(<<"hej">>).
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c
new file mode 100644
index 0000000000..0ac86d0b7d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c
@@ -0,0 +1,92 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void sub_fun(char* x){
+ *x = *x + 1; /* x == 3*/
+ *x = *x + 1; /* x == 4*/
+}
+
+int sub_fun2(int x, int y){
+ return x+y;
+}
+
+int fun(char x){
+ int y;
+ (void)y;
+ x = x + 1; /* x == 2*/
+ sub_fun(((&x)));
+ sub_fun2(10, 20);
+ y = sub_fun2(10, 20);
+ YCF_YIELD();
+ x = x + 1; /* x == 5*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 5){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out
new file mode 100644
index 0000000000..20db8bc359
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_fun_call.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 5
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c
new file mode 100644
index 0000000000..49316af598
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int empty_fun(){
+ return 1;
+}
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ (void)hej;
+ }
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c
new file mode 100644
index 0000000000..5c3778e203
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+int empty_fun(){
+ return 1;
+}
+
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD_NO_REDS();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ (void)hej;
+ return x;
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 777;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ if(nr_of_reductions != 777){
+ printf("SHOULD NOT HAPPEN\n");
+ }
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yield_no_reds.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c
new file mode 100644
index 0000000000..99fcabaf99
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c
@@ -0,0 +1,111 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void sub_fun_1(char* res, char x, char y){
+ printf("sub_fun_1_1\n");
+ YCF_YIELD();
+ printf("sub_fun_1_2\n");
+ *res = x + y;
+ YCF_YIELD();
+ printf("sub_fun_1_3\n");
+ YCF_YIELD();
+ printf("sub_fun_1_4\n");
+}
+
+void sub_fun_2(){
+ printf("sub_fun_2_1\n");
+ YCF_YIELD();
+ printf("sub_fun_2_2\n");
+}
+
+char sub_fun_3(char x, char y){
+ char res;
+ printf("sub_fun_2_1\n");
+ YCF_YIELD();
+ res = x + y;
+ YCF_YIELD();
+ printf("sub_fun_2_2\n");
+ return res;
+}
+
+int fun(char x){
+ char y;
+ char z = 10
+ YCF_YIELD();
+ sub_fun_1(&y,x,1); /* y == 2 */
+ YCF_YIELD();
+ sub_fun_2();
+ YCF_YIELD();
+ y = sub_fun_3(y, 1); /* y == 3 */
+ YCF_YIELD();
+ return y + z;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 13){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out
new file mode 100644
index 0000000000..cc807db6c3
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/simple_yielding_fun_call.c.out
@@ -0,0 +1,18 @@
+sub_fun_1_1
+TRAPPED
+sub_fun_1_2
+TRAPPED
+sub_fun_1_3
+TRAPPED
+sub_fun_1_4
+TRAPPED
+sub_fun_2_1
+TRAPPED
+sub_fun_2_2
+TRAPPED
+sub_fun_2_1
+TRAPPED
+TRAPPED
+sub_fun_2_2
+TRAPPED
+RETURNED 13
diff --git a/erts/lib_src/yielding_c_fun/test/examples/stack_array.c b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c
new file mode 100644
index 0000000000..9f251749d6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c
@@ -0,0 +1,135 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define YCF_YIELD()
+
+
+void fun(int x3content[]){
+ int x1[2];
+ int x2[2][2];
+ int *x3[2];
+ int z = 42;
+ (void)z;
+ x1[0] = 1;
+ x1[1] = 2;
+ x2[0][0] = 3;
+ x2[0][1] = 4;
+ x2[1][0] = 5;
+ x2[1][1] = 6;
+ x3[0] = x3content;
+ x3[1] = x3content;
+ x3[0][0] = 7;
+ x3[0][1] = 8;
+ x3[1][0] = 9;
+ x3[1][1] = 10;
+ printf("%d %d %d %d %d %d %d %d %d %d\n",
+ x1[0],
+ x1[1],
+ x2[0][0],
+ x2[0][1],
+ x2[1][0],
+ x2[1][1],
+ x3[0][0],
+ x3[0][1],
+ x3[1][0],
+ x3[1][1]);
+ YCF_YIELD();
+ printf("%d %d %d %d %d %d %d %d %d %d\n",
+ x1[0],
+ x1[1],
+ x2[0][0],
+ x2[0][1],
+ x2[1][0],
+ x2[1][1],
+ x3[0][0],
+ x3[0][1],
+ x3[1][0],
+ x3[1][1]);
+ return;
+}
+
+void fun_reset(int x3content[]){
+ int x1[2];
+ int x2[2][2];
+ int *x3[2];
+ (void)x2;
+ (void)x1;
+ x1[0] = 42;
+ x1[1] = 42;
+ x2[0][0] = 42;
+ x2[0][1] = 42;
+ x2[1][0] = 42;
+ x2[1][1] = 42;
+ x3[0] = x3content;
+ x3[1] = x3content;
+ x3[0][0] = 42;
+ x3[0][1] = 42;
+ x3[1][0] = 42;
+ x3[1][1] = 42;
+ return;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int x3content[2];
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL, x3content);
+ fun_reset(x3content);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(x3content);
+#endif
+ printf("RETURNED\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out
new file mode 100644
index 0000000000..c5cd129186
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/stack_array.c.out
@@ -0,0 +1,4 @@
+1 2 3 4 5 6 9 10 9 10
+TRAPPED
+1 2 3 4 5 6 42 42 42 42
+RETURNED
diff --git a/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c
new file mode 100644
index 0000000000..146364d936
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#if __STDC_VERSION__ >= 199901L
+
+#else
+#define inline
+#endif
+
+#define YCF_YIELD()
+
+static
+inline
+int fun(char x);
+
+
+static
+inline
+int fun(char x){
+ x = x + 1; /* x == 2*/
+ {
+ int y;
+ y = 1;
+ {
+ int z = 1;
+ YCF_YIELD();
+ x = x + y + z; /* x == 4*/
+ return x;
+ }
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+ (void)fun;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/static_inline_function.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/lib/erl_interface/src/legacy/erl_internal.h b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c
index 25cf3e4f42..fd1240c180 100644
--- a/lib/erl_interface/src/legacy/erl_internal.h
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_code.c
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,35 +14,53 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* %CopyrightEnd%
*/
-#ifndef _ERL_INTERNAL_H
-#define _ERL_INTERNAL_H
-/*
- * Function: Some useful stuff not to be exported to users.
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
*/
-#define HEAD(ep) ep->uval.lval.head
-#define TAIL(ep) ep->uval.lval.tail
-#define ERL_NO_REF(x) (ERL_COUNT(x) == 0)
-
-#ifdef DEBUG
-#define ASSERT(e) \
- if (e) { \
- ; \
- } else { \
- erl_assert_error(#e, __FILE__, __LINE__); \
- }
-extern void erl_assert_error(char* expr, char* file, int line)
- __attribute__ ((__noreturn__));
+#include <stdio.h>
+#include <stdlib.h>
-#else
+#define YCF_YIELD()
-#define ASSERT(e)
+int A(int depth);
+int B(int depth);
-#endif
+int A(int depth){
+ int b;
+ YCF_YIELD();
+ depth++;
+ printf("A ");
+ YCF_YIELD();
+ if(depth == 100){
+ return 1;
+ } else {
+ b = B(depth);
+ }
+ YCF_YIELD();
+ return b + 1;
+}
-#endif /* _ERL_INTERNAL_H */
+int B(int depth){
+ int a;
+ YCF_YIELD();
+ depth++;
+ printf("B ");
+ YCF_YIELD();
+ if(depth == 100){
+ YCF_YIELD();
+ return 1;
+ } else {
+ a = A(depth);
+ }
+ YCF_YIELD();
+ return a + 1;
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c
new file mode 100644
index 0000000000..b8d377f724
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c
@@ -0,0 +1,71 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#ifdef YCF_YIELD_CODE_GENERATED
+#include "tmp_dir/tmp.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int A(int depth);
+int B(int depth);
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = A_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,0);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+#else
+ ret = A(0);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != A(0)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out
new file mode 100644
index 0000000000..c41fe372ae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_generated_header_file_main.c.out
@@ -0,0 +1,302 @@
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 100
+A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c
new file mode 100644
index 0000000000..04e071e94b
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int empty_fun(){
+ return 1;
+}
+
+#include "tmp.inc"
+
+static int fun(char x){
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ {
+ int hej = !!empty_fun();
+ return x + hej;
+ }
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+ (void)fun;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 4){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out
new file mode 100644
index 0000000000..cf0b7ff2c6
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_only_output_yielding_funs.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 4
diff --git a/erts/lib_src/yielding_c_fun/test/examples/test_trap.c b/erts/lib_src/yielding_c_fun/test/examples/test_trap.c
new file mode 100644
index 0000000000..7799de618e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/test_trap.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(char x){
+ int y = 10;
+ int i = 0;
+ printf("BEFORE YCF_YIELD()\n");
+ y = y + x;/*y == 11*/
+ for (i = 0; i < 100; i++){
+ printf("ITER %d\n", i);
+ y = y + x;
+ YCF_YIELD();
+ }
+ printf("AFTER YCF_YIELD()\n");/*y == 111*/
+ y = y*3;
+ return y;/*y == 333*/
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun(1,&wb,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ return 0;
+}
+
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/thread_example.c b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c
new file mode 100644
index 0000000000..875e9fe990
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD_NO_REDS()
+
+static int f_2(char* name, int n) {
+ for(int y = 0; y < 2; y++) {
+ printf("%s f_2: y=%d\n", name, y);
+ }
+ return n*2;
+}
+
+static void f_1(char* name, int x) {
+ YCF_YIELD_NO_REDS();
+ while (x > 0) {
+ int f_2_ret = f_2(name, x);
+ printf("%s f_1: x=%d f_2_ret=%d\n", name, x, f_2_ret);
+ x--;
+ }
+ printf("%s f_1: DONE\n", name);
+}
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+static void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] ) {
+#ifdef YCF_YIELD_CODE_GENERATED
+ long t1_nr_of_reds = 1;
+ void* t1_state = NULL;
+ long t2_nr_of_reds = 2;
+ void* t2_state = NULL;
+ long t3_nr_of_reds = 1000;
+ void* t3_state = NULL;
+ /* Start t1, t2 and t3*/
+ f_1_ycf_gen_yielding(&t1_nr_of_reds, &t1_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t1", 2);
+ f_1_ycf_gen_yielding(&t2_nr_of_reds, &t2_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t2", 4);
+ f_1_ycf_gen_yielding(&t3_nr_of_reds, &t3_state, NULL,
+ ycf_alloc, ycf_free, NULL, 0, NULL,
+ "t3", 2);
+ printf("THREADS STARTED\n");
+ /* Execute t1, t2 and t3*/
+ while (t1_state != NULL ||
+ t2_state != NULL ||
+ t3_state != NULL) {
+ t1_nr_of_reds = 1;
+ t2_nr_of_reds = 2;
+ if (t1_state != NULL) {
+ printf("SCHEDULING THREAD: t1\n");
+ f_1_ycf_gen_continue(&t1_nr_of_reds, &t1_state, NULL);
+ if (t1_state == NULL) {
+ printf("THREAD t1 DONE (number of reductions left = %ld)\n",
+ t1_nr_of_reds);
+ }
+ }
+ if (t2_state != NULL) {
+ printf("SCHEDULING THREAD: t2\n");
+ f_1_ycf_gen_continue(&t2_nr_of_reds, &t2_state, NULL);
+ if (t2_state == NULL) {
+ printf("THREAD t2 DONE (number of reductions left = %ld)\n",
+ t2_nr_of_reds);
+ }
+ }
+ if (t3_state != NULL) {
+ printf("SCHEDULING THREAD: t3\n");
+ f_1_ycf_gen_continue(&t3_nr_of_reds, &t3_state, NULL);
+ if (t3_state == NULL) {
+ printf("THREAD t3 DONE (number of reductions left = %ld)\n",
+ t3_nr_of_reds);
+ }
+ }
+ }
+#endif
+ (void)f_1;
+ printf("DONE\n");
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out
new file mode 100644
index 0000000000..015f2204ea
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/thread_example.c.out
@@ -0,0 +1,47 @@
+THREADS STARTED
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+SCHEDULING THREAD: t3
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=2 f_2_ret=4
+t3 f_2: y=0
+t3 f_2: y=1
+t3 f_1: x=1 f_2_ret=2
+t3 f_1: DONE
+THREAD t3 DONE (number of reductions left = 994)
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=4 f_2_ret=8
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=3 f_2_ret=6
+SCHEDULING THREAD: t1
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+t2 f_2: y=1
+t2 f_1: x=2 f_2_ret=4
+SCHEDULING THREAD: t1
+t1 f_2: y=0
+SCHEDULING THREAD: t2
+t2 f_2: y=0
+SCHEDULING THREAD: t1
+t1 f_2: y=1
+t1 f_1: x=1 f_2_ret=2
+t1 f_1: DONE
+THREAD t1 DONE (number of reductions left = 1)
+SCHEDULING THREAD: t2
+t2 f_2: y=1
+t2 f_1: x=1 f_2_ret=2
+t2 f_1: DONE
+THREAD t2 DONE (number of reductions left = 2)
+DONE
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_param.c b/erts/lib_src/yielding_c_fun/test/examples/void_param.c
new file mode 100644
index 0000000000..f5e089b27a
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_param.c
@@ -0,0 +1,79 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+int fun(void){
+ int z = 1;
+ char x = z + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun();
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out b/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out
new file mode 100644
index 0000000000..96f574d048
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_param.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c
new file mode 100644
index 0000000000..10dd51bfae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c
@@ -0,0 +1,85 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+
+void fun(int* y){
+ int x = 1;
+ x = x + 1; /* x == 2*/
+ YCF_YIELD();
+ x = x + 1; /* x == 3*/
+ *y = x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int nr_of_trapps = 0;
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,&ret);
+ if(wb != NULL){
+ printf("TRAP\n");
+ nr_of_trapps++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+ if(nr_of_trapps != 1){
+ printf("ERROR\n");
+ exit(1);
+ }
+#else
+ fun(&ret);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 3){
+ printf("ERROR\n");
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out
new file mode 100644
index 0000000000..4b85bbb30e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/void_ret_fun.c.out
@@ -0,0 +1,2 @@
+TRAP
+RETURNED 3
diff --git a/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c
new file mode 100644
index 0000000000..20fe2a444d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c
@@ -0,0 +1,126 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+#define YCF_CONSUME_REDS(X)
+#ifndef YCF_YIELD_CODE_GENERATED
+#define YCF_STACK_ALLOC(X) malloc(1)
+#endif
+
+int sub_fun(int x){
+ int i;
+ int count = 0;
+ char* my_data = YCF_STACK_ALLOC(5);
+ my_data[0] = 's';
+ my_data[1] = 'u';
+ my_data[2] = 'b';
+ my_data[3] = '\n';
+ my_data[4] = '\0';
+ for(i = 0; i < 2; i++){
+ count = count + 2;
+ YCF_YIELD();
+ printf("%s", my_data);
+ }
+ return count + x;
+}
+
+int fun(int x){
+ int ret;
+ char* my_data = YCF_STACK_ALLOC(5);
+ char* my_data2 = YCF_STACK_ALLOC(5);
+ char* my_data3;
+ my_data[0] = 'h';
+ my_data[1] = 'e';
+ my_data[2] = 'j';
+ my_data[3] = '\n';
+ my_data[4] = '\0';
+ my_data2[0] = 's';
+ my_data2[1] = 'o';
+ my_data2[2] = 's';
+ my_data2[3] = '\n';
+ my_data2[4] = '\0';
+ YCF_YIELD();
+ printf("%s", my_data);
+ printf("%s", my_data2);
+ ret = sub_fun(x);
+ ret = sub_fun(x);
+ my_data3 = YCF_STACK_ALLOC(5);
+ printf("BEFORE OVERWRITE %s", my_data3);
+ my_data3[0] = 'e';
+ my_data3[1] = 'r';
+ my_data3[2] = 'l';
+ my_data3[3] = '\n';
+ my_data3[4] = '\0';
+ printf("AFTER OVERWRITE %s", my_data3);
+ return ret;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ int nr_of_yields = 0;
+ long nr_of_reductions;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ nr_of_reductions = 101;
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,15,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED %ld\n", nr_of_reductions);
+ nr_of_yields++;
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = fun(1);
+#endif
+ printf("Number of yields %d\n", nr_of_yields);
+ printf("RETURNED %d\n", ret);
+ if(ret != 5){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out
new file mode 100644
index 0000000000..8b3c31c9e5
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/ycf_stack_alloc.c.out
@@ -0,0 +1,15 @@
+TRAPPED 0
+hej
+sos
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+TRAPPED 0
+sub
+BEFORE OVERWRITE sub
+AFTER OVERWRITE erl
+Number of yields 5
+RETURNED 5
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c
new file mode 100644
index 0000000000..3506d83499
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct my_struct {
+ struct my_struct* my_struct;
+ int len;
+} my_struct;
+
+
+#define YCF_YIELD()
+
+int fun(char x){
+ my_struct s;
+ my_struct* sp;
+ int len = 55;
+ s.len = 10;
+ s.my_struct = NULL;
+ sp = malloc(sizeof(my_struct));
+ sp->len = 10;
+ sp->my_struct = NULL;
+ YCF_YIELD();
+ return
+ s.len == 10 &&
+ s.my_struct == NULL &&
+ sp->len == 10 &&
+ sp->my_struct == NULL &&
+ len == 55;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != 1){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out
new file mode 100644
index 0000000000..7c3cfb5078
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yield_with_struct.c.out
@@ -0,0 +1,2 @@
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c
new file mode 100644
index 0000000000..c0d7ac6c13
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int sub_fun(char x){
+ YCF_YIELD();
+ x = x + 1; /* x == 1*/
+ return (x == 10 ? 0 : 1);
+}
+
+
+int fun(char x){
+ if(sub_fun(0)){
+ printf("hej\n");
+ }
+ if(0) printf("NO");
+ else if(sub_fun(0)){
+ printf("hej 2\n");
+ }
+ if(sub_fun(10)){
+ printf("hej 3\n");
+ } else {
+ printf("HEJ\n");
+ }
+ while(sub_fun(9)){
+ printf("hoo\n");
+ }
+ do{
+ printf("haa\n");
+ }while(sub_fun(9));
+ return x;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+ long nr_of_reductions = 1;
+#endif
+ int ret = 0;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = fun_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,1);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ fun(1);
+#endif
+ printf("RETURNED %d\n", ret);
+ return 0;
+}
+
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out
new file mode 100644
index 0000000000..11b01b7d63
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_fun_in_control.c.out
@@ -0,0 +1,10 @@
+TRAPPED
+hej
+TRAPPED
+hej 2
+TRAPPED
+hej 3
+TRAPPED
+haa
+TRAPPED
+RETURNED 1
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c
new file mode 100644
index 0000000000..655c94e956
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c
@@ -0,0 +1,104 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description:
+ *
+ * Author: Kjell Winblad
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define YCF_YIELD()
+
+int A(int depth);
+int B(int depth);
+
+int A(int depth){
+ int b;
+ YCF_YIELD();
+ depth++;
+ printf("A ");
+ YCF_YIELD();
+ if(depth == 100){
+ return 1;
+ } else {
+ b = B(depth);
+ }
+ YCF_YIELD();
+ return b + 1;
+}
+
+int B(int depth){
+ int a;
+ YCF_YIELD();
+ depth++;
+ printf("B ");
+ YCF_YIELD();
+ if(depth == 100){
+ YCF_YIELD();
+ return 1;
+ } else {
+ a = A(depth);
+ }
+ YCF_YIELD();
+ return a + 1;
+}
+
+void* allocator(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
+
+void freer(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+int main( int argc, const char* argv[] )
+{
+#ifdef YCF_YIELD_CODE_GENERATED
+ void* wb = NULL;
+#endif
+ int ret = 0;
+ long nr_of_reductions = 1;
+#ifdef YCF_YIELD_CODE_GENERATED
+ do{
+ ret = A_ycf_gen_yielding(&nr_of_reductions,&wb,NULL,allocator,freer,NULL,0,NULL,0);
+ if(wb != NULL){
+ printf("TRAPPED\n");
+ }
+ }while(wb != NULL);
+ if(wb != NULL){
+ free(wb);
+ }
+#else
+ ret = A(0);
+#endif
+ printf("RETURNED %d\n", ret);
+ if(ret != A(0)){
+ return 1;
+ }else{
+ return 0;
+ }
+}
diff --git a/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out
new file mode 100644
index 0000000000..c41fe372ae
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/examples/yielding_mutual_recursion.c.out
@@ -0,0 +1,302 @@
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+A TRAPPED
+TRAPPED
+B TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+TRAPPED
+RETURNED 100
+A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B \ No newline at end of file
diff --git a/erts/lib_src/yielding_c_fun/test/test.sh b/erts/lib_src/yielding_c_fun/test/test.sh
new file mode 100755
index 0000000000..a0270475fb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/test/test.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+
+
+#
+# Description:
+#
+# Author: Kjell Winblad
+#
+
+#Code to find directory of this file from https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+
+GC=$1
+
+RR=-rr
+RR=""
+
+PATH=$PATH:$DIR/../bin
+
+set -e
+set -x
+
+TMP_DIR=$DIR/tmp_dir
+
+mkdir -p $TMP_DIR
+
+TMP_FILE=$TMP_DIR/tmp
+
+TMP_C_FILE=$TMP_DIR/tmp.c
+TMP_INC_FILE=$TMP_DIR/tmp.inc
+TMP_C_FILE2=$TMP_DIR/tmp2.c
+TMP_O_FILE1=$TMP_DIR/tmp1.o
+TMP_O_FILE2=$TMP_DIR/tmp2.o
+TMP_H_FILE=$TMP_DIR/tmp.h
+TMP_CC_OUT=$TMP_DIR/a.out
+
+CC_ARGS="-std=c99 -pedantic -Wall"
+
+CC=clang
+
+SIMPLE_TEST_FILES=("$DIR/examples/simple_yield.c"
+ "$DIR/examples/multi_scope_yield.c"
+ "$DIR/examples/nested_loop_yield.c"
+ "$DIR/examples/void_ret_fun.c"
+ "$DIR/examples/declarations.c"
+ "$DIR/examples/void_param.c"
+ "$DIR/examples/simple_fun_call.c"
+ "$DIR/examples/control_statements.c"
+ "$DIR/examples/simple_yielding_fun_call.c"
+ "$DIR/examples/yielding_mutual_recursion.c"
+ "$DIR/examples/stack_array.c"
+ "$DIR/examples/custom_code_save_restore_yield_state.c"
+ "$DIR/examples/custom_code_save_restore_yield_state_alt_syntax.c"
+ "$DIR/examples/destroy_while_yielded.c"
+ "$DIR/examples/consume_reds.c"
+ "$DIR/examples/nested_call_consume_reds.c"
+ "$DIR/examples/auto_yield.c"
+ "$DIR/examples/yield_with_struct.c"
+ "$DIR/examples/const_defenition.c"
+ "$DIR/examples/ycf_stack_alloc.c"
+ "$DIR/examples/declarations_in_for_loops.c"
+ "$DIR/examples/in_code_var_declaration.c"
+ "$DIR/examples/static_inline_function.c"
+ "$DIR/examples/yielding_fun_in_control.c"
+ "$DIR/examples/debug_ptr_to_stack.c"
+ "$DIR/examples/simple_yield_no_reds.c"
+ "$DIR/examples/thread_example.c")
+
+SIMPLE_TEST_FILES_YIELD_FUNS=("-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun_1 -fnoauto sub_fun_2 -fnoauto sub_fun_3"
+ "-fnoauto A -fnoauto B"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-frec fun -frec rec_inc"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-f fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-fnoauto fun -fnoauto sub_fun"
+ "-fnoauto fun"
+ "-fnoauto fun"
+ "-f f_1 -f f_2")
+
+# Check that yielding_c_fun can repeat files correctly
+for C_FILE in $SIMPLE_TEST_FILES
+do
+ yielding_c_fun $GC $RR -repeat $C_FILE > $TMP_C_FILE
+ cmp $C_FILE $TMP_C_FILE
+done
+
+# Check that the default action is to generate correct yield code
+yielding_c_fun $GC -yield $DIR/examples/simple_yield.c > $TMP_C_FILE
+yielding_c_fun $GC $DIR/examples/simple_yield.c > $TMP_C_FILE2
+cmp $TMP_C_FILE $TMP_C_FILE2
+
+# Check yielding
+for ((i = 0; i < ${#SIMPLE_TEST_FILES[@]}; i++))
+do
+ yielding_c_fun $GC $RR -yield ${SIMPLE_TEST_FILES_YIELD_FUNS[$i]} "${SIMPLE_TEST_FILES[$i]}" > $TMP_C_FILE
+ $CC $CC_ARGS -g $TMP_C_FILE -o $TMP_CC_OUT
+ $TMP_CC_OUT > $TMP_FILE
+ cmp $TMP_FILE "${SIMPLE_TEST_FILES[$i]}.out"
+done
+
+
+# Check generated header file and output file
+yielding_c_fun $GC $RR -yield -fnoauto A -fnoauto B -header_file_name $TMP_H_FILE -output_file_name $TMP_C_FILE "$DIR/examples/test_generated_header_file_code.c"
+$CC $CC_ARGS -c $TMP_C_FILE -o $TMP_O_FILE1
+$CC $CC_ARGS -I$DIR -DYCF_YIELD_CODE_GENERATED=1 -c "$DIR/examples/test_generated_header_file_main.c" -o $TMP_O_FILE2
+$CC $CC_ARGS $TMP_O_FILE1 $TMP_O_FILE2 -o $TMP_CC_OUT
+$TMP_CC_OUT > $TMP_FILE
+cmp $TMP_FILE "$DIR/examples/test_generated_header_file_main.c.out"
+
+
+# Check generation of only yielding fun
+echo $TMP_INC_FILE
+yielding_c_fun.bin $GC $RR -yield -static_aux_funs -only_yielding_funs -fnoauto fun -output_file_name $TMP_INC_FILE "$DIR/examples/test_only_output_yielding_funs.c"
+$CC $CC_ARGS -I "$TMP_DIR" "$DIR/examples/test_only_output_yielding_funs.c" -o $TMP_CC_OUT
+$TMP_CC_OUT > $TMP_FILE
+cmp $TMP_FILE "$DIR/examples/test_only_output_yielding_funs.c.out"
+
+# Check that debug mode can detect pointers to stack
+yielding_c_fun $GC $RR -yield -debug -fnoauto fun "$DIR/examples/debug_ptr_to_stack.c" > $TMP_C_FILE
+$CC $CC_ARGS $TMP_C_FILE -o $TMP_CC_OUT
+(set +e ; $TMP_CC_OUT ; [ $? -ne 0 ])
+
+# Check that memory usage of the tool can be logged
+MEM_LOG_FILE="$TMP_DIR/my_mem_log_file.txt"
+yielding_c_fun $GC $RR -log_max_mem_usage "$MEM_LOG_FILE" -yield -debug -fnoauto fun "$DIR/examples/multi_scope_yield.c" > $TMP_C_FILE
+test -f "$MEM_LOG_FILE"
+#rm "$MEM_LOG_FILE"
+
+# Uncomment to the test the Erlang NIF example
+# if [ `rebar3 > /dev/null 2>&1 ; echo $?` = 0 ]
+# then
+# (cd "$DIR/examples/sha256_erlang_nif/" && make clean && make test)
+# fi
+
+
diff --git a/lib/erl_interface/src/legacy/portability.h b/erts/lib_src/yielding_c_fun/ycf_helpers.h
index 42a78662d5..ae32f60f10 100644
--- a/lib/erl_interface/src/legacy/portability.h
+++ b/erts/lib_src/yielding_c_fun/ycf_helpers.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,21 +14,27 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* %CopyrightEnd%
*/
-#ifndef _PORTABILITY_H
-#define _PORTABILITY_H
-
-#if !defined(__GNUC__) || __GNUC__ < 2
-
/*
- * GCC's attributes are too useful to not use. Other compilers
- * just lose opportunities to optimize and warn.
+ * Author: Kjell Winblad
*/
-# define __attribute__(foo) /* nothing */
-#endif
+#ifndef YIELDING_C_FUN_HELPERS_H
+#define YIELDING_C_FUN_HELPERS_H
+
+#include <stdlib.h>
+
+static void* ycf_alloc(size_t size, void* context){
+ (void)context;
+ return malloc(size);
+}
-#endif /* _PORTABILITY_H */
+void ycf_free(void* data, void* context){
+ (void)context;
+ free(data);
+}
+
+#endif
diff --git a/erts/lib_src/yielding_c_fun/ycf_lexer.c b/erts/lib_src/yielding_c_fun/ycf_lexer.c
new file mode 100644
index 0000000000..8fcebcc6b0
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_lexer.c
@@ -0,0 +1,483 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include "lib/tiny_regex_c/re.h"
+#include "ycf_yield_fun.h"
+#include "ycf_utils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+int ycf_symbol_is_text_eq(ycf_symbol* symbol, char* str){
+ unsigned long symbol_length = symbol->stop - symbol->start;
+ return
+ symbol_length == strlen(str) &&
+ strncmp(str, &symbol->source[symbol->start], symbol_length) == 0;
+}
+
+char* ycf_symbol_text_between(ycf_symbol* s1, ycf_symbol* s2){
+ int size = s2->stop - s1->start;
+ char* str = ycf_malloc(size+1);
+ strncpy(str, &s1->source[s1->start], size);
+ str[size] = 0;
+ return str;
+}
+
+char* get_symbol_type_text(ycf_symbol_type type){
+ switch(type) {
+ case ycf_symbol_type_comment: return "ycf_symbol_type_comment";
+ case ycf_symbol_type_string_literal: return "ycf_symbol_type_string_literal";
+ case ycf_symbol_type_macro_define: return "ycf_symbol_type_macro_define";
+ case ycf_symbol_type_macro_command: return "ycf_symbol_type_macro_command";
+ case ycf_symbol_type_whitespace: return "ycf_symbol_type_whitespace";
+ case ycf_symbol_type_identifier: return "ycf_symbol_type_identifier";
+ case ycf_symbol_type_number: return "ycf_symbol_type_number";
+ case ycf_symbol_type_star: return "ycf_symbol_type_star";
+ case ycf_symbol_type_neg: return "ycf_symbol_type_neg";
+ case ycf_symbol_type_equal_equal_sign: return "ycf_symbol_type_equal_equal_sign";
+ case ycf_symbol_type_not_equal_sign: return "ycf_symbol_type_not_equal_sign";
+ case ycf_symbol_type_open_parenthesis: return "ycf_symbol_type_open_parenthesis";
+ case ycf_symbol_type_end_parenthesis: return "ycf_symbol_type_end_parenthesis";
+ case ycf_symbol_type_open_curly_brace: return "ycf_symbol_type_open_curly_brace";
+ case ycf_symbol_type_end_curly_brace: return "ycf_symbol_type_end_curly_brace";
+ case ycf_symbol_type_open_square_bracket: return "ycf_symbol_type_open_square_bracket";
+ case ycf_symbol_type_end_square_bracket: return "ycf_symbol_type_end_square_bracket";
+ case ycf_symbol_type_equal_sign: return "ycf_symbol_type_equal_sign";
+ case ycf_symbol_type_semicolon: return "ycf_symbol_type_semicolon";
+ case ycf_symbol_type_comma: return "ycf_symbol_type_comma";
+ case ycf_symbol_type_consume_reds: return "ycf_symbol_type_consume_reds";
+ case ycf_symbol_type_pointer_field_access: return "ycf_symbol_type_pointer_field_access";
+ case ycf_symbol_type_period: return "ycf_symbol_type_period";
+ case ycf_symbol_type_const: return "ycf_symbol_type_const";
+ case ycf_symbol_type_void: return "ycf_symbol_type_void";
+ case ycf_symbol_type_volatile: return "ycf_symbol_type_volatile";
+ case ycf_symbol_type_static: return "ycf_symbol_type_static";
+ case ycf_symbol_type_inline: return "ycf_symbol_type_inline";
+ case ycf_symbol_type_return: return "ycf_symbol_type_return";
+ case ycf_symbol_type_if: return "ycf_symbol_type_if";
+ case ycf_symbol_type_else: return "ycf_symbol_type_else";
+ case ycf_symbol_type_goto: return "ycf_symbol_type_goto";
+ case ycf_symbol_type_break: return "ycf_symbol_type_break";
+ case ycf_symbol_type_while: return "ycf_symbol_type_while";
+ case ycf_symbol_type_do: return "ycf_symbol_type_do";
+ case ycf_symbol_type_for: return "ycf_symbol_type_for";
+ case ycf_symbol_type_switch: return "ycf_symbol_type_switch";
+ case ycf_symbol_type_continue: return "ycf_symbol_type_continue";
+ case ycf_symbol_type_something_else: return "ycf_symbol_type_something_else";
+ case ycf_symbol_type_special_code_start: return "ycf_symbol_type_special_code_start";
+ case ycf_symbol_type_special_code_end: return "ycf_symbol_type_special_code_end";
+ }
+ return "non_existing_symbol?";
+}
+
+typedef struct symbol_finder {
+ int (*finder)(struct symbol_finder*,char*);
+ ycf_symbol_type type;
+ int length;
+ char *str_1;
+ char *str_2;
+} symbol_finder;
+
+int starts_with(char *str, char *prefix)
+{
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+int until_no_match(symbol_finder* f, char* text){
+ int pos = 0;
+ while(re_match(f->str_1, &(text[pos])) == 0){
+ pos++;
+ }
+ return pos;
+}
+
+int string_litteral_finder(symbol_finder* f, char* text){
+ int pos = 0;
+ if (starts_with(text, "\"")){
+ pos++;
+ //\"(\\.|[^"\\])*\"
+ while(re_match("\\.", &(text[pos])) == 0 ||
+ re_match("[^\"]", &(text[pos])) == 0){
+ pos++;
+ }
+ if(starts_with(&(text[pos]), "\"")){
+ return pos + 1;
+ }else {
+ printf("Broken string litteral\n");
+ exit(1);
+ }
+ }
+ return pos;
+}
+
+int macro_define_finder(symbol_finder* f, char* text){
+ int pos = 0;
+ if (starts_with(text, "#define")){
+ pos = pos + strlen("#define");
+ while(1){
+ if(starts_with(&(text[pos]), "\\\n")){
+ pos = pos + 2;
+ } else if (starts_with(&(text[pos]), "\n")){
+ break;
+ } else {
+ pos++;
+ }
+ }
+ }
+ return pos;
+}
+
+
+int starts_with_until_no_match(symbol_finder* f, char* text){
+ int pos = 0;
+ if(re_match(f->str_1, text) == 0){
+ while(re_match(f->str_2, &(text[pos])) == 0){
+ pos++;
+ }
+ }
+ return pos;
+}
+
+int starts_with_ends_with(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1)){
+ int pos = 1;
+ while(!starts_with(&(text[pos]), f->str_2)){
+ pos++;
+ }
+ return pos+strlen(f->str_2);
+ }
+ return 0;
+}
+
+int fixed_string(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1)){
+ return strlen(f->str_1);
+ }
+ return 0;
+}
+
+int fixed_alpha_string(symbol_finder* f, char* text){
+ if(starts_with(text, f->str_1) &&
+ re_match("[^\\W]", &text[strlen(f->str_1)])){
+ return strlen(f->str_1);
+ }
+ return 0;
+}
+
+int regex_char(symbol_finder* f, char* text){
+ if(re_match(f->str_1, text) == 0){
+ return 1;
+ }
+ return 0;
+}
+
+void fold_whitespace_and_comments(ycf_symbol_list* symbols){
+ ycf_symbol* prev = NULL;
+ ycf_symbol* current = symbols->head;
+ ycf_symbol* dummy = ycf_malloc(sizeof(ycf_symbol));
+ while(current != NULL){
+ current->whitespace_or_comment_before = NULL;
+ if(prev != NULL && (prev->type == ycf_symbol_type_whitespace ||
+ prev->type == ycf_symbol_type_comment)){
+ current->whitespace_or_comment_before = prev;
+ }
+ prev = current;
+ current = current->next;
+ }
+ // remove ycf_symbol_type_whitespace and comments from list
+ dummy->type = ycf_symbol_type_void;
+ dummy->next = symbols->head;
+ prev = dummy;
+ current = prev->next;
+ while(current != NULL &&
+ current != symbols->last){
+ if(current->type == ycf_symbol_type_whitespace ||
+ current->type == ycf_symbol_type_comment){
+ prev->next = current->next;
+ current = current->next;
+ }else {
+ prev = current;
+ current = current->next;
+ }
+ }
+ symbols->head = dummy->next;
+}
+
+ycf_symbol_list ycf_symbol_list_from_text(char* text){
+ int pos = 0;
+ int nr_of_finders = 41;
+ int i;
+ ycf_symbol_list ret = ycf_symbol_list_empty();
+ symbol_finder symbol_finders[] =
+ {
+ {
+ .type = ycf_symbol_type_special_code_start,
+ .str_1 = "/*special_code_start:",
+ .str_2 = "*/",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_special_code_end,
+ .str_1 = "/*special_code_end*/",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_comment,
+ .str_1 = "/*",
+ .str_2 = "*/",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_string_literal,
+ .finder = string_litteral_finder
+ },
+ {
+ .type = ycf_symbol_type_macro_define,
+ .finder = macro_define_finder
+ },
+ {
+ .type = ycf_symbol_type_macro_command,
+ .str_1 = "#",
+ .str_2 = "\n",
+ .finder = starts_with_ends_with
+ },
+ {
+ .type = ycf_symbol_type_whitespace,
+ .str_1 = "\\s",
+ .finder = until_no_match
+ },
+ {
+ .type = ycf_symbol_type_void,
+ .str_1 = "void",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_static,
+ .str_1 = "static",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_inline,
+ .str_1 = "inline",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_const,
+ .str_1 = "const",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_volatile,
+ .str_1 = "volatile",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_consume_reds,
+ .str_1 = "YCF_CONSUME_REDS",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_return,
+ .str_1 = "return",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_if,
+ .str_1 = "if",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_else,
+ .str_1 = "else",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_goto,
+ .str_1 = "goto",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_break,
+ .str_1 = "break",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_continue,
+ .str_1 = "continue",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_while,
+ .str_1 = "while",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_do,
+ .str_1 = "do",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_for,
+ .str_1 = "for",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_switch,
+ .str_1 = "switch",
+ .finder = fixed_alpha_string
+ },
+ {
+ .type = ycf_symbol_type_identifier,
+ .str_1 = "[a-zA-Z]",
+ .str_2 = "\\w",
+ .finder = starts_with_until_no_match
+ },
+ {
+ .type = ycf_symbol_type_number,
+ .str_1 = "\\d",
+ .finder = until_no_match
+ },
+ {
+ .type = ycf_symbol_type_open_parenthesis,
+ .str_1 = "(",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_parenthesis,
+ .str_1 = ")",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_open_curly_brace,
+ .str_1 = "{",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_curly_brace,
+ .str_1 = "}",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_open_square_bracket,
+ .str_1 = "[",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_end_square_bracket,
+ .str_1 = "]",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_equal_sign,
+ .str_1 = "=",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_not_equal_sign,
+ .str_1 = "!=",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_equal_sign,
+ .str_1 = "==",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_star,
+ .str_1 = "*",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_neg,
+ .str_1 = "!",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_semicolon,
+ .str_1 = ";",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_comma,
+ .str_1 = ",",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_period,
+ .str_1 = ".",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_pointer_field_access,
+ .str_1 = "->",
+ .finder = fixed_string
+ },
+ {
+ .type = ycf_symbol_type_something_else,
+ .str_1 = ".",
+ .finder = regex_char
+ }
+ };
+ while(text[pos] != 0){
+ int last_pos = pos;
+ for(i = 0; i < nr_of_finders; i++) {
+ symbol_finder f = symbol_finders[i];
+ int stop = f.finder(&f, &text[pos]);
+ if(stop){
+ ycf_symbol* s = ycf_malloc(sizeof(ycf_symbol));
+ s->type = f.type;
+ s->source = text;
+ s->start = pos;
+ s->stop = pos + stop;
+ s->next = NULL;
+ ycf_symbol_list_append(&ret, s);
+ pos = s->stop;
+ break;
+ }
+ }
+ if (last_pos == pos){
+ printf("Lexer: NOTHING MATCH Stuck at: \n%s\n", &text[pos]);
+ exit(1);
+ }
+ }
+ fold_whitespace_and_comments(&ret);
+ return ret;
+}
+
+void ycf_symbol_list_print(char* text){
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(text);
+ ycf_symbol* s = symbols.head;
+ while(s != NULL){
+ printf("TYPE %s, START=%d, STOP=%d\n",
+ get_symbol_type_text(s->type),
+ s->start,
+ s->stop);
+ s = s->next;
+ }
+ printf("||||| END OF SYMBOLS\n");
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_lists.h b/erts/lib_src/yielding_c_fun/ycf_lists.h
new file mode 100644
index 0000000000..7bb3487ecf
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_lists.h
@@ -0,0 +1,316 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_LISTS_H
+#define YIELDING_C_FUN_LISTS_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#define INIT_LIST(L) \
+ do{ \
+ (L)->head = NULL; \
+ (L)->last = NULL; \
+ }while(0)
+
+#define APPEND_LIST(L,E) \
+ do{ \
+ void* e = E; \
+ if((L)->head == NULL){ \
+ (L)->head = e; \
+ } else { \
+ (L)->last->next = e; \
+ } \
+ (L)->last = e; \
+ }while(0)
+
+#define PREPEND_LIST(T,L,E) \
+ do{ \
+ T* e = E; \
+ if((L)->head == NULL){ \
+ (L)->last = e; \
+ } else { \
+ e->next = (L)->head; \
+ } \
+ (L)->head = e; \
+ }while(0)
+
+#define CONCAT_LIST(T,L1,L2) \
+ do{ \
+ T* current = (L2)->head; \
+ while(current != NULL){ \
+ APPEND_LIST(L1, current); \
+ current = current->next; \
+ } \
+ }while(0)
+
+#define PRINT_LIST(T,L, NAME) \
+ do{ \
+ printf("NAME %s\n", NAME); \
+ printf("HEAD %p\n", (L)->head); \
+ printf("LAST %p\n", (L)->last); \
+ printf("ELEMS:\n"); \
+ T* current = (L)->head; \
+ while(current != NULL){ \
+ printf("E: %p\n", current); \
+ current = current->next; \
+ } \
+ }while(0)
+
+#define REMOVE_LIST(T,L,E) \
+ do{ \
+ T* prev = NULL; \
+ T* rl_current = (L)->head; \
+ T* e = E; \
+ while(rl_current != e && rl_current != NULL){ \
+ prev = rl_current ; \
+ rl_current = rl_current ->next; \
+ } \
+ if(rl_current == NULL){ \
+ exit(1); \
+ } \
+ if(prev == NULL){ \
+ if((L)->head == (L)->last){ \
+ (L)->last = NULL; \
+ } \
+ (L)->head = (L)->head->next; \
+ }else{ \
+ if(rl_current == (L)->last){ \
+ (L)->last = prev; \
+ } \
+ prev->next = rl_current ->next; \
+ } \
+ }while(0)
+
+#define INSERT_AFTER_LIST(T,L,E,TO_INSERT) \
+ do{ \
+ T* current_y = (L)->head; \
+ T* elem_x = E; \
+ T* to_insert2 = TO_INSERT; \
+ if(elem_x == NULL){ \
+ PREPEND_LIST(T,L,to_insert2); \
+ break; \
+ }else if(elem_x == (L)->last){ \
+ APPEND_LIST(L,to_insert2); \
+ break; \
+ } \
+ while(current_y != elem_x && current_y != NULL){ \
+ current_y = current_y->next; \
+ } \
+ if(current_y == NULL){ \
+ printf("CANNOT INSERT AFTER NONEXISTING\n"); \
+ exit(1); \
+ } \
+ to_insert2->next = current_y->next; \
+ current_y->next = to_insert2; \
+ }while(0)
+
+#define INSERT_BEFORE_LIST(T,L,E_BEFORE,TO_INSERT) \
+ do{ \
+ T* prev_x = NULL; \
+ T* current_x = (L)->head; \
+ T* to_insert_x = TO_INSERT; \
+ T* e_before_x = E_BEFORE; \
+ while(current_x != e_before_x && current_x != NULL){ \
+ prev_x = current_x; \
+ current_x = current_x->next; \
+ } \
+ if(current_x == NULL){ \
+ printf("CANNOT INSERT AFTER NONEXISTING\n"); \
+ exit(1); \
+ } \
+ INSERT_AFTER_LIST(T,L,prev_x,to_insert_x); \
+ }while(0)
+
+
+#define REPLACE_LIST(T,L,OLD,NEW) \
+ do{ \
+ T* old = OLD; \
+ T* new = NEW; \
+ T* prev_old_next = old->next; \
+ INSERT_AFTER_LIST(T,L,old,new); \
+ REMOVE_LIST(T,L,old); \
+ old->next = prev_old_next; \
+ }while(0)
+
+
+/* list functions */
+
+#define GENERATE_LIST_FUNCTIONS(NODE_TYPE) \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_empty(){ \
+ NODE_TYPE##_list list; \
+ INIT_LIST(&list); \
+ return list; \
+ } \
+ \
+ NODE_TYPE* NODE_TYPE##_shallow_copy(NODE_TYPE* n){ \
+ NODE_TYPE* new = ycf_malloc(sizeof(NODE_TYPE)); \
+ *new = *n; \
+ new->next = NULL; \
+ return new; \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_shallow_copy(NODE_TYPE##_list n){ \
+ NODE_TYPE##_list new; \
+ NODE_TYPE* current = n.head; \
+ INIT_LIST(&new); \
+ while(current != NULL){ \
+ APPEND_LIST(&new, NODE_TYPE##_shallow_copy(current)); \
+ current = current->next; \
+ } \
+ return new; \
+ } \
+ \
+ int NODE_TYPE##_list_get_item_position(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ NODE_TYPE* current = list->head; \
+ int pos = 0; \
+ while(current != NULL){ \
+ if(current == node){ \
+ return pos; \
+ } \
+ pos = pos + 1; \
+ current = current->next; \
+ } \
+ return -1; \
+ } \
+ \
+ NODE_TYPE* NODE_TYPE##_list_get_item_at_position(NODE_TYPE##_list* list, int pos){ \
+ NODE_TYPE* current = list->head; \
+ int current_pos = 0; \
+ while(current != NULL){ \
+ if(current_pos == pos){ \
+ return current; \
+ } \
+ current_pos = current_pos + 1; \
+ current = current->next; \
+ } \
+ return NULL; \
+ } \
+ \
+ void NODE_TYPE##_list_append(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ APPEND_LIST(list, node); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_append(NODE_TYPE##_list list, NODE_TYPE* node){ \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* node_copy = NODE_TYPE##_shallow_copy(node); \
+ NODE_TYPE##_list_append(&list_copy, node_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_prepend(NODE_TYPE##_list* list, NODE_TYPE* node){ \
+ PREPEND_LIST(NODE_TYPE, list, node); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_prepend(NODE_TYPE##_list list, NODE_TYPE* node){ \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* node_copy = NODE_TYPE##_shallow_copy(node); \
+ NODE_TYPE##_list_prepend(&list_copy, node_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_insert_before(NODE_TYPE##_list* list, NODE_TYPE* before_this, NODE_TYPE* to_insert_z){ \
+ INSERT_BEFORE_LIST(NODE_TYPE, list, before_this, to_insert_z); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_insert_before(NODE_TYPE##_list list, NODE_TYPE* before_this, NODE_TYPE* to_insert){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, before_this); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list);; \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* to_insert_copy = NODE_TYPE##_shallow_copy(to_insert); \
+ NODE_TYPE##_list_insert_before(&list_copy, actual_this, to_insert_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_insert_after(NODE_TYPE##_list* list, NODE_TYPE* after_this, NODE_TYPE* to_insert_z){ \
+ INSERT_AFTER_LIST(NODE_TYPE, list, after_this, to_insert_z); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_insert_after(NODE_TYPE##_list list, NODE_TYPE* after_this, NODE_TYPE* to_insert){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, after_this); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* to_insert_copy = NODE_TYPE##_shallow_copy(to_insert); \
+ NODE_TYPE##_list_insert_after(&list_copy, actual_this, to_insert_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_remove(NODE_TYPE##_list* list, NODE_TYPE* to_remove){ \
+ REMOVE_LIST(NODE_TYPE, list, to_remove); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_remove(NODE_TYPE##_list list, NODE_TYPE* to_remove){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, to_remove); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE##_list_remove(&list_copy, actual_this); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_replace(NODE_TYPE##_list* list, NODE_TYPE* to_replace, NODE_TYPE* replace_with){ \
+ REPLACE_LIST(NODE_TYPE, list, to_replace, replace_with); \
+ } \
+ \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_replace(NODE_TYPE##_list list, NODE_TYPE* to_replace, NODE_TYPE* replace_with){ \
+ int pos = NODE_TYPE##_list_get_item_position(&list, to_replace); \
+ NODE_TYPE##_list list_copy = NODE_TYPE##_list_shallow_copy(list); \
+ NODE_TYPE* actual_this = NODE_TYPE##_list_get_item_at_position(&list_copy, pos); \
+ NODE_TYPE* replace_with_copy = NODE_TYPE##_shallow_copy(replace_with); \
+ NODE_TYPE##_list_replace(&list_copy, actual_this, replace_with_copy); \
+ return list_copy; \
+ } \
+ \
+ void NODE_TYPE##_list_concat(NODE_TYPE##_list* list1, NODE_TYPE##_list* list2){ \
+ CONCAT_LIST(NODE_TYPE, list1, list2); \
+ } \
+ NODE_TYPE##_list NODE_TYPE##_list_copy_concat(NODE_TYPE##_list list1, NODE_TYPE##_list list2){ \
+ NODE_TYPE##_list list1_copy = NODE_TYPE##_list_shallow_copy(list1); \
+ NODE_TYPE##_list list2_copy = NODE_TYPE##_list_shallow_copy(list2); \
+ CONCAT_LIST(NODE_TYPE, &list1_copy, &list2_copy); \
+ return list1_copy; \
+ } \
+ \
+ size_t NODE_TYPE##_list_length(NODE_TYPE##_list list){ \
+ NODE_TYPE* current = list.head; \
+ size_t count = 0; \
+ while(current != NULL){ \
+ count = count + 1; \
+ current = current->next; \
+ } \
+ return count; \
+ }
+
+/* void print_string_list(string_list n){ */
+/* string_list_item* current = n.head; */
+/* printf("START\n"); */
+/* while(current != NULL){ */
+/* printf("%s\n", current->str); */
+/* current = current->next; */
+/* } */
+/* printf("END\n"); */
+/* } */
+
+#endif //YIELDING_C_FUN_LISTS_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_main.c b/erts/lib_src/yielding_c_fun/ycf_main.c
new file mode 100644
index 0000000000..2287db8161
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_main.c
@@ -0,0 +1,330 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include "ycf_utils.h"
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+#include "lib/simple_c_gc/simple_c_gc.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+char* file_to_str(const char* filename) {
+ char * buffer;
+ long length;
+ FILE * f = fopen (filename, "rb");
+
+ if (f) {
+ fseek (f, 0, SEEK_END);
+ length = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ buffer = ycf_malloc (length+1);
+ size_t nr_of_read_bytes =
+ fread (buffer, 1, length, f);
+ if (nr_of_read_bytes != length) {
+ printf("error: while reading file %s\n", filename);
+ exit (1);
+ }
+ fclose (f);
+ buffer[length] = 0;
+ } else {
+ printf("error: could not open file %s\n", filename);
+ exit(1);
+ }
+
+ return buffer;
+}
+
+void str_to_file(const char *filepath, const char *data)
+{
+ FILE *fp = fopen(filepath, "w");
+ if (fp != NULL)
+ {
+ fputs(data, fp);
+ fclose(fp);
+ }
+}
+
+void parse_and_repeat_src_from_ast(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_node* tree = ycf_node_from_string(content);
+ ycf_node_print(tree, NULL);
+}
+
+void parse_and_print_symbols(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_symbol_list_print(content);
+}
+
+void parse_and_print_ast(const char* file_name){
+ char* content = file_to_str(file_name);
+ ycf_node* tree = ycf_node_from_string(content);
+ print_abstract_syntax_tree(tree);
+}
+
+void print_help_text_and_exit(char* program_name, int error_code) {
+ printf("Usage: %s [-h]\n"
+ " %s [-use_gc [-print_gc_info]]\n"
+ " [-log_max_mem_usage log_file]\n"
+ " [(( -f | -frec | -fnoauto ) function_name)...\n"
+ " [-output_file_name output_file]\n"
+ " [-header_file_name header_file]\n"
+ " [-debug]\n"
+ " [-only_yielding_funs]\n"
+ " [-static_aux_funs]\n"
+ " input_c_file]]\n"
+ "\n"
+ "Please see the README.md file for more details.\n",
+ program_name,
+ program_name);
+ exit(0);
+}
+
+int ycf_main( int argc, char* argv[] )
+{
+ int i = 1;
+ for(i = 1; i < argc; i++ ){
+ bool repeat = ycf_string_is_equal("-repeat", argv[i]);
+ bool print_symbols = ycf_string_is_equal("-print_symbols", argv[i]);
+ bool print_ast = ycf_string_is_equal("-print_ast", argv[i]);;
+ bool yield = ycf_string_is_equal("-yield", argv[i]);
+ if(repeat || print_symbols || print_ast || yield){
+ i++;
+ if(i >= argc){
+ printf("ERROR: Expected at least one more argument\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ }
+ if (print_ast) {
+ parse_and_print_ast(argv[i]);
+ } else if (repeat) {
+ parse_and_repeat_src_from_ast(argv[i]);
+ } else if (print_symbols) {
+ parse_and_print_symbols(argv[i]);
+ } else /* Default is yield */ {
+ ycf_string_item_list funs_to_yield = ycf_string_item_list_empty();
+ ycf_string_item_list funs_to_yield_no_auto = ycf_string_item_list_empty();
+ ycf_string_item_list funs_to_yield_frec = ycf_string_item_list_empty();
+ ycf_string_item_list all_yield_funs = ycf_string_item_list_empty();
+ char* header_file_name = NULL;
+ char* output_file_name = NULL;
+ bool debug = false;
+ bool only_yielding_funs = false;
+ bool static_aux_funs = false;
+ while(i < argc && (ycf_string_is_equal(argv[i], "-f") ||
+ ycf_string_is_equal(argv[i], "-frec") ||
+ ycf_string_is_equal(argv[i], "-fnoauto") ||
+ ycf_string_is_equal(argv[i], "-header_file_name") ||
+ ycf_string_is_equal(argv[i], "-output_file_name") ||
+ ycf_string_is_equal(argv[i], "-debug") ||
+ ycf_string_is_equal(argv[i], "-only_yielding_funs") ||
+ ycf_string_is_equal(argv[i], "-static_aux_funs"))){
+ bool frec = ycf_string_is_equal(argv[i], "-frec");
+ bool noauto = ycf_string_is_equal(argv[i], "-fnoauto");
+ bool header = ycf_string_is_equal(argv[i], "-header_file_name");
+ bool output = ycf_string_is_equal(argv[i], "-output_file_name");
+ i++;
+ if(ycf_string_is_equal(argv[i-1], "-debug")){
+ debug = true;
+ continue;
+ }
+ if(ycf_string_is_equal(argv[i-1], "-only_yielding_funs")){
+ only_yielding_funs = true;
+ continue;
+ }
+ if(ycf_string_is_equal(argv[i-1], "-static_aux_funs")){
+ static_aux_funs = true;
+ continue;
+ }
+ if(i >= argc){
+ fprintf(stderr, "ERROR: Expected a name after %s\n\n", argv[i-1]);
+ print_help_text_and_exit(argv[0], 1);
+ }
+ if(header){
+ if(header_file_name != NULL){
+ fprintf(stderr, "ERROR: Can only print a single header file\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ header_file_name = (char*)argv[i];
+ } else if(output){
+ if(output_file_name != NULL){
+ fprintf(stderr, "ERROR: Can only print a single output file\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ output_file_name = (char*)argv[i];
+ } else if(frec){
+ ycf_string_item_list_append(&funs_to_yield_frec, ycf_string_item_new((char *) argv[i]));
+ } else if(noauto){
+ ycf_string_item_list_append(&funs_to_yield_no_auto, ycf_string_item_new((char *) argv[i]));
+ } else{
+ ycf_string_item_list_append(&funs_to_yield, ycf_string_item_new((char *) argv[i]));
+ }
+ i++;
+ }
+ ycf_string_item_list funs_to_yield_copy = ycf_string_item_list_shallow_copy(funs_to_yield);
+ ycf_string_item_list funs_to_yield_no_auto_copy = ycf_string_item_list_shallow_copy(funs_to_yield_no_auto);
+ ycf_string_item_list funs_to_yield_frec_copy = ycf_string_item_list_shallow_copy(funs_to_yield_frec);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_copy);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_no_auto_copy);
+ ycf_string_item_list_concat(&all_yield_funs, &funs_to_yield_frec_copy);
+ if(funs_to_yield.head == NULL && funs_to_yield_no_auto.head == NULL && funs_to_yield_frec.head == NULL){
+ fprintf(stderr, "ERROR: Expected at least one \"(-f|-frec|-fnoauto) function_name\" argument\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ if(i >= argc){
+ fprintf(stderr, "ERROR: Expected an input file name as the last parameter\n\n");
+ print_help_text_and_exit(argv[0], 1);
+ }
+ {
+ char* file_name = (char*)argv[i];
+ char* src = file_to_str(file_name);
+ ycf_string_item* current_fun = funs_to_yield.head;
+ ycf_node* header_file = ycf_node_from_string("");
+ ycf_node* tree = ycf_node_from_string(src);
+ ycf_node* only_yielding_funs_tree = NULL;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ true,
+ false,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ current_fun = funs_to_yield_no_auto.head;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ false,
+ false,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ current_fun = funs_to_yield_frec.head;
+ while(current_fun != NULL){
+ tree = ast_get_ast_with_yieldified_function(tree,
+ header_file,
+ current_fun->str,
+ &all_yield_funs,
+ true,
+ true,
+ debug,
+ only_yielding_funs,
+ &only_yielding_funs_tree,
+ static_aux_funs);
+ current_fun = current_fun->next;
+ }
+ ast_add_yield_code_generated_define(tree, debug);
+ ast_add_yield_code_generated_define(header_file, debug);
+ if(only_yielding_funs){
+ ast_add_yield_code_generated_define(only_yielding_funs_tree, debug);
+ }
+ if(header_file_name != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(header_file, b);
+ str_to_file(header_file_name, b->buffer);
+ }
+ if(only_yielding_funs){
+ tree = only_yielding_funs_tree;
+ }
+ if(output_file_name != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(tree, b);
+ str_to_file(output_file_name, b->buffer);
+ } else {
+ ycf_node_print(tree, NULL);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+bool is_help_option(char* string) {
+return
+ ycf_string_is_equal("-h", string) ||
+ ycf_string_is_equal("--h", string) ||
+ ycf_string_is_equal("-help", string) ||
+ ycf_string_is_equal("--help", string);
+}
+
+int main( int argc, char* argv[] )
+{
+ bool log_max_mem_usage = false;
+ bool use_gc = false;
+ char * log_max_mem_usage_file = NULL;
+ int i = 1;
+ if (argc == 1) {
+ print_help_text_and_exit(argv[0], 0);
+ }
+ while(i < argc &&
+ (ycf_string_is_equal("-use_gc", argv[i]) ||
+ ycf_string_is_equal("-log_max_mem_usage", argv[i]) ||
+ ycf_string_is_equal("-print_gc_info", argv[i]) ||
+ is_help_option(argv[i]))) {
+ if (is_help_option(argv[i])) {
+ print_help_text_and_exit(argv[0], 0);
+ } else if (ycf_string_is_equal("-use_gc", argv[i])) {
+ use_gc = true;
+ i++;
+ } else if(ycf_string_is_equal("-print_gc_info", argv[i])) {
+ scgc_enable_print_gc_info();
+ i++;
+ } else if(ycf_string_is_equal(argv[i], "-log_max_mem_usage")) {
+ ycf_enable_memory_tracking();
+ log_max_mem_usage = true;
+ i++;
+ log_max_mem_usage_file = (char*)argv[i];
+ i++;
+ }
+ }
+ int nr_of_params_to_remove = i - 1;
+ int ret;
+ if (!use_gc) {
+ ret = ycf_main(argc - nr_of_params_to_remove ,
+ argv + nr_of_params_to_remove);
+ } else {
+ ycf_enable_gc();
+ ret = scgc_start_gced_code(ycf_main,
+ argc - nr_of_params_to_remove,
+ argv + nr_of_params_to_remove,
+ ycf_raw_malloc,
+ ycf_free);
+ }
+ if(log_max_mem_usage){
+ ycf_malloc_log(log_max_mem_usage_file, "all");
+ }
+ return ret;
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_node.c b/erts/lib_src/yielding_c_fun/ycf_node.c
new file mode 100644
index 0000000000..517c7e991d
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_node.c
@@ -0,0 +1,915 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "ycf_symbol.h"
+#include "ycf_node.h"
+#include "ycf_string.h"
+#include "ycf_lists.h"
+#include "ycf_yield_fun.h"
+#include "ycf_parser.h"
+
+GENERATE_LIST_FUNCTIONS(ycf_node)
+
+#define YCF_UNIQ_VARIABLE_NAME_NUMBER_PREFIX "_N"
+
+static int ycf_uniq_variable_name_number_counter = 0;
+
+static void uniqify_local_vars_in_node_rename_var_helper(ycf_node* n,
+ char* old_name,
+ char* new_name);
+static void uniqify_local_vars_in_scope_rename_var_helper(ycf_node_code_scope* s,
+ char* old_name,
+ char* new_name);
+
+
+static void uniqify_local_vars_in_expression_rename_var_helper(ycf_node_expression* n,
+ char* old_name,
+ char* new_name){
+ ycf_node* current = n->content.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(current,
+ old_name,
+ new_name);
+ current = current->next;
+ }
+}
+
+static void uniqify_local_vars_in_paran_expression_rename_var_helper(ycf_node_parentheses_expression* n,
+ char* old_name,
+ char* new_name){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->content,
+ old_name,
+ new_name);
+}
+
+static void uniqify_local_vars_in_fun_call_rename_var_helper(ycf_node_function_call* n,
+ char* old_name,
+ char* new_name)
+{
+ ycf_node* e = n->parameter_expressions.head;
+ while(e != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ e = e->next;
+ }
+ if(ycf_symbol_is_text_eq(n->identifier, old_name)){
+ n->identifier = ycf_symbol_copy_change_text(n->identifier, new_name);
+ }
+}
+
+static void uniqify_local_vars_in_assignment_fun_call_rename_var_helper(ycf_node_function_call_assignment* n,
+ char* old_name,
+ char* new_name)
+{
+ uniqify_local_vars_in_expression_rename_var_helper(&n->left_side, old_name, new_name);
+ uniqify_local_vars_in_fun_call_rename_var_helper(&n->fun_call, old_name, new_name);
+}
+
+static void uniqify_local_vars_in_node_rename_var_helper(ycf_node* n,
+ char* old_name,
+ char* new_name){
+ if(n == NULL){
+ return;
+ } else if(n->type == ycf_node_type_code_scope){
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.code_scope, old_name, new_name);
+ } else if(n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code){
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.special_code_block.code.if_statement->u.code_scope, old_name, new_name);
+ } else if(n->type == ycf_node_type_other &&
+ n->u.other.what->type == ycf_symbol_type_identifier &&
+ ycf_symbol_is_text_eq(n->u.other.what, old_name)){
+ n->u.other.what =
+ ycf_symbol_copy_change_text(n->u.other.what, new_name);
+ } else if(n->type == ycf_node_type_assignment){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.a.left_side, old_name, new_name);
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.a.right_side, old_name, new_name);
+ } else if (n->type == ycf_node_type_function_call){
+ uniqify_local_vars_in_fun_call_rename_var_helper(&n->u.function_call, old_name, new_name);
+ } else if (n->type == ycf_node_type_statement){
+ ycf_node* e = n->u.statement.expression;
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ } else if (n->type == ycf_node_type_return_statement && n->u.return_n.return_expression != NULL){
+ ycf_node* e = n->u.return_n.return_expression;
+ uniqify_local_vars_in_node_rename_var_helper(e, old_name, new_name);
+ } else if (n->type == ycf_node_type_expression){
+ uniqify_local_vars_in_expression_rename_var_helper(&n->u.expression, old_name, new_name);
+ } else if (n->type == ycf_node_type_assignment_function_call){
+ uniqify_local_vars_in_assignment_fun_call_rename_var_helper(&n->u.function_call_assignment, old_name, new_name);
+ }else if (n->type == ycf_node_type_parentheses_expression){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.parentheses_expression, old_name, new_name);
+ } else if (n->type == ycf_node_type_if){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.if_n.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_n.if_statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_if_else){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.if_else.if_part.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_else.if_part.if_statement, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.if_else.else_statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_while){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.while_n.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.while_n.statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_do_while){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.do_while.expression, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.do_while.statement, old_name, new_name);
+ } else if (n->type == ycf_node_type_switch){
+ uniqify_local_vars_in_paran_expression_rename_var_helper(&n->u.switch_n.expression, old_name, new_name);
+ uniqify_local_vars_in_scope_rename_var_helper(&n->u.switch_n.scope, old_name, new_name);
+ } else if (n->type == ycf_node_type_for){
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.init, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.stop_cond, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.end_exp, old_name, new_name);
+ uniqify_local_vars_in_node_rename_var_helper(n->u.for_n.statement, old_name, new_name);
+ }
+}
+
+static void uniqify_local_vars_in_scope_rename_var_helper(ycf_node_code_scope* s,
+ char* old_name,
+ char* new_name){
+ ycf_node* current = s->other_nodes.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node_rename_var_helper(current, old_name, new_name);
+ current = current->next;
+ }
+}
+
+
+
+static void uniqify_definitions_in_scope(ycf_node_list* definition_nodes, ycf_node_code_scope* s){
+ ycf_node* current = definition_nodes->head;
+ while(current != NULL){
+ ycf_symbol * old_symbol = NULL;
+ if(current->type == ycf_node_type_variable_definition){
+ old_symbol = current->u.definition.identifier;
+ }else if(current->type == ycf_node_type_variable_definition_init){
+ old_symbol = current->u.definition_init.definition.identifier;
+ }
+ ycf_uniq_variable_name_number_counter++;
+ {
+ char* old_name = ycf_symbol_get_text(old_symbol);
+ char* new_name = ycf_string_new("%s%s%lu", old_name, YCF_UNIQ_VARIABLE_NAME_NUMBER_PREFIX,
+ ycf_uniq_variable_name_number_counter);
+ if(current->type == ycf_node_type_variable_definition){
+ current->u.definition.identifier = ycf_symbol_copy_change_text(old_symbol, new_name);
+ }else if(current->type == ycf_node_type_variable_definition_init){
+ current->u.definition_init.definition.identifier = ycf_symbol_copy_change_text(old_symbol, new_name);
+ }
+ uniqify_local_vars_in_scope_rename_var_helper(s, old_name, new_name);
+ current = current->next;
+ }
+ }
+}
+
+static void uniqify_local_vars_in_scope(ycf_node_code_scope* s);
+
+static void uniqify_local_vars_in_node(ycf_node* n){
+ if(n->type == ycf_node_type_code_scope){
+ uniqify_local_vars_in_scope(&n->u.code_scope);
+ } else if(n->type == ycf_node_type_on_restore_yield_state_code){
+ uniqify_local_vars_in_scope(&n->u.special_code_block.code.if_statement->u.code_scope);
+ } else if(n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code){
+ uniqify_local_vars_in_scope(&n->u.special_code_block.code.if_statement->u.code_scope);
+ } else if (n->type == ycf_node_type_if){
+ uniqify_local_vars_in_node(n->u.if_n.if_statement);
+ } else if (n->type == ycf_node_type_if_else){
+ uniqify_local_vars_in_node(n->u.if_else.if_part.if_statement);
+ uniqify_local_vars_in_node(n->u.if_else.else_statement);
+ } else if (n->type == ycf_node_type_while){
+ uniqify_local_vars_in_node(n->u.while_n.statement);
+ } else if (n->type == ycf_node_type_do_while){
+ uniqify_local_vars_in_node(n->u.do_while.statement);
+ } else if (n->type == ycf_node_type_switch){
+ uniqify_local_vars_in_scope(&n->u.switch_n.scope);
+ } else if (n->type == ycf_node_type_for){
+ uniqify_local_vars_in_node(n->u.for_n.statement);
+ }
+}
+
+static void uniqify_local_vars_in_scope(ycf_node_code_scope* s){
+ ycf_node* current = s->other_nodes.head;
+ while(current != NULL){
+ uniqify_local_vars_in_node(current);
+ current = current->next;
+ }
+ uniqify_definitions_in_scope(&s->definition_nodes, s);
+}
+
+void ycf_uniqify_local_vars_in_function(ycf_node* function){
+ uniqify_local_vars_in_scope(&function->u.function.body);
+ uniqify_definitions_in_scope(&function->u.function.definition.parameters,
+ &function->u.function.body);
+}
+
+
+ycf_node* ycf_node_new_assignment_from_definition_init(ycf_node_definition_init* n){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->type = ycf_node_type_assignment;
+ res->next = NULL;
+ res->u.a.assignment_symbol = n->definition.end;
+ {
+ ycf_symbol* ident = ycf_symbol_copy(n->definition.identifier);
+ ident->whitespace_or_comment_before =
+ n->definition.type_specifiers.head->whitespace_or_comment_before;
+ res->u.a.left_side = parse_expression(ident).result->u.expression;
+ }
+ res->u.a.right_side = parse_expression(n->initializer_expression.head).result->u.expression;
+ res->u.a.end = ycf_symbol_new_semicolon();
+ return res;
+}
+
+ycf_node* ycf_node_new_definition_from_definition_init(ycf_node_definition_init* n){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->type = ycf_node_type_variable_definition;
+ res->next = NULL;
+ res->u.definition = n->definition;
+ res->u.definition.end = ycf_symbol_new_semicolon();
+ return res;
+}
+
+ycf_node* ycf_node_code_scope_shallow_copy(ycf_node* n){
+ ycf_node* new_scope = ycf_node_shallow_copy(n);
+ new_scope->u.code_scope.definition_nodes = ycf_node_list_shallow_copy(new_scope->u.code_scope.definition_nodes);
+ new_scope->u.code_scope.other_nodes = ycf_node_list_shallow_copy(new_scope->u.code_scope.other_nodes);
+ return new_scope;
+}
+
+
+void ycf_node_search_and_replace_statements_in_node(ycf_node* n, ycf_node* (*replacer) (ycf_node*,
+ ycf_node_code_scope*,
+ void*), void* context,
+ ycf_node_code_scope* s){
+ if(n->type == ycf_node_type_code_scope){
+ ycf_node_search_and_replace_statements_in_scope(&n->u.code_scope, replacer, context);
+ } else if(n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code ||
+ n->type == ycf_node_type_on_return_code) {
+ ycf_node* replace_candidate = n->u.special_code_block.code.if_statement;
+ ycf_node* possible_replacement = replacer(replace_candidate, s, context);
+ if(replace_candidate != possible_replacement){
+ n->u.special_code_block.code.if_statement = possible_replacement;
+ } else {
+ ycf_node_search_and_replace_statements_in_node(n->u.special_code_block.code.if_statement,
+ replacer,
+ context,
+ s);
+ }
+ } else if (n->type == ycf_node_type_if){
+ ycf_node_search_and_replace_statements_in_node(n->u.if_n.if_statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_if_else){
+ ycf_node_search_and_replace_statements_in_node(n->u.if_else.if_part.if_statement, replacer, context, s);
+ ycf_node_search_and_replace_statements_in_node(n->u.if_else.else_statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_while){
+ ycf_node_search_and_replace_statements_in_node(n->u.while_n.statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_do_while){
+ ycf_node_search_and_replace_statements_in_node(n->u.do_while.statement, replacer, context, s);
+ } else if (n->type == ycf_node_type_switch){
+ ycf_node_search_and_replace_statements_in_scope(&n->u.switch_n.scope, replacer, context);
+ } else if (n->type == ycf_node_type_for){
+ ycf_node_search_and_replace_statements_in_node(n->u.for_n.statement, replacer, context, s);
+ }
+}
+
+void ycf_node_search_and_replace_statements_in_scope(ycf_node_code_scope* s,
+ ycf_node* (*replacer) (ycf_node* canditate,
+ ycf_node_code_scope* candidates_scope,
+ void* context),
+ void* context){
+ ycf_node* n = s->other_nodes.head;
+ while(n != NULL){
+ ycf_node* replace_candidate = n;
+ ycf_node* possible_replacement = replacer(replace_candidate, s, context);
+ if(replace_candidate != possible_replacement){
+ ycf_node_list_replace(&s->other_nodes, replace_candidate, possible_replacement);
+ } else {
+ ycf_node_search_and_replace_statements_in_node(n, replacer, context, s);
+ }
+ n = n->next;
+ }
+ return;
+}
+
+ycf_node_code_scope ycf_node_copy_search_and_replace_statements_in_scope(ycf_node_code_scope s,
+ ycf_node* (*replacer) (ycf_node*,
+ ycf_node_code_scope*,
+ void*),
+ void* context){
+ ycf_node_search_and_replace_statements_in_scope(&s, replacer, context);
+ return s;
+}
+
+char* ycf_node_to_string(ycf_node* n){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node_print(n, b);
+ return b->buffer;
+}
+
+char* ycf_node_list_to_string(ycf_node_list* l){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node* current = l->head;
+ while(current != NULL){
+ ycf_node_print(current, b);
+ current = current->next;
+ }
+ return b->buffer;
+}
+
+ycf_node* ycf_node_new_text_node(char* text){
+ ycf_node* n = ycf_node_new();
+ n->type = ycf_node_type_other;
+ n->u.other.what = ycf_symbol_new_something_else(text);
+ return n;
+}
+
+ycf_node* ycf_node_new(){
+ ycf_node* new = ycf_malloc(sizeof(ycf_node));
+ new->next = NULL;
+ return new;
+}
+
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_function_definition &&
+ ycf_symbol_is_text_eq(current->u.function.definition.definition.identifier,
+ fun_name)){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+ycf_node* ycf_node_find_function_declaration(ycf_node* c_file_node, char* fun_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_function_declaration &&
+ ycf_symbol_is_text_eq(current->u.function_definition.definition.identifier,
+ fun_name)){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+ycf_node* ycf_node_find_define_node(ycf_node* c_file_node, char* define_name){
+ ycf_node* current = c_file_node->u.c_file.content.head;
+ while(current != NULL){
+ if(current->type == ycf_node_type_other &&
+ ycf_symbol_is_text_eq(current->u.other.what,
+ ycf_string_new("#define %s", define_name))){
+ return current;
+ }
+ current = current->next;
+ }
+ return NULL;
+}
+
+bool ycf_node_is_void_ret_fun(ycf_node* f_node){
+ ycf_symbol_list type = f_node->u.function.definition.definition.type_specifiers;
+ return type.last->type == ycf_symbol_type_void;
+}
+
+ycf_node* ycf_node_get_from_code_scope_text(char* code){
+ char* f_code = ycf_string_new("void f(){\n"
+ "{\n"
+ "%s\n"
+ "}\n"
+ "}\n",
+ code);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(f_code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head->u.function.body.other_nodes.head;
+}
+
+ycf_node* ycf_node_get_function_from_text(char* code){
+ char* f_code = ycf_string_new("%s",
+ code);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(f_code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("ycf_get_function_from_text: NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head;
+}
+
+ycf_node* ycf_node_from_string(char* src){
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(src);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ return tree;
+}
+
+ycf_node* mk_scope_wrapper(ycf_node* statement){
+ return ycf_node_get_from_code_scope_text(ycf_string_new("{\n"
+ " %s\n"
+ "}\n",
+ ycf_node_to_string(statement)));
+}
+
+static ycf_node* scope_inserter(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ (void)context;
+ (void)s;
+ if (candidate->type == ycf_node_type_while){
+ if(candidate->u.while_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.while_n.statement->u.code_scope);
+ }
+ candidate->u.while_n.statement = mk_scope_wrapper(candidate->u.while_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_do_while){
+ if(candidate->u.do_while.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.do_while.statement->u.code_scope);
+ }
+ candidate->u.do_while.statement = mk_scope_wrapper(candidate->u.do_while.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.for_n.statement->u.code_scope);
+ }
+ candidate->u.for_n.statement = mk_scope_wrapper(candidate->u.for_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_if){
+ if(candidate->u.if_n.if_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_n.if_statement->u.code_scope);
+ }
+ candidate->u.if_n.if_statement = mk_scope_wrapper(candidate->u.if_n.if_statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_if_else){
+ if(candidate->u.if_else.if_part.if_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_else.if_part.if_statement->u.code_scope);
+ }
+ candidate->u.if_else.if_part.if_statement = mk_scope_wrapper(candidate->u.if_else.if_part.if_statement);
+ if(candidate->u.if_else.else_statement->type == ycf_node_type_code_scope){
+ ycf_node_insert_scopes_in_complex_statements(&candidate->u.if_else.else_statement->u.code_scope);
+ }
+ candidate->u.if_else.else_statement = mk_scope_wrapper(candidate->u.if_else.else_statement);
+ return candidate;
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_insert_scopes_in_complex_statements(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ scope_inserter,
+ NULL);
+}
+
+
+static ycf_node* scope_remover(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ uintptr_t* removed_scopes = context;
+ if(candidate->type == ycf_node_type_code_scope &&
+ ycf_node_list_length(candidate->u.code_scope.definition_nodes) == 0 &&
+ ycf_node_list_length(candidate->u.code_scope.other_nodes) == 1 &&
+ candidate->u.code_scope.other_nodes.head->type == ycf_node_type_code_scope) {
+ *removed_scopes = *removed_scopes + 1;
+ return candidate->u.code_scope.other_nodes.head;
+ }
+ return candidate;
+}
+
+void ycf_node_remove_unecessary_scopes(ycf_node_code_scope* s){
+ uintptr_t removed_scopes;
+ do{
+ removed_scopes = 0;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ scope_remover,
+ &removed_scopes);
+ }while(removed_scopes > 0);
+}
+
+static ycf_node* for_declaration_normalizer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ (void)context;
+ (void)s;
+ if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.init != NULL &&
+ (candidate->u.for_n.init->type == ycf_node_type_variable_definition ||
+ candidate->u.for_n.init->type == ycf_node_type_variable_definition_init)){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ ycf_node_normalize_for_var_declarations(&candidate->u.for_n.statement->u.code_scope);
+ }
+ ycf_string_printable_buffer * b = ycf_string_printable_buffer_new();
+ ycf_node_print(candidate->u.for_n.init, b);
+ candidate->u.for_n.init = ycf_node_statement_new(ycf_node_expression_new(ycf_node_list_empty()),
+ ycf_symbol_new_semicolon());
+ ycf_node_print(candidate, b);
+ return ycf_node_get_from_code_scope_text(b->buffer);
+ }else {
+ return candidate;
+ }
+
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_normalize_for_var_declarations(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ for_declaration_normalizer,
+ NULL);
+}
+
+
+
+static ycf_node* ycf_node_move_in_code_var_declarations_to_top_mover(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ if (candidate->type == ycf_node_type_variable_definition) {
+ ycf_node_list_append(&candidates_scope->definition_nodes,
+ ycf_node_shallow_copy(candidate));
+ return ycf_node_get_from_code_scope_text(ycf_string_new("\n/* moved declaration (%s)*/\n",
+ ycf_symbol_get_text(candidate->u.definition.identifier)));
+ } else if (candidate->type == ycf_node_type_variable_definition_init) {
+ ycf_node_list_append(&candidates_scope->definition_nodes,
+ ycf_node_new_definition_from_definition_init(&candidate->u.definition_init));
+ return ycf_node_new_assignment_from_definition_init(&candidate->u.definition_init);
+ } else {
+ return candidate;
+ }
+}
+
+void ycf_node_move_in_code_var_declarations_to_top(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_move_in_code_var_declarations_to_top_mover,
+ NULL);
+}
+
+static ycf_node* ycf_node_remove_declarations_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ candidates_scope->definition_nodes = ycf_node_list_empty();
+ return candidate;
+}
+
+void ycf_node_remove_declarations_in_scope(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_remove_declarations_in_scope_helper,
+ NULL);
+}
+
+static void normalize_init_definitions_in_scope_helper_insert_assignments(ycf_node* current_def,
+ ycf_node_list* other_nodes){
+ if(current_def == NULL){
+ return;
+ }
+ normalize_init_definitions_in_scope_helper_insert_assignments(current_def->next,
+ other_nodes);
+ if(current_def->type == ycf_node_type_variable_definition_init){
+ ycf_node* assignment = ycf_node_new_assignment_from_definition_init(&current_def->u.definition_init);
+ ycf_node_list_prepend(other_nodes, assignment);
+ return;
+ }else{
+ return;
+ }
+}
+
+static ycf_node* ycf_node_normalize_init_definitions_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ (void)context;
+ (void)candidates_scope;
+ ycf_node_code_scope* s = NULL;
+ if(candidate == NULL){
+ s = context;
+ } else if(candidate->type == ycf_node_type_code_scope){
+ s = &candidate->u.code_scope;
+
+ } else if(candidate->type == ycf_node_type_switch){
+ s = &candidate->u.switch_n.scope;
+ }
+ if(s != NULL) {
+ ycf_node * current = s->definition_nodes.head;
+ normalize_init_definitions_in_scope_helper_insert_assignments(s->definition_nodes.head,
+ &s->other_nodes);
+ while (current != NULL) {
+ if (current->type == ycf_node_type_variable_definition_init) {
+ ycf_node *def_node = ycf_node_new_definition_from_definition_init(&current->u.definition_init);
+ ycf_node_list_replace(&s->definition_nodes, current, def_node);
+ }
+ current = current->next;
+ }
+ }
+ return candidate;
+}
+
+void ycf_node_normalize_init_definitions_in_scope(ycf_node_code_scope* s){
+ ycf_node_normalize_init_definitions_in_scope_helper(NULL,
+ NULL,
+ s);
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_normalize_init_definitions_in_scope_helper,
+ NULL);
+}
+
+
+static ycf_node* ycf_node_get_declarations_in_scope_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ ycf_node_list* list = (ycf_node_list*)context;
+ if(candidate->type == ycf_node_type_code_scope){
+ ycf_node_list to_append = ycf_node_list_shallow_copy(candidate->u.code_scope.definition_nodes);
+ ycf_node_list_concat(list, &to_append);
+ } else if(candidate->type == ycf_node_type_switch){
+ ycf_node_list to_append = ycf_node_list_shallow_copy(candidate->u.switch_n.scope.definition_nodes);
+ ycf_node_list_concat(list, &to_append);
+ }
+ return candidate;
+}
+
+ycf_node_list ycf_node_get_declarations_in_scope(ycf_node_code_scope* s){
+ ycf_node_list res = s->definition_nodes;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ ycf_node_get_declarations_in_scope_helper,
+ &res);
+ return res;
+}
+
+ycf_node_list ycf_node_definition_list_from_string(char* str){
+ return ycf_node_get_from_code_scope_text(str)->u.code_scope.definition_nodes;
+}
+
+void ycf_node_rename_function(ycf_node_function* f, char* new_name){
+ f->definition.definition.identifier =
+ ycf_symbol_copy_change_text(f->definition.definition.identifier,
+ new_name);
+}
+
+static bool is_scope_ends_with_void_ret(ycf_node_code_scope* scope_node){
+ if(scope_node->other_nodes.last->type == ycf_node_type_code_scope){
+ return is_scope_ends_with_void_ret(&scope_node->other_nodes.last->u.code_scope);
+ }else{
+ ycf_node* prev = NULL;
+ ycf_node* current = scope_node->other_nodes.head;
+ while(current != NULL){
+ prev = current;
+ current = current->next;
+ }
+ return
+ prev != NULL &&
+ current != NULL &&
+ prev->type == ycf_node_type_other &&
+ prev->u.other.what->type == ycf_symbol_type_return &&
+ current->type == ycf_node_type_other &&
+ current->u.other.what->type == ycf_symbol_type_semicolon;
+ }
+}
+
+bool ycf_node_is_void_ret_ending_fun(ycf_node* f_node){
+ return is_scope_ends_with_void_ret(&f_node->u.function.body);
+}
+
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f){
+ ycf_node_list all = ycf_node_get_declarations_in_scope(&f->body);
+ ycf_node_list all_copy = ycf_node_list_shallow_copy(all);
+ ycf_node_list params = f->definition.parameters;
+ ycf_node_list_concat(&all_copy, &params);
+ return all_copy;
+}
+
+void ycf_node_remove_const_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_const){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_static_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_static){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_inline_specifiers_from_declaration(ycf_node* declaration){
+ ycf_symbol* current = declaration->u.definition.type_specifiers.head;
+ while(current != NULL) {
+ if(current->type == ycf_symbol_type_inline){
+ ycf_symbol_list_remove(&declaration->u.definition.type_specifiers,
+ current);
+ }
+ current = current->next;
+ }
+}
+
+void ycf_node_remove_array_size_info_from_declaration(ycf_node* declaration){
+ ycf_node* current = declaration->u.definition.array_brackets.head;
+ while(current != NULL) {
+ current->u.array_bracket.empty = true;
+ current = current->next;
+ }
+}
+
+void ycf_node_modify_declarations(ycf_node_list declarations, void (*modifer)(ycf_node*)){
+ ycf_node* current = declarations.head;
+ while(current != NULL) {
+ modifer(current);
+ current = current->next;
+ }
+}
+
+ycf_symbol_list ycf_node_get_return_type(ycf_node* f_node_or_f_dec_node){
+ ycf_node_function_definition def;
+ ycf_node * tmp;
+ if (f_node_or_f_dec_node->type == ycf_node_type_function_declaration) {
+ def = f_node_or_f_dec_node->u.function_definition;
+ } else {
+ def = f_node_or_f_dec_node->u.function.definition;
+ }
+ tmp =
+ ycf_node_defenition_new(ycf_symbol_list_shallow_copy(def.definition.type_specifiers),
+ ycf_symbol_new_identifier(ycf_string_new("tmp")),
+ ycf_node_list_empty(),
+ ycf_symbol_new_semicolon());
+ ycf_node_remove_inline_specifiers_from_declaration(tmp);
+ ycf_node_remove_static_specifiers_from_declaration(tmp);
+ return tmp->u.definition.type_specifiers;
+}
+
+ycf_symbol_list ycf_node_find_function_return_type(ycf_node* c_file_node, char* fun_name)
+{
+ ycf_node* f_node_or_f_dec_node = ycf_node_find_function(c_file_node, fun_name);
+ if (f_node_or_f_dec_node == NULL) {
+ f_node_or_f_dec_node = ycf_node_find_function_declaration(c_file_node, fun_name);
+ }
+ if (f_node_or_f_dec_node == NULL) {
+ fprintf(stderr, "ycf_node_find_function_return_type: Could not find function declaration or definition: %s\n", fun_name);
+ exit(1);
+ }
+ return ycf_node_get_return_type(f_node_or_f_dec_node);
+}
+
+char* ycf_node_get_node_type_string(ycf_node_type t){
+ switch (t){
+ case ycf_node_type_c_file:
+ return "ycf_node_type_c_file";
+ case ycf_node_type_variable_definition:
+ return "ycf_node_type_variable_definition";
+ case ycf_node_type_variable_definition_init:
+ return "ycf_node_type_variable_definition_init";
+ case ycf_node_type_function_definition:
+ return "ycf_node_type_function_definition";
+ case ycf_node_type_function_declaration:
+ return "ycf_node_type_function_declaration";
+ case ycf_node_type_code_scope:
+ return "ycf_node_type_code_scope";
+ case ycf_node_type_other:
+ return "ycf_node_type_other";
+ case ycf_node_type_gen_typedef_struct:
+ return "ycf_node_type_gen_typedef_struct";
+ case ycf_node_type_expression:
+ return "ycf_node_type_expression";
+ case ycf_node_type_statement:
+ return "ycf_node_type_statement";
+ case ycf_node_type_return_statement:
+ return "ycf_node_type_return_statement";
+ case ycf_node_type_assignment:
+ return "ycf_node_type_assignment";
+ case ycf_node_type_yield:
+ return "ycf_node_type_yield";
+ case ycf_node_type_consume_reds:
+ return "ycf_node_type_consume_reds";
+ case ycf_node_type_goto:
+ return "ycf_node_type_goto";
+ case ycf_node_type_parentheses_expression:
+ return "ycf_node_type_parentheses_expression";
+ case ycf_node_type_function_call:
+ return "ycf_node_type_function_call";
+ case ycf_node_type_assignment_function_call:
+ return "ycf_node_type_assignment_function_call";
+ case ycf_node_type_while:
+ return "ycf_node_type_while";
+ case ycf_node_type_do_while:
+ return "ycf_node_type_do_while";
+ case ycf_node_type_for:
+ return "ycf_node_type_for";
+ case ycf_node_type_switch:
+ return "ycf_node_type_switch";
+ case ycf_node_type_if:
+ return "ycf_node_type_if";
+ case ycf_node_type_if_else:
+ return "ycf_node_type_if_else";
+ case ycf_node_type_comma:
+ return "ycf_node_type_comma";
+ case ycf_node_type_array_bracket:
+ return "ycf_node_type_array_bracket";
+ case ycf_node_type_macro_cmd:
+ return "ycf_node_type_macro_cmd";
+ case ycf_node_type_period_field_access:
+ return "ycf_node_type_period_field_access";
+ case ycf_node_type_pointer_field_access:
+ return "ycf_node_type_pointer_field_access";
+ case ycf_node_type_on_save_yield_state_code:
+ return "ycf_node_type_on_save_yield_state_code";
+ case ycf_node_type_on_restore_yield_state_code:
+ return "ycf_node_type_on_restore_yield_state_code";
+ case ycf_node_type_on_destroy_state_code:
+ return "ycf_node_type_on_destroy_state_code";
+ case ycf_node_type_on_return_code:
+ return "ycf_node_type_on_return_code";
+ case ycf_node_type_on_destroy_state_or_return_code:
+ return "ycf_node_type_on_destroy_state_or_return_code";
+ }
+ return "unrecognized type";
+}
+
+void ycf_node_print_node_type(ycf_node_type t){
+ printf("%s\n",ycf_node_get_node_type_string(t));
+}
+
+ycf_node_assignment* ycf_node_get_assignment(ycf_node* n) {
+ if(n->type == ycf_node_type_assignment){
+ return &n->u.a;
+ } else if (n->type == ycf_node_type_statement &&
+ n->u.statement.expression->u.expression.content.head->type == ycf_node_type_assignment){
+ return &n->u.statement.expression->u.expression.content.head->u.a;
+ } else {
+ fprintf(stderr,
+ "Trying to get %s from a node of type %s\n",
+ "ycf_node_type_assignment",
+ ycf_node_get_node_type_string(n->type));
+ exit(1);
+ }
+}
+
+void ycf_node_normalize_function(ycf_node* fun){
+ /* Remove array size info from parameters */
+ ycf_node_modify_declarations(fun->u.function.definition.parameters,
+ ycf_node_remove_array_size_info_from_declaration);
+ /* Insert scope in nest statements (e.g., if(1) print(); -> if(1) {print();}) */
+ ycf_node_insert_scopes_in_complex_statements(&fun->u.function.body);
+ /* Move out declarations from for loops */
+ ycf_node_normalize_for_var_declarations(&fun->u.function.body);
+ /* Move in code declations to top of scope */
+ ycf_node_move_in_code_var_declarations_to_top(&fun->u.function.body);
+ /* Normalize declarations */
+ ycf_node_normalize_init_definitions_in_scope(&fun->u.function.body);
+ /* Uniqify local variables */
+ ycf_uniqify_local_vars_in_function(fun);
+ /* Save scope declarations and function declarations (includes
+ parameters) */
+ ycf_node_list scope_defs = ycf_node_get_declarations_in_scope(&fun->u.function.body);
+ scope_defs = ycf_node_list_shallow_copy(scope_defs);
+ ycf_node_list defs = ycf_node_get_all_definitions_in_function(&fun->u.function);
+ defs = ycf_node_list_shallow_copy(defs);
+ /* Remove const from const declarations */
+ ycf_node_modify_declarations(defs, ycf_node_remove_const_specifiers_from_declaration);
+ ycf_node_modify_declarations(scope_defs, ycf_node_remove_const_specifiers_from_declaration);
+ ycf_node_modify_declarations(fun->u.function.definition.parameters,
+ ycf_node_remove_const_specifiers_from_declaration);
+ /* Move all declarations to top scope */
+ ycf_node_remove_declarations_in_scope(&fun->u.function.body);
+ fun->u.function.body.definition_nodes = scope_defs;
+ /* Add return statement if function is missing return statement */
+ if(ycf_node_is_void_ret_fun(fun) &&
+ !ycf_node_is_void_ret_ending_fun(fun)){
+ ycf_node_list_append(&fun->u.function.body.other_nodes,
+ ycf_node_get_from_code_scope_text("return;"));
+ }
+ ycf_node_remove_unecessary_scopes(&fun->u.function.body);
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_node.h b/erts/lib_src/yielding_c_fun/ycf_node.h
new file mode 100644
index 0000000000..d230a1a2c2
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_node.h
@@ -0,0 +1,455 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_NODE_FUNS_H
+#define YIELDING_C_FUN_YCF_NODE_FUNS_H
+
+#include "ycf_utils.h"
+#include "ycf_string.h"
+#include "ycf_symbol.h"
+
+/* Types for nodes */
+typedef enum {
+ ycf_node_type_c_file,
+ ycf_node_type_variable_definition,
+ ycf_node_type_variable_definition_init,
+ ycf_node_type_function_definition,
+ ycf_node_type_function_declaration,
+ ycf_node_type_code_scope,
+ ycf_node_type_other,
+ ycf_node_type_gen_typedef_struct,
+ ycf_node_type_expression,
+ ycf_node_type_statement,
+ ycf_node_type_return_statement,
+ ycf_node_type_assignment,
+ ycf_node_type_yield,
+ ycf_node_type_consume_reds,
+ ycf_node_type_goto,
+ ycf_node_type_parentheses_expression,
+ ycf_node_type_function_call,
+ ycf_node_type_assignment_function_call,
+ ycf_node_type_while,
+ ycf_node_type_do_while,
+ ycf_node_type_for,
+ ycf_node_type_switch,
+ ycf_node_type_if,
+ ycf_node_type_if_else,
+ ycf_node_type_comma,
+ ycf_node_type_array_bracket,
+ ycf_node_type_macro_cmd,
+ ycf_node_type_period_field_access,
+ ycf_node_type_pointer_field_access,
+ ycf_node_type_on_save_yield_state_code,
+ ycf_node_type_on_restore_yield_state_code,
+ ycf_node_type_on_destroy_state_code,
+ ycf_node_type_on_return_code,
+ ycf_node_type_on_destroy_state_or_return_code
+} ycf_node_type;
+
+struct ycf_node;
+
+typedef struct {
+ struct ycf_node* head;
+ struct ycf_node* last;
+} ycf_node_list;
+
+typedef struct {
+ ycf_node_list content;
+} ycf_node_c_file;
+
+typedef struct {
+ ycf_node_list content;
+ ycf_symbol* end_symbol;
+} ycf_node_expression;
+
+typedef struct {
+ ycf_symbol* start_symbol;
+ ycf_node_expression content;
+ ycf_symbol* end_symbol;
+} ycf_node_parentheses_expression;
+
+typedef struct {
+ ycf_symbol_list neg_symbols;
+ ycf_symbol* identifier;
+ ycf_symbol* start_symbol;
+ ycf_node_list parameter_expressions;
+ ycf_symbol* end_symbol;
+} ycf_node_function_call;
+
+typedef struct array_bracket {
+ ycf_symbol* start;
+ bool empty;
+ ycf_node_expression content;
+ ycf_symbol* end;
+} ycf_node_array_bracket;
+
+
+typedef struct {
+ ycf_symbol_list type_specifiers;
+ ycf_symbol* identifier;
+ ycf_node_list array_brackets;
+ ycf_symbol* end;
+} ycf_node_definition;
+
+typedef struct {
+ ycf_node_definition definition;
+ ycf_symbol_list initializer_expression;
+ ycf_symbol* end;
+} ycf_node_definition_init;
+
+typedef struct {
+ struct ycf_node* expression;
+ ycf_symbol* end_symbol;
+} ycf_node_statement;
+
+typedef struct {
+ ycf_node_list definition_nodes;
+ ycf_node_list other_nodes;
+ ycf_symbol* start;
+ ycf_symbol* end;
+} ycf_node_code_scope;
+
+typedef struct {
+ ycf_symbol* while_word;
+ ycf_node_parentheses_expression expression;
+ struct ycf_node* statement;
+} ycf_node_while;
+
+typedef struct {
+ ycf_symbol* do_word;
+ struct ycf_node* statement;
+ ycf_symbol* while_word;
+ ycf_node_parentheses_expression expression;
+ ycf_symbol* end;
+} ycf_node_do_while;
+
+typedef struct {
+ ycf_symbol* for_word;
+ ycf_symbol* start_parentheses;
+ struct ycf_node* init;
+ struct ycf_node* stop_cond;
+ ycf_symbol* stop_cond_end;
+ struct ycf_node* end_exp;
+ ycf_symbol* end_parentheses;
+ struct ycf_node* statement;
+} ycf_node_for;
+
+typedef struct {
+ ycf_symbol* switch_word;
+ ycf_node_parentheses_expression expression;
+ ycf_node_code_scope scope;
+} ycf_node_switch;
+
+typedef struct {
+ ycf_symbol* if_word;
+ ycf_node_parentheses_expression expression;
+ struct ycf_node* if_statement;
+} ycf_node_if;
+
+typedef struct {
+ ycf_node_if if_part;
+ ycf_symbol* else_word;
+ struct ycf_node* else_statement;
+} ycf_node_if_else;
+
+typedef struct {
+ ycf_node_expression left_side;
+ ycf_symbol* assignment_symbol;
+ ycf_node_expression right_side;
+ ycf_symbol* end;
+} ycf_node_assignment;
+
+/*typedef struct {
+ ycf_symbol* id;
+} ycf_node_identifier;*/
+
+typedef struct {
+ ycf_symbol* yield_symbol;
+ ycf_symbol* end_symbol;
+} ycf_node_yield;
+
+typedef struct {
+ ycf_symbol* consume_reds_symbol;
+ ycf_node_parentheses_expression nr_of_reds_expression;
+ ycf_symbol* end_symbol;
+} ycf_node_consume_reds;
+
+typedef struct {
+ ycf_symbol* what;
+} ycf_node_other;
+
+typedef struct {
+ ycf_node_definition definition;
+ int ignore_param_ending;
+ ycf_node_list parameters;
+ ycf_symbol_list end;
+} ycf_node_function_definition;
+
+typedef struct {
+ ycf_node_function_definition definition;
+ ycf_node_code_scope body;
+} ycf_node_function;
+
+typedef struct {
+ ycf_node_expression left_side;
+ ycf_symbol* assignment_symbol;
+ ycf_node_function_call fun_call;
+} ycf_node_function_call_assignment;
+
+typedef struct {
+ ycf_node_list definition_nodes;
+ char* name;
+} ycf_node_gen_typedef_struct;
+
+typedef struct {
+ ycf_symbol* comma_symbol;
+} ycf_node_comma;
+
+typedef struct {
+ ycf_symbol* symbol;
+} ycf_node_macro_cmd;
+
+typedef struct {
+ struct ycf_symbol* start;
+ ycf_node_if code;
+ struct ycf_symbol* end;
+} ycf_node_special_code_block;
+
+
+typedef struct {
+ struct ycf_symbol* goto_symbol;
+ struct ycf_symbol* label_symbol;
+ struct ycf_symbol* end_symbol;
+} ycf_node_goto;
+
+typedef struct {
+ struct ycf_symbol* return_symbol;
+ struct ycf_node* return_expression;
+ struct ycf_symbol* end_symbol;
+} ycf_node_return;
+
+typedef struct {
+ struct ycf_symbol* period;
+ struct ycf_symbol* field_name;
+} ycf_node_period_field_access;
+
+typedef struct {
+ struct ycf_symbol* pointer;
+ struct ycf_symbol* field_name;
+} ycf_pointer_field_access;
+
+typedef struct ycf_node {
+ ycf_node_type type;
+ struct ycf_node* next;
+ union {
+ ycf_node_c_file c_file;
+ ycf_node_definition definition;
+ ycf_node_definition_init definition_init;
+ ycf_node_code_scope code_scope;
+ ycf_node_other other;
+ ycf_node_function function;
+ ycf_node_function_definition function_definition;
+ ycf_node_assignment a;
+ ycf_node_gen_typedef_struct gen_typedef_struct;
+ ycf_node_yield yield;
+ ycf_node_consume_reds consume_reds;
+ ycf_node_expression expression;
+ ycf_node_parentheses_expression parentheses_expression;
+ ycf_node_function_call function_call;
+ ycf_node_statement statement;
+ ycf_node_while while_n;
+ ycf_node_do_while do_while;
+ ycf_node_for for_n;
+ ycf_node_switch switch_n;
+ ycf_node_if if_n;
+ ycf_node_if_else if_else;
+ ycf_node_function_call_assignment function_call_assignment;
+ ycf_node_comma comma;
+ ycf_node_array_bracket array_bracket;
+ ycf_node_macro_cmd macro_cmd;
+ ycf_node_special_code_block special_code_block;
+ ycf_node_goto goto_n;
+ ycf_node_return return_n;
+ ycf_node_period_field_access period_field_access;
+ ycf_pointer_field_access pointer_field_access;
+ } u;
+} ycf_node;
+
+void ycf_node_search_and_replace_statements_in_scope(ycf_node_code_scope* s,
+ ycf_node* (*replacer) (ycf_node* canditate,
+ ycf_node_code_scope* candidates_scope,
+ void* context),
+ void* context);
+
+char* ycf_node_to_string(ycf_node* n);
+ycf_node* ycf_node_new_text_node(char* text);
+ycf_node* ycf_node_new();
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name);
+ycf_node* ycf_node_find_function_declaration(ycf_node* c_file_node, char* fun_name);
+ycf_node* ycf_node_find_define_node(ycf_node* c_file_node, char* define_name);
+bool ycf_node_is_void_ret_fun(ycf_node* f_node);
+ycf_node* ycf_node_get_from_code_scope_text(char* code);
+ycf_node* ycf_node_get_function_from_text(char* code);
+void ycf_node_print(ycf_node* node, ycf_string_printable_buffer* b);
+ycf_node* ycf_node_deep_copy(ycf_node *n);
+ycf_node* ycf_node_from_string(char* src);
+void ycf_node_insert_scopes_in_complex_statements(ycf_node_code_scope* s);
+void ycf_node_normalize_for_var_declarations(ycf_node_code_scope* s);
+void ycf_node_move_in_code_var_declarations_to_top(ycf_node_code_scope* s);
+void ycf_node_remove_declarations_in_scope(ycf_node_code_scope* s);
+ycf_node_list ycf_node_get_declarations_in_scope(ycf_node_code_scope* s);
+void ycf_node_normalize_init_definitions_in_scope(ycf_node_code_scope* s);
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f);
+void ycf_uniqify_local_vars_in_function(ycf_node* function);
+
+ycf_node* ycf_node_new_assignment_from_definition_init(ycf_node_definition_init* n);
+ycf_node* ycf_node_new_definition_from_definition_init(ycf_node_definition_init* n);
+ycf_node* ycf_node_defenition_new(ycf_symbol_list type_spec,
+ ycf_symbol* id,
+ ycf_node_list array_brackets,
+ ycf_symbol* end);
+ycf_node* ycf_node_defenition_with_init_new(ycf_node_definition def,
+ ycf_symbol_list expression,
+ ycf_symbol* end);
+ycf_node* ycf_node_c_file_new(ycf_node_list content);
+ycf_node* ycf_node_function_def_new(ycf_node_definition def_node,
+ ycf_node_list parameters,
+ bool ignore_param_ending,
+ ycf_symbol_list end);
+ycf_node* ycf_node_yield_new(ycf_symbol* yield_symbol,
+ ycf_symbol* end);
+ycf_node* ycf_node_consume_reds_new(ycf_symbol* consume_reds_symbol,
+ ycf_node_parentheses_expression nr_of_reds_expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_other_new(ycf_symbol* other_symbol);
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end);
+ycf_node* ycf_node_function_new(ycf_node_function_definition fun_def,
+ ycf_node_code_scope body);
+ycf_node* ycf_node_function_call_new(ycf_symbol_list neg_symbols,
+ ycf_symbol* ident,
+ ycf_symbol* paran_start,
+ ycf_node_list parameters,
+ ycf_symbol* paran_end);
+ycf_node* ycf_node_paran_expression_new(ycf_symbol* start, ycf_node_expression expr, ycf_symbol* end);
+ycf_node* ycf_node_expression_new(ycf_node_list expr);
+ycf_node* ycf_node_statement_new(ycf_node* expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_while_new(ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node* statement);
+ycf_node* ycf_node_do_while_new(ycf_symbol* do_word,
+ ycf_node* statm,
+ ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_symbol* end);
+ycf_node* ycf_node_for_new(ycf_symbol* for_word,
+ ycf_symbol* start_paran,
+ struct ycf_node* init,
+ ycf_node* stop_cond,
+ ycf_symbol* stop_cond_end,
+ ycf_node* end_exp,
+ ycf_symbol* end_paran,
+ ycf_node* statem);
+ycf_node* ycf_node_switch_new(ycf_symbol* switch_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node_code_scope scope);
+ycf_node* ycf_node_if_new(ycf_symbol* if_word,
+ ycf_node_parentheses_expression expression,
+ struct ycf_node* if_statem);
+ycf_node* ycf_node_if_else_new(ycf_node_if if_n,
+ ycf_symbol* else_word,
+ struct ycf_node* else_statement);
+ycf_node* ycf_node_fun_call_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_function_call fun_call);
+ycf_node* ycf_node_comma_new(ycf_symbol* comma_symbol);
+ycf_node* ycf_node_array_bracket_new(ycf_symbol* start,
+ bool empty,
+ ycf_node_expression content,
+ ycf_symbol* end);
+ycf_node* ycf_node_macro_cmd_new(ycf_symbol* macro_symbol);
+ycf_node* ycf_node_special_code_block_new(ycf_node_type type,
+ ycf_symbol* start,
+ ycf_node_if code,
+ ycf_symbol* end);
+ycf_node* ycf_node_goto_new(ycf_symbol* goto_symbol,
+ ycf_symbol* label_symbol,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_return_new(ycf_symbol* return_symbol,
+ ycf_node* return_expression,
+ ycf_symbol* end_symbol);
+ycf_node* ycf_node_period_field_access_new(ycf_symbol* period,
+ ycf_symbol* field_name);
+ycf_node* ycf_pointer_field_access_new(ycf_symbol* pointer,
+ ycf_symbol* field_name);
+
+ycf_node_list ycf_node_definition_list_from_string(char* str);
+void ycf_node_rename_function(ycf_node_function* f, char* new_name);
+bool ycf_node_is_void_ret_ending_fun(ycf_node* f_node);
+void ycf_node_remove_const_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_static_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_inline_specifiers_from_declaration(ycf_node* declaration);
+void ycf_node_remove_array_size_info_from_declaration(ycf_node* declaration);
+void ycf_node_modify_declarations(ycf_node_list declarations, void (*modifer)(ycf_node*));
+ycf_symbol_list ycf_node_get_return_type(ycf_node* f_node);
+ycf_symbol_list ycf_node_find_function_return_type(ycf_node* c_file_node, char* fun_name);
+
+
+/* Functions for node lists */
+
+int ycf_node_list_get_item_position(ycf_node_list* list, ycf_node* node);
+
+ycf_node* ycf_node_shallow_copy(ycf_node* n);
+ycf_node* ycf_node_list_get_item_at_position(ycf_node_list* list, int pos);
+
+void ycf_node_list_append(ycf_node_list* list, ycf_node* node);
+void ycf_node_list_prepend(ycf_node_list* list, ycf_node* node);
+void ycf_node_list_insert_before(ycf_node_list* list, ycf_node* before_this, ycf_node* to_insert);
+void ycf_node_list_insert_after(ycf_node_list* list, ycf_node* after_this, ycf_node* to_insert);
+void ycf_node_list_remove(ycf_node_list* list, ycf_node* to_remove);
+void ycf_node_list_replace(ycf_node_list* list, ycf_node* to_replace, ycf_node* replace_with);
+void ycf_node_list_concat(ycf_node_list* list1, ycf_node_list* list2);
+
+ycf_node_list ycf_node_list_empty();
+ycf_node_list ycf_node_list_shallow_copy(ycf_node_list n);
+ycf_node_list ycf_node_list_copy_append(ycf_node_list list, ycf_node* node);
+ycf_node_list ycf_node_list_copy_prepend(ycf_node_list list, ycf_node* node);
+ycf_node_list ycf_node_list_copy_insert_before(ycf_node_list list, ycf_node* before_this, ycf_node* to_insert);
+ycf_node_list ycf_node_list_copy_insert_after(ycf_node_list list, ycf_node* after_this, ycf_node* to_insert);
+ycf_node_list ycf_node_list_copy_remove(ycf_node_list list, ycf_node* to_remove);
+ycf_node_list ycf_node_list_copy_replace(ycf_node_list list, ycf_node* to_replace, ycf_node* replace_with);
+ycf_node_list ycf_node_list_copy_concat(ycf_node_list list1, ycf_node_list list2);
+size_t ycf_node_list_length(ycf_node_list list);
+char* ycf_node_list_to_string(ycf_node_list* l);
+char* ycf_node_get_node_type_string(ycf_node_type t);
+
+ycf_node_assignment* ycf_node_get_assignment(ycf_node* n);
+void ycf_node_print_node_type(ycf_node_type t);
+void ycf_node_normalize_function(ycf_node* fun);
+void ycf_node_remove_unecessary_scopes(ycf_node_code_scope* s);
+
+#endif //YIELDING_C_FUN_YCF_NODE_FUNS_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_parser.c b/erts/lib_src/yielding_c_fun/ycf_parser.c
new file mode 100644
index 0000000000..ed2747c955
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_parser.c
@@ -0,0 +1,1498 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_yield_fun.h"
+#include "ycf_utils.h"
+#include "ycf_node.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void print_symbol_code(ycf_symbol* s);
+ycf_parse_result parse_exp_statement(ycf_symbol* symbols);
+
+ycf_parse_result fail_parse_result(){
+ ycf_parse_result r;
+ r.success = false;
+ r.next_symbol = NULL;
+ r.result = NULL;
+ return r;
+}
+
+ycf_parse_result success_parse_result(ycf_symbol* next,
+ ycf_node* result){
+ ycf_parse_result r;
+ r.success = true;
+ r.next_symbol = next;
+ r.result = result;
+ return r;
+}
+
+typedef struct {
+ ycf_node_list list;
+ ycf_symbol* next_symbol;
+} parse_list_res;
+
+typedef struct {
+ ycf_symbol_list list;
+ ycf_symbol* next_symbol;
+} parse_symbol_list_res;
+
+
+parse_list_res parse_list_generic(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ bool use_break_symbol,
+ int nr_of_break_symbols,
+ ycf_symbol_type break_symbols[]){
+ parse_list_res r;
+ ycf_symbol* current = symbols;
+ int i;
+ r.list = ycf_node_list_empty();
+ while(current != NULL){
+ if(use_break_symbol){
+ bool is_break = false;
+ for(int i = 0; i < nr_of_break_symbols; i++){
+ if(break_symbols[i] == current->type){
+ is_break = true;
+ }
+ }
+ if(is_break){
+ break;
+ }
+ }
+ ycf_symbol* prev_current_symbol = current;
+ for(i = 0; i < number_of_parsers; i++){
+ ycf_parse_result res = parsers[i](current);
+ if(res.success){
+ ycf_node_list_append(&r.list, res.result);
+ current = res.next_symbol;
+ break;
+ }
+ }
+ if(prev_current_symbol == current){
+ break;
+ }
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+parse_list_res parse_list(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols){
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ false,
+ 0,
+ NULL);
+
+}
+
+parse_list_res parse_list_break_symbol(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ ycf_symbol_type break_symbol){
+ ycf_symbol_type break_s[1] = {break_symbol};
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ true,
+ 1,
+ break_s);
+
+}
+
+parse_list_res parse_list_break_symbols(int number_of_parsers,
+ ycf_parse_result (*parsers[])(ycf_symbol*),
+ ycf_symbol* symbols,
+ int nr_of_break_symbols,
+ ycf_symbol_type break_symbols[]){
+ return parse_list_generic(number_of_parsers,
+ parsers,
+ symbols,
+ true,
+ nr_of_break_symbols,
+ break_symbols);
+
+}
+
+
+ycf_node* ycf_node_defenition_new(ycf_symbol_list type_spec,
+ ycf_symbol* id,
+ ycf_node_list array_brackets,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_variable_definition;
+ n->next = NULL;
+ n->u.definition.identifier = ycf_symbol_copy(id);
+ n->u.definition.type_specifiers = type_spec;
+ n->u.definition.array_brackets = array_brackets;
+ n->u.definition.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_defenition_with_init_new(ycf_node_definition def,
+ ycf_symbol_list expression,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_variable_definition_init;
+ n->next = NULL;
+ n->u.definition_init.definition = def;
+ n->u.definition_init.initializer_expression = expression;
+ n->u.definition_init.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_c_file_new(ycf_node_list content){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_c_file;
+ n->next = NULL;
+ n->u.c_file.content = content;
+ return n;
+}
+
+
+ycf_node* ycf_node_function_def_new(ycf_node_definition def_node,
+ ycf_node_list parameters,
+ bool ignore_param_ending,
+ ycf_symbol_list end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_declaration;
+ n->next = NULL;
+ n->u.function_definition.definition = def_node;
+ n->u.function_definition.parameters = parameters;
+ n->u.function_definition.ignore_param_ending = ignore_param_ending;
+ n->u.function_definition.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_yield_new(ycf_symbol* yield_symbol,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_yield;
+ n->next = NULL;
+ n->u.yield.yield_symbol = ycf_symbol_copy(yield_symbol);
+ n->u.yield.end_symbol = ycf_symbol_copy(end);
+ return n;
+}
+
+
+
+ycf_node* ycf_node_consume_reds_new(ycf_symbol* consume_reds_symbol,
+ ycf_node_parentheses_expression nr_of_reds_expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_consume_reds;
+ n->next = NULL;
+ n->u.consume_reds.consume_reds_symbol = ycf_symbol_copy(consume_reds_symbol);
+ n->u.consume_reds.nr_of_reds_expression = nr_of_reds_expression;
+ n->u.consume_reds.end_symbol = ycf_symbol_copy(end_symbol);
+ return n;
+}
+
+
+ycf_node* ycf_node_other_new(ycf_symbol* other_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_other;
+ n->next = NULL;
+ n->u.other.what = ycf_symbol_copy(other_symbol);
+ return n;
+}
+
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_code_scope;
+ n->next = NULL;
+ n->u.code_scope.start = ycf_symbol_copy(start);
+ n->u.code_scope.definition_nodes = declaration_nodes;
+ n->u.code_scope.other_nodes = other_nodes;
+ n->u.code_scope.end = ycf_symbol_copy(end);
+ return n;
+}
+
+ycf_node* ycf_node_function_new(ycf_node_function_definition fun_def,
+ ycf_node_code_scope body){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_definition;
+ n->next = NULL;
+ n->u.function.definition = fun_def;
+ n->u.function.body = body;
+ return n;
+}
+
+ycf_node* ycf_node_function_call_new(ycf_symbol_list neg_symbols,
+ ycf_symbol* ident,
+ ycf_symbol* paran_start,
+ ycf_node_list parameters,
+ ycf_symbol* paran_end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_function_call;
+ n->next = NULL;
+ n->u.function_call.neg_symbols = neg_symbols;
+ n->u.function_call.identifier = ident;
+ n->u.function_call.start_symbol = paran_start;
+ n->u.function_call.parameter_expressions = parameters;
+ n->u.function_call.end_symbol = paran_end;
+ return n;
+}
+
+ycf_node* ycf_node_paran_expression_new(ycf_symbol* start, ycf_node_expression expr, ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_parentheses_expression;
+ n->next = NULL;
+ n->u.parentheses_expression.start_symbol = start;
+ n->u.parentheses_expression.content = expr;
+ n->u.parentheses_expression.end_symbol = end;
+ return n;
+}
+
+ycf_node* ycf_node_expression_new(ycf_node_list expr){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_expression;
+ n->next = NULL;
+ n->u.expression.content = expr;
+ n->u.expression.end_symbol = NULL;
+ return n;
+}
+
+ycf_node* ycf_node_statement_new(ycf_node* expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->type = ycf_node_type_statement;
+ n->next = NULL;
+ n->u.statement.expression = expression;
+ n->u.statement.end_symbol = end_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_while_new(ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node* statement){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_while;
+ n->u.while_n.while_word = while_word;
+ n->u.while_n.expression = expression;
+ n->u.while_n.statement = statement;
+ return n;
+}
+
+
+
+ycf_node* ycf_node_do_while_new(ycf_symbol* do_word,
+ ycf_node* statm,
+ ycf_symbol* while_word,
+ ycf_node_parentheses_expression expression,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_do_while;
+ n->u.do_while.do_word = do_word;
+ n->u.do_while.statement = statm;
+ n->u.do_while.while_word = while_word;
+ n->u.do_while.expression = expression;
+ n->u.do_while.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_for_new(ycf_symbol* for_word,
+ ycf_symbol* start_paran,
+ struct ycf_node* init,
+ ycf_node* stop_cond,
+ ycf_symbol* stop_cond_end,
+ ycf_node* end_exp,
+ ycf_symbol* end_paran,
+ ycf_node* statem){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_for;
+ n->u.for_n.for_word = for_word;
+ n->u.for_n.start_parentheses = start_paran;
+ n->u.for_n.init = init;
+ n->u.for_n.stop_cond = stop_cond;
+ n->u.for_n.stop_cond_end = stop_cond_end;
+ n->u.for_n.end_exp = end_exp;
+ n->u.for_n.end_parentheses = end_paran;
+ n->u.for_n.statement = statem;
+ return n;
+}
+
+ycf_node* ycf_node_switch_new(ycf_symbol* switch_word,
+ ycf_node_parentheses_expression expression,
+ ycf_node_code_scope scope){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_switch;
+ n->u.switch_n.switch_word = switch_word;
+ n->u.switch_n.expression = expression;
+ n->u.switch_n.scope = scope;
+ return n;
+}
+
+ycf_node* ycf_node_if_new(ycf_symbol* if_word,
+ ycf_node_parentheses_expression expression,
+ struct ycf_node* if_statem){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_if;
+ n->u.if_n.if_word = if_word;
+ n->u.if_n.expression = expression;
+ n->u.if_n.if_statement = if_statem;
+ return n;
+}
+
+ycf_node* ycf_node_if_else_new(ycf_node_if if_n,
+ ycf_symbol* else_word,
+ struct ycf_node* else_statement){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_if_else;
+ n->u.if_else.if_part = if_n;
+ n->u.if_else.else_word = else_word;
+ n->u.if_else.else_statement = else_statement;
+ return n;
+}
+
+
+ycf_node* ycf_node_fun_call_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_function_call fun_call){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_assignment_function_call;
+ n->u.function_call_assignment.left_side = left_side;
+ n->u.function_call_assignment.assignment_symbol = assignment_symbol;
+ n->u.function_call_assignment.fun_call = fun_call;
+ return n;
+}
+
+ycf_node* ycf_node_assignment_new(ycf_node_expression left_side,
+ ycf_symbol* assignment_symbol,
+ ycf_node_expression right_side,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_assignment;
+ n->u.a.left_side = left_side;
+ n->u.a.right_side = right_side;
+ n->u.a.assignment_symbol = assignment_symbol;
+ n->u.a.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_comma_new(ycf_symbol* comma_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_comma;
+ n->u.comma.comma_symbol = comma_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_array_bracket_new(ycf_symbol* start,
+ bool empty,
+ ycf_node_expression content,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_array_bracket;
+ n->u.array_bracket.start = start;
+ n->u.array_bracket.empty = empty;
+ n->u.array_bracket.content = content;
+ n->u.array_bracket.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_macro_cmd_new(ycf_symbol* macro_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_macro_cmd;
+ n->u.macro_cmd.symbol = macro_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_special_code_block_new(ycf_node_type type,
+ ycf_symbol* start,
+ ycf_node_if code,
+ ycf_symbol* end){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = type;
+ n->u.special_code_block.start = start;
+ n->u.special_code_block.code = code;
+ n->u.special_code_block.end = end;
+ return n;
+}
+
+ycf_node* ycf_node_goto_new(ycf_symbol* goto_symbol,
+ ycf_symbol* label_symbol,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_goto;
+ n->u.goto_n.goto_symbol = goto_symbol;
+ n->u.goto_n.label_symbol = label_symbol;
+ n->u.goto_n.end_symbol = end_symbol;
+ return n;
+}
+
+ycf_node* ycf_node_return_new(ycf_symbol* return_symbol,
+ ycf_node* return_expression,
+ ycf_symbol* end_symbol){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_return_statement;
+ n->u.return_n.return_symbol = return_symbol;
+ n->u.return_n.return_expression = return_expression;
+ n->u.return_n.end_symbol = end_symbol;
+ return n;
+}
+
+
+ycf_node* ycf_node_period_field_access_new(ycf_symbol* period,
+ ycf_symbol* field_name){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_period_field_access;
+ n->u.period_field_access.period = period;
+ n->u.period_field_access.field_name = field_name;
+ return n;
+}
+
+ycf_node* ycf_pointer_field_access_new(ycf_symbol* pointer,
+ ycf_symbol* field_name){
+ ycf_node* n = ycf_malloc(sizeof(ycf_node));
+ n->next = NULL;
+ n->type = ycf_node_type_period_field_access;
+ n->u.pointer_field_access.pointer = pointer;
+ n->u.pointer_field_access.field_name = field_name;
+ return n;
+}
+
+parse_symbol_list_res parse_symbol_list(int nr_of_aloved_symbols,
+ ycf_symbol_type accept_symbols[],
+ ycf_symbol* symbols){
+ int i;
+ parse_symbol_list_res r;
+ ycf_symbol* current = symbols;
+ r.list = ycf_symbol_list_empty();
+ while (current != NULL){
+ ycf_symbol* prev_current = current;
+ for(i = 0; i < nr_of_aloved_symbols; i++){
+ if(current->type == accept_symbols[i]){
+ ycf_symbol_list_append(&r.list, ycf_symbol_copy(current));
+ current = current->next;
+ }
+ }
+ if(prev_current == current){
+ break;
+ }
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+parse_symbol_list_res
+parse_symbol_list_until(ycf_symbol* symbols, ycf_symbol_type end_symbol){
+ parse_symbol_list_res r;
+ ycf_symbol* current = symbols;
+ r.list = ycf_symbol_list_empty();
+ while (current->type != end_symbol){
+ ycf_symbol_list_append(&r.list, ycf_symbol_copy(current));
+ current = current->next;
+ }
+ r.next_symbol = current;
+ return r;
+}
+
+ycf_parse_result parse_expression_end_end_squarebracket(ycf_symbol* symbols);
+
+ycf_parse_result parse_array_bracket(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_open_square_bracket){
+ return fail_parse_result();
+ }
+ if(symbols->next->type == ycf_symbol_type_end_square_bracket){
+ ycf_node_expression empty = {.content = ycf_node_list_empty(), .end_symbol = NULL};
+ return success_parse_result(symbols->next->next,
+ ycf_node_array_bracket_new(symbols, true, empty, symbols->next));
+ }
+ ycf_parse_result square_bracket_content =
+ parse_expression_end_end_squarebracket(symbols->next);
+ if(!square_bracket_content.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(square_bracket_content.next_symbol->next,
+ ycf_node_array_bracket_new(symbols,
+ false,
+ square_bracket_content.result->u.expression,
+ square_bracket_content.next_symbol));
+}
+
+ycf_parse_result parse_defenition_until_identifier(ycf_symbol* symbols,
+ ycf_symbol_type end_symbol_type){
+ ycf_symbol* current = symbols;
+ ycf_symbol_list type_specifier = ycf_symbol_list_empty();
+ ycf_symbol* ident = NULL;
+ ycf_node_list array_brackets;
+ /* Parse modifiers */
+ {
+ int nr_of_aloved_symbols = 4;
+ ycf_symbol_type accept_symbols[4];
+ accept_symbols[0] = ycf_symbol_type_static;
+ accept_symbols[1] = ycf_symbol_type_const;
+ accept_symbols[2] = ycf_symbol_type_inline;
+ accept_symbols[3] = ycf_symbol_type_volatile;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ /* Parse first symbol */
+ if(current->type == ycf_symbol_type_identifier ||
+ current->type == ycf_symbol_type_void){
+ ycf_symbol_list_append(&type_specifier, ycf_symbol_copy(current));
+ current = current->next;
+ } else {
+ return fail_parse_result();
+ }
+ if(type_specifier.last->type == ycf_symbol_type_identifier){
+ /* Parse remaining identifiers if first symbol is ycf_symbol_type_identifier */
+ int nr_of_aloved_symbols = 2;
+ ycf_symbol_type accept_symbols[2];
+ accept_symbols[0] = ycf_symbol_type_identifier;
+ accept_symbols[1] = ycf_symbol_type_const;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ {
+ /* Parse stars */
+ int nr_of_aloved_symbols = 1;
+ ycf_symbol_type accept_symbols[1];
+ accept_symbols[0] = ycf_symbol_type_star;
+ parse_symbol_list_res more =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ current);
+ ycf_symbol_list_concat(&type_specifier, &more.list);
+ current = more.next_symbol;
+ }
+ /* Handle ycf_symbol_type_identifier */
+ if(type_specifier.last->type == ycf_symbol_type_identifier){
+ ident = type_specifier.last;
+ ycf_symbol_list_remove(&type_specifier, type_specifier.last);
+ if (type_specifier.head == NULL) {
+ return fail_parse_result();
+ }
+ } else if (current->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ } else {
+ ident = current;
+ current = current->next;
+ }
+ /* Handle array brackets */
+ {
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_array_bracket
+ };
+ parse_list_res res = parse_list(1, parsers, current);
+ array_brackets = res.list;
+ current = res.next_symbol;
+ }
+ /* Parse end symbol */
+ if (current == NULL ||
+ current->type != end_symbol_type){
+ return fail_parse_result();
+ } else {
+ return success_parse_result(current->next,
+ ycf_node_defenition_new(type_specifier,
+ ident,
+ array_brackets,
+ current));
+ }
+}
+
+ycf_parse_result parse_defenition_no_init(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_semicolon);
+ return res;
+}
+
+ycf_parse_result parse_defenition_with_init(ycf_symbol* symbols){
+ ycf_parse_result dec = parse_defenition_until_identifier(symbols, ycf_symbol_type_equal_sign);
+ parse_symbol_list_res expression_list_res;
+ if(!dec.success){
+ return fail_parse_result();
+ }
+ expression_list_res =
+ parse_symbol_list_until(dec.next_symbol, ycf_symbol_type_semicolon);
+ return success_parse_result(expression_list_res.next_symbol->next,
+ ycf_node_defenition_with_init_new(dec.result->u.definition,
+ expression_list_res.list,
+ expression_list_res.next_symbol));
+}
+
+ycf_parse_result parse_defenition_comma(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_comma);
+ return res;
+}
+
+ycf_parse_result parse_defenition_end_paran(ycf_symbol* symbols){
+ ycf_parse_result res = parse_defenition_until_identifier(symbols, ycf_symbol_type_end_parenthesis);
+ return res;
+}
+
+ycf_parse_result parse_function_head(ycf_symbol* symbols,
+ ycf_symbol_type end_symbol,
+ bool exclude_end_symbol){
+ ycf_symbol* current = symbols;
+ ycf_parse_result def_res =
+ parse_defenition_until_identifier(current,
+ ycf_symbol_type_open_parenthesis);
+ if(!def_res.success){
+ return fail_parse_result();
+ }
+ current = def_res.next_symbol;
+
+ /* Parse function without parameters */
+
+ if(current->type == ycf_symbol_type_end_parenthesis &&
+ current->next->type == end_symbol){
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ ycf_node_list parameter_list = ycf_node_list_empty();
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current));
+ if(!exclude_end_symbol){
+ next_symbol = current->next->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next));
+ } else {
+ next_symbol = current->next;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ parameter_list,
+ false,
+ end));
+ } else if(current->type == ycf_symbol_type_void &&
+ current->next->type == ycf_symbol_type_end_parenthesis &&
+ current->next->next->type == end_symbol){
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ ycf_node_list parameter_list = ycf_node_list_empty();
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current));
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next));
+ if(!exclude_end_symbol){
+ next_symbol = current->next->next->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(current->next->next));
+ }else{
+ next_symbol = current->next->next;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ parameter_list,
+ false,
+ end));
+ }
+
+ /* Parse parameters */
+
+ int number_of_parsers = 2;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_comma,
+ parse_defenition_end_paran
+ };
+ parse_list_res param_list =
+ parse_list(number_of_parsers, parsers, current);
+ if(param_list.next_symbol == NULL ||
+ param_list.next_symbol->type != end_symbol){
+ return fail_parse_result();
+ }
+
+ /* Parse ending */
+
+ {
+ ycf_symbol_list end = ycf_symbol_list_empty();
+ ycf_symbol* next_symbol;
+ if(!exclude_end_symbol){
+ next_symbol = param_list.next_symbol->next;
+ ycf_symbol_list_append(&end, ycf_symbol_copy(param_list.next_symbol));
+ }else{
+ next_symbol = param_list.next_symbol;
+ }
+ return success_parse_result(next_symbol,
+ ycf_node_function_def_new(def_res.result->u.definition,
+ param_list.list,
+ false,
+ end));
+ }
+}
+
+ycf_parse_result parse_function_def(ycf_symbol* symbols){
+ return parse_function_head(symbols, ycf_symbol_type_semicolon, false);
+}
+
+ycf_parse_result parse_other(ycf_symbol* symbols){
+ ycf_symbol* what = symbols;
+ return success_parse_result(symbols->next,
+ ycf_node_other_new(what));
+}
+
+
+ycf_parse_result parse_goto(ycf_symbol* symbols){
+ ycf_symbol* goto_symbol = symbols;
+ if(goto_symbol->type != ycf_symbol_type_goto){
+ return fail_parse_result();
+ }
+ ycf_symbol* label_symbol = goto_symbol->next;
+ if(label_symbol->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ ycf_symbol* end_symbol = label_symbol->next;
+ if(end_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(end_symbol->next,
+ ycf_node_goto_new(goto_symbol, label_symbol, end_symbol));
+}
+
+
+ycf_parse_result parse_comma(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_comma){
+ return success_parse_result(symbols->next, ycf_node_comma_new(symbols));
+ } else {
+ return fail_parse_result();
+ }
+}
+
+
+ycf_parse_result parse_pointer_field_access_node(ycf_symbol* symbols){
+ ycf_symbol* pointer = symbols;
+ if(pointer->type != ycf_symbol_type_pointer_field_access){
+ return fail_parse_result();
+ }
+ ycf_symbol* field_name = pointer->next;
+ if(field_name->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ return success_parse_result(field_name->next,
+ ycf_pointer_field_access_new(pointer, field_name));
+}
+
+ycf_parse_result parse_period_field_access_node(ycf_symbol* symbols){
+ ycf_symbol* p = symbols;
+ if(p->type != ycf_symbol_type_period){
+ return fail_parse_result();
+ }
+ ycf_symbol* field_name = p->next;
+ if(field_name->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ return success_parse_result(field_name->next,
+ ycf_node_period_field_access_new(p, field_name));
+}
+
+ycf_parse_result parse_function_call(ycf_symbol* symbols);
+ycf_parse_result parse_expression_generic(ycf_symbol* symbols,
+ bool use_end_symbol,
+ bool no_function_call_assignment,
+ int nr_of_end_symbols,
+ ycf_symbol_type end_symbols[]);
+
+ycf_parse_result parse_paran_expression(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ ycf_symbol* start = symbols;
+ ycf_parse_result exp = parse_expression_generic(start->next,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_parenthesis});
+ if(!exp.success || exp.next_symbol->type != ycf_symbol_type_end_parenthesis ){
+ return fail_parse_result();
+ }
+ return success_parse_result(exp.next_symbol->next,
+ ycf_node_paran_expression_new(start,
+ exp.result->u.expression,
+ exp.next_symbol));
+}
+
+ycf_parse_result parse_function_call_assignment(ycf_symbol* symbols);
+ycf_parse_result parse_assignment(ycf_symbol* symbols);
+
+ycf_parse_result parse_expression_generic(ycf_symbol* symbols,
+ bool use_end_symbol,
+ bool no_function_call_assignment,
+ int nr_of_end_symbols,
+ ycf_symbol_type end_symbols[]){
+ int number_of_parsers = (!no_function_call_assignment ? 1 : 0) + 5;
+ ycf_parse_result (*parsers[7])(ycf_symbol *);
+ if(!no_function_call_assignment){
+ parsers[0] = parse_function_call;
+ parsers[1] = parse_function_call_assignment;
+ parsers[2] = parse_paran_expression;
+ parsers[3] = parse_period_field_access_node;
+ parsers[4] = parse_pointer_field_access_node;
+ parsers[5] = parse_other;
+ }else{
+ parsers[0] = parse_function_call;
+ parsers[1] = parse_paran_expression;
+ parsers[2] = parse_period_field_access_node;
+ parsers[3] = parse_pointer_field_access_node;
+ parsers[4] = parse_other;
+ }
+ parse_list_res res = parse_list_break_symbols(number_of_parsers, parsers, symbols, nr_of_end_symbols,end_symbols);
+ if(res.list.head == NULL || (use_end_symbol &&
+ (res.next_symbol == NULL ||res.next_symbol->type != end_symbols[0]))){
+ return fail_parse_result();
+ }
+ return success_parse_result(res.next_symbol,
+ ycf_node_expression_new(res.list));
+}
+
+ycf_parse_result parse_expression(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ false,
+ false,
+ 0 /* ignored */,
+ NULL);
+}
+
+ycf_parse_result parse_expression_end_comma(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 2,
+ (ycf_symbol_type[]){ycf_symbol_type_comma, ycf_symbol_type_end_parenthesis});
+}
+
+ycf_parse_result parse_expression_end_end_paren(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_parenthesis});
+}
+
+ycf_parse_result parse_expression_end_end_semicolon(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_semicolon});
+}
+
+ycf_parse_result parse_expression_end_end_squarebracket(ycf_symbol* symbols){
+ return parse_expression_generic(symbols,
+ true,
+ false,
+ 1,
+ (ycf_symbol_type[]){ycf_symbol_type_end_square_bracket});
+}
+
+
+ycf_parse_result parse_function_call(ycf_symbol* symbols){
+ parse_symbol_list_res negs;
+ {
+ /* Parse neg symbols */
+ int nr_of_aloved_symbols = 1;
+ ycf_symbol_type accept_symbols[1];
+ accept_symbols[0] = ycf_symbol_type_neg;
+ negs =
+ parse_symbol_list(nr_of_aloved_symbols,
+ accept_symbols,
+ symbols);
+ }
+ ycf_symbol* ident = negs.next_symbol;
+ if(ident == NULL ||
+ ident->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ ycf_symbol* paran_start = ident->next;
+ if(paran_start == NULL || paran_start->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ int number_of_parsers = 3;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_comma,
+ parse_expression_end_comma,
+ parse_expression_end_end_paren
+ };
+ parse_list_res param_list_res =
+ parse_list_break_symbol(number_of_parsers,
+ parsers,
+ paran_start->next,
+ ycf_symbol_type_end_parenthesis);
+ if(param_list_res.next_symbol == NULL ||
+ param_list_res.next_symbol->type != ycf_symbol_type_end_parenthesis){
+ return fail_parse_result();
+ }
+ return success_parse_result(param_list_res.next_symbol->next,
+ ycf_node_function_call_new(negs.list,
+ ident,
+ paran_start,
+ param_list_res.list,
+ param_list_res.next_symbol));
+}
+
+ycf_parse_result parse_assignment(ycf_symbol* symbols) {
+ if(symbols->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ if(symbols->next == NULL || symbols->next->type != ycf_symbol_type_equal_sign){
+ return fail_parse_result();
+ }
+ ycf_parse_result left_expression = parse_expression_generic(symbols,
+ false,
+ true,
+ 3,
+ (ycf_symbol_type[]){
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_end_parenthesis});
+ if(!left_expression.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* equal_sign = left_expression.next_symbol;
+ if(equal_sign == NULL || equal_sign->type != ycf_symbol_type_equal_sign) {
+ return fail_parse_result();
+ }
+ if(equal_sign->next == NULL || equal_sign->next->type != ycf_symbol_type_identifier){
+ return fail_parse_result();
+ }
+ if(equal_sign->next->next == NULL || equal_sign->next->next->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ ycf_parse_result right_expression = parse_expression_generic(equal_sign->next,
+ false,
+ true,
+ 3,
+ (ycf_symbol_type[]){
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_end_parenthesis});
+ if(!right_expression.success ||
+ right_expression.next_symbol == NULL ||
+ right_expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ ycf_node* node = ycf_node_assignment_new(left_expression.result->u.expression,
+ ycf_symbol_copy(equal_sign),
+ right_expression.result->u.expression,
+ ycf_symbol_copy(right_expression.next_symbol));
+ return success_parse_result(right_expression.next_symbol->next,
+ node);
+}
+
+ycf_parse_result parse_function_call_assignment(ycf_symbol* symbols){
+ ycf_parse_result expression = parse_expression_generic(symbols,
+ true,
+ true,
+ 3,
+ (ycf_symbol_type[]){ycf_symbol_type_equal_sign,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_end_parenthesis});
+ if(!expression.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* equal_sign = expression.next_symbol;
+ ycf_parse_result fun_call = parse_function_call(equal_sign->next);
+ if(!fun_call.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call.next_symbol,
+ ycf_node_fun_call_assignment_new(expression.result->u.expression,
+ equal_sign,
+ fun_call.result->u.function_call));
+}
+
+
+ycf_parse_result parse_function_call_assignment_statement(ycf_symbol* symbols){
+ ycf_parse_result fun_call_ass = parse_function_call_assignment(symbols);
+ if(!fun_call_ass.success || fun_call_ass.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call_ass.next_symbol->next,
+ ycf_node_statement_new(fun_call_ass.result,
+ fun_call_ass.next_symbol));
+}
+
+
+ycf_parse_result parse_function_call_statement(ycf_symbol* symbols){
+ ycf_parse_result fun_call = parse_function_call(symbols);
+ if(!fun_call.success || fun_call.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(fun_call.next_symbol->next,
+ ycf_node_statement_new(fun_call.result,
+ fun_call.next_symbol));
+}
+
+ycf_parse_result parse_statement(ycf_symbol* symbols);
+
+
+ycf_parse_result parse_cond_statement(ycf_symbol* symbols, ycf_symbol_type cond_keyword){
+ if(symbols->type != cond_keyword){
+ return fail_parse_result();
+ }
+ ycf_symbol* cond_word = symbols;
+ ycf_parse_result cond_exp = parse_paran_expression(cond_word->next);
+ if(!cond_exp.success){
+ return fail_parse_result();
+ }
+ ycf_parse_result cond_statement = parse_statement(cond_exp.next_symbol);
+ if(!cond_statement.success){
+ return fail_parse_result();
+ }
+ /* Return if node even though it might be something else */
+ return success_parse_result(cond_statement.next_symbol,
+ ycf_node_if_new(cond_word,
+ cond_exp.result->u.parentheses_expression,
+ cond_statement.result));
+}
+
+ycf_parse_result parse_if_statement(ycf_symbol* symbols){
+ return parse_cond_statement(symbols, ycf_symbol_type_if);
+}
+
+ycf_parse_result parse_if_else_statement(ycf_symbol* symbols){
+ ycf_parse_result if_statem = parse_if_statement(symbols);
+ if(!if_statem.success){
+ return fail_parse_result();
+ }
+ if(if_statem.next_symbol->type != ycf_symbol_type_else){
+ return fail_parse_result();
+ }
+ ycf_symbol* else_w = if_statem.next_symbol;
+ ycf_parse_result else_statement = parse_statement(else_w->next);
+ if(!else_statement.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(else_statement.next_symbol,
+ ycf_node_if_else_new(if_statem.result->u.if_n,
+ else_w,
+ else_statement.result));
+}
+
+ycf_parse_result parse_while_statement(ycf_symbol* symbols){
+ ycf_parse_result cond_statem = parse_cond_statement(symbols, ycf_symbol_type_while);
+ if(!cond_statem.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond_statem.next_symbol,
+ ycf_node_while_new(cond_statem.result->u.if_n.if_word,
+ cond_statem.result->u.if_n.expression,
+ cond_statem.result->u.if_n.if_statement));
+}
+
+ycf_parse_result parse_do_while_statement(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_do){
+ return fail_parse_result();
+ }
+ ycf_symbol* do_word = symbols;
+ ycf_parse_result statement = parse_statement(do_word->next);
+ if(!statement.success || statement.next_symbol->type != ycf_symbol_type_while){
+ return fail_parse_result();
+ }
+ ycf_symbol* while_word = statement.next_symbol;
+ ycf_parse_result cond = parse_paran_expression(while_word->next);
+ if(!cond.success){
+ return fail_parse_result();
+ }
+ if(cond.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond.next_symbol->next,
+ ycf_node_do_while_new(do_word, statement.result, while_word,
+ cond.result->u.parentheses_expression, cond.next_symbol));
+}
+
+ycf_parse_result parse_for_statement(ycf_symbol* symbols){
+ if(symbols->type != ycf_symbol_type_for){
+ return fail_parse_result();
+ }
+ ycf_symbol* for_word = symbols;
+ if(for_word->next->type != ycf_symbol_type_open_parenthesis){
+ return fail_parse_result();
+ }
+ ycf_symbol* start_paran = for_word->next;
+ ycf_node* init = NULL;
+ ycf_parse_result init_res = parse_defenition_with_init(start_paran->next);
+ if(!init_res.success){
+ init_res = parse_defenition_no_init(start_paran->next);
+ }
+ if(!init_res.success){
+ init_res = parse_exp_statement(start_paran->next);
+ }
+ if(!init_res.success){
+ return fail_parse_result();
+ }
+ init = init_res.result;
+ ycf_node* stop_cond = NULL;
+ ycf_symbol* stop_cond_end = NULL;
+ if(init_res.next_symbol->type == ycf_symbol_type_semicolon){
+ stop_cond_end = init_res.next_symbol;
+ }else{
+ ycf_parse_result stop_cond_res = parse_expression_end_end_semicolon(init_res.next_symbol);
+ if(!stop_cond_res.success || stop_cond_res.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ stop_cond = stop_cond_res.result;
+ stop_cond_end = stop_cond_res.next_symbol;
+ }
+ ycf_node* end_exp = NULL;
+ ycf_symbol* end_exp_end = NULL;
+ if(stop_cond_end->next->type == ycf_symbol_type_end_parenthesis){
+ end_exp_end = stop_cond_end->next;
+ }else{
+ ycf_parse_result end_exp_res = parse_expression_end_end_paren(stop_cond_end->next);
+ if(!end_exp_res.success || end_exp_res.next_symbol->type != ycf_symbol_type_end_parenthesis){
+ return fail_parse_result();
+ }
+ end_exp = end_exp_res.result;
+ end_exp_end = end_exp_res.next_symbol;
+ }
+ ycf_parse_result for_statement = parse_statement(end_exp_end->next);
+ if(!for_statement.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(for_statement.next_symbol,
+ ycf_node_for_new(for_word,
+ start_paran,
+ init,
+ stop_cond,
+ stop_cond_end,
+ end_exp,
+ end_exp_end,
+ for_statement.result));
+}
+
+
+ycf_parse_result parse_switch_statement(ycf_symbol* symbols){
+ ycf_parse_result cond_statem = parse_cond_statement(symbols, ycf_symbol_type_switch);
+ if(!cond_statem.success){
+ return fail_parse_result();
+ }
+ if(cond_statem.result->u.if_n.if_statement->type != ycf_node_type_code_scope){
+ return fail_parse_result();
+ }
+ return success_parse_result(cond_statem.next_symbol,
+ ycf_node_switch_new(cond_statem.result->u.if_n.if_word,
+ cond_statem.result->u.if_n.expression,
+ cond_statem.result->u.if_n.if_statement->u.code_scope));
+}
+
+ycf_parse_result parse_exp_statement(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_semicolon){
+ /*Empty statement*/
+ return success_parse_result(symbols->next,
+ ycf_node_statement_new(ycf_node_expression_new(ycf_node_list_empty()),
+ symbols));
+ }
+ ycf_parse_result expression = parse_expression_end_end_semicolon(symbols);
+ if(!expression.success || expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(expression.next_symbol->next,
+ ycf_node_statement_new(expression.result,
+ expression.next_symbol));
+}
+
+ycf_parse_result parse_return_statement(ycf_symbol* symbols){
+ ycf_symbol* return_symbol = symbols;
+ if(return_symbol->type != ycf_symbol_type_return){
+ return fail_parse_result();
+ }
+ if(return_symbol->next->type == ycf_symbol_type_semicolon){
+ return success_parse_result(return_symbol->next->next,
+ ycf_node_return_new(return_symbol,
+ NULL,
+ return_symbol->next));
+ }
+ ycf_parse_result expression = parse_expression_end_end_semicolon(return_symbol->next);
+ if(!expression.success || expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(expression.next_symbol->next,
+ ycf_node_return_new(return_symbol,
+ expression.result,
+ expression.next_symbol));
+}
+
+ycf_parse_result parse_macro_command(ycf_symbol* symbols){
+ if(symbols->type == ycf_symbol_type_macro_command || symbols->type == ycf_symbol_type_macro_define){
+ return success_parse_result(symbols->next, ycf_node_macro_cmd_new(symbols));
+ } else {
+ return fail_parse_result();
+ }
+}
+
+ycf_parse_result parse_scope(ycf_symbol* symbols);
+
+ycf_parse_result parse_special_code_block(ycf_symbol* symbols, char* code_block_name, ycf_node_type type){
+ ycf_symbol* start = symbols;
+ if(symbols->type != ycf_symbol_type_special_code_start ||
+ !ycf_symbol_is_text_eq(start,
+ ycf_string_new("/*special_code_start:%s*/", code_block_name))){
+ return fail_parse_result();
+ }
+ ycf_parse_result code = parse_if_statement(start->next);
+ if(!code.success){
+ return fail_parse_result();
+ }
+ ycf_symbol* end = code.next_symbol;
+ if(end->type != ycf_symbol_type_special_code_end){
+ return fail_parse_result();
+ }
+ return success_parse_result(end->next,
+ ycf_node_special_code_block_new(type,
+ start,
+ code.result->u.if_n,
+ end));
+}
+
+ycf_parse_result parse_on_save_yield_state(ycf_symbol* symbols){
+ return parse_special_code_block(symbols, "ON_SAVE_YIELD_STATE", ycf_node_type_on_save_yield_state_code);
+}
+
+ycf_parse_result parse_on_restore_yield_state(ycf_symbol* symbols){
+ return parse_special_code_block(symbols, "ON_RESTORE_YIELD_STATE", ycf_node_type_on_restore_yield_state_code);
+}
+
+ycf_parse_result parse_destroy_state_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_DESTROY_STATE", ycf_node_type_on_destroy_state_code);
+ return res;
+}
+
+ycf_parse_result parse_on_return_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_RETURN", ycf_node_type_on_return_code);
+ return res;
+}
+
+ycf_parse_result parse_on_destroy_state_or_return_code(ycf_symbol* symbols){
+ ycf_parse_result res = parse_special_code_block(symbols, "ON_DESTROY_STATE_OR_RETURN", ycf_node_type_on_destroy_state_or_return_code);
+ return res;
+}
+
+ycf_parse_result parse_consume_reds(ycf_symbol* symbols){
+ ycf_symbol* consume_reds_symbol = symbols;
+ if(consume_reds_symbol->type != ycf_symbol_type_consume_reds){
+ return fail_parse_result();
+ }
+ ycf_parse_result paran_expression =
+ parse_paran_expression(consume_reds_symbol->next);
+ if(!paran_expression.success){
+ return fail_parse_result();
+ }
+ if(paran_expression.next_symbol->type != ycf_symbol_type_semicolon){
+ return fail_parse_result();
+ }
+ return success_parse_result(paran_expression.next_symbol->next,
+ ycf_node_consume_reds_new(consume_reds_symbol,
+ paran_expression.result->u.parentheses_expression,
+ paran_expression.next_symbol));
+}
+
+ycf_parse_result parse_statement(ycf_symbol* symbols){
+ int number_of_parsers = 22;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init,
+ parse_if_else_statement,
+ parse_if_statement,
+ parse_while_statement,
+ parse_do_while_statement,
+ parse_for_statement,
+ parse_switch_statement,
+ parse_scope,
+ parse_goto,
+ parse_return_statement,
+ parse_consume_reds,
+ parse_function_call_statement,
+ parse_function_call_assignment_statement,
+ parse_assignment,
+ parse_on_save_yield_state,
+ parse_on_restore_yield_state,
+ parse_on_return_code,
+ parse_on_destroy_state_or_return_code,
+ parse_destroy_state_code,
+ parse_macro_command,
+ parse_exp_statement
+ };
+ for(int i = 0; i < number_of_parsers; i++){
+ ycf_parse_result res = parsers[i](symbols);
+ if(res.success){
+ return res;
+ }
+ }
+ return fail_parse_result();
+}
+
+ycf_parse_result parse_scope(ycf_symbol* symbols){
+ ycf_symbol* current = symbols;
+ ycf_symbol* start;
+ if(current->type != ycf_symbol_type_open_curly_brace){
+ return fail_parse_result();
+ }
+ start = current;
+ current = current->next;
+ int number_of_parsers = 2;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init
+ };
+ parse_list_res decs = parse_list(number_of_parsers, parsers, current);
+ current = decs.next_symbol;
+ /* parse rest */
+ number_of_parsers = 1;
+ ycf_parse_result (*rest_parsers[])(ycf_symbol *) = {
+ parse_statement
+ };
+ parse_list_res others =
+ parse_list_break_symbol(number_of_parsers,
+ rest_parsers,
+ current,
+ ycf_symbol_type_end_curly_brace);
+ current = others.next_symbol;
+ if (current == NULL) {
+ return fail_parse_result();
+ }
+ return success_parse_result(current->next, ycf_node_scope_new(start,
+ decs.list,
+ others.list,
+ current));
+}
+
+ycf_parse_result parse_function(ycf_symbol* symbols){
+ ycf_parse_result fun_head = parse_function_head(symbols, ycf_symbol_type_open_curly_brace, true);
+ if(!fun_head.success){
+ return fail_parse_result();
+ }
+ ycf_parse_result scope = parse_scope(fun_head.next_symbol);
+ if(!scope.success){
+ return fail_parse_result();
+ }
+ return success_parse_result(scope.next_symbol,
+ ycf_node_function_new(fun_head.result->u.function_definition,
+ scope.result->u.code_scope));
+}
+
+ycf_parse_result parse_c_file(ycf_symbol* symbols){
+ int number_of_parsers = 5;
+ ycf_parse_result (*parsers[])(ycf_symbol *) = {
+ parse_defenition_no_init,
+ parse_defenition_with_init,
+ parse_function_def,
+ parse_function,
+ parse_other
+ };
+ parse_list_res program_list =
+ parse_list(number_of_parsers, parsers, symbols);
+ if(program_list.next_symbol != NULL){
+ return fail_parse_result();
+ } else {
+ return success_parse_result(NULL,
+ ycf_node_c_file_new(program_list.list));
+ }
+}
+
+
+ycf_node* get_abstract_syntax_tree_root(ycf_symbol_list* symbols){
+ ycf_parse_result res = parse_c_file(symbols->head);
+ if(!res.success){
+ printf("ERROR: Could not parse file\n");
+ exit(1);
+ }
+ return res.result;
+}
+
+ycf_node* ycf_node_deep_copy(ycf_node *n) {
+ ycf_string_printable_buffer *b = ycf_string_printable_buffer_new();
+ ycf_node_print(n, b);
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(b->buffer);
+ ycf_parse_result res;
+ if (n->type == ycf_node_type_c_file) {
+ res = parse_c_file(symbols.head);
+ } else if (n->type == ycf_node_type_variable_definition ||
+ n->type == ycf_node_type_variable_definition_init ||
+ n->type == ycf_node_type_other ||
+ n->type == ycf_node_type_code_scope ||
+ n->type == ycf_node_type_assignment ||
+ n->type == ycf_node_type_yield ||
+ n->type == ycf_node_type_statement ||
+ n->type == ycf_node_type_if ||
+ n->type == ycf_node_type_if_else ||
+ n->type == ycf_node_type_while ||
+ n->type == ycf_node_type_do_while ||
+ n->type == ycf_node_type_switch ||
+ n->type == ycf_node_type_for ||
+ n->type == ycf_node_type_assignment_function_call ||
+ n->type == ycf_node_type_on_save_yield_state_code ||
+ n->type == ycf_node_type_on_restore_yield_state_code ||
+ n->type == ycf_node_type_on_destroy_state_code ||
+ n->type == ycf_node_type_on_return_code ||
+ n->type == ycf_node_type_on_destroy_state_or_return_code ||
+ n->type == ycf_node_type_goto ||
+ n->type == ycf_node_type_return_statement ||
+ n->type == ycf_node_type_consume_reds) {
+ res = parse_statement(symbols.head);
+ } else if (n->type == ycf_node_type_function_declaration) {
+ res = parse_function_def(symbols.head);
+ } else if (n->type == ycf_node_type_function_definition) {
+ res = parse_function(symbols.head);
+ } else if (n->type == ycf_node_type_function_call) {
+ res = parse_function_call(symbols.head);
+ } else if (n->type == ycf_node_type_expression) {
+ res = parse_expression(symbols.head);
+ } else if (n->type == ycf_node_type_parentheses_expression) {
+ res = parse_paran_expression(symbols.head);
+ } else if (n->type == ycf_node_type_comma) {
+ res = parse_comma(symbols.head);
+ } else if (n->type == ycf_node_type_array_bracket) {
+ res = parse_array_bracket(symbols.head);
+ } else if (n->type == ycf_node_type_macro_cmd) {
+ res = parse_macro_command(symbols.head);
+ } else if (n->type == ycf_node_type_period_field_access) {
+ res = parse_period_field_access_node(symbols.head);
+ } else if (n->type == ycf_node_type_pointer_field_access) {
+ res = parse_pointer_field_access_node(symbols.head);
+ } else {
+ fprintf(stderr, "Unknown node type %d\n", n->type);
+ exit(1);
+ }
+ if(!res.success) {
+ fprintf(stderr, "An error has been detected in the function ycf_node_deep_copy\n");
+ exit(1);
+ }
+ return res.result;
+}
+
diff --git a/lib/erl_interface/src/legacy/erl_connect.h b/erts/lib_src/yielding_c_fun/ycf_parser.h
index 6cb5d5cd1b..566c7ec503 100644
--- a/lib/erl_interface/src/legacy/erl_connect.h
+++ b/erts/lib_src/yielding_c_fun/ycf_parser.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,12 +14,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* %CopyrightEnd%
*/
-#ifndef _ERL_CONNECT_H
-#define _ERL_CONNECT_H
-erlang_pid *erl_self(void);
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_node.h"
-#endif /* _ERL_CONNECT_H */
+ycf_node* get_abstract_syntax_tree_root(ycf_symbol_list* symbols);
diff --git a/erts/lib_src/yielding_c_fun/ycf_printers.c b/erts/lib_src/yielding_c_fun/ycf_printers.c
new file mode 100644
index 0000000000..0cebfc5ebc
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_printers.c
@@ -0,0 +1,493 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+
+void print_symbol_code(ycf_symbol* s, ycf_string_printable_buffer* b){
+ if(s == NULL){
+ return;
+ }
+ print_symbol_code(s->whitespace_or_comment_before, b);
+ ycf_string_printable_buffer_printf(b, "%s", ycf_symbol_get_text(s));
+}
+
+void print_symbol_list(ycf_symbol_list* l, ycf_string_printable_buffer* b){
+ ycf_symbol* s = l->head;
+ while(s != NULL){
+ print_symbol_code(s, b);
+ s = s->next;
+ }
+}
+
+void print_node_code_array_bracket(ycf_node_array_bracket n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.start, b);
+ if(!n.empty){
+ print_node_code_expression(n.content, b);
+ }
+ print_symbol_code(n.end, b);
+}
+
+void print_node_code_definition_custom_end(ycf_node_definition d, bool dyn_array_to_ptr, ycf_symbol* end, ycf_string_printable_buffer* b){
+ int nr_of_empty_brackets = 0;
+ {
+ ycf_node* current = d.array_brackets.head;
+ while(current != NULL){
+ if(current->u.array_bracket.empty){
+ nr_of_empty_brackets++;
+ }
+ current = current->next;
+ }
+ }
+ print_symbol_list(&d.type_specifiers, b);
+ if(dyn_array_to_ptr){
+ for(int i = 0; i < nr_of_empty_brackets; i++){
+ ycf_string_printable_buffer_printf(b, "*");
+ }
+ }
+ print_symbol_code(d.identifier, b);
+ if (!dyn_array_to_ptr || nr_of_empty_brackets == 0){
+ print_node_list_code(d.array_brackets.head, b);
+ }
+ print_symbol_code(end, b);
+}
+
+void print_node_code_definition(ycf_node_definition d, ycf_string_printable_buffer* b){
+ print_node_code_definition_custom_end(d, false, d.end, b);
+}
+
+void print_node_code_definition_init(ycf_node_definition_init d, ycf_string_printable_buffer* b){
+ print_node_code_definition(d.definition, b);
+ print_symbol_list(&d.initializer_expression, b);
+ print_symbol_code(d.end, b);
+}
+
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b){
+ while(n != NULL){
+ ycf_node_print(n, b);
+ n = n->next;
+ }
+}
+
+void print_node_code_function_def_parameters(ycf_node_function_definition f_def, ycf_string_printable_buffer* b){
+ if(f_def.ignore_param_ending){
+ ycf_node* n = f_def.parameters.head;
+ while(n != NULL){
+ if(n->next == NULL){
+ print_node_code_definition_custom_end(n->u.definition, false, ycf_symbol_new_parenthesis(), b);
+ }else{
+ print_node_code_definition_custom_end(n->u.definition, false, ycf_symbol_new_comma(), b);
+ }
+ n = n->next;
+ }
+ if(f_def.end.head != NULL && f_def.end.head->type == ycf_symbol_type_end_parenthesis){
+ ycf_symbol_list end = f_def.end;
+ end.head = end.head->next;
+ print_symbol_list(&end, b);
+ }else if(f_def.end.head != NULL && f_def.end.head->type != ycf_symbol_type_void){
+ print_symbol_list(&f_def.end, b);
+ }
+ }else {
+ print_node_list_code(f_def.parameters.head, b);
+ print_symbol_list(&f_def.end, b);
+ }
+}
+void print_node_code_function_def(ycf_node_function_definition f_def, ycf_string_printable_buffer* b){
+ print_node_code_definition(f_def.definition, b);
+ print_node_code_function_def_parameters(f_def, b);
+}
+
+void print_node_code_scope(ycf_node_code_scope s, ycf_string_printable_buffer* b){
+ print_symbol_code(s.start, b);
+ print_node_list_code(s.definition_nodes.head, b);
+ print_node_list_code(s.other_nodes.head, b);
+ print_symbol_code(s.end, b);
+}
+
+void print_node_code_function(ycf_node_function f, ycf_string_printable_buffer* b){
+ print_node_code_function_def(f.definition, b);
+ print_node_code_scope(f.body, b);
+}
+
+void print_node_code_assignment(ycf_node_assignment a, ycf_string_printable_buffer* b){
+ print_node_code_expression(a.left_side, b);
+ print_symbol_code(a.assignment_symbol, b);
+ print_node_code_expression(a.right_side, b);
+ print_symbol_code(a.end, b);
+}
+
+void print_node_code_gen_struct(ycf_node_gen_typedef_struct n, ycf_string_printable_buffer* b){
+ ycf_string_printable_buffer_printf(b, "\n\n\nstruct %s {", n.name);
+ ycf_node* current = n.definition_nodes.head;
+ while(current != NULL){
+ print_node_code_definition_custom_end(current->u.definition, true, ycf_symbol_new_semicolon(), b);
+ current = current->next;
+ }
+ ycf_string_printable_buffer_printf(b, "\n};\n");
+}
+
+void print_node_code_yield(ycf_node_yield n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.yield_symbol,b);
+ print_symbol_code(n.end_symbol,b);
+
+}
+
+void print_node_code_function_call(ycf_node_function_call n, ycf_string_printable_buffer* b){
+ print_symbol_list(&n.neg_symbols, b);
+ print_symbol_code(n.identifier, b);
+ print_symbol_code(n.start_symbol, b);
+ print_node_list_code(n.parameter_expressions.head, b);
+ print_symbol_code(n.end_symbol, b);
+}
+
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b){
+ print_node_list_code(e.content.head, b);
+}
+
+
+void print_node_code_paran_expression(ycf_node_parentheses_expression e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.start_symbol, b);
+ print_node_code_expression(e.content, b);
+ print_symbol_code(e.end_symbol, b);
+}
+
+void print_node_code_consume_reds(ycf_node_consume_reds e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.consume_reds_symbol, b);
+ print_node_code_paran_expression(e.nr_of_reds_expression, b);
+ print_symbol_code(e.end_symbol, b);
+}
+
+void print_node_code_if_statement(ycf_node_if e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.if_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ ycf_node_print(e.if_statement, b);
+}
+
+void print_node_code_while_statement(ycf_node_while e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.while_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ ycf_node_print(e.statement, b);
+}
+
+void print_node_code_do_while_statement(ycf_node_do_while e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.do_word, b);
+ ycf_node_print(e.statement, b);
+ print_symbol_code(e.while_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ print_symbol_code(e.end, b);
+}
+
+void print_node_code_switch_statement(ycf_node_switch e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.switch_word, b);
+ print_node_code_paran_expression(e.expression, b);
+ print_node_code_scope(e.scope, b);
+}
+
+void print_node_code_for_statement(ycf_node_for e, ycf_string_printable_buffer* b){
+ print_symbol_code(e.for_word, b);
+ print_symbol_code(e.start_parentheses, b);
+ ycf_node_print(e.init, b);
+ ycf_node_print(e.stop_cond, b);
+ print_symbol_code(e.stop_cond_end, b);
+ ycf_node_print(e.end_exp, b);
+ print_symbol_code(e.end_parentheses, b);
+ ycf_node_print(e.statement, b);
+}
+
+void print_node_code_if_else_statement(ycf_node_if_else e, ycf_string_printable_buffer* b){
+ print_node_code_if_statement(e.if_part, b);
+ print_symbol_code(e.else_word, b);
+ ycf_node_print(e.else_statement, b);
+}
+
+void print_node_code_for_assignment_fun_call(ycf_node_function_call_assignment e, ycf_string_printable_buffer* b){
+ print_node_code_expression(e.left_side, b);
+ print_symbol_code(e.assignment_symbol, b);
+ print_node_code_function_call(e.fun_call, b);
+}
+
+void print_node_code_special_code_block(ycf_node_special_code_block block, ycf_string_printable_buffer* b){
+ print_symbol_code(block.start, b);
+ print_node_code_if_statement(block.code, b);
+ print_symbol_code(block.end, b);
+
+}
+
+void print_node_code_goto(ycf_node_goto n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.goto_symbol, b);
+ print_symbol_code(n.label_symbol, b);
+ print_symbol_code(n.end_symbol, b);
+}
+
+void print_node_code_period_field_access(ycf_node_period_field_access n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.period, b);
+ print_symbol_code(n.field_name, b);
+}
+
+void print_node_code_pointer_field_access(ycf_pointer_field_access n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.pointer, b);
+ print_symbol_code(n.field_name, b);
+}
+
+void print_node_code_return(ycf_node_return n, ycf_string_printable_buffer* b){
+ print_symbol_code(n.return_symbol, b);
+ if (n.return_expression != NULL) {
+ ycf_node_print(n.return_expression, b);
+ }
+ print_symbol_code(n.end_symbol, b);
+}
+
+void ycf_node_print(ycf_node* node, ycf_string_printable_buffer* b){
+ if(node == NULL){
+ return;
+ } else if(node->type == ycf_node_type_c_file){
+ print_node_list_code(node->u.c_file.content.head, b);
+ } else if(node->type == ycf_node_type_variable_definition){
+ print_node_code_definition(node->u.definition, b);
+ } else if(node->type == ycf_node_type_variable_definition_init){
+ print_node_code_definition_init(node->u.definition_init, b);
+ } else if(node->type == ycf_node_type_function_declaration){
+ print_node_code_function_def(node->u.function_definition, b);
+ } else if(node->type == ycf_node_type_other){
+ print_symbol_code(node->u.other.what, b);
+ } else if(node->type == ycf_node_type_code_scope){
+ print_node_code_scope(node->u.code_scope, b);
+ }else if(node->type == ycf_node_type_function_definition){
+ print_node_code_function(node->u.function, b);
+ }else if(node->type == ycf_node_type_assignment){
+ print_node_code_assignment(node->u.a, b);
+ }else if(node->type == ycf_node_type_gen_typedef_struct){
+ print_node_code_gen_struct(node->u.gen_typedef_struct, b);
+ }else if(node->type == ycf_node_type_yield){
+ print_node_code_yield(node->u.yield, b);
+ } else if(node->type == ycf_node_type_statement){
+ ycf_node_print(node->u.statement.expression, b);
+ print_symbol_code(node->u.statement.end_symbol, b);
+ } else if(node->type == ycf_node_type_function_call){
+ print_node_code_function_call(node->u.function_call, b);
+ } else if(node->type == ycf_node_type_expression){
+ print_node_code_expression(node->u.expression, b);
+ } else if(node->type == ycf_node_type_parentheses_expression){
+ print_node_code_paran_expression(node->u.parentheses_expression, b);
+ } else if(node->type == ycf_node_type_if){
+ print_node_code_if_statement(node->u.if_n, b);
+ }else if(node->type == ycf_node_type_if_else){
+ print_node_code_if_else_statement(node->u.if_else, b);
+ }else if(node->type == ycf_node_type_while){
+ print_node_code_while_statement(node->u.while_n, b);
+ }else if(node->type == ycf_node_type_do_while){
+ print_node_code_do_while_statement(node->u.do_while, b);
+ }else if(node->type == ycf_node_type_switch){
+ print_node_code_switch_statement(node->u.switch_n, b);
+ }else if(node->type == ycf_node_type_for){
+ print_node_code_for_statement(node->u.for_n, b);
+ }else if(node->type == ycf_node_type_assignment_function_call){
+ print_node_code_for_assignment_fun_call(node->u.function_call_assignment, b);
+ } else if(node->type == ycf_node_type_comma){
+ print_symbol_code(node->u.comma.comma_symbol, b);
+ } else if(node->type == ycf_node_type_array_bracket){
+ print_node_code_array_bracket(node->u.array_bracket, b);
+ } else if(node->type == ycf_node_type_macro_cmd){
+ print_symbol_code(node->u.macro_cmd.symbol, b);
+ } else if(node->type == ycf_node_type_on_save_yield_state_code ||
+ node->type == ycf_node_type_on_restore_yield_state_code ||
+ node->type == ycf_node_type_on_destroy_state_code ||
+ node->type == ycf_node_type_on_return_code ||
+ node->type == ycf_node_type_on_destroy_state_or_return_code){
+ print_node_code_special_code_block(node->u.special_code_block, b);
+ } else if(node->type == ycf_node_type_consume_reds){
+ print_node_code_consume_reds(node->u.consume_reds, b);
+ } else if(node->type == ycf_node_type_goto){
+ print_node_code_goto(node->u.goto_n, b);
+ } else if(node->type == ycf_node_type_return_statement){
+ print_node_code_return(node->u.return_n, b);
+ } else if(node->type == ycf_node_type_period_field_access){
+ print_node_code_period_field_access(node->u.period_field_access, b);
+ } else if(node->type == ycf_node_type_pointer_field_access){
+ print_node_code_pointer_field_access(node->u.pointer_field_access, b);
+ } else {
+ fprintf(stderr, "Unknown node type %d\n", node->type);
+ exit(1);
+ }
+}
+
+
+void print_definition(ycf_node_definition d){
+ printf("NODE: definition (type=%s,%s=%s)\n",
+ ycf_symbol_text_between(d.type_specifiers.head,
+ d.type_specifiers.last),
+ get_symbol_type_text(d.identifier->type),
+ ycf_symbol_get_text(d.identifier) );
+}
+
+void print_scope(ycf_node_code_scope node){
+ printf("NODE: scope\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("Defenition Nodes:\n");
+ struct ycf_node* n = node.definition_nodes.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("Other Nodes:\n");
+ n = node.other_nodes.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("END SCOPE\n");
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_function_def(ycf_node_function_definition node){
+ printf("NODE: function def:\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("definition:\n");
+ print_definition(node.definition);
+ printf("parameters:\n");
+ struct ycf_node* n = node.parameters.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_fun_call(ycf_node_function_call f_call){
+ printf("NODE: fun_call %s parameters:\n", ycf_symbol_get_text(f_call.identifier));
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ struct ycf_node* current = f_call.parameter_expressions.head;
+ int i = 1;
+ while(current != NULL){
+ printf("param %d: \n", i);
+ ycf_node_print(current, NULL);
+ current = current->next;
+ i++;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+void print_abstract_syntax_tree(ycf_node* node){
+ printf("%p\n",(void*)node);
+ if(node == NULL){
+ return;
+ } else if(node->type == ycf_node_type_c_file){
+ printf("NODE: c_file\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ struct ycf_node* n = node->u.c_file.content.head;
+ while(n != NULL){
+ print_abstract_syntax_tree(n);
+ n = n->next;
+ }
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ } else if(node->type == ycf_node_type_variable_definition){
+ print_definition(node->u.definition);
+ } else if(node->type == ycf_node_type_variable_definition_init){
+ printf("NODE: definition_init (type=%s,%s=%s,init=%s)\n",
+ ycf_symbol_text_between(node->u.definition_init.definition.type_specifiers.head,
+ node->u.definition_init.definition.type_specifiers.last),
+ get_symbol_type_text(node->u.definition_init.definition.identifier->type),
+ ycf_symbol_get_text(node->u.definition_init.definition.identifier),
+ ycf_symbol_text_between(node->u.definition_init.initializer_expression.head,
+ node->u.definition_init.initializer_expression.last));
+ } else if(node->type == ycf_node_type_function_declaration){
+ print_function_def(node->u.function_definition);
+ } else if(node->type == ycf_node_type_other){
+ printf("NODE: other (%s)\n", get_symbol_type_text(node->u.other.what->type));
+ } else if(node->type == ycf_node_type_code_scope){
+ print_scope(node->u.code_scope);
+ }else if(node->type == ycf_node_type_function_definition){
+ printf("NODE: function\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_function_def(node->u.function.definition);
+ print_scope(node->u.function.body);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_assignment){
+ printf("NODE: assignment\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_node_code_assignment(node->u.a, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_yield){
+ printf("NODE: yield\n");
+ }else if(node->type == ycf_node_type_statement){
+ printf("NODE: statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ print_abstract_syntax_tree(node->u.statement.expression);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_if){
+ printf("NODE: if_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_if_else){
+ printf("NODE: if_else_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_while){
+ printf("NODE: while_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_do_while){
+ printf("NODE: do_while_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_for){
+ printf("NODE: for_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }else if(node->type == ycf_node_type_switch){
+ printf("NODE: switch_statement\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ } else if(node->type == ycf_node_type_function_call) {
+ print_fun_call(node->u.function_call);
+ } else if(node->type == ycf_node_type_assignment_function_call) {
+ printf("NODE: assignment fun call\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ printf("Assignment expression:\n");
+ print_node_code_expression(node->u.function_call_assignment.left_side, NULL);
+ print_fun_call(node->u.function_call_assignment.fun_call);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+
+
+ } else {
+ printf("NODE: OTHER???\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>\n");
+ ycf_node_print(node, NULL);
+ printf("<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+
+}
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_printers.h b/erts/lib_src/yielding_c_fun/ycf_printers.h
new file mode 100644
index 0000000000..1cc23165c8
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_printers.h
@@ -0,0 +1,30 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "ycf_node.h"
+
+void print_node_code_function_def_parameters(ycf_node_function_definition f_def, ycf_string_printable_buffer* b);
+void print_node_code_function_def(ycf_node_function_definition f_def, ycf_string_printable_buffer* b);
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b);
+
diff --git a/erts/lib_src/yielding_c_fun/ycf_string.c b/erts/lib_src/yielding_c_fun/ycf_string.c
new file mode 100644
index 0000000000..aabb0c3c61
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_string.c
@@ -0,0 +1,111 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ycf_string.h"
+#include "ycf_lists.h"
+#include "ycf_utils.h"
+
+bool ycf_string_is_equal(const char* str1, const char* str2)
+{
+ size_t str1_length = strlen(str1);
+ size_t str2_length = strlen(str2);
+ return
+ str1_length == str2_length &&
+ strncmp(str1, str2, str1_length) == 0;
+}
+
+
+ycf_string_item* ycf_string_item_new(char* str){
+ ycf_string_item* item = ycf_malloc(sizeof(ycf_string_item));
+ item->str = str;
+ item->next = NULL;
+ return item;
+}
+
+char* ycf_string_new(char* format, ...){
+ va_list args;
+ va_start (args, format);
+ int n = vsnprintf(NULL, 0, format, args);
+ va_end (args);
+ char* new = ycf_malloc(n +1);
+ va_start (args, format);
+ vsnprintf(new, n +1, format, args);
+ va_end (args);
+ return new;
+}
+
+
+ycf_string_printable_buffer* ycf_string_printable_buffer_new(){
+ const size_t init_size = 128;
+ ycf_string_printable_buffer* b = ycf_malloc(sizeof(ycf_string_printable_buffer));
+ b->current_pos = 0;
+ b->buffer = ycf_malloc(init_size);
+ b->size = init_size;
+ return b;
+}
+
+void ycf_string_printable_buffer_printf(ycf_string_printable_buffer* buf, char* format, ...){
+ va_list args;
+ if(buf == NULL){
+ va_start (args, format);
+ vprintf(format, args);
+ va_end (args);
+ return;
+ }
+ va_start (args, format);
+ int n = vsnprintf(NULL, 0, format, args) + 1;
+ va_end (args);
+ while(buf->current_pos + n + 64 > buf->size){
+ char* new_buf = ycf_malloc(buf->size * 2);
+ for(int i = 0; i < buf->size; i++){
+ new_buf[i] = buf->buffer[i];
+ }
+ buf->buffer = new_buf;
+ buf->size = buf->size * 2;
+ }
+ va_start (args, format);
+ vsnprintf(&buf->buffer[buf->current_pos], n, format, args);
+ va_end (args);
+ buf->current_pos = buf->current_pos + n - 1;
+}
+
+bool ycf_string_item_list_contains(ycf_string_item_list* l, char* str){
+ ycf_string_item* current = l->head;
+ while(current != NULL){
+ if(strcmp(current->str, str) == 0){
+ return true;
+ }
+ current = current->next;
+ }
+ return false;
+}
+
+
+GENERATE_LIST_FUNCTIONS(ycf_string_item)
diff --git a/erts/lib_src/yielding_c_fun/ycf_string.h b/erts/lib_src/yielding_c_fun/ycf_string.h
new file mode 100644
index 0000000000..0c19e08572
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_string.h
@@ -0,0 +1,89 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_STRING_H
+#define YIELDING_C_FUN_YCF_STRING_H
+
+#include <stdlib.h>
+#include "ycf_utils.h"
+
+/* Types for printable buffers */
+
+typedef struct {
+ char* buffer;
+ size_t current_pos;
+ size_t size;
+} ycf_string_printable_buffer;
+
+/* Types for string lists */
+
+typedef struct ycf_string_item {
+ char* str;
+ struct ycf_string_item* next;
+} ycf_string_item;
+
+typedef struct string_item_list {
+ ycf_string_item* head;
+ ycf_string_item* last;
+} ycf_string_item_list;
+
+
+bool ycf_string_is_equal(const char* str1, const char* str2);
+ycf_string_item* ycf_string_item_new(char* str);
+char* ycf_string_new(char* format, ...);
+
+/* Functions for printable buffers */
+
+ycf_string_printable_buffer* ycf_string_printable_buffer_new(void);
+void ycf_string_printable_buffer_printf(ycf_string_printable_buffer* buf, char* format, ...);
+
+/* Functions for string lists */
+
+int ycf_string_item_list_get_item_position(ycf_string_item_list* list, ycf_string_item* node);
+
+bool ycf_string_item_list_contains(ycf_string_item_list* l, char* str);
+
+ycf_string_item* ycf_string_item_shallow_copy(ycf_string_item* n);
+ycf_string_item* ycf_string_item_list_get_item_at_position(ycf_string_item_list* list, int pos);
+
+void ycf_string_item_list_append(ycf_string_item_list* list, ycf_string_item* node);
+void ycf_string_item_list_prepend(ycf_string_item_list* list, ycf_string_item* node);
+void ycf_string_item_list_insert_before(ycf_string_item_list* list, ycf_string_item* before_this, ycf_string_item* to_insert);
+void ycf_string_item_list_insert_after(ycf_string_item_list* list, ycf_string_item* after_this, ycf_string_item* to_insert);
+void ycf_string_item_list_remove(ycf_string_item_list* list, ycf_string_item* to_remove);
+void ycf_string_item_list_replace(ycf_string_item_list* list, ycf_string_item* to_replace, ycf_string_item* replace_with);
+void ycf_string_item_list_concat(ycf_string_item_list* list1, ycf_string_item_list* list2);
+
+ycf_string_item_list ycf_string_item_list_empty();
+ycf_string_item_list ycf_string_item_list_shallow_copy(ycf_string_item_list n);
+ycf_string_item_list ycf_string_item_list_copy_append(ycf_string_item_list list, ycf_string_item* node);
+ycf_string_item_list ycf_string_item_list_copy_prepend(ycf_string_item_list list, ycf_string_item* node);
+ycf_string_item_list ycf_string_item_list_copy_insert_before(ycf_string_item_list list, ycf_string_item* before_this, ycf_string_item* to_insert);
+ycf_string_item_list ycf_string_item_list_copy_insert_after(ycf_string_item_list list, ycf_string_item* after_this, ycf_string_item* to_insert);
+ycf_string_item_list ycf_string_item_list_copy_remove(ycf_string_item_list list, ycf_string_item* to_remove);
+ycf_string_item_list ycf_string_item_list_copy_replace(ycf_string_item_list list, ycf_string_item* to_replace, ycf_string_item* replace_with);
+ycf_string_item_list ycf_string_item_list_copy_concat(ycf_string_item_list list1, ycf_string_item_list list2);
+
+
+#endif //YIELDING_C_FUN_YCF_STRING_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_symbol.c b/erts/lib_src/yielding_c_fun/ycf_symbol.c
new file mode 100644
index 0000000000..826aac3afd
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_symbol.c
@@ -0,0 +1,166 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_utils.h"
+#include "ycf_lists.h"
+
+
+
+ycf_symbol* ycf_symbol_copy(ycf_symbol* symbol){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = symbol->source;
+ new_symbol->start = symbol->start;
+ new_symbol->stop = symbol->stop;
+ new_symbol->type = symbol->type;
+ new_symbol->whitespace_or_comment_before = symbol->whitespace_or_comment_before;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_copy_change_text(ycf_symbol* to_copy, char* new_text){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = new_text;
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_text);
+ new_symbol->type = to_copy->type;
+ new_symbol->whitespace_or_comment_before = to_copy->whitespace_or_comment_before;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_something_else(char* text){
+ ycf_symbol* new = ycf_malloc(sizeof(ycf_symbol));
+ new->type = ycf_symbol_type_something_else;
+ new->next = NULL;
+ new->source = text;
+ new->whitespace_or_comment_before = NULL;
+ new->start = 0;
+ new->stop = strlen(text);
+ return new;
+}
+
+ycf_symbol* ycf_symbol_new_semicolon(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ";";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(";");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_identifier(char* name){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = name;
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_identifier;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_star(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "*";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen("*");
+ new_symbol->type = ycf_symbol_type_star;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_parenthesis(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ")";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(")");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_open_curly_brace(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "\n{\n";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_open_curly_brace;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+ycf_symbol* ycf_symbol_new_end_curly_brace(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = "\n}\n";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(new_symbol->source);
+ new_symbol->type = ycf_symbol_type_end_curly_brace;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+
+ycf_symbol* ycf_symbol_new_comma(){
+ ycf_symbol* new_symbol = ycf_malloc(sizeof(ycf_symbol));
+ new_symbol->next = NULL;
+ new_symbol->source = ",";
+ new_symbol->start = 0;
+ new_symbol->stop = strlen(",");
+ new_symbol->type = ycf_symbol_type_semicolon;
+ new_symbol->whitespace_or_comment_before = NULL;
+ return new_symbol;
+}
+
+
+char* ycf_symbol_get_text(ycf_symbol* symbol){
+ int size = symbol->stop - symbol->start;
+ char* str = ycf_malloc(size+1);
+ strncpy(str, &symbol->source[symbol->start], size);
+ str[size] = 0;
+ return str;
+}
+
+char* ycf_symbol_list_to_str(ycf_symbol_list* l){
+ ycf_symbol* s = l->head;
+ char* str = "";
+ while(s != NULL){
+ str = ycf_string_new(" %s %s ", str, ycf_symbol_get_text(s));
+ s = s->next;
+ }
+ return str;
+}
+
+GENERATE_LIST_FUNCTIONS(ycf_symbol)
diff --git a/erts/lib_src/yielding_c_fun/ycf_symbol.h b/erts/lib_src/yielding_c_fun/ycf_symbol.h
new file mode 100644
index 0000000000..b6d69cc5cb
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_symbol.h
@@ -0,0 +1,110 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef YIELDING_C_FUN_YCF_SYMBOL_H
+#define YIELDING_C_FUN_YCF_SYMBOL_H
+
+/* Types for symbols */
+
+typedef enum {
+ ycf_symbol_type_comment,
+ ycf_symbol_type_string_literal,
+ ycf_symbol_type_macro_define,
+ ycf_symbol_type_macro_command,
+ ycf_symbol_type_whitespace,
+ ycf_symbol_type_identifier,
+ ycf_symbol_type_number,
+ ycf_symbol_type_open_parenthesis,
+ ycf_symbol_type_end_parenthesis,
+ ycf_symbol_type_open_curly_brace,
+ ycf_symbol_type_end_curly_brace,
+ ycf_symbol_type_open_square_bracket,
+ ycf_symbol_type_end_square_bracket,
+ ycf_symbol_type_not_equal_sign,
+ ycf_symbol_type_equal_sign,
+ ycf_symbol_type_equal_equal_sign,
+ ycf_symbol_type_star,
+ ycf_symbol_type_neg,
+ ycf_symbol_type_semicolon,
+ ycf_symbol_type_comma,
+ ycf_symbol_type_pointer_field_access,
+ ycf_symbol_type_period,
+ ycf_symbol_type_special_code_start,
+ ycf_symbol_type_special_code_end,
+ ycf_symbol_type_const,
+ ycf_symbol_type_void,
+ ycf_symbol_type_static,
+ ycf_symbol_type_inline,
+ ycf_symbol_type_volatile,
+ ycf_symbol_type_consume_reds,
+ ycf_symbol_type_return,
+ ycf_symbol_type_if,
+ ycf_symbol_type_else,
+ ycf_symbol_type_goto,
+ ycf_symbol_type_while,
+ ycf_symbol_type_do,
+ ycf_symbol_type_for,
+ ycf_symbol_type_switch,
+ ycf_symbol_type_break,
+ ycf_symbol_type_continue,
+ ycf_symbol_type_something_else
+} ycf_symbol_type;
+
+
+typedef struct ycf_symbol {
+ ycf_symbol_type type;
+ int start;
+ int stop;
+ char* source;
+ struct ycf_symbol* whitespace_or_comment_before;
+ struct ycf_symbol* next;
+} ycf_symbol;
+
+typedef struct symbol_list {
+ struct ycf_symbol* head;
+ struct ycf_symbol* last;
+} ycf_symbol_list;
+
+/* Functions for symbols */
+
+ycf_symbol_list ycf_symbol_list_shallow_copy(ycf_symbol_list n);
+
+ycf_symbol* ycf_symbol_new_something_else(char* text);
+ycf_symbol* ycf_symbol_copy_change_text(ycf_symbol* to_copy, char* new_text);
+ycf_symbol* ycf_symbol_new_semicolon(void);
+ycf_symbol* ycf_symbol_new_star();
+ycf_symbol* ycf_symbol_new_parenthesis(void);
+ycf_symbol* ycf_symbol_new_comma(void);
+ycf_symbol* ycf_symbol_new_open_curly_brace(void);
+ycf_symbol* ycf_symbol_new_end_curly_brace(void);
+ycf_symbol* ycf_symbol_new_identifier(char* name);
+char* ycf_symbol_get_text(ycf_symbol* symbol);
+ycf_symbol_list ycf_symbol_list_from_text(char* text);
+void ycf_symbol_list_print(char* text);
+char* get_symbol_type_text(ycf_symbol_type type);
+ycf_symbol* ycf_symbol_copy(ycf_symbol* symbol);
+char* ycf_symbol_text_between(ycf_symbol* s1, ycf_symbol* s2);
+int ycf_symbol_is_text_eq(ycf_symbol* symbol, char* str);
+char* ycf_symbol_list_to_str(ycf_symbol_list* l);
+#endif //YIELDING_C_FUN_YCF_SYMBOL_H
diff --git a/erts/lib_src/yielding_c_fun/ycf_utils.c b/erts/lib_src/yielding_c_fun/ycf_utils.c
new file mode 100644
index 0000000000..1c51043b41
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_utils.c
@@ -0,0 +1,106 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ycf_utils.h"
+#include "lib/simple_c_gc/simple_c_gc.h"
+#include <stdint.h>
+
+bool ycf_use_gc = false;
+bool ycf_track_memory = false;
+
+size_t ycf_memory_usage = 0;
+size_t ycf_max_memory_usage = 0;
+
+void ycf_enable_gc(){
+ ycf_use_gc = true;
+}
+
+void ycf_enable_memory_tracking(){
+ ycf_track_memory = true;
+}
+
+void ycf_malloc_log(char* log_file, char* log_entry_id) {
+ FILE* out = fopen(log_file, "a");
+ fprintf(out,
+ "(%s)\nMax memory consumption %zu bytes ~ %zu kilo bytes ~ %zu mega bytes (after=%zu)\n",
+ log_entry_id,
+ ycf_max_memory_usage,
+ ycf_max_memory_usage / 1000,
+ ycf_max_memory_usage / 1000000,
+ ycf_memory_usage);
+ fclose(out);
+}
+
+void* ycf_raw_malloc(size_t size) {
+ if (ycf_track_memory) {
+ void* block = malloc(size + sizeof(intptr_t));
+ intptr_t* size_ptr = block;
+ *size_ptr = size + sizeof(intptr_t);
+ ycf_memory_usage = ycf_memory_usage + size + sizeof(intptr_t);
+ if (ycf_memory_usage > ycf_max_memory_usage) {
+ ycf_max_memory_usage = ycf_memory_usage;
+ }
+ if(block == NULL) {
+ fprintf(stderr, "ycf_malloc failed: is there enough memory in the machine?\n");
+ exit(1);
+ }
+ return (void*)(((char*)block) + sizeof(intptr_t));
+ } else {
+ void* block = malloc(size);
+ if(block == NULL) {
+ fprintf(stderr, "ycf_malloc failed: is there enough memory in the machine?\n");
+ exit(1);
+ }
+ return block;
+ }
+}
+
+void* ycf_malloc(size_t size) {
+ if (ycf_use_gc) {
+ return scgc_new(size);
+ } else {
+ return ycf_raw_malloc(size);
+ }
+}
+
+
+void ycf_free(void* to_free) {
+ if (ycf_track_memory) {
+ char* to_free_cp = to_free;
+ char* start = to_free_cp - sizeof(intptr_t);
+ intptr_t* size_ptr = (intptr_t*)start;
+ ycf_memory_usage = ycf_memory_usage - *size_ptr;
+ free(start);
+ } else {
+ free(to_free);
+ }
+}
+
+
+
diff --git a/lib/erl_interface/src/legacy/erl_marshal.h b/erts/lib_src/yielding_c_fun/ycf_utils.h
index c1963b832d..d713fafc09 100644
--- a/lib/erl_interface/src/legacy/erl_marshal.h
+++ b/erts/lib_src/yielding_c_fun/ycf_utils.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,17 +14,27 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* %CopyrightEnd%
*/
-#ifndef _ERL_MARSHALL_H
-#define _ERL_MARSHALL_H
-#include "erl_eterm.h" /* FIXME don't want to include this here */
+/*
+ * Author: Kjell Winblad
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+
+void ycf_enable_gc(void);
+void ycf_enable_memory_tracking(void);
+void* ycf_malloc(size_t size);
+void ycf_malloc_log(char* log_file, char* log_entry_id);
+void* ycf_raw_malloc(size_t size);
+void ycf_free(void* to_free);
-/* FIXME: not documented, may be internal */
-int erl_verify_magic(unsigned char*);
-void erl_init_marshal(void);
-int erl_encode_it(ETERM *ep, unsigned char **ext, int dist);
-#endif /* _ERL_MARSHALL_H */
+#endif
diff --git a/erts/lib_src/yielding_c_fun/ycf_yield_fun.c b/erts/lib_src/yielding_c_fun/ycf_yield_fun.c
new file mode 100644
index 0000000000..6260e5402e
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_yield_fun.c
@@ -0,0 +1,1625 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "ycf_utils.h"
+#include "ycf_yield_fun.h"
+#include "ycf_node.h"
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_printers.h"
+#include "ycf_parser.h"
+
+static int ycf_yield_location_id_counter = 0;
+
+static
+ycf_node* mk_typedef_struct_node(ycf_node_list definitions, char* name){
+ ycf_node* res = ycf_malloc(sizeof(ycf_node));
+ res->next = NULL;
+ res->type = ycf_node_type_gen_typedef_struct;
+ res->u.gen_typedef_struct.definition_nodes = definitions;
+ res->u.gen_typedef_struct.name = name;
+ return res;
+}
+
+static
+ycf_node_list mk_ycf_trap_state_params(){
+ return ycf_node_definition_list_from_string(ycf_string_new(
+ "long* ycf_nr_of_reductions_param;\n"
+ "void** ycf_trap_state;\n"
+ "void* ycf_extra_context;\n"));
+}
+
+static
+ycf_node_list mk_saved_ycf_trap_state_params(){
+ return ycf_node_definition_list_from_string(
+ "ycf_yield_alloc_type ycf_yield_alloc;\n"
+ "ycf_yield_free_type ycf_yield_free;\n"
+ "void* ycf_yield_alloc_free_context;\n"
+ "size_t ycf_stack_alloc_size_or_max_size;\n"
+ "void* ycf_stack_alloc_data;\n");
+}
+
+static
+ycf_node_list mk_trap_extra_state(){
+ return ycf_node_definition_list_from_string(
+ "int ycf_trap_location;\n"
+ "long ycf_nr_of_reductions;\n"
+ "struct ycf_alloc_data ycf_frame_alloc_data;\n");;
+}
+
+static
+char* get_ycf_trap_state_assignments(ycf_node_list ycf_trap_state_defines){
+ ycf_node* current = ycf_trap_state_defines.head;
+ char* str = "";
+ while(current != NULL){
+ char* ident = ycf_symbol_get_text(current->u.definition.identifier);
+ if(current->u.definition.array_brackets.head != NULL && !current->u.definition.array_brackets.head->u.array_bracket.empty){
+ ycf_string_printable_buffer* array_size_exp = ycf_string_printable_buffer_new();
+ ycf_node* bracket = current->u.definition.array_brackets.head;
+ while(bracket != NULL){
+ print_node_code_expression(bracket->u.array_bracket.content,array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, " * ");
+ bracket = bracket->next;
+ }
+ ycf_string_printable_buffer_printf(array_size_exp, " sizeof(");
+ print_symbol_list(&current->u.definition.type_specifiers, array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, ")");
+ str = ycf_string_new("%s"
+ " memcpy(ycf_my_trap_state->%s, %s, %s);\n",
+ str,
+ ident,
+ ident,
+ array_size_exp->buffer);
+ }else{
+ str = ycf_string_new("%s"
+ " ycf_my_trap_state->%s = %s;\n",
+ str,
+ ident,
+ ident);
+ }
+ current = current->next;
+ }
+ return str;
+}
+
+static
+char* mk_code_from_special_code_list(ycf_node_list special_code_list){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_node* current = special_code_list.head;
+ ycf_string_printable_buffer_printf(b, "\n/* YCF SPECIAL CUSTOM CODE START */\n");
+ while(current != NULL) {
+ ycf_node_print(current->u.special_code_block.code.if_statement, b);
+ ycf_string_printable_buffer_printf(b, "\n");
+ current = current->next;
+ }
+ ycf_string_printable_buffer_printf(b, "\n/* YCF SPECIAL CUSTOM CODE END */\n");
+ return b->buffer;
+}
+
+static
+ycf_node* mk_yield_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines,
+ ycf_node_list on_save_yield_state_code_list,
+ bool debug){
+ char* ret_code;
+ if(ycf_node_is_void_ret_fun(f_node)){
+ ret_code = "return;\n";
+ } else{
+ ycf_symbol_list ret_type = ycf_node_get_return_type(f_node);
+ ret_code =
+ ycf_string_new(" {\n"
+ " %s ycf_ret_value;\n"
+ " return ycf_ret_value;\n"
+ " }\n",
+ ycf_symbol_list_to_str(&ret_type));
+ }
+ char *debug_check_for_stack_ptrs = "";
+ if(debug){
+ debug_check_for_stack_ptrs =
+ ycf_string_new("ycf_debug_check_block(\"%s\",\n"
+ " ycf_find_stack_bottom_conservative(),\n"
+ " ycf_trap_state,\n"
+ " ycf_my_trap_state,\n"
+ " sizeof(struct %s));\n",
+ ycf_trap_state_name,
+ ycf_trap_state_name);
+ }
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " struct %s* ycf_my_trap_state;\n"
+ " ycf_do_yield_label_%s:;\n"
+ " %s"
+ " if (*ycf_trap_state == NULL) {\n"
+ " ycf_my_trap_state = ycf_yield_alloc(sizeof(struct %s), ycf_yield_alloc_free_context);\n"
+ " } else {\n"
+ " ycf_my_trap_state = *ycf_trap_state;\n"
+ " }\n"
+ " %s\n"
+ " *ycf_nr_of_reductions_param = ycf_nr_of_reductions;\n"
+ " *ycf_trap_state = ycf_my_trap_state;\n"
+ " %s"
+ " %s\n"
+ "}\n"
+ "\n",
+ ycf_trap_state_name,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ mk_code_from_special_code_list(on_save_yield_state_code_list),
+ ycf_trap_state_name,
+ get_ycf_trap_state_assignments(ycf_trap_state_defines),
+ debug_check_for_stack_ptrs,
+ ret_code
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+ycf_node* mk_goto_yield_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines){
+ ycf_yield_location_id_counter++;
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_nr_of_reductions = 0;\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ ycf_yield_location_id_counter,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ ycf_yield_location_id_counter
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+static
+ycf_node* mk_goto_yield_no_reds_code(ycf_node* f_node,
+ char* ycf_trap_state_name,
+ ycf_node_list ycf_trap_state_defines){
+ ycf_yield_location_id_counter++;
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ ycf_yield_location_id_counter,
+ ycf_symbol_get_text(f_node->u.function.definition.definition.identifier),
+ ycf_yield_location_id_counter
+ );
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+
+static
+ycf_node* mk_yield_fun_call_state_var(ycf_node* f){
+ ycf_node_list code = ycf_node_list_empty();
+ char* fun_name = NULL;
+ if(f->u.statement.expression->type == ycf_node_type_function_call){
+ fun_name = ycf_symbol_get_text(f->u.statement.expression->u.function_call.identifier);
+ }else{
+ fun_name = ycf_symbol_get_text(f->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ }
+ ycf_node * scope_with_dec =
+ ycf_node_get_from_code_scope_text(ycf_string_new("void* ycf_sub_fun_trap_state_wb = NULL;\n"
+ "/*special_code_start:ON_DESTROY_STATE*/\n"
+ "if(0){\n"
+ " if(ycf_sub_fun_trap_state_wb != NULL)\n{"
+ " %s_ycf_gen_destroy(ycf_sub_fun_trap_state_wb);\n"
+ " }\n"
+ "}\n"
+ "/*special_code_end*/\n", fun_name));
+ ycf_node_list_append(&code, ycf_node_shallow_copy(f));
+ ycf_node_list_append(&code, scope_with_dec->u.code_scope.other_nodes.head);
+ ycf_node* ret = ycf_node_scope_new(ycf_symbol_new_open_curly_brace(),
+ scope_with_dec->u.code_scope.definition_nodes,
+ code,
+ ycf_symbol_new_end_curly_brace());
+ return ret;
+}
+
+static
+ycf_node* mk_yield_fun_call(ycf_node* c_file_node,
+ ycf_node* f,
+ char* ycf_trap_state_var_name,
+ char* calling_fun_name,
+ bool auto_yield){
+ ycf_yield_location_id_counter++;
+ ycf_node_function_call f_node;
+ char* assignment_code = "";
+ char* tmp_assignment_var_name = "ycf_tmp_fun_call_tmp_var";
+ char* tmp_assignment_var_declaration = "";
+ char* parameters = "";
+ char* finalize_call_code = "";
+ if(f->u.statement.expression->type == ycf_node_type_function_call){
+ f_node = f->u.statement.expression->u.function_call;
+ }else{
+ ycf_symbol_list called_fun_ret_type;
+ ycf_string_printable_buffer* assign_to_b = ycf_string_printable_buffer_new();
+ f_node = f->u.statement.expression->u.function_call_assignment.fun_call;
+ called_fun_ret_type =
+ ycf_node_find_function_return_type(c_file_node,
+ ycf_symbol_get_text(f_node.identifier));
+ tmp_assignment_var_declaration =
+ ycf_string_new("%s %s;\n",
+ ycf_symbol_list_to_str(&called_fun_ret_type),
+ tmp_assignment_var_name);
+ print_node_code_expression(f->u.statement.expression->u.function_call_assignment.left_side, assign_to_b);
+ assignment_code = ycf_string_new("%s = ", tmp_assignment_var_name);
+ finalize_call_code = ycf_string_new("%s = %s;\n",
+ assign_to_b->buffer,
+ tmp_assignment_var_name);
+ }
+ if (f_node.parameter_expressions.head != NULL){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ print_node_list_code(f_node.parameter_expressions.head, b);
+ parameters = ycf_string_new(",%s", b->buffer);
+ }
+
+ char* code = ycf_string_new("%s"
+ "%s"
+ "%s %s%s_ycf_gen_yielding(&ycf_nr_of_reductions,\n"
+ " &%s,ycf_extra_context,\n"
+ " ycf_yield_alloc,ycf_yield_free,\n"
+ " ycf_yield_alloc_free_context,\n"
+ " YCF_ALLOC_NEXT_MAX_SIZE(),\n"
+ " YCF_ALLOC_NEXT_BLOCK()\n"
+ " %s);\n"
+ "while(YCF_IS_YIELDED(%s)){\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ " %s %s%s_ycf_gen_continue(&ycf_nr_of_reductions,\n"
+ " &%s,\n"
+ " ycf_extra_context);\n"
+ "}\n"
+ "%s\n",
+ tmp_assignment_var_declaration,
+ auto_yield ? "YCF_CONSUME_REDS(1);" : "",
+ assignment_code,
+ ycf_symbol_list_to_str(&f_node.neg_symbols),
+ ycf_symbol_get_text(f_node.identifier),
+ ycf_trap_state_var_name,
+ parameters,
+ ycf_trap_state_var_name,
+ ycf_yield_location_id_counter,
+ calling_fun_name,
+ ycf_yield_location_id_counter,
+ assignment_code,
+ ycf_symbol_list_to_str(&f_node.neg_symbols),
+ ycf_symbol_get_text(f_node.identifier),
+ ycf_trap_state_var_name,
+ finalize_call_code
+ );
+
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+
+static
+char* get_trap_restore_assignments(ycf_node_list ycf_trap_state_defines){
+ ycf_node* current = ycf_trap_state_defines.head;
+ char* str = "\n";
+ while(current != NULL){
+ char* ident = ycf_symbol_get_text(current->u.definition.identifier);
+ if(current->u.definition.array_brackets.head != NULL && !current->u.definition.array_brackets.head->u.array_bracket.empty){
+ ycf_string_printable_buffer* array_size_exp = ycf_string_printable_buffer_new();
+ ycf_node* bracket = current->u.definition.array_brackets.head;
+ while(bracket != NULL){
+ print_node_code_expression(bracket->u.array_bracket.content,array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, " * ");
+ bracket = bracket->next;
+ }
+ ycf_string_printable_buffer_printf(array_size_exp, " sizeof(");
+ print_symbol_list(&current->u.definition.type_specifiers, array_size_exp);
+ ycf_string_printable_buffer_printf(array_size_exp, ")");
+ str = ycf_string_new("%s"
+ " memcpy(%s, ycf_my_trap_state->%s, %s);\n",
+ str,
+ ident,
+ ident,
+ array_size_exp->buffer);
+ }else{
+ str = ycf_string_new("%s"
+ " %s = ycf_my_trap_state->%s;\n",
+ str,
+ ident,
+ ident);
+ }
+ current = current->next;
+ }
+ return str;
+}
+
+static
+char* get_goto_ycf_trap_location_switch(int nr_of_ycf_trap_locations){
+ int i;
+ char* str = "switch(ycf_trap_location){\n";
+ for(i = 1; i <= nr_of_ycf_trap_locations; i++){
+ str = ycf_string_new("%s"
+ "case %d: goto ycf_yield_location_label_%d;\n", str, i, i);
+ }
+ str = ycf_string_new("%s\n}", str, i, i);
+ return str;
+}
+
+typedef struct {
+ ycf_node* f_node;
+ char* ycf_trap_state_name;
+ ycf_node_list ycf_trap_state_defines;
+} yield_code_replacer_context;
+
+ycf_node* yield_code_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ yield_code_replacer_context* c = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call &&
+ ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_YIELD") &&
+ candidate->u.statement.expression->u.function_call.parameter_expressions.head == NULL){
+ ycf_node* yield_code = mk_goto_yield_code(c->f_node, c->ycf_trap_state_name, c->ycf_trap_state_defines);
+ return yield_code;
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_yield_code(ycf_node* f_node, char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ yield_code_replacer,
+ &(yield_code_replacer_context) {f_node, ycf_trap_state_name,
+ ycf_trap_state_defines});
+}
+
+static
+ycf_node* special_code_section_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ (void)context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call) {
+ ycf_node_function_call fun_call = candidate->u.statement.expression->u.function_call;
+ ycf_symbol* fun_name = fun_call.identifier;
+ if(ycf_symbol_is_text_eq(fun_name, "YCF_SPECIAL_CODE_START") &&
+ ycf_node_list_length(fun_call.parameter_expressions) == 1) {
+ char* parameter = ycf_node_to_string(fun_call.parameter_expressions.head);
+ ycf_node* special_code_start =
+ ycf_node_new_text_node(ycf_string_new("/*special_code_start:%s*/\n"
+ "if(0){\n",
+ parameter));
+ return special_code_start;
+ } else if (ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_SPECIAL_CODE_END") &&
+ ycf_node_list_length(fun_call.parameter_expressions) == 0) {
+ return ycf_node_new_text_node("}/*special_code_end*/\n");
+ }
+ }
+ return candidate;
+}
+
+static
+ycf_node* replace_alt_syntax_special_code_section_code(ycf_node* f_node){
+ ycf_node_search_and_replace_statements_in_scope(&f_node->u.function.body,
+ special_code_section_replacer,
+ NULL);
+ return ycf_node_get_function_from_text(ycf_node_to_string(f_node));
+}
+
+
+static
+ycf_node* yield_no_reds_code_replacer(ycf_node* candidate, ycf_node_code_scope* s,void* context){
+ yield_code_replacer_context* c = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ candidate->u.statement.expression != NULL &&
+ candidate->u.statement.expression->type == ycf_node_type_function_call &&
+ ycf_symbol_is_text_eq(candidate->u.statement.expression->u.function_call.identifier,
+ "YCF_YIELD_NO_REDS") &&
+ candidate->u.statement.expression->u.function_call.parameter_expressions.head == NULL){
+ ycf_node* yield_code = mk_goto_yield_no_reds_code(c->f_node, c->ycf_trap_state_name, c->ycf_trap_state_defines);
+ return yield_code;
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_yield_no_reds_code(ycf_node* f_node, char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ yield_no_reds_code_replacer,
+ &(yield_code_replacer_context){f_node, ycf_trap_state_name,
+ ycf_trap_state_defines});
+}
+
+typedef struct {
+ ycf_node_list on_return_code_list;
+ ycf_node_list on_return_or_destroy_code_list;
+ bool auto_yield;
+} replace_return_ctx;
+
+static
+ycf_node* save_nr_of_reductions_before_return_replacer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ replace_return_ctx* ctx = context;
+ (void)s;
+ char* consume_reds_code = ctx->auto_yield ? "YCF_CONSUME_REDS(1);\n" : "";
+ if(candidate->type == ycf_node_type_return_statement){
+ char* code =
+ ycf_string_new("\n"
+ "%s"
+ "if (*ycf_trap_state != NULL) {\n"
+ " ycf_yield_free(*ycf_trap_state, ycf_yield_alloc_free_context);\n"
+ " *ycf_trap_state = NULL;\n"
+ "}\n"
+ "ycf_destroy_stack_allocator(&ycf_frame_alloc_data,\n"
+ " ycf_yield_free,\n"
+ " ycf_yield_alloc_free_context);\n"
+ "*ycf_nr_of_reductions_param = ycf_nr_of_reductions;"
+ "%s"
+ "%s"
+ "%s",
+ consume_reds_code,
+ mk_code_from_special_code_list(
+ ctx->on_return_or_destroy_code_list),
+ mk_code_from_special_code_list(ctx->on_return_code_list),
+ ycf_node_to_string(candidate));
+ return ycf_node_get_from_code_scope_text(code);
+ } else {
+ return candidate;
+ }
+}
+
+static
+void save_nr_of_reductions_before_return(ycf_node_code_scope* s,
+ ycf_node_list on_return_code_list,
+ ycf_node_list on_return_or_destroy_code_list,
+ bool auto_yield){
+ replace_return_ctx ctx;
+ ctx.on_return_code_list = on_return_code_list;
+ ctx.on_return_or_destroy_code_list = on_return_or_destroy_code_list;
+ ctx.auto_yield = auto_yield;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ save_nr_of_reductions_before_return_replacer,
+ &ctx);
+}
+
+ycf_node* mk_consume_reds_code(char* function_name, ycf_node* consume_reds_node){
+ ycf_string_printable_buffer* b = ycf_string_printable_buffer_new();
+ ycf_yield_location_id_counter++;
+ print_node_code_paran_expression(consume_reds_node->u.consume_reds.nr_of_reds_expression, b);
+ return ycf_node_get_from_code_scope_text(ycf_string_new("ycf_nr_of_reductions = ycf_nr_of_reductions - %s;\n"
+ "if (ycf_nr_of_reductions <= 0) {\n"
+ " ycf_trap_location = %d;\n"
+ " goto ycf_do_yield_label_%s;\n"
+ " ycf_yield_location_label_%d:;\n"
+ "}\n",
+ b->buffer,
+ ycf_yield_location_id_counter,
+ function_name,
+ ycf_yield_location_id_counter));
+}
+
+static
+ycf_node* consume_reds_replacer(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ char* function_name = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_consume_reds){
+ return mk_consume_reds_code(function_name, candidate);
+ } else {
+ return candidate;
+ }
+}
+
+static
+void insert_consume_reds_code(char* function_name, ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ consume_reds_replacer,
+ function_name);
+}
+
+static
+ycf_node* mk_consume_reds_wrapped_statement(ycf_node* statement){
+ return ycf_node_get_from_code_scope_text(ycf_string_new("YCF_CONSUME_REDS(1);\n"
+ "%s\n", ycf_node_to_string(statement)));
+}
+
+static
+void insert_consume_reds_calls(ycf_node_code_scope* s);
+
+static
+ycf_node* consume_reds_calls_inserter(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ if(candidate->type == ycf_node_type_goto){
+ return mk_consume_reds_wrapped_statement(candidate);
+ } else if (candidate->type == ycf_node_type_while){
+ if(candidate->u.while_n.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.while_n.statement->u.code_scope);
+ }
+ candidate->u.while_n.statement = mk_consume_reds_wrapped_statement(candidate->u.while_n.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_do_while){
+ if(candidate->u.do_while.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.do_while.statement->u.code_scope);
+ }
+ candidate->u.do_while.statement = mk_consume_reds_wrapped_statement(candidate->u.do_while.statement);
+ return candidate;
+ } else if (candidate->type == ycf_node_type_for){
+ if(candidate->u.for_n.statement->type == ycf_node_type_code_scope){
+ insert_consume_reds_calls(&candidate->u.for_n.statement->u.code_scope);
+ }
+ candidate->u.for_n.statement = mk_consume_reds_wrapped_statement(candidate->u.for_n.statement);
+ return candidate;
+ } else {
+ return candidate;
+ }
+}
+
+static void insert_consume_reds_calls(ycf_node_code_scope* s){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ consume_reds_calls_inserter,
+ NULL);
+}
+
+typedef struct {
+ ycf_node_list code;
+ ycf_node_type code_type;
+} special_code_context;
+
+ycf_node* do_special_code_replace(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ special_code_context* c = context;
+ (void)s;
+ if(candidate->type == c->code_type){
+ ycf_node_list_append(&c->code, ycf_node_shallow_copy(candidate));
+ return ycf_node_new_text_node("\n/* YCF Replaced special code */\n");
+ } else {
+ return candidate;
+ }
+}
+
+static
+ycf_node_list save_and_replace_special_code(ycf_node_code_scope* s, ycf_node_type code_type){
+ special_code_context context;
+ context.code = ycf_node_list_empty();
+ context.code_type = code_type;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ do_special_code_replace,
+ &context);
+ return context.code;
+}
+
+static
+ycf_node* insert_fun_call_state_var_replacer(ycf_node* candidate, ycf_node_code_scope* s, void* context){
+ ycf_string_item_list* yielding_funs = context;
+ (void)s;
+ if(candidate->type == ycf_node_type_statement &&
+ (candidate->u.statement.expression->type == ycf_node_type_function_call ||
+ candidate->u.statement.expression->type == ycf_node_type_assignment_function_call)){
+ ycf_string_item* current = yielding_funs->head;
+ while(current != NULL){
+ char* fun_name = NULL;
+ if(candidate->u.statement.expression->type == ycf_node_type_function_call){
+ fun_name = ycf_symbol_get_text(candidate->u.statement.expression->u.function_call.identifier);
+ } else if(candidate->u.statement.expression->type == ycf_node_type_assignment_function_call){
+ fun_name = ycf_symbol_get_text(
+ candidate->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ } else {
+ current = current->next;
+ continue;
+ }
+ if(ycf_string_is_equal(current->str, fun_name)){
+ return mk_yield_fun_call_state_var(candidate);
+ }
+ current = current->next;
+ }
+ }
+ return candidate;
+}
+
+static
+void insert_fun_call_state_var(ycf_node_code_scope* s, ycf_string_item_list* yielding_funs){
+ ycf_node_search_and_replace_statements_in_scope(s,
+ insert_fun_call_state_var_replacer,
+ yielding_funs);
+}
+
+typedef struct {
+ ycf_node* c_file_node;
+ char* calling_fun_name;
+ ycf_string_item_list* yielding_funs;
+ ycf_string_item_list* starte_vars_wb;
+ bool auto_yield;
+} yielding_fun_call_code_context;
+
+static
+ycf_node* insert_yielding_fun_call_code_replacer(ycf_node* candidate,
+ ycf_node_code_scope* s,
+ void* context){
+ yielding_fun_call_code_context* c = context;
+ ycf_node* c_file_node = c->c_file_node;
+ ycf_string_item_list* yielding_funs = c->yielding_funs;
+ (void)s;
+ if (candidate->type == ycf_node_type_code_scope &&
+ candidate->u.code_scope.other_nodes.head != candidate->u.code_scope.other_nodes.last){
+ ycf_node* call_candidate = candidate->u.code_scope.other_nodes.head->next;
+ if (call_candidate->type == ycf_node_type_statement &&
+ (call_candidate->u.statement.expression->type == ycf_node_type_function_call ||
+ call_candidate->u.statement.expression->type == ycf_node_type_assignment_function_call)) {
+ ycf_string_item* current = yielding_funs->head;
+ while(current != NULL){
+ char* fun_name = NULL;
+ if (call_candidate->u.statement.expression->type == ycf_node_type_function_call) {
+ fun_name = ycf_symbol_get_text(call_candidate->u.statement.expression->u.function_call.identifier);
+ } else if (call_candidate->u.statement.expression->type == ycf_node_type_assignment_function_call) {
+ fun_name = ycf_symbol_get_text(
+ call_candidate->u.statement.expression->u.function_call_assignment.fun_call.identifier);
+ } else {
+ current = current->next;
+ continue;
+ }
+ if(ycf_string_is_equal(current->str, fun_name)){
+ char* ycf_trap_state_var_name =
+ ycf_symbol_get_text(ycf_node_get_assignment(candidate->u.code_scope.other_nodes.head)->left_side.content.head->u.other.what);
+ ycf_string_item_list_append(c->starte_vars_wb, ycf_string_item_new(ycf_trap_state_var_name));
+ ycf_node* new_call_code = mk_yield_fun_call(c_file_node,
+ call_candidate,
+ ycf_trap_state_var_name,
+ c->calling_fun_name,
+ c->auto_yield);
+ ycf_node_list_replace(&candidate->u.code_scope.other_nodes, call_candidate, new_call_code);
+ return ycf_node_scope_new(ycf_symbol_new_open_curly_brace(),
+ candidate->u.code_scope.definition_nodes,
+ candidate->u.code_scope.other_nodes,
+ ycf_symbol_new_end_curly_brace());;
+ }
+ current = current->next;
+ }
+ }
+ }
+ return candidate;
+}
+
+ycf_string_item_list insert_yielding_fun_call_code(ycf_node* c_file_node,
+ ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_funs,
+ char* calling_fun_name,
+ bool auto_yield){
+ yielding_fun_call_code_context context;
+ ycf_string_item_list state_vars = ycf_string_item_list_empty();
+ context.c_file_node = c_file_node;
+ context.calling_fun_name = calling_fun_name;
+ context.yielding_funs = yielding_funs;
+ context.starte_vars_wb = &state_vars;
+ context.auto_yield = auto_yield;
+ ycf_node_search_and_replace_statements_in_scope(s,
+ insert_yielding_fun_call_code_replacer,
+ &context);
+ return state_vars;
+}
+
+static
+ycf_node* mk_yield_init_code(char* ycf_trap_state_name, ycf_node_list ycf_trap_state_defines, ycf_node_list on_restore_yield_state_code_list, bool auto_yield, char *function_name){
+ char* code = ycf_string_new("\n"
+ "{\n"
+ " ycf_nr_of_reductions = *ycf_nr_of_reductions_param;\n"
+ " ycf_frame_alloc_data.size = 0;\n"
+ " ycf_frame_alloc_data.max_size = ycf_stack_alloc_size_or_max_size;\n"
+ " ycf_frame_alloc_data.data = ycf_stack_alloc_data;\n"
+ " ycf_frame_alloc_data.needs_freeing = 0;\n"
+ " if(*ycf_trap_state != NULL){\n"
+ " struct %s* ycf_my_trap_state = *ycf_trap_state;\n"
+ " %s\n"
+ " %s\n"
+ " ycf_nr_of_reductions = *ycf_nr_of_reductions_param;\n"
+ " %s\n"
+ " }\n"
+ "}\n",
+ ycf_trap_state_name,
+ get_trap_restore_assignments(ycf_trap_state_defines),
+ mk_code_from_special_code_list(on_restore_yield_state_code_list),
+ get_goto_ycf_trap_location_switch(ycf_yield_location_id_counter));
+ return ycf_node_get_from_code_scope_text(code);
+}
+
+static
+ycf_node* mk_destroy_state_function_node(char* yielding_function_name,
+ ycf_node_list defs,
+ char* ycf_trap_state_struct_name,
+ ycf_node_list destroy_code,
+ ycf_node_list on_destroy_state_or_return_code_list,
+ bool static_aux_funs){
+ ycf_node_list my_defs = ycf_node_list_shallow_copy(defs);
+ ycf_node* current = my_defs.head;
+ while(current != NULL){
+ current->u.definition.end = ycf_symbol_new_semicolon();
+ int empty_array_brackets = 0;
+ {
+ current->u.definition.array_brackets = ycf_node_list_shallow_copy(current->u.definition.array_brackets);
+ ycf_node* b = current->u.definition.array_brackets.head;
+ while(b != NULL && b->u.array_bracket.empty){
+ empty_array_brackets++;
+ ycf_node_list_remove(&current->u.definition.array_brackets, b);
+ b = b->next;
+ }
+ }
+ current->u.definition.type_specifiers = ycf_symbol_list_shallow_copy(current->u.definition.type_specifiers);
+ if(empty_array_brackets > 0){
+ for(int i = 0; i < empty_array_brackets; i++){
+ ycf_symbol_list_append(&current->u.definition.type_specifiers, ycf_symbol_new_star());
+ }
+ }
+ current = current->next;
+ }
+ char* code =
+ ycf_string_new("\n"
+ "%s void %s_ycf_gen_destroy(struct %s* ycf_my_trap_state){\n"
+ " {\n"
+ " %s\n"
+ " %s\n"
+ " %s\n"
+ " %s\n"
+ " ycf_destroy_stack_allocator(&ycf_frame_alloc_data, ycf_yield_free, ycf_yield_alloc_free_context);\n"
+ " ycf_yield_free(ycf_my_trap_state, ycf_yield_alloc_free_context);\n"
+ " }\n"
+ "}\n",
+ static_aux_funs ? "static" : "",
+ yielding_function_name,
+ ycf_trap_state_struct_name,
+ ycf_node_list_to_string(&my_defs),
+ get_trap_restore_assignments(my_defs),
+ mk_code_from_special_code_list(on_destroy_state_or_return_code_list),
+ mk_code_from_special_code_list(destroy_code));
+ return ycf_node_get_function_from_text(code);
+}
+
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree/*Will be changed*/, bool debug_mode)
+{
+ char *debug_mode_code =
+ (debug_mode ?
+ "\n"
+ "#include <setjmp.h>\n"
+ "#include <stdint.h>\n"
+ "#include <string.h>\n"
+ "static void* ycf_find_stack_bottom_conservative_helper() {\n"
+ " void* p = NULL;\n"
+ " volatile intptr_t tmp = (intptr_t)&p;\n"
+ " return (void*)tmp;\n"
+ "}\n"
+ "static void* ycf_find_stack_bottom_conservative() {\n"
+ " jmp_buf env;\n"
+ " setjmp(env);\n"
+ "\n"
+ " {\n"
+ " volatile int noinline = 1;\n"
+ " void* (*bottom)(void) = noinline\n"
+ " ? ycf_find_stack_bottom_conservative_helper\n"
+ " : (void*(*)(void))(NULL);\n"
+ "\n"
+ " return bottom();\n"
+ " }\n"
+ "}\n"
+ "static void ycf_debug_check_block(char* struct_name, void* stack_start, void* stack_end, void* block, size_t block_size) {\n"
+ " char* p;\n"
+ " for (p = block; p < (((char*)block) + block_size); p += sizeof(void*)) {\n"
+ " if(*((char**)p) > (char*)stack_start && *((char**)p) <= (char*)stack_end){\n"
+ " fprintf(stderr, \"Pointer to stack in yielded functions state!!!!! (pointer_address=%p, struct %s,offset=%lu)\\n\", (void*)p, struct_name, (unsigned long)(p-(size_t)block));\n"
+ " exit(1);\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ :
+ "");
+ char* ycf_yielding_c_fun_helpers_code =
+ ycf_string_new("#ifndef YCF_YIELDING_C_FUN_HELPERS\n"
+ "#define YCF_YIELDING_C_FUN_HELPERS 1\n"
+ "#include <string.h>\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "/*\n"
+ " * YCF_GCC_DIAG_ON and YCF_GCC_DIAG_OFF can be used to temporarly\n"
+ " * disable a gcc or clang warning in a file.\n"
+ " *\n"
+ " * Example:\n"
+ " * YCF_GCC_DIAG_OFF(unused-function)\n"
+ " * static int test(){ return 0;}\n"
+ " * YCF_GCC_DIAG_ON(unused-function)\n"
+ " *\n"
+ " * These macros were orginally authored by Jonathan Wakely and has\n"
+ " * been modified by Patrick Horgan.\n"
+ " *\n"
+ " * Source: http://dbp-consulting.com/tutorials/SuppressingGCCWarnings.html\n"
+ " *\n"
+ " */\n"
+ "#if defined(_MSC_VER)\n"
+ "# define YCF_GCC_DIAG_OFF(x) __pragma(warning(push, 0))\n"
+ "# define YCF_GCC_DIAG_ON(x) __pragma(warning(pop))\n"
+ "#elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402\n"
+ "#define YCF_GCC_DIAG_STR(s) #s\n"
+ "#define YCF_GCC_DIAG_JOINSTR(x,y) YCF_GCC_DIAG_STR(x ## y)\n"
+ "# define YCF_GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)\n"
+ "# define YCF_GCC_DIAG_PRAGMA(x) YCF_GCC_DIAG_DO_PRAGMA(GCC diagnostic x)\n"
+ "# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406\n"
+ "# define YCF_GCC_DIAG_OFF(x) YCF_GCC_DIAG_PRAGMA(push) \\\n"
+ " YCF_GCC_DIAG_PRAGMA(ignored YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# define YCF_GCC_DIAG_ON(x) YCF_GCC_DIAG_PRAGMA(pop)\n"
+ "# else\n"
+ "# define YCF_GCC_DIAG_OFF(x) YCF_GCC_DIAG_PRAGMA(ignored YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# define YCF_GCC_DIAG_ON(x) YCF_GCC_DIAG_PRAGMA(warning YCF_GCC_DIAG_JOINSTR(-W,x))\n"
+ "# endif\n"
+ "#else\n"
+ "# define YCF_GCC_DIAG_OFF(x)\n"
+ "# define YCF_GCC_DIAG_ON(x)\n"
+ "#endif\n"
+ "#ifdef __GNUC__\n"
+ "# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__)\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED __attribute__ ((unused))\n"
+ "# else\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "# endif\n"
+ "#else\n"
+ "# define YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "#endif\n"
+ "\n"
+ "typedef void* (*ycf_yield_alloc_type) (size_t,void*);\n"
+ "typedef void (*ycf_yield_free_type) (void*,void*);\n"
+ "\n"
+ "struct ycf_alloc_data {\n"
+ " size_t size;\n"
+ " size_t max_size;\n"
+ " int needs_freeing;\n"
+ " char* data;\n"
+ "};\n"
+ "\n"
+ "#define YCF_ALLOC_NEXT_BLOCK() (\\\n"
+ " ycf_frame_alloc_data.data == NULL \\\n"
+ " ? NULL \\\n"
+ " : ((void*)(&ycf_frame_alloc_data.data[ycf_frame_alloc_data.size]))\\\n"
+ ")\n"
+ "#define YCF_ALLOC_NEXT_MAX_SIZE() (\\\n"
+ " ycf_frame_alloc_data.data == NULL \\\n"
+ " ? 0 \\\n"
+ " : (ycf_frame_alloc_data.max_size - ycf_frame_alloc_data.size)\\\n"
+ ")\n"
+ "\n"
+ "/* Macros for special code sections */\n"
+ "#define ON_SAVE_YIELD_STATE ON_SAVE_YIELD_STATE\n"
+ "#define ON_RESTORE_YIELD_STATE ON_RESTORE_YIELD_STATE\n"
+ "#define ON_DESTROY_STATE ON_DESTROY_STATE\n"
+ "#define ON_RETURN ON_RETURN\n"
+ "#define ON_DESTROY_STATE_OR_RETURN ON_DESTROY_STATE_OR_RETURN\n"
+ "#define YCF_SPECIAL_CODE_START(PARAM) \\\n"
+ " /*special_code_start:PARAM*/ \\\n"
+ " if(0){\n"
+ "#define YCF_SPECIAL_CODE_END() \\\n"
+ " }\\\n"
+ " /*special_code_end*/\n"
+ "\n"
+ "YCF_GCC_ATTRIBUTE_UNUSED\n"
+ "static void* ycf_stack_alloc(size_t size,\n"
+ " struct ycf_alloc_data* data,\n"
+ " ycf_yield_alloc_type allocator,\n"
+ " void* ycf_yield_alloc_free_context,\n"
+ " size_t default_stack_size){\n"
+ " void * ret = NULL;"
+ " if (data->data == NULL) {\n"
+ " if (default_stack_size == 0) {\n"
+ " fprintf(stderr, \"ycf_alloc: not enough stack!!\\n\");\n"
+ " exit(1);\n"
+ " }\n"
+ " data->data = allocator(default_stack_size, ycf_yield_alloc_free_context);\n"
+ " data->needs_freeing = 1;"
+ " data->max_size = default_stack_size;"
+ " data->size = 0;"
+ " }\n"
+ " if (data->size + size > data->max_size) {\n"
+ " fprintf(stderr, \"ycf_alloc: not enough stack!\\n\");\n"
+ " exit(1);\n"
+ " }\n"
+ " ret = &data->data[data->size];\n"
+ " data->size = data->size + size;\n"
+ " return ret;\n"
+ "}\n"
+ "static void ycf_destroy_stack_allocator(struct ycf_alloc_data* data,\n"
+ " ycf_yield_free_type freer,\n"
+ " void* ycf_yield_alloc_free_context){\n"
+ " if(data->needs_freeing){\n"
+ " freer(data->data, ycf_yield_alloc_free_context);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "#include <limits.h>\n"
+ "#define YCF_MAX_NR_OF_REDS LONG_MAX\n"
+ "#define YCF_NR_OF_REDS_LEFT() ycf_nr_of_reductions\n"
+ "#define YCF_SET_NR_OF_REDS_LEFT(NEW_NR_OF_REDS_LEFT) \\\n"
+ " do{ycf_nr_of_reductions = (NEW_NR_OF_REDS_LEFT);}while(0)\n"
+ "\n"
+ "#define YCF_GET_EXTRA_CONTEXT() ycf_extra_context\n"
+ "\n"
+ "#define YCF_IS_YIELDED(CTX) (CTX != NULL)\n"
+ "\n"
+ "#define YCF_YIELD_CODE_GENERATED 1\n"
+ "\n"
+ "%s"
+ "\n"
+ "/* end of YCF_YIELDING_C_FUN_HELPERS guard */\n"
+ "#endif\n", debug_mode_code);
+ ycf_node_list_prepend(&source_out_tree->u.c_file.content,
+ ycf_node_new_text_node(ycf_yielding_c_fun_helpers_code));
+}
+
+static
+ycf_node* mk_fun_def(ycf_node* fun_node){
+ ycf_node* fun_def = ycf_malloc(sizeof(ycf_node));
+ fun_def->next = NULL;
+ fun_def->type = ycf_node_type_function_declaration;
+ fun_def->u.function_definition = fun_node->u.function.definition;
+ ycf_symbol_list fun_node_def_end = ycf_symbol_list_empty();
+ ycf_symbol_list_append(&fun_node_def_end, ycf_symbol_new_semicolon());
+ fun_def->u.function_definition.end = fun_node_def_end;
+ return fun_def;
+}
+
+
+static
+ycf_node* mk_null_vars_code(ycf_string_item_list fun_call_state_vars){
+ ycf_string_item* current = fun_call_state_vars.head;
+ char* assignments = "";
+ while(current != NULL){
+ assignments = ycf_string_new("%s\n"
+ "%s = NULL;\n",
+ assignments,
+ current->str);
+ current = current->next;
+ }
+ ycf_node* code = ycf_node_get_from_code_scope_text(assignments);
+ return code;
+}
+
+static
+ycf_node* mk_continue_function_node(char* yielding_function_name,
+ char* ycf_trap_state_struct_name,
+ ycf_node* yielding_fun,
+ ycf_node_list uniqified_parameters){
+ char* parameters = "";
+ ycf_node* current = uniqified_parameters.head;
+ while(current != NULL){
+ parameters = ycf_string_new("%s,ycf_my_trap_state->%s", parameters, ycf_symbol_get_text(current->u.definition.identifier));
+ current = current->next;
+ }
+ char* fun_call_str =
+ ycf_string_new("%s_ycf_gen_yielding(ycf_number_of_reduction_param,\n"
+ " ycf_trap_state,\n"
+ " ycf_extra_context,\n"
+ " ycf_my_trap_state->ycf_yield_alloc,\n"
+ " ycf_my_trap_state->ycf_yield_free,\n"
+ " ycf_my_trap_state->ycf_yield_alloc_free_context,\n"
+ " ycf_my_trap_state->ycf_stack_alloc_size_or_max_size,\n"
+ " ycf_my_trap_state->ycf_stack_alloc_data\n"
+ " %s)\n",
+ yielding_function_name,
+ parameters);
+ char* code = ycf_string_new("\n"
+ "%s %s_ycf_gen_continue(long* ycf_number_of_reduction_param,\n"
+ " void** ycf_trap_state,\n"
+ " void* ycf_extra_context){\n"
+ " struct %s* ycf_my_trap_state = *ycf_trap_state;\n"
+ "%s"
+ "}\n",
+ ycf_symbol_list_to_str(&yielding_fun->u.function.definition.definition.type_specifiers),
+ yielding_function_name,
+ ycf_trap_state_struct_name,
+ (ycf_node_is_void_ret_fun(yielding_fun) ?
+ ycf_string_new("%s;\n"
+ "return;\n",
+ fun_call_str):
+ ycf_string_new("return %s;\n",
+ fun_call_str)));
+ ycf_symbol_list symbols = ycf_symbol_list_from_text(code);
+ ycf_node* tree = get_abstract_syntax_tree_root(&symbols);
+ if(tree->u.c_file.content.head->type != ycf_node_type_function_definition){
+ printf("NOT A FUNCTION\n");
+ exit(1);
+ }
+ return tree->u.c_file.content.head;
+}
+
+static
+ycf_node* mk_debug_yielding_fun_call_wrapper(char* yielding_function_name,
+ char* ycf_trap_state_struct_name,
+ ycf_node* yielding_fun){
+ char* parameters_2 = "";
+ {
+ ycf_node* current = yielding_fun->u.function.definition.parameters.head;
+ bool first = true;
+ char* identifier;
+ while(current != NULL){
+ identifier = ycf_symbol_get_text(current->u.definition.identifier);
+ if(strcmp(identifier, "ycf_trap_state") == 0){
+ identifier = ycf_string_new("(void**)(&ycf_my_trap_state)");
+ }
+ parameters_2 = ycf_string_new(first ? "%s%s" :"%s,%s",
+ parameters_2,
+ identifier);
+ current = current->next;
+ first = false;
+ }
+ }
+ char* parameters = "";
+ {
+ ycf_node* current = yielding_fun->u.function.definition.parameters.head;
+ bool first = true;
+ while(current != NULL){
+ parameters = ycf_string_new(first ? "%s%s" :"%s,%s",
+ parameters,
+ ycf_symbol_get_text(current->u.definition.identifier));
+ current = current->next;
+ first = false;
+ }
+ }
+ ycf_string_printable_buffer* header_2 = ycf_string_printable_buffer_new();
+ ycf_node_function_definition def_2 = yielding_fun->u.function.definition;
+ def_2.definition.identifier =
+ ycf_symbol_copy_change_text(def_2.definition.identifier,
+ ycf_string_new("%s_2",
+ ycf_symbol_get_text(def_2.definition.identifier)));
+ print_node_code_function_def(def_2, header_2);
+ ycf_string_printable_buffer* header = ycf_string_printable_buffer_new();
+ print_node_code_function_def(yielding_fun->u.function.definition, header);
+ ycf_string_printable_buffer* parameter_types = ycf_string_printable_buffer_new();
+ print_node_code_function_def_parameters(yielding_fun->u.function.definition, parameter_types);
+ ycf_symbol_list ret_type = ycf_node_get_return_type(yielding_fun);
+ char* wrapper_assignment = NULL;
+ char* wrapper_return = NULL;
+ if(ycf_node_is_void_ret_fun(yielding_fun)){
+ wrapper_assignment = ycf_string_new("");
+ wrapper_return = ycf_string_new("return;");
+ }else{
+ wrapper_assignment = ycf_string_new("%s ycf_to_return = ", ycf_symbol_list_to_str(&ret_type));
+ wrapper_return = ycf_string_new("return ycf_to_return;");
+ }
+ char* code = ycf_string_new("\n"
+ "%s{\n"
+ " volatile int noinline = 1;\n"
+ " volatile void* ycf_my_trap_state = *ycf_trap_state;\n"
+ " %s (*next)(%s = noinline\n"
+ " ? %s_ycf_gen_yielding_3\n"
+ " : (%s(*)(%s)(NULL);\n"
+ " {\n"
+ " %s next(%s);\n"
+ " *ycf_trap_state = (struct %s*)ycf_my_trap_state;\n"
+ " %s\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "\n"
+ "%s{\n"
+ " volatile int noinline = 1;\n"
+ " %s (*next)(%s = noinline\n"
+ " ? %s_ycf_gen_yielding_2\n"
+ " : (%s(*)(%s)(NULL);\n"
+ "\n"
+ " {\n"
+ " %s next(%s);\n"
+ " %s;\n"
+ " }\n"
+ "}\n",
+ header_2->buffer,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ yielding_function_name,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ wrapper_assignment,
+ parameters_2,
+ ycf_trap_state_struct_name,
+ wrapper_return,
+ /* Second function*/
+ header->buffer,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ yielding_function_name,
+ ycf_symbol_list_to_str(&ret_type),
+ parameter_types->buffer,
+ wrapper_assignment,
+ parameters,
+ wrapper_return);
+
+ return ycf_node_new_text_node(code);
+}
+
+
+static
+void break_up_control_expressions(ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_function_names);
+
+
+static ycf_node* break_up_control_expressions_helper(ycf_node* candidate,
+ ycf_node_code_scope* candidates_scope,
+ void* context){
+ ycf_string_item_list* list = (ycf_string_item_list*)context;
+ if(candidate->type == ycf_node_type_if &&
+ ycf_node_list_length(candidate->u.if_n.expression.content.content) == 1 &&
+ candidate->u.if_n.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.if_n.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.if_n.if_statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "if(ycf_gen_control_tmp)\n"
+ "%s",
+ ycf_node_to_string(candidate->u.if_n.expression.content.content.head),
+ ycf_node_to_string(candidate->u.if_n.if_statement)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_if_else &&
+ ycf_node_list_length(candidate->u.if_else.if_part.expression.content.content) == 1 &&
+ candidate->u.if_else.if_part.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.if_else.if_part.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.if_else.if_part.if_statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "if(ycf_gen_control_tmp)\n"
+ "%s\n"
+ "%s\n"
+ "%s\n",
+ ycf_node_to_string(candidate->u.if_else.if_part.expression.content.content.head),
+ ycf_node_to_string(candidate->u.if_else.if_part.if_statement),
+ ycf_symbol_get_text(candidate->u.if_else.else_word),
+ ycf_node_to_string(candidate->u.if_else.else_statement)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_do_while &&
+ ycf_node_list_length(candidate->u.do_while.expression.content.content) == 1 &&
+ candidate->u.do_while.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.do_while.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.do_while.statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "do {\n"
+ " %s\n"
+ " ycf_gen_control_tmp = !!%s;\n"
+ "}while(ycf_gen_control_tmp);\n",
+ ycf_node_to_string(candidate->u.do_while.statement),
+ ycf_node_to_string(candidate->u.do_while.expression.content.content.head)));
+ return wrapper;
+ } else if(candidate->type == ycf_node_type_while &&
+ ycf_node_list_length(candidate->u.while_n.expression.content.content) == 1 &&
+ candidate->u.while_n.expression.content.content.head->type == ycf_node_type_function_call &&
+ ycf_string_item_list_contains(list,
+ ycf_symbol_get_text(candidate->u.while_n.expression.content.content.head->u.function_call.identifier))){
+ break_up_control_expressions(&candidate->u.while_n.statement->u.code_scope, list);
+ ycf_node * wrapper =
+ ycf_node_get_from_code_scope_text(ycf_string_new("int ycf_gen_control_tmp;\n"
+ "ycf_gen_control_tmp = !!%s;\n"
+ "while(ycf_gen_control_tmp){\n"
+ " %s\n"
+ " ycf_gen_control_tmp = !!%s;\n"
+ "}\n",
+ ycf_node_to_string(candidate->u.while_n.expression.content.content.head),
+ ycf_node_to_string(candidate->u.while_n.statement),
+ ycf_node_to_string(candidate->u.while_n.expression.content.content.head)));
+ return wrapper;
+ }
+ return candidate;
+}
+
+static
+void break_up_control_expressions(ycf_node_code_scope* s,
+ ycf_string_item_list* yielding_function_names){
+ ycf_node_insert_scopes_in_complex_statements(s);
+ ycf_node_search_and_replace_statements_in_scope(s,
+ break_up_control_expressions_helper,
+ yielding_function_names);
+}
+
+static
+ycf_node* mk_wrap_in_surpress_warn(char* warning, ycf_node* to_wrap) {
+ ycf_string_printable_buffer* buf = ycf_string_printable_buffer_new();
+ ycf_string_printable_buffer_printf(buf,
+ "\n"
+ "/* clang-format off */\n"
+ "YCF_GCC_DIAG_OFF(%s)\n"
+ "/* clang-format on */\n", warning);
+ ycf_node_print(to_wrap, buf);
+ ycf_string_printable_buffer_printf(buf,
+ "\n"
+ "/* clang-format off */\n"
+ "YCF_GCC_DIAG_ON(%s)\n"
+ "/* clang-format on */\n", warning);
+ return ycf_node_new_text_node(buf->buffer);
+
+}
+
+static
+ycf_node* supress_warnings_wrap_yielding_fun(ycf_node* yielding_fun){
+ ycf_node* ret = yielding_fun;
+ ret = mk_wrap_in_surpress_warn("uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("maybe-uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("sometimes-uninitialized", ret);
+ ret = mk_wrap_in_surpress_warn("unknown-warning-option", ret);
+ ret = mk_wrap_in_surpress_warn("pragmas", ret);
+ return ret;
+}
+
+static
+ycf_node* supress_warnings_wrap_destroy_fun(ycf_node* fun){
+ ycf_node* ret = fun;
+ ret = mk_wrap_in_surpress_warn("unused-function", ret);
+ ret = mk_wrap_in_surpress_warn("unused-but-set-variable", ret);
+ ret = mk_wrap_in_surpress_warn("unknown-warning-option", ret);
+ ret = mk_wrap_in_surpress_warn("pragmas", ret);
+ return ret;
+}
+
+ycf_node* insert_yielding_function_with_prefix_suffix(ycf_node* tree_to_insert_to,
+ ycf_node* insert_before,
+ ycf_node* yielding_fun,
+ bool debug_mode,
+ char* yielding_function_name,
+ char* ycf_trap_state_struct_name){
+ ycf_node* prefix = ycf_node_new_text_node("\n"
+ "#define YCF_IN_YIELDING_FUN 1\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#define YCF_STACK_ALLOC(SIZE) \\\n"
+ " ycf_stack_alloc(SIZE,\\\n"
+ " &ycf_frame_alloc_data,\\\n"
+ " ycf_yield_alloc, ycf_yield_alloc_free_context,\\\n"
+ " ycf_stack_alloc_size_or_max_size)\n");
+ if(insert_before == NULL){
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content, prefix);
+ }else{
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ prefix);
+ }
+ if(debug_mode){
+ ycf_node* wrapper_funs =
+ mk_debug_yielding_fun_call_wrapper(yielding_function_name,
+ ycf_trap_state_struct_name,
+ yielding_fun);
+ ycf_node_rename_function(&yielding_fun->u.function,
+ ycf_string_new("%s_ycf_gen_yielding_3",
+ yielding_function_name));
+
+ if(insert_before == NULL){
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content, wrapper_funs);
+ } else {
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ wrapper_funs);
+ }
+ }else {
+ if(insert_before == NULL){
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ yielding_fun);
+ }else{
+ yielding_fun = supress_warnings_wrap_yielding_fun(yielding_fun);
+ ycf_node_list_insert_before(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ yielding_fun);
+ }
+ }
+ if(insert_before == NULL){
+ ycf_node_list_append(&tree_to_insert_to->u.c_file.content,
+ ycf_node_new_text_node("\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#undef YCF_IN_YIELDING_FUN\n"));
+ }else{
+ ycf_node_list_insert_after(&tree_to_insert_to->u.c_file.content,
+ yielding_fun,
+ ycf_node_new_text_node("\n"
+ "#undef YCF_STACK_ALLOC\n"
+ "#undef YCF_IN_YIELDING_FUN\n"
+ "#define YCF_STACK_ALLOC(SIZE) malloc(SIZE)\n"));
+ ycf_node_list_insert_after(&tree_to_insert_to->u.c_file.content,
+ insert_before,
+ ycf_node_new_text_node("\n#undef YCF_STACK_ALLOC\n"));
+ }
+ return yielding_fun;
+}
+
+ycf_node* ast_get_ast_with_yieldified_function(ycf_node* source_tree,
+ ycf_node* header_tree,
+ char* yielding_function_name,
+ ycf_string_item_list* all_yielding_function_names,
+ bool auto_yield,
+ bool recusive_auto_yield,
+ bool debug_mode,
+ bool only_yielding_funs,
+ ycf_node** only_yielding_funs_tree,
+ bool static_aux_funs)
+{
+ ycf_yield_location_id_counter = 0;
+ ycf_node* tree_ret = ycf_node_deep_copy(source_tree);
+ if (*only_yielding_funs_tree == NULL){
+ *only_yielding_funs_tree = ycf_node_c_file_new(ycf_node_list_empty());
+ }
+ /* Find function */
+ ycf_node* fun = ycf_node_find_function(tree_ret, yielding_function_name);
+ if(fun == NULL){
+ fprintf(stderr, "Could not find function %s\n", yielding_function_name);
+ exit(1);
+ }
+ ycf_node* fun_change = ycf_node_deep_copy(fun);
+ /* Replace alternative syntax for special code sections */
+ fun_change = replace_alt_syntax_special_code_section_code(fun_change);
+ ycf_node_normalize_function(fun_change);
+ /* Brake up control expressions with simple calls to yielding functions */
+ break_up_control_expressions(&fun_change->u.function.body, all_yielding_function_names);
+ /* Insert trap state var for calls to yielding functions */
+ insert_fun_call_state_var(&fun_change->u.function.body, all_yielding_function_names);
+ /* Normalize the function to make transformation easier (move all declarations to the top etc) */
+ ycf_node_normalize_function(fun_change);
+ /* Insert YCF_CONSUME_REDS(1) code if auto yielding is on */
+ if(auto_yield){
+ insert_consume_reds_calls(&fun_change->u.function.body);
+ }
+ /* Save variable declaraions */
+ ycf_node_list uniqified_parameters =
+ ycf_node_list_shallow_copy(fun_change->u.function.definition.parameters);
+ ycf_node_list scope_defs =
+ ycf_node_list_shallow_copy(ycf_node_get_declarations_in_scope(&fun_change->u.function.body));
+ ycf_node_list defs =
+ ycf_node_list_shallow_copy(ycf_node_get_all_definitions_in_function(&fun_change->u.function));
+ /* Add extra vaiables that are needed for yielding */
+ ycf_node_list extra_ycf_trap_state = mk_trap_extra_state();
+ ycf_node_list_concat(&scope_defs, &extra_ycf_trap_state);
+ fun_change->u.function.body.definition_nodes = scope_defs;
+ /* Generate trap state struct */
+ ycf_node_list trap_state_struct_var_declarations =
+ ycf_node_list_shallow_copy(extra_ycf_trap_state);
+ ycf_node_list_concat(&trap_state_struct_var_declarations, &defs);
+ char* ycf_trap_state_struct_name =
+ ycf_string_new("gen_ycf_trap_state_for_%s",
+ yielding_function_name);
+ trap_state_struct_var_declarations =
+ ycf_node_list_copy_concat(mk_saved_ycf_trap_state_params(),
+ trap_state_struct_var_declarations);
+ ycf_node* ycf_trap_state_struct =
+ mk_typedef_struct_node(trap_state_struct_var_declarations,
+ ycf_trap_state_struct_name);
+ /* Add extra parameters for trapping */
+ {
+ ycf_node_list trap_params = mk_ycf_trap_state_params();
+ ycf_node_list saved_params = mk_saved_ycf_trap_state_params();
+ ycf_node_list_concat(&trap_params, &saved_params);
+ ycf_node_list_concat(&trap_params, &fun_change->u.function.definition.parameters);
+ fun_change->u.function.definition.parameters = trap_params;
+ fun_change->u.function.definition.ignore_param_ending = 1;
+ }
+ /* Collect and replace special code */
+ ycf_node_list on_save_yield_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_save_yield_state_code);
+ ycf_node_list on_restore_yield_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_restore_yield_state_code);
+ ycf_node_list on_destroy_state_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_destroy_state_code);
+ ycf_node_list on_destroy_state_or_return_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body,
+ ycf_node_type_on_destroy_state_or_return_code);
+ ycf_node_list on_return_code_list =
+ save_and_replace_special_code(&fun_change->u.function.body, ycf_node_type_on_return_code);
+ save_nr_of_reductions_before_return(&fun_change->u.function.body,
+ on_destroy_state_or_return_code_list,
+ on_return_code_list,
+ recusive_auto_yield);
+ /* Insert goto yield code in function */
+ insert_yield_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ &fun_change->u.function.body);
+ insert_yield_no_reds_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ &fun_change->u.function.body);
+ {
+ ycf_string_item_list fun_call_state_vars =
+ insert_yielding_fun_call_code(source_tree,
+ &fun_change->u.function.body,
+ all_yielding_function_names,
+ yielding_function_name,
+ recusive_auto_yield);
+ /* Null all fun_call_state_vars */
+ ycf_node_list_prepend(&fun_change->u.function.body.other_nodes, mk_null_vars_code(fun_call_state_vars));
+ }
+ /* Replace YCF_CONSUME_REDS calls */
+ insert_consume_reds_code(yielding_function_name, &fun_change->u.function.body);
+ /* Insert yield initialization in the beginning of the function */
+ {
+ ycf_node* trap_init = mk_yield_init_code(ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ on_restore_yield_state_code_list,
+ auto_yield,
+ yielding_function_name);
+ ycf_node_list_prepend(&fun_change->u.function.body.other_nodes, trap_init);
+ }
+ /* Add code that saves the state and yields to the end of the function */
+ ycf_node_list_append(&fun_change->u.function.body.other_nodes,
+ mk_yield_code(fun_change,
+ ycf_trap_state_struct_name,
+ trap_state_struct_var_declarations,
+ on_save_yield_state_code_list,
+ debug_mode));
+ /* Change name of function */
+ ycf_node_rename_function(&fun_change->u.function,
+ ycf_string_new("%s_ycf_gen_yielding",
+ yielding_function_name));
+ /* Remove unecessary scopes */
+ ycf_node_remove_unecessary_scopes(&fun_change->u.function.body);
+ /* Make continue function */
+ ycf_node* continue_function =
+ mk_continue_function_node(yielding_function_name,
+ ycf_trap_state_struct_name,
+ fun,
+ uniqified_parameters);
+ /* Make destroy state function */
+ ycf_node* destroy_state_function =
+ mk_destroy_state_function_node(yielding_function_name,
+ trap_state_struct_var_declarations,
+ ycf_trap_state_struct_name,
+ on_destroy_state_code_list,
+ on_destroy_state_or_return_code_list,
+ static_aux_funs);
+ ycf_node* fun_change_dec = mk_fun_def(fun_change);
+ /****************************************************************
+ *
+ * The following code inserts the changed function and its helper
+ * function into the output AST(s)
+ *
+ ****************************************************************/
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ /* Insert changed function into tree that will be printed */
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ ycf_node* fun_ret = ycf_node_find_function(tree_ret, yielding_function_name);
+ ycf_node* fun_change_wrapper =
+ insert_yielding_function_with_prefix_suffix(tree_ret,
+ fun_ret,
+ fun_change,
+ debug_mode,
+ yielding_function_name,
+ ycf_trap_state_struct_name);
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /* END: Insert changed function into tree that will be printed */
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ /* Insert declarations in the top */
+ /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ ycf_node* fun_dec_node = ycf_node_find_function_declaration(tree_ret, yielding_function_name);
+ ycf_node_list_prepend(&tree_ret->u.c_file.content, ycf_node_new_text_node("\n"));
+ if(fun_dec_node == NULL){
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ } else {
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+
+ }
+ ycf_node_list_prepend(&header_tree->u.c_file.content, ycf_node_shallow_copy(fun_change_dec));
+ ycf_node_list_prepend(&header_tree->u.c_file.content, ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ ycf_node* ycf_trap_state_struct_dec =
+ ycf_node_new_text_node(ycf_string_new("\n\nstruct %s;",
+ ycf_trap_state_struct->u.gen_typedef_struct.name));
+ ycf_node_list_prepend(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct_dec);
+ ycf_node_list_prepend(&header_tree->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct_dec));
+ ycf_node_list_append(&header_tree->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ /* Insert definition of changed function */
+ if(fun_dec_node != NULL){
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_dec_node,
+ ycf_node_shallow_copy(ycf_node_shallow_copy(fun_change_dec)));
+ }else{
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content,
+ fun_change_wrapper,
+ ycf_node_shallow_copy(ycf_node_shallow_copy(fun_change_dec)));
+ }
+ ycf_node_list_insert_before(&tree_ret->u.c_file.content, fun_change_wrapper, ycf_trap_state_struct);
+ ycf_node_list_insert_after(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct,
+ supress_warnings_wrap_destroy_fun(destroy_state_function));
+ ycf_node_list_insert_after(&tree_ret->u.c_file.content,
+ ycf_trap_state_struct,
+ mk_wrap_in_surpress_warn("unused-function",
+ continue_function));
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ /* END: Insert declarations in the top */
+ /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ if(only_yielding_funs){
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(fun_change_dec));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",mk_fun_def(destroy_state_function)));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",
+ mk_fun_def(continue_function)));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct_dec));
+ ycf_node_list_prepend(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_shallow_copy(ycf_trap_state_struct));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ supress_warnings_wrap_destroy_fun(destroy_state_function));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ ycf_node_new_text_node("\n"));
+ ycf_node_list_append(&(*only_yielding_funs_tree)->u.c_file.content,
+ mk_wrap_in_surpress_warn("unused-function",continue_function));
+ insert_yielding_function_with_prefix_suffix((*only_yielding_funs_tree),
+ NULL,
+ fun_change,
+ debug_mode,
+ yielding_function_name,
+ ycf_trap_state_struct_name);
+ }
+ return tree_ret;
+}
diff --git a/erts/lib_src/yielding_c_fun/ycf_yield_fun.h b/erts/lib_src/yielding_c_fun/ycf_yield_fun.h
new file mode 100644
index 0000000000..410256e71c
--- /dev/null
+++ b/erts/lib_src/yielding_c_fun/ycf_yield_fun.h
@@ -0,0 +1,116 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB and Kjell Winblad 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+
+#ifndef YCF_AST_H
+#define YCF_AST_H
+
+#include "ycf_utils.h"
+#include "ycf_symbol.h"
+#include "ycf_string.h"
+#include "ycf_node.h"
+
+
+typedef struct {
+ bool success;
+ ycf_symbol* next_symbol;
+ ycf_node* result;
+} ycf_parse_result;
+
+
+/* Functions for strings */
+
+void string_item_list_print(ycf_string_item_list n);
+
+
+/* Functions for symbol lists */
+
+int ycf_symbol_list_get_item_position(ycf_symbol_list* list, ycf_symbol* node);
+
+ycf_symbol* ycf_symbol_shallow_copy(ycf_symbol* n);
+ycf_symbol* ycf_symbol_list_get_item_at_position(ycf_symbol_list* list, int pos);
+
+void ycf_symbol_list_append(ycf_symbol_list* list, ycf_symbol* node);
+void ycf_symbol_list_prepend(ycf_symbol_list* list, ycf_symbol* node);
+void ycf_symbol_list_insert_before(ycf_symbol_list* list, ycf_symbol* before_this, ycf_symbol* to_insert);
+void ycf_symbol_list_insert_after(ycf_symbol_list* list, ycf_symbol* after_this, ycf_symbol* to_insert);
+void ycf_symbol_list_remove(ycf_symbol_list* list, ycf_symbol* to_remove);
+void ycf_symbol_list_replace(ycf_symbol_list* list, ycf_symbol* to_replace, ycf_symbol* replace_with);
+void ycf_symbol_list_concat(ycf_symbol_list* list1, ycf_symbol_list* list2);
+
+ycf_symbol_list ycf_symbol_list_empty();
+ycf_symbol_list ycf_symbol_list_shallow_copy(ycf_symbol_list n);
+ycf_symbol_list ycf_symbol_list_copy_append(ycf_symbol_list list, ycf_symbol* node);
+ycf_symbol_list ycf_symbol_list_copy_prepend(ycf_symbol_list list, ycf_symbol* node);
+ycf_symbol_list ycf_symbol_list_copy_insert_before(ycf_symbol_list list, ycf_symbol* before_this, ycf_symbol* to_insert);
+ycf_symbol_list ycf_symbol_list_copy_insert_after(ycf_symbol_list list, ycf_symbol* after_this, ycf_symbol* to_insert);
+ycf_symbol_list ycf_symbol_list_copy_remove(ycf_symbol_list list, ycf_symbol* to_remove);
+ycf_symbol_list ycf_symbol_list_copy_replace(ycf_symbol_list list, ycf_symbol* to_replace, ycf_symbol* replace_with);
+ycf_symbol_list ycf_symbol_list_copy_concat(ycf_symbol_list list1, ycf_symbol_list list2);
+
+
+
+
+/* Functions for parsing text to AST */
+
+ycf_node* ycf_node_scope_new(ycf_symbol* start,
+ ycf_node_list declaration_nodes,
+ ycf_node_list other_nodes,
+ ycf_symbol* end);
+ycf_parse_result parse_expression(ycf_symbol* symbols);
+
+
+void print_abstract_syntax_tree(ycf_node* node);
+void print_node_code_paran_expression(ycf_node_parentheses_expression e, ycf_string_printable_buffer* b);
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree/*Will be changed*/, bool debug_mode);
+void print_symbol_list(ycf_symbol_list* l, ycf_string_printable_buffer* b);
+
+
+/* Abstract syntax tree functions */
+
+
+
+ycf_node* ycf_node_find_function(ycf_node* c_file_node, char* fun_name);
+ycf_node_list ycf_node_get_all_definitions_in_function(ycf_node_function* f);
+
+
+ycf_node* ast_get_ast_with_yieldified_function(ycf_node* source_tree,
+ ycf_node* header_tree, /*Will be changed*/
+ char* yielding_function_name,
+ ycf_string_item_list* all_yielding_function_names,
+ bool auto_yield,
+ bool recusive_auto_yield,
+ bool debug_mode,
+ bool only_yielding_funs,
+ ycf_node** only_yielding_funs_tree,
+ bool static_aux_funs);
+
+void print_abstract_syntax_tree(ycf_node* node);
+void print_node_code_expression(ycf_node_expression e, ycf_string_printable_buffer* b);
+void print_node_list_code(ycf_node* n, ycf_string_printable_buffer* b);
+void ast_add_yield_code_generated_define(ycf_node* source_out_tree, bool debug_mode);
+
+#endif
diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam
index aa246f8808..903ec90218 100644
--- a/erts/preloaded/ebin/atomics.beam
+++ b/erts/preloaded/ebin/atomics.beam
Binary files differ
diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam
index 1fb85276f9..53a839ce71 100644
--- a/erts/preloaded/ebin/counters.beam
+++ b/erts/preloaded/ebin/counters.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam
index 04559ff767..feef7ae343 100644
--- a/erts/preloaded/ebin/erl_init.beam
+++ b/erts/preloaded/ebin/erl_init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index b8adf71297..9490303fb8 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index 4430489710..3df4d13400 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index b744d97a41..4c69aa5a74 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index a94b37854e..816d198ce8 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
index dd92fb9582..1e211e9662 100644
--- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
+++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index d52ae9a2dc..a5583cdbf5 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
index 44103e5d56..37c3c988b6 100644
--- a/erts/preloaded/ebin/erts_literal_area_collector.beam
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 06074d1717..b29a8031d0 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam
index dc4a1aceb5..752c5dc65f 100644
--- a/erts/preloaded/ebin/persistent_term.beam
+++ b/erts/preloaded/ebin/persistent_term.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam
index a54a956fac..cac7cd0abb 100644
--- a/erts/preloaded/ebin/prim_buffer.beam
+++ b/erts/preloaded/ebin/prim_buffer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 7b39f3e5ba..86c88f30b4 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 d0ef080a30..927eb70558 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 197ff59503..f77df9795f 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_net.beam b/erts/preloaded/ebin/prim_net.beam
index 76d4f0fc69..88e0237262 100644
--- a/erts/preloaded/ebin/prim_net.beam
+++ b/erts/preloaded/ebin/prim_net.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 75413bb065..69b14fa353 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 1a5f34f3ee..34168521e5 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket_registry.beam b/erts/preloaded/ebin/socket_registry.beam
index a5eb2c190f..ed205ead81 100644
--- a/erts/preloaded/ebin/socket_registry.beam
+++ b/erts/preloaded/ebin/socket_registry.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 14558a7424..d2d178a281 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 89c535c43a..658a138694 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -300,18 +300,18 @@ check_file_result(Func, Target, {error,Reason}) ->
%% This is equal to calling logger:error/2 which
%% we don't want to do from code_server during system boot.
%% We don't want to call logger:timestamp() either.
- try
- logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
- #{pid=>self(),
- gl=>group_leader(),
- time=>os:system_time(microsecond),
- error_logger=>#{tag=>error_report,
- type=>std_error}}}
- catch _:_ ->
- %% If logger has not been started yet we just display it
- erlang:display({?MODULE,file_error}),
- erlang:display(Report)
- end,
+ _ = try
+ logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
+ #{pid=>self(),
+ gl=>group_leader(),
+ time=>os:system_time(microsecond),
+ error_logger=>#{tag=>error_report,
+ type=>std_error}}}
+ catch _:_ ->
+ %% If logger has not been started yet we just display it
+ erlang:display({?MODULE,file_error}),
+ erlang:display(Report)
+ end,
error
end;
check_file_result(_, _, Other) ->
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 82d0aa91f8..600818f6c6 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -20,12 +20,14 @@
-module(erlang).
-export([apply/2,apply/3,spawn/4,spawn_link/4,
- spawn_monitor/1,spawn_monitor/3,
+ spawn_monitor/1,spawn_monitor/2,
+ spawn_monitor/3,spawn_monitor/4,
spawn_opt/2,spawn_opt/3,spawn_opt/4,spawn_opt/5,
- disconnect_node/1]).
+ spawn_request/1, spawn_request/2,
+ spawn_request/3, spawn_request/4, spawn_request/5,
+ spawn_request_abandon/1, disconnect_node/1]).
-export([spawn/1, spawn_link/1, spawn/2, spawn_link/2]).
-export([yield/0]).
--export([crasher/6]).
-export([fun_info/1]).
-export([send_nosuspend/2, send_nosuspend/3]).
-export([localtime_to_universaltime/1]).
@@ -52,7 +54,13 @@
dist_ctrl_set_opt/3,
dist_get_stat/1]).
--deprecated([get_stacktrace/0,now/0]).
+-deprecated([{get_stacktrace,0,
+ "use the new try/catch syntax for retrieving the "
+ "stack backtrace"}]).
+-deprecated([{now,0,
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more information"}]).
+-removed([{hash,2,"use erlang:phash2/2 instead"}]).
%% Get rid of autoimports of spawn to avoid clashes with ourselves.
-compile({no_auto_import,[spawn_link/1]}).
@@ -64,8 +72,13 @@
-export_type([timestamp/0]).
-export_type([time_unit/0]).
-export_type([deprecated_time_unit/0]).
+-export_type([spawn_opt_option/0]).
+-export_type([priority_level/0]).
+-export_type([max_heap_size/0]).
+-export_type([message_queue_data/0]).
-type ext_binary() :: binary().
+-type ext_iovec() :: iovec().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
@@ -109,8 +122,11 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([adler32/1, adler32/2, adler32_combine/3, append_element/2]).
--export([atom_to_binary/2, atom_to_list/1, binary_part/2, binary_part/3]).
--export([binary_to_atom/2, binary_to_existing_atom/2, binary_to_float/1]).
+-export([atom_to_binary/1, atom_to_binary/2]).
+-export([atom_to_list/1, binary_part/2, binary_part/3]).
+-export([binary_to_atom/1, binary_to_atom/2]).
+-export([binary_to_existing_atom/1, binary_to_existing_atom/2]).
+-export([binary_to_float/1]).
-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]).
@@ -178,14 +194,37 @@
make_tuple/2, make_tuple/3, nodes/1, open_port/2,
port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2,
process_info/2, send/2, send/3, seq_trace_info/1,
- setelement/3, spawn_opt/1,
+ setelement/3,
statistics/1, subtract/2, system_flag/2,
- term_to_binary/1, term_to_binary/2, tl/1, trace_pattern/2,
+ term_to_binary/1, term_to_binary/2,
+ term_to_iovec/1, term_to_iovec/2,
+ tl/1, trace_pattern/2,
trace_pattern/3, tuple_to_list/1, system_info/1,
universaltime_to_localtime/1]).
-export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1,
dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]).
+%% Operators
+
+-export(['=='/2, '=:='/2,
+ '/='/2, '=/='/2,
+ '=<'/2, '>='/2,
+ '<'/2, '>'/2]).
+
+-export(['-'/1, '+'/1,
+ '-'/2, '+'/2,
+ '/'/2, '*'/2,
+ 'div'/2, 'rem'/2,
+ 'bsl'/2, 'bsr'/2,
+ 'bor'/2, 'band'/2,
+ 'bxor'/2, 'bnot'/1]).
+
+-export(['and'/2, 'or'/2,
+ 'xor'/2, 'not'/1]).
+
+-export(['--'/2, '++'/2]).
+
+-export(['!'/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Simple native code BIFs
@@ -341,6 +380,12 @@ adler32_combine(_FirstAdler, _SecondAdler, _SecondSize) ->
append_element(_Tuple1, _Term) ->
erlang:nif_error(undefined).
+%% atom_to_binary/1
+-spec atom_to_binary(Atom) -> binary() when
+ Atom :: atom().
+atom_to_binary(Atom) ->
+ erlang:atom_to_binary(Atom, utf8).
+
%% atom_to_binary/2
-spec atom_to_binary(Atom, Encoding) -> binary() when
Atom :: atom(),
@@ -371,6 +416,12 @@ binary_part(_Subject, _PosLen) ->
binary_part(_Subject, _Start, _Length) ->
erlang:nif_error(undefined).
+%% binary_to_atom/1
+-spec binary_to_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_atom(Binary) ->
+ erlang:binary_to_atom(Binary, utf8).
+
%% binary_to_atom/2
-spec binary_to_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -378,6 +429,12 @@ binary_part(_Subject, _Start, _Length) ->
binary_to_atom(_Binary, _Encoding) ->
erlang:nif_error(undefined).
+%% binary_to_existing_atom/1
+-spec binary_to_existing_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_existing_atom(Binary) ->
+ erlang:binary_to_existing_atom(Binary, utf8).
+
%% binary_to_existing_atom/2
-spec binary_to_existing_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -585,7 +642,7 @@ date() ->
HttpHeader :: {'http_header',
integer(),
HttpField,
- Reserved :: term(),
+ UnmodifiedField :: HttpString,
Value :: HttpString},
HttpError :: {'http_error', HttpString},
HttpMethod :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE'
@@ -1710,11 +1767,9 @@ setnode(_P1, _P2) ->
-spec erlang:setnode(Node, DistCtrlr, Opts) -> dist_handle() when
Node :: atom(),
DistCtrlr :: port() | pid(),
- Opts :: {integer(), integer(), atom(), atom()}.
-setnode(Node, DistCtrlr, {Flags, Ver, IC, OC} = Opts) when erlang:is_atom(IC),
- erlang:is_atom(OC) ->
- case case erts_internal:create_dist_channel(Node, DistCtrlr,
- Flags, Ver) of
+ Opts :: {integer(), integer(), pos_integer()}.
+setnode(Node, DistCtrlr, {_Flags, _Ver, _Creation} = Opts) ->
+ case case erts_internal:create_dist_channel(Node, DistCtrlr, Opts) of
{ok, DH} -> DH;
{message, Ref} -> receive {Ref, Res} -> Res end;
Err -> Err
@@ -2186,7 +2241,7 @@ nodes(_Arg) ->
-spec open_port(PortName, PortSettings) -> port() when
PortName :: {spawn, Command :: string() | binary()} |
{spawn_driver, Command :: string() | binary()} |
- {spawn_executable, FileName :: file:name() } |
+ {spawn_executable, FileName :: file:name_all() } |
{fd, In :: non_neg_integer(), Out :: non_neg_integer()},
PortSettings :: [Opt],
Opt :: {packet, N :: 1 | 2 | 4}
@@ -2395,20 +2450,6 @@ seq_trace_info(_What) ->
setelement(_Index, _Tuple1, _Value) ->
erlang:nif_error(undefined).
--spec erlang:spawn_opt({Module, Function, Args, Options}) -> pid() | {pid(), reference()} when
- Module :: module(),
- Function :: atom(),
- Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {max_heap_size, Size :: max_heap_size()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
-spawn_opt(_Tuple) ->
- erlang:nif_error(undefined).
-
-spec statistics(active_tasks) -> [ActiveTasks] when
ActiveTasks :: non_neg_integer();
(active_tasks_all) -> [ActiveTasks] when
@@ -2556,11 +2597,24 @@ term_to_binary(_Term) ->
-spec term_to_binary(Term, Options) -> ext_binary() when
Term :: term(),
Options :: [compressed |
- {compressed, Level :: 0..9} |
- {minor_version, Version :: 0..2} ].
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..2} ].
term_to_binary(_Term, _Options) ->
erlang:nif_error(undefined).
+-spec term_to_iovec(Term) -> ext_iovec() when
+ Term :: term().
+term_to_iovec(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_iovec(Term, Options) -> ext_iovec() when
+ Term :: term(),
+ Options :: [compressed |
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..2} ].
+term_to_iovec(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
%% Shadowed by erl_bif_types: erlang:tl/1
-spec tl(List) -> term() when
List :: [term(), ...].
@@ -2836,10 +2890,24 @@ spawn_link(N, F) ->
-spec spawn_monitor(Fun) -> {pid(), reference()} when
Fun :: function().
spawn_monitor(F) when erlang:is_function(F, 0) ->
- erlang:spawn_opt({erlang,apply,[F,[]],[monitor]});
+ erlang:spawn_opt(erlang,apply,[F,[]],[monitor]);
spawn_monitor(F) ->
erlang:error(badarg, [F]).
+-spec spawn_monitor(Node, Fun) -> {pid(), reference()} when
+ Node :: node(),
+ Fun :: function().
+
+spawn_monitor(Node, F) when erlang:is_atom(Node), erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_monitor(Node,erlang,apply,[F,[]])
+ catch
+ error:Err ->
+ erlang:error(Err, [Node, F])
+ end;
+spawn_monitor(Node, F) ->
+ erlang:error(badarg, [Node, F]).
+
-spec spawn_monitor(Module, Function, Args) -> {pid(), reference()} when
Module :: module(),
Function :: atom(),
@@ -2847,7 +2915,7 @@ spawn_monitor(F) ->
spawn_monitor(M, F, A) when erlang:is_atom(M),
erlang:is_atom(F),
erlang:is_list(A) ->
- erlang:spawn_opt({M,F,A,[monitor]});
+ erlang:spawn_opt(M,F,A,[monitor]);
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
@@ -2873,24 +2941,23 @@ spawn_monitor(M, F, A) ->
Fun :: function(),
Options :: [spawn_opt_option()].
spawn_opt(F, O) when erlang:is_function(F) ->
- spawn_opt(erlang, apply, [F, []], O);
+ erlang:spawn_opt(erlang, apply, [F, []], O);
spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
- spawn_opt(erlang, apply, [MF, []], O);
-spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility
- spawn_opt(M, F, A, O);
+ erlang:spawn_opt(erlang, apply, [MF, []], O);
spawn_opt(F, O) ->
erlang:error(badarg, [F, O]).
-spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when
Node :: node(),
Fun :: function(),
- Options :: [spawn_opt_option()].
+ Options :: [monitor | link | OtherOption],
+ OtherOption :: term().
spawn_opt(N, F, O) when N =:= erlang:node() ->
- spawn_opt(F, O);
-spawn_opt(N, F, O) when erlang:is_function(F) ->
- spawn_opt(N, erlang, apply, [F, []], O);
+ erlang:spawn_opt(F, O);
+spawn_opt(N, F, O) when erlang:is_function(F, 0) ->
+ erlang:spawn_opt(N, erlang, apply, [F, []], O);
spawn_opt(N, {M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
- spawn_opt(N, erlang, apply, [MF, []], O);
+ erlang:spawn_opt(N, erlang, apply, [MF, []], O);
spawn_opt(N, F, O) ->
erlang:error(badarg, [N, F, O]).
@@ -2909,24 +2976,11 @@ spawn(N,M,F,A) when N =:= erlang:node(),
spawn(N,M,F,A) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case is_well_formed_list(A) of
- true ->
- ok;
- false ->
- erlang:error(badarg, [N, M, F, A])
- end,
- case catch gen_server:call({net_kernel,N},
- {spawn,M,F,A,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A]);
- Pid ->
- Pid
- end
+ try
+ erlang:spawn_opt(N, M, F, A, [])
+ catch
+ _:Reason ->
+ erlang:error(Reason, [N, M, F, A])
end;
spawn(N,M,F,A) ->
erlang:error(badarg, [N, M, F, A]).
@@ -2944,41 +2998,69 @@ spawn_link(N,M,F,A) when N =:= erlang:node(),
spawn_link(N,M,F,A) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case is_well_formed_list(A) of
- true ->
- ok;
- _ ->
- erlang:error(badarg, [N, M, F, A])
- end,
- case catch gen_server:call({net_kernel,N},
- {spawn_link,M,F,A,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {link, N, M, F, A, []}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A]);
- Pid ->
- Pid
- end
+ try
+ erlang:spawn_opt(N, M, F, A, [link])
+ catch
+ _:Reason ->
+ erlang:error(Reason, [N, M, F, A])
end;
spawn_link(N,M,F,A) ->
erlang:error(badarg, [N, M, F, A]).
+-spec spawn_monitor(Node, Module, Function, Args) -> {pid(), reference()} when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn_monitor(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ try
+ erlang:spawn_monitor(M,F,A)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A])
+ end;
+spawn_monitor(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
+ Ref = try
+ erlang:spawn_request(N, M, F, A, [monitor])
+ catch
+ error:Err0 ->
+ erlang:error(Err0, [N, M, F, A])
+ end,
+ receive
+ {spawn_reply, Ref, ok, Pid} when erlang:is_pid(Pid) ->
+ {Pid, Ref};
+ {spawn_reply, Ref, error, badopt} ->
+ erlang:error(badarg, [N, M, F, A]);
+ {spawn_reply, Ref, error, noconnection} ->
+ try
+ erlang:spawn_opt(erts_internal,crasher,
+ [N,M,F,A,[monitor],
+ noconnection],
+ [monitor])
+ catch
+ _:Err1 ->
+ erlang:error(Err1, [N, M, F, A])
+ end;
+ {spawn_reply, Ref, error, Err2} ->
+ erlang:error(Err2, [N, M, F, A])
+ end;
+spawn_monitor(N,M,F,A) ->
+ erlang:error(badarg, [N, M, F, A]).
+
-spec spawn_opt(Module, Function, Args, Options) ->
pid() | {pid(), reference()} when
Module :: module(),
Function :: atom(),
Args :: [term()],
Options :: [spawn_opt_option()].
-spawn_opt(M, F, A, Opts) ->
- case catch erlang:spawn_opt({M,F,A,Opts}) of
- {'EXIT',{Reason,_}} ->
- erlang:error(Reason, [M,F,A,Opts]);
- Res ->
- Res
- end.
+spawn_opt(_Module, _Function, _Args, _Options) ->
+ erlang:nif_error(undefined).
+
-spec spawn_opt(Node, Module, Function, Args, Options) ->
pid() | {pid(), reference()} when
@@ -2986,47 +3068,76 @@ spawn_opt(M, F, A, Opts) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [spawn_opt_option()].
+ Options :: [monitor | link | OtherOption],
+ OtherOption :: term().
+
spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
erlang:is_atom(M), erlang:is_atom(F),
erlang:is_list(A), erlang:is_list(O) ->
- spawn_opt(M, F, A, O);
+ erlang:spawn_opt(M, F, A, O);
spawn_opt(N, M, F, A, O) when erlang:is_atom(N),
erlang:is_atom(M),
erlang:is_atom(F) ->
- case {is_well_formed_list(A), is_well_formed_list(O)} of
- {true, true} ->
- ok;
- _ ->
- erlang:error(badarg, [N, M, F, A, O])
- end,
- case lists:member(monitor, O) of
- false -> ok;
- true -> erlang:error(badarg, [N, M, F, A, O])
- end,
- {L,NO} = lists:foldl(fun (link, {_, NewOpts}) ->
- {link, NewOpts};
- (Opt, {LO, NewOpts}) ->
- {LO, [Opt|NewOpts]}
- end,
- {no_link,[]},
- O),
- case catch gen_server:call({net_kernel,N},
- {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
- infinity) of
- Pid when erlang:is_pid(Pid) ->
- Pid;
- Error ->
- case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
- {fault, Fault} ->
- erlang:error(Fault, [N, M, F, A, O]);
- Pid ->
- Pid
- end
+ {Ref, MonOpt} = case erts_internal:dist_spawn_request(N, {M, F, A}, O, spawn_opt) of
+ {R, MO} when erlang:is_reference(R) -> {R, MO};
+ badarg -> erlang:error(badarg, [N, M, F, A, O])
+ end,
+ receive
+ {spawn_reply, Ref, ok, Pid} when erlang:is_pid(Pid) ->
+ case MonOpt of
+ true -> {Pid, Ref};
+ false -> Pid
+ end;
+ {spawn_reply, Ref, error, badopt} ->
+ erlang:error(badarg, [N, M, F, A, O]);
+ {spawn_reply, Ref, error, noconnection} ->
+ try
+ erlang:spawn_opt(erts_internal,crasher,
+ [N,M,F,A,O,noconnection], O)
+ catch
+ _:Err1 ->
+ erlang:error(Err1, [N, M, F, A, O])
+ end;
+ {spawn_reply, Ref, error, notsup} ->
+ case old_remote_spawn_opt(N, M, F, A, O) of
+ Pid when erlang:is_pid(Pid) ->
+ Pid;
+ Err2 ->
+ erlang:error(Err2, [N, M, F, A, O])
+ end;
+ {spawn_reply, Ref, error, Err3} ->
+ erlang:error(Err3, [N, M, F, A, O])
end;
spawn_opt(N,M,F,A,O) ->
erlang:error(badarg, [N,M,F,A,O]).
+old_remote_spawn_opt(N, M, F, A, O) ->
+ case lists:member(monitor, O) of
+ true ->
+ badarg;
+ _ ->
+ {L,NO} = lists:foldl(fun (link, {_, NewOpts}) ->
+ {link, NewOpts};
+ (Opt, {LO, NewOpts}) ->
+ {LO, [Opt|NewOpts]}
+ end,
+ {no_link,[]},
+ O),
+ case catch gen_server:call({net_kernel,N},
+ {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
+ infinity) of
+ Pid when erlang:is_pid(Pid) ->
+ Pid;
+ Error ->
+ case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
+ {fault, Fault} ->
+ Fault;
+ Pid ->
+ Pid
+ end
+ end
+ end.
+
remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) ->
{Opts, LL} = case L =:= link of
true ->
@@ -3034,7 +3145,7 @@ remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) ->
false ->
{O, []}
end,
- spawn_opt(erlang,crasher,[N,M,F,A,Opts,noconnection], LL);
+ erlang:spawn_opt(erts_internal,crasher,[N,M,F,A,Opts,noconnection], LL);
remote_spawn_error({'EXIT', {Reason, _}}, _) ->
{fault, Reason};
remote_spawn_error({'EXIT', Reason}, _) ->
@@ -3042,21 +3153,177 @@ remote_spawn_error({'EXIT', Reason}, _) ->
remote_spawn_error(Other, _) ->
{fault, Other}.
-is_well_formed_list([]) ->
- true;
-is_well_formed_list([_|Rest]) ->
- is_well_formed_list(Rest);
-is_well_formed_list(_) ->
- false.
-
-crasher(Node,Mod,Fun,Args,[],Reason) ->
- error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
- [Mod,Fun,Args,Node]),
- erlang:exit(Reason);
-crasher(Node,Mod,Fun,Args,Opts,Reason) ->
- error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
- [Mod,Fun,Args,Opts,Node]),
- erlang:exit(Reason).
+%%
+%% spawn_request/1
+%%
+
+-spec spawn_request(Fun) -> ReqId when
+ Fun :: function(),
+ ReqId :: reference().
+
+spawn_request(F) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(erlang, apply, [F, []], [])
+ catch
+ error:Err ->
+ erlang:error(Err, [F])
+ end;
+spawn_request(F) ->
+ erlang:error(badarg, [F]).
+
+%%
+%% spawn_request/2
+%%
+
+-spec spawn_request(Fun, Options) -> ReqId when
+ Fun :: function(),
+ Option :: {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | spawn_opt_option(),
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ Options :: [Option],
+ ReqId :: reference();
+ (Node, Fun) -> ReqId when
+ Node :: node(),
+ Fun :: function(),
+ ReqId :: reference().
+
+spawn_request(F, O) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(erlang, apply, [F, []], O)
+ catch
+ error:Err ->
+ erlang:error(Err, [F, O])
+ end;
+spawn_request(N, F) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(N, erlang, apply, [F, []], [])
+ catch
+ error:Err ->
+ erlang:error(Err, [N, F])
+ end;
+spawn_request(A1, A2) ->
+ erlang:error(badarg, [A1, A2]).
+
+%%
+%% spawn_request/3
+%%
+
+-spec spawn_request(Node, Fun, Options) -> ReqId when
+ Node :: node(),
+ Fun :: function(),
+ Options :: [Option],
+ Option :: monitor
+ | link
+ | {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | OtherOption,
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ OtherOption :: term(),
+ ReqId :: reference();
+ (Module, Function, Args) ->
+ ReqId when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ ReqId :: reference().
+
+spawn_request(N, F, O) when erlang:is_function(F, 0) ->
+ try
+ erlang:spawn_request(N, erlang, apply, [F, []], O)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, F, O])
+ end;
+spawn_request(M, F, A) ->
+ try
+ erlang:spawn_request(M, F, A, [])
+ catch
+ error:Err ->
+ erlang:error(Err, [M, F, A])
+ end.
+
+%%
+%% spawn_request/4
+%%
+
+-spec spawn_request(Node, Module, Function, Args) ->
+ ReqId when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ ReqId :: reference();
+ (Module, Function, Args, Options) ->
+ ReqId when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Option :: {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | spawn_opt_option(),
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ Options :: [Option],
+ ReqId :: reference().
+
+spawn_request(N, M, F, A) when erlang:is_atom(F) ->
+ try
+ erlang:spawn_request(N, M, F, A, [])
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A])
+ end;
+spawn_request(M, F, A, O) ->
+ case erts_internal:spawn_request(M, F, A, O) of
+ Ref when erlang:is_reference(Ref) ->
+ Ref;
+ badarg ->
+ erlang:error(badarg, [M, F, A, O])
+ end.
+
+%%
+%% spawn_request/5
+%%
+
+-spec spawn_request(Node, Module, Function, Args, Options) ->
+ ReqId when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Options :: [Option],
+ Option :: monitor
+ | link
+ | {reply_tag, ReplyTag}
+ | {reply, Reply}
+ | OtherOption,
+ ReplyTag :: term(),
+ Reply :: yes | no | error_only | success_only,
+ OtherOption :: term(),
+ ReqId :: reference().
+
+spawn_request(N, M, F, A, O) when N =:= erlang:node() ->
+ try
+ erlang:spawn_request(M, F, A, O)
+ catch
+ error:Err ->
+ erlang:error(Err, [N, M, F, A, O])
+ end;
+spawn_request(N, M, F, A, O) ->
+ case erts_internal:dist_spawn_request(N, {M, F, A}, O, spawn_request) of
+ Ref when erlang:is_reference(Ref) ->
+ Ref;
+ badarg ->
+ erlang:error(badarg, [N, M, F, A, O])
+ end.
+
+-spec spawn_request_abandon(ReqId :: reference()) -> boolean().
+
+spawn_request_abandon(_ReqId) ->
+ erlang:nif_error(undefined).
-spec erlang:yield() -> 'true'.
yield() ->
@@ -3337,7 +3604,7 @@ dist_ctrl_put_data(_DHandle, _Data) ->
-spec erlang:dist_ctrl_get_data(DHandle) -> {Size, Data} | Data | 'none' when
Size :: non_neg_integer(),
DHandle :: dist_handle(),
- Data :: iodata().
+ Data :: iovec().
dist_ctrl_get_data(_DHandle) ->
erlang:nif_error(undefined).
@@ -3671,15 +3938,6 @@ get_memval(code, #memory{code = V}) -> V;
get_memval(ets, #memory{ets = V}) -> V;
get_memval(_, #memory{}) -> erlang:error(badarg).
-get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) ->
- get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{blocks_size, Sz} | Rest], Acc) ->
- get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([_ | Rest], Acc) ->
- get_blocks_size(Rest, Acc);
-get_blocks_size([], Acc) ->
- Acc.
-
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
ProcType == monitor;
ProcType == link;
@@ -3722,14 +3980,15 @@ au_mem_acc(#memory{ total = Tot,
processes = Proc,
processes_used = ProcU } = Mem,
eheap_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
processes = Proc+Sz,
processes_used = ProcU+Sz};
au_mem_acc(#memory{ total = Tot,
system = Sys,
- ets = Ets } = Mem, ets_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ ets = Ets } = Mem,
+ ets_alloc, Data) ->
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz,
ets = Ets+Sz };
@@ -3737,31 +3996,45 @@ au_mem_acc(#memory{total = Tot,
system = Sys,
binary = Bin } = Mem,
binary_alloc, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz,
binary = Bin+Sz};
au_mem_acc(#memory{ total = Tot,
system = Sys } = Mem,
_Type, Data) ->
- Sz = get_blocks_size(Data, 0),
+ Sz = acc_blocks_size(Data, 0),
Mem#memory{ total = Tot+Sz,
system = Sys+Sz }.
-au_mem_foreign(Mem, [{Type, SizeList} | Rest]) ->
- au_mem_foreign(au_mem_acc(Mem, Type, SizeList), Rest);
-au_mem_foreign(Mem, []) ->
+acc_blocks_size([{size, Sz, _, _} | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc+Sz);
+acc_blocks_size([{size, Sz} | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc+Sz);
+acc_blocks_size([_ | Rest], Acc) ->
+ acc_blocks_size(Rest, Acc);
+acc_blocks_size([], Acc) ->
+ Acc.
+
+au_mem_blocks([{blocks, L} | Rest], Mem0) ->
+ Mem = au_mem_blocks_1(L, Mem0),
+ au_mem_blocks(Rest, Mem);
+au_mem_blocks([_ | Rest], Mem) ->
+ au_mem_blocks(Rest, Mem);
+au_mem_blocks([], Mem) ->
+ Mem.
+
+au_mem_blocks_1([{Type, SizeList} | Rest], Mem) ->
+ au_mem_blocks_1(Rest, au_mem_acc(Mem, Type, SizeList));
+au_mem_blocks_1([], Mem) ->
Mem.
-au_mem_current(Mem0, Type, [{mbcs_pool, MBCS} | Rest]) ->
- [Foreign] = [Foreign || {foreign_blocks, Foreign} <- MBCS],
- SizeList = MBCS -- [Foreign],
- Mem = au_mem_foreign(Mem0, Foreign),
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
-au_mem_current(Mem, Type, [{mbcs, SizeList} | Rest]) ->
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
-au_mem_current(Mem, Type, [{sbcs, SizeList} | Rest]) ->
- au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
+au_mem_current(Mem, Type, [{mbcs_pool, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
+au_mem_current(Mem, Type, [{mbcs, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
+au_mem_current(Mem, Type, [{sbcs, Stats} | Rest]) ->
+ au_mem_current(au_mem_blocks(Stats, Mem), Type, Rest);
au_mem_current(Mem, Type, [_ | Rest]) ->
au_mem_current(Mem, Type, Rest);
au_mem_current(Mem, _Type, []) ->
@@ -3917,3 +4190,98 @@ gc_info(Ref, N, {OrigColls,OrigRecl}) ->
{Ref, {_,Colls, Recl}} ->
gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl})
end.
+
+%% Operators
+
+-spec erlang:'=='(term(), term()) -> boolean().
+'=='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=:='(term(), term()) -> boolean().
+'=:='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/='(term(), term()) -> boolean().
+'/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=/='(term(), term()) -> boolean().
+'=/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=<'(term(), term()) -> boolean().
+'=<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>='(term(), term()) -> boolean().
+'>='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'<'(term(), term()) -> boolean().
+'<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>'(term(), term()) -> boolean().
+'>'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'-'(number()) -> number().
+'-'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number()) -> number().
+'+'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'-'(number(), number()) -> number().
+'-'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number(), number()) -> number().
+'+'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/'(number(), number()) -> float().
+'/'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'*'(number(), number()) -> number().
+'*'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'div'(integer(), integer()) -> integer().
+'div'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'rem'(integer(), integer()) -> integer().
+'rem'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsl'(integer(), integer()) -> integer().
+'bsl'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsr'(integer(), integer()) -> integer().
+'bsr'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bor'(integer(), integer()) -> integer().
+'bor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'band'(integer(), integer()) -> integer().
+'band'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bxor'(integer(), integer()) -> integer().
+'bxor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bnot'(integer()) -> integer().
+'bnot'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'--'(list(), list()) -> list().
+'--'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'++'(list(), term()) -> term().
+'++'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'and'(boolean(), boolean()) -> boolean().
+'and'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'or'(boolean(), boolean()) -> boolean().
+'or'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'xor'(boolean(), boolean()) -> boolean().
+'xor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'not'(boolean()) -> boolean().
+'not'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'!'(dst(), term()) -> term().
+'!'(_Dst, _Msg) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index d3dbe0f2d1..f8b2338c44 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -42,7 +42,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.5", "kernel-6.5.1", "sasl-3.3"]}
+ {runtime_dependencies, ["stdlib-@OTP-15251@", "kernel-@OTP-15251@", "sasl-3.3"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 0dd65d3e27..6c23b47895 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -67,6 +67,7 @@
-export([dist_ctrl_put_data/2]).
-export([get_dflags/0]).
+-export([get_creation/0]).
-export([new_connection/1]).
-export([abort_pending_connection/2]).
@@ -89,7 +90,7 @@
-export([process_flag/3]).
--export([create_dist_channel/4]).
+-export([create_dist_channel/3]).
-export([erase_persistent_terms/0]).
@@ -105,6 +106,10 @@
-export([get_internal_state_blocked/1]).
+-export([spawn_request/4, spawn_init/1, dist_spawn_request/4, dist_spawn_init/1]).
+
+-export([crasher/6]).
+
%%
%% Await result of send to port
%%
@@ -564,6 +569,10 @@ dist_ctrl_put_data(DHandle, IoList) ->
get_dflags() ->
erlang:nif_error(undefined).
+-spec erts_internal:get_creation() -> pos_integer().
+get_creation() ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:new_connection(Node) -> ConnId when
Node :: atom(),
ConnId :: {integer(), erlang:dist_handle()}.
@@ -703,17 +712,18 @@ process_display(_Pid, _Type) ->
process_flag(_Pid, _Flag, _Value) ->
erlang:nif_error(undefined).
--spec create_dist_channel(Node, DistCtrlr, Flags, Ver) -> Result when
+-spec create_dist_channel(Node, DistCtrlr, {Flags, Ver, Cr}) -> Result when
Node :: atom(),
DistCtrlr :: port() | pid(),
Flags :: integer(),
Ver :: integer(),
+ Cr :: pos_integer(),
Result :: {'ok', erlang:dist_handle()}
| {'message', reference()}
| 'badarg'
| 'system_limit'.
-create_dist_channel(_Node, _DistCtrlr, _Flags, _Ver) ->
+create_dist_channel(_Node, _DistCtrlr, _Tpl) ->
erlang:nif_error(undefined).
-spec erase_persistent_terms() -> 'ok'.
@@ -830,3 +840,85 @@ get_internal_state_blocked(Arg) ->
erlang:system_flag(multi_scheduling, unblock)
end,
Result.
+
+-spec spawn_request(Module, Function, Args, Opts) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: reference() | 'badarg'.
+
+spawn_request(_Module, _Function, _Args, _Opts) ->
+ erlang:nif_error(undef).
+
+-spec spawn_init({Module, Function, Args}) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Res :: term().
+
+spawn_init({M, F, A}) ->
+ apply(M, F, A).
+
+-spec dist_spawn_request(Node, MFA, Opts, spawn_request) -> Res when
+ Node :: node(),
+ MFA :: {Module, Function, Args},
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: reference() | 'badarg';
+ (Node, MFA, Opts, spawn_opt) -> Res when
+ Node :: node(),
+ MFA :: {Module, Function, Args},
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Opts :: [term()],
+ Res :: {reference(), boolean()} | 'badarg'.
+
+dist_spawn_request(_Node, _MFA, _Opts, _Type) ->
+ erlang:nif_error(undef).
+
+-spec dist_spawn_init(MFA) -> Res when
+ MFA :: {Module, Function, non_neg_integer()},
+ Module :: module(),
+ Function :: atom(),
+ Res :: term().
+
+dist_spawn_init(MFA) ->
+ %%
+ %% The argument list is passed as a message
+ %% to the newly created process. This since
+ %% it might be large and require a substantial
+ %% amount of work to decode. This way we put
+ %% this work on the newly created process
+ %% (which can execute in parallel with all
+ %% other tasks) instead of on the distribution
+ %% channel code which is a bottleneck in the
+ %% system.
+ %%
+ %% erl_create_process() ensures that the
+ %% argument list to use in apply is
+ %% guaranteed to be the first message in the
+ %% message queue.
+ %%
+ {M, F, _NoA} = MFA,
+ receive
+ A ->
+ erlang:apply(M, F, A)
+ end.
+
+%%
+%% Failed distributed spawn(), spawn_link(), spawn_monitor(), spawn_opt()
+%% spawns a dummy process executing the crasher/6 function...
+%%
+
+crasher(Node,Mod,Fun,Args,[],Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
+ [Mod,Fun,Args,Node]),
+ erlang:exit(Reason);
+crasher(Node,Mod,Fun,Args,Opts,Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
+ [Mod,Fun,Args,Opts,Node]),
+ erlang:exit(Reason).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 5ea67347ec..76077880b6 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -48,7 +48,7 @@
-module(init).
--export([restart/0,reboot/0,stop/0,stop/1,
+-export([restart/1,restart/0,reboot/0,stop/0,stop/1,
get_status/0,boot/1,get_arguments/0,get_plain_arguments/0,
get_argument/1,script_id/0]).
@@ -63,6 +63,7 @@
-include_lib("kernel/include/file.hrl").
-type internal_status() :: 'starting' | 'started' | 'stopping'.
+-type mode() :: 'embedded' | 'interactive'.
-record(state, {flags = [],
args = [],
@@ -164,7 +165,15 @@ request(Req) ->
end.
-spec restart() -> 'ok'.
-restart() -> init ! {stop,restart}, ok.
+restart() -> restart([]).
+
+-spec restart([{mode, mode()}]) -> 'ok'.
+restart([]) ->
+ init ! {stop,restart}, ok;
+restart([{mode, Mode}]) when Mode =:= embedded; Mode =:= interactive ->
+ init ! {stop,{restart,Mode}}, ok;
+restart(Opts) when is_list(Opts) ->
+ erlang:error(badarg, [Opts]).
-spec reboot() -> 'ok'.
reboot() -> init ! {stop,reboot}, ok.
@@ -546,11 +555,11 @@ stop(Reason,State) ->
clear_system(BootPid,State1),
do_stop(Reason,State1).
-do_stop(restart,#state{start = Start, flags = Flags, args = Args}) ->
- %% Make sure we don't have any outstanding messages before doing the restart.
- flush(),
- erts_internal:erase_persistent_terms(),
- boot(Start,Flags,Args);
+do_stop({restart,Mode},#state{start=Start, flags=Flags0, args=Args}) ->
+ Flags = update_flag(mode, Flags0, atom_to_binary(Mode)),
+ do_restart(Start,Flags,Args);
+do_stop(restart,#state{start=Start, flags=Flags, args=Args}) ->
+ do_restart(Start,Flags,Args);
do_stop(reboot,_) ->
halt();
do_stop(stop,State) ->
@@ -560,6 +569,11 @@ do_stop({stop,Status},State) ->
stop_heart(State),
halt(Status).
+do_restart(Start,Flags,Args) ->
+ flush(),
+ erts_internal:erase_persistent_terms(),
+ boot(Start,Flags,Args).
+
clear_system(BootPid,State) ->
Heart = get_heart(State#state.kernel),
Logger = get_logger(State#state.kernel),
@@ -798,7 +812,7 @@ do_boot(Init,Flags,Start) ->
start_prim_loader(Init, bs2ss(Path), PathFls),
BootFile = bootfile(Flags,Root),
BootList = get_boot(BootFile,Root),
- LoadMode = b2a(get_flag(mode, Flags, false)),
+ LoadMode = b2a(get_flag(mode, Flags, interactive)),
Deb = b2a(get_flag(init_debug, Flags, false)),
catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb},
BootVars = get_boot_vars(Root, Flags),
@@ -1233,6 +1247,13 @@ get_args([B|Bs], As) ->
end;
get_args([], As) -> {reverse(As),[]}.
+update_flag(Flag, [{Flag, _} | Flags], Value) ->
+ [{Flag, [Value]} | Flags];
+update_flag(Flag, [Head | Flags], Value) ->
+ [Head | update_flag(Flag, Flags, Value)];
+update_flag(Flag, [], Value) ->
+ [{Flag, [Value]}].
+
%%
%% Internal get_flag function, with default value.
%% Return: true if flag given without args
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index f241be8569..ae3980022a 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -34,6 +34,7 @@
-export([read_link/1, read_link_all/1,
read_link_info/1, read_link_info/2,
read_file_info/1, read_file_info/2,
+ read_handle_info/1, read_handle_info/2,
write_file_info/2, write_file_info/3]).
-export([list_dir/1, list_dir_all/1]).
@@ -497,6 +498,8 @@ get_handle_nif(_FileRef) ->
erlang:nif_error(undef).
delayed_close_nif(_FileRef) ->
erlang:nif_error(undef).
+read_handle_info_nif(_FileRef) ->
+ erlang:nif_error(undef).
%%
%% Quality-of-life helpers
@@ -598,20 +601,37 @@ read_link_info(Name, Opts) ->
read_info_1(Name, FollowLinks, TimeType) ->
try
case read_info_nif(encode_path(Name), FollowLinks) of
- {error, Reason} ->
- {error, Reason};
- FileInfo ->
- CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
- MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
- ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
- {ok, FileInfo#file_info{ ctime = CTime,
- mtime = MTime,
- atime = ATime }}
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
+ end
+ catch
+ error:_ -> {error, badarg}
+ end.
+
+read_handle_info(Fd) ->
+ read_handle_info_1(Fd, local).
+read_handle_info(Fd, Opts) ->
+ read_handle_info_1(Fd, proplist_get_value(time, Opts, local)).
+
+read_handle_info_1(Fd, TimeType) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ case read_handle_info_nif(FRef) of
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
end
catch
error:_ -> {error, badarg}
end.
+adjust_times(FileInfo, TimeType) ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }.
+
write_file_info(Filename, Info) ->
write_file_info_1(Filename, Info, local).
write_file_info(Filename, Info, Opts) ->
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index eb1bcdce66..ec447d3b3e 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -39,7 +39,7 @@
]).
-export([
- open/2, open/3, open/4,
+ open/1, open/2, open/3, open/4,
bind/2, bind/3,
connect/2, connect/3,
listen/1, listen/2,
@@ -176,6 +176,7 @@
type := type(),
protocol := protocol(),
ctrl := pid(),
+ ctype := normal | fromfd | {fromfd, integer()},
counters := socket_counters(),
num_readers := non_neg_integer(),
num_writers := non_neg_integer(),
@@ -351,6 +352,9 @@
sockaddr_un() |
sockaddr_ll().
+-define(OPEN2_OPTS_DEFAULTS, #{debug => false, dup => true}).
+-define(OPEN4_OPTS_DEFAULTS, #{debug => false}).
+
-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0,
addr => A}).
-define(SOCKADDR_IN4_DEFAULTS, ?SOCKADDR_IN4_DEFAULTS(any)).
@@ -947,6 +951,7 @@
-define(ESOCK_SUPPORTS_LOCAL, 16#0004).
-define(ESOCK_SUPPORTS_SEND_FLAGS, 16#0005).
-define(ESOCK_SUPPORTS_RECV_FLAGS, 16#0006).
+-define(ESOCK_SUPPORTS_NETNS, 16#0007).
%% ===========================================================================
@@ -1094,15 +1099,19 @@ info(#socket{ref = SockRef}) ->
{ipv6, boolean()} |
{local, boolean()} |
{send_flags, supports_send_flags()} |
- {recv_flags, supports_recv_flags()}].
+ {recv_flags, supports_recv_flags()} |
+ {netns, boolean()}].
supports() ->
- [{options, supports(options)},
+ [
+ {options, supports(options)},
{sctp, supports(sctp)},
{ipv6, supports(ipv6)},
{local, supports(local)},
{send_flags, supports(send_flags)},
- {recv_flags, supports(recv_flags)}].
+ {recv_flags, supports(recv_flags)},
+ {netns, supports(netns)}
+ ].
-dialyzer({no_contracts, supports/1}).
@@ -1112,7 +1121,8 @@ supports() ->
(local) -> boolean();
(send_flags) -> supports_send_flags();
(recv_flags) -> supports_recv_flags();
- (Key1) -> false when
+ (netns) -> boolean();
+ (Key1) -> false when
Key1 :: term().
supports(options) ->
@@ -1127,6 +1137,8 @@ supports(send_flags) ->
nif_supports(?ESOCK_SUPPORTS_SEND_FLAGS);
supports(recv_flags) ->
nif_supports(?ESOCK_SUPPORTS_RECV_FLAGS);
+supports(netns) ->
+ nif_supports(?ESOCK_SUPPORTS_NETNS);
supports(_Key1) ->
false.
@@ -1187,29 +1199,6 @@ supports(_Key1, _Key2, _Key3) ->
%%
%% <KOLLA>
%%
-%% How do we handle the case when an fd has been created (somehow)
-%% and we shall create a socket "from it".
-%% Can we figure out Domain, Type and Protocol from fd?
-%% No we can't: For instance, its not possible to 'get' domain on FreeBSD.
-%%
-%% Instead, require: open(Domain, Stream, Proto, #{fd => FD}).
-%% The last argument, Extra, is used to provide the fd.
-%%
-%% </KOLLA>
-%%
-%%
-%% <KOLLA>
-%%
-%% Possibly add a "registry" in the nif, allowing the user processes to
-%% "register" themselves.
-%% The point of this would be to ensure that these processes are
-%% informed if the socket "terminates". Could possibly be used for
-%% other things? If gen_tcp implements the active feature using
-%% a reader process, the nif may need to know about this process,
-%% since its probably "hidden" from the socket "owner" (someone
-%% needs to handle it if it dies).
-%% Register under a name?
-%%
%% The nif sets up a monitor to this process, and if it dies the socket
%% is closed. It is also used if someone wants to monitor the socket.
%%
@@ -1230,12 +1219,33 @@ supports(_Key1, _Key2, _Key3) ->
%% Extra: Currently only used for netns
%%
--spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when
+-spec open(FD) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Socket :: socket(),
+ Reason :: term().
+
+open(FD) ->
+ open(FD, ?OPEN2_OPTS_DEFAULTS).
+
+-spec open(FD, Opts) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Opts :: map(),
+ Socket :: socket(),
+ Reason :: term();
+ (Domain, Type) -> {ok, Socket} | {error, Reason} when
Domain :: domain(),
Type :: type(),
Socket :: socket(),
Reason :: term().
+open(FD, Opts) when is_integer(FD) andalso is_map(Opts) ->
+ case nif_open(FD, ensure_open2_opts(Opts)) of
+ {ok, SockRef} ->
+ Socket = #socket{ref = SockRef},
+ {ok, Socket};
+ {error, _} = ERROR ->
+ ERROR
+ end;
open(Domain, Type) ->
open(Domain, Type, default).
@@ -1247,23 +1257,23 @@ open(Domain, Type) ->
Reason :: term().
open(Domain, Type, Protocol) ->
- open(Domain, Type, Protocol, #{}).
+ open(Domain, Type, Protocol, ?OPEN4_OPTS_DEFAULTS).
--spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when
+-spec open(Domain, Type, Protocol, Opts) -> {ok, Socket} | {error, Reason} when
Domain :: domain(),
Type :: type(),
Protocol :: default | protocol(),
- Extra :: map(),
+ Opts :: map(),
Socket :: socket(),
Reason :: term().
-open(Domain, Type, Protocol, Extra) when is_map(Extra) ->
+open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
try
begin
EDomain = enc_domain(Domain),
EType = enc_type(Type),
EProtocol = enc_protocol(Protocol),
- nif_open(EDomain, EType, EProtocol, Extra)
+ nif_open(EDomain, EType, EProtocol, Opts)
end
of
{ok, SockRef} ->
@@ -3908,6 +3918,25 @@ ensure_sockaddr(SockAddr) ->
invalid_address(SockAddr).
+ensure_open2_opts(M) when is_map(M) ->
+ ensure_open2_opts(maps:to_list(M), ?OPEN2_OPTS_DEFAULTS).
+
+ensure_open2_opts([], Acc) ->
+ Acc;
+ensure_open2_opts([{domain, D}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{domain => enc_domain(D)});
+ensure_open2_opts([{type, T}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{type => enc_type(T)});
+ensure_open2_opts([{protocol, P}|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc#{protocol => enc_protocol(P)});
+ensure_open2_opts([{dup, D}|Opts], Acc) when is_boolean(D) ->
+ ensure_open2_opts(Opts, Acc#{dup => D});
+ensure_open2_opts([{debug, D}|Opts], Acc) when is_boolean(D) ->
+ ensure_open2_opts(Opts, Acc#{debug => D});
+ensure_open2_opts([_|Opts], Acc) ->
+ ensure_open2_opts(Opts, Acc).
+
+
cancel(SockRef, Op, OpRef) ->
case nif_cancel(SockRef, Op, OpRef) of
@@ -4080,7 +4109,10 @@ nif_command(_Command) ->
nif_supports(_Key) ->
erlang:nif_error(undef).
-nif_open(_Domain, _Type, _Protocol, _Extra) ->
+nif_open(_FD, _Opts) ->
+ erlang:nif_error(undef).
+
+nif_open(_Domain, _Type, _Protocol, _Opts) ->
erlang:nif_error(undef).
nif_bind(_SRef, _SockAddr) ->
diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl
index 463d890688..0a5987df88 100644
--- a/erts/test/erl_print_SUITE.erl
+++ b/erts/test/erl_print_SUITE.erl
@@ -324,6 +324,9 @@ run_case(Config, TestArgs, Fun) ->
-define(PORT_EXT, 102).
-define(PID_EXT, 103).
-define(NEW_REFERENCE_EXT, 114).
+-define(NEW_PID_EXT, $X).
+-define(NEW_PORT_EXT, $Y).
+-define(NEWER_REFERENCE_EXT, $Z).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
@@ -351,13 +354,13 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PID_EXT,
+ ?NEW_PID_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
uint32_be(Serial),
- uint8(Creation)])) of
+ uint32_be(Creation)])) of
Pid when is_pid(Pid) ->
Pid;
{'EXIT', {badarg, _}} ->
@@ -370,12 +373,12 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?PORT_EXT,
+ ?NEW_PORT_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
- uint8(Creation)])) of
+ uint32_be(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
@@ -388,33 +391,16 @@ mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
is_integer(Creation),
is_list(Numbers) ->
mk_ref({atom_to_list(NodeName), Creation}, Numbers);
-mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- is_integer(Creation),
- is_integer(Number) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
is_integer(Creation),
is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_REFERENCE_EXT,
+ ?NEWER_REFERENCE_EXT,
uint16_be(length(Numbers)),
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
- uint8(Creation),
+ uint32_be(Creation),
lists:map(fun (N) ->
uint32_be(N)
end,
@@ -429,11 +415,10 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
my_cre() -> erlang:system_info(creation).
-oth_cre(0) -> 1;
-oth_cre(1) -> 2;
-oth_cre(2) -> 3;
-oth_cre(3) -> 1;
-oth_cre(N) -> exit({invalid_creation, N}).
+oth_cre(N) when N >= 0, N < (1 bsl 32) ->
+ (N rem ((1 bsl 32) - 1)) + 1;
+oth_cre(N) ->
+ exit({invalid_creation, N}).
str_1_bsl_10000() ->
"19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709376".
diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl
index 952e6da4dc..f8c9f7ae5a 100644
--- a/erts/test/erlexec_SUITE.erl
+++ b/erts/test/erlexec_SUITE.erl
@@ -129,21 +129,26 @@ args_file(Config) when is_list(Config) ->
" -args_file ~s#acomment~n"
"~n"
"-MiscArg7~n"
+ "-MiscArg8 'val with double space'~n"
+ "-MiscArg9 '~n'~n"
+ "-MiscArg10 \\~n\\\t~n"
"#~n"
"+\\#700~n"
+ "#~s~n"
"-extra +XtraArg6~n",
- [AFN2]),
+ [AFN2,lists:duplicate(1024*1024, $a)]),
write_file(AFN2,
- "-MiscArg3~n"
+ "-MiscArg3 \t\v\f\r\n ~n"
"+\\#300~n"
"-args_file ~s~n"
- "-MiscArg5~n"
- "+\\#500#anothercomment -MiscArg10~n"
+ "-MiscArg5 ' '~n"
+ "+\\#500#anothercomment -MiscArg11~n"
"-args_file ~s~n"
"-args_file ~s~n"
"-args_file ~s~n"
+ "# ~s~n"
"-extra +XtraArg5~n",
- [AFN3, AFN4, AFN5, AFN6]),
+ [AFN3, AFN4, AFN5, AFN6,lists:duplicate(1758, $a)]),
write_file(AFN3,
"# comment again~n"
" -MiscArg4 +\\#400 -extra +XtraArg1"),
@@ -156,33 +161,37 @@ args_file(Config) when is_list(Config) ->
write_file(AFN6, "-extra # +XtraArg10~n"),
CmdLine = "+#100 -MiscArg1 "
++ "-args_file " ++ AFN1
- ++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8",
+ ++ " +#800 -MiscArgCLI \\\t -extra +XtraArg7 +XtraArg8",
{Emu, Misc, Extra} = emu_args(CmdLine),
verify_args(["-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800"], Emu),
verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
- "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
+ "-MiscArg5", " ", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "val with double space",
+ "-MiscArg9","\n","-MiscArg10","\n\t","-MiscArgCLI"],
Misc),
verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Extra),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
"-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "-MiscArg9", "-MiscArg10","-MiscArgCLI",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Emu),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
"+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
Misc),
- verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
+ verify_not_args(["","-MiscArg11", "-#1000", "+XtraArg10",
"-#100", "-#200", "-#300", "-#400",
"-#500", "-#600", "-#700", "-#800",
"-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
- "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
+ "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
+ "-MiscArg9","-MiscArg10","-MiscArgCLI"],
Extra),
ok.
@@ -220,8 +229,7 @@ evil_args_file(Config) when is_list(Config) ->
ANums),
Misc),
ok.
-
-
+
env(Config) when is_list(Config) ->
os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"),
@@ -414,9 +422,9 @@ verify_not_args(Xs, Ys) ->
emu_args(CmdLineArgs) ->
io:format("CmdLineArgs = ~ts~n", [CmdLineArgs]),
{ok,[[Erl]]} = init:get_argument(progname),
- EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs),
- io:format("EmuCL = ~ts", [EmuCL]),
- split_emu_clt(string:lexemes(EmuCL, [$ ,$\t,$\n,[$\r,$\n]])).
+ EmuCL = os:cmd(Erl ++ " -emu_qouted_cmd_exit " ++ CmdLineArgs),
+ ct:pal("EmuCL = ~ts", [EmuCL]),
+ split_emu_clt(string:split(string:trim(EmuCL,both,"\n \""), "\" \"", all)).
split_emu_clt(EmuCLT) ->
split_emu_clt(EmuCLT, [], [], [], emu).
diff --git a/erts/test/erlexec_SUITE_data/erlexec_tests.c b/erts/test/erlexec_SUITE_data/erlexec_tests.c
index bd28d2900c..057d674a8f 100644
--- a/erts/test/erlexec_SUITE_data/erlexec_tests.c
+++ b/erts/test/erlexec_SUITE_data/erlexec_tests.c
@@ -23,7 +23,7 @@
* Author: Sverker Eriksson
*/
-#if defined (__WIN32__) || defined(VXWORKS)
+#if defined (__WIN32__)
int main() {return 0;}
#else /* UNIX only */
diff --git a/erts/test/nt_SUITE_data/nt_info.c b/erts/test/nt_SUITE_data/nt_info.c
index 8ef52cad2d..87917a1559 100644
--- a/erts/test/nt_SUITE_data/nt_info.c
+++ b/erts/test/nt_SUITE_data/nt_info.c
@@ -27,12 +27,7 @@
#include <stdlib.h>
#include <string.h>
-#if defined(VXWORKS)
-int nt_info(int argc, char **argv){
- printf("Hello lvsj!\n");
- return 0;
-}
-#elif !defined(__WIN32__)
+#if !defined(__WIN32__)
int main(int argc, char **argv){
printf("Hello lvsj!\n");
return 0;
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 2372e8b9ac..3f4f3f9574 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -57,8 +57,14 @@ init_per_suite(Config) ->
{error,bad_name} ->
Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
{ok,_} = xref:add_directory(Server, Erts, []);
- _ ->
- ok
+ LibDir ->
+ case file:read_file_info(filename:join([LibDir,"ebin"])) of
+ {error,enoent} ->
+ Erts = filename:join([LibDir, "preloaded","ebin"]),
+ {ok,_} = xref:add_directory(Server, Erts, []);
+ _ ->
+ ok
+ end
end,
[{xref_server,Server}|Config].
@@ -79,8 +85,7 @@ undefined_functions(Config) when is_list(Config) ->
[UndefS,ExcludeFrom]),
{ok,Undef0} = xref:q(Server, lists:flatten(Q)),
Undef1 = hipe_filter(Undef0),
- Undef2 = ssl_crypto_filter(Undef1),
- Undef3 = edoc_filter(Undef2),
+ Undef3 = ssl_crypto_filter(Undef1),
Undef4 = eunit_filter(Undef3),
Undef5 = dialyzer_filter(Undef4),
Undef6 = wx_filter(Undef5),
@@ -92,9 +97,9 @@ undefined_functions(Config) when is_list(Config) ->
_ ->
Fd = open_log(Config, "undefined_functions"),
foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls undefined ~s",
- [format_mfa(Server, MFA1),
- format_mfa(MFA2)]),
+ ct:pal("~s calls undefined ~s",
+ [format_mfa(Server, MFA1),
+ format_mfa(MFA2)]),
io:format(Fd, "~s ~s\n",
[format_mfa(Server, MFA1),
format_mfa(MFA2)])
@@ -157,12 +162,6 @@ ssl_crypto_filter(Undef) ->
{_,_} -> Undef
end.
-edoc_filter(Undef) ->
- %% Filter away function call that is catched.
- filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false;
- (_) -> true
- end, Undef).
-
eunit_filter(Undef) ->
filter(fun({{eunit_test,wrapper_test_exported_,0},
{eunit_test,nonexisting_function,0}}) -> false;
diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl
index f92c25bdb4..f4c8aae810 100644
--- a/erts/test/upgrade_SUITE.erl
+++ b/erts/test/upgrade_SUITE.erl
@@ -20,6 +20,8 @@
-compile(export_all).
+-compile(r21).
+
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -80,7 +82,7 @@ end_per_testcase(_Case,Config) ->
ok.
all() ->
- [minor,major].
+ [minor,major,ancient_major].
%% If this is major release X, then this test performs an upgrade from
%% major release X-1 to the current release.
@@ -89,6 +91,13 @@ major(Config) ->
PreviousMajor = previous_major(Current),
upgrade_test(PreviousMajor,Current,Config).
+%% If this is major release X, then this test performs an upgrade from
+%% major release X-2 to the current release.
+ancient_major(Config) ->
+ Current = erlang:system_info(otp_release),
+ PreviousPreviousMajor = previous_major(previous_major(Current)),
+ upgrade_test(PreviousPreviousMajor,Current,Config).
+
%% If this is a patched version of major release X, then this test
%% performs an upgrade from major release X to the current release.
minor(Config) ->
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 536212af2e..38b43d50f0 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -48,7 +48,7 @@ all() ->
core_files(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
- {skipped, "No idea searching for core-files on windows"};
+ win32_search(true, os:getenv("OTP_DAILY_BUILD_TOP_DIR"));
{unix, darwin} ->
core_file_search(
core_search_conf(true,
@@ -63,7 +63,7 @@ core_files(Config) when is_list(Config) ->
search_for_core_files(Dir) ->
case os:type() of
{win32, _} ->
- io:format("No idea searching for core-files on windows");
+ win32_search(false, Dir);
{unix, darwin} ->
core_file_search(core_search_conf(false, Dir, "/cores"));
_ ->
@@ -103,18 +103,7 @@ core_search_conf(RunByTS, DBTop) ->
core_search_conf(RunByTS, DBTop, false).
core_search_conf(RunByTS, DBTop, XDir) ->
- SearchDir = case is_dir(DBTop) of
- false ->
- case code:which(test_server) of
- non_existing ->
- {ok, CWD} = file:get_cwd(),
- CWD;
- TS ->
- filename:dirname(filename:dirname(TS))
- end;
- true ->
- DBTop
- end,
+ SearchDir = search_dir(DBTop),
XSearchDir = case is_dir(XDir) of
false ->
false;
@@ -130,6 +119,20 @@ core_search_conf(RunByTS, DBTop, XDir) ->
file = os:find_executable("file"),
run_by_ts = RunByTS}.
+search_dir(DBTop) ->
+ case is_dir(DBTop) of
+ false ->
+ case code:which(test_server) of
+ non_existing ->
+ {ok, CWD} = file:get_cwd(),
+ CWD;
+ TS ->
+ filename:dirname(filename:dirname(TS))
+ end;
+ true ->
+ DBTop
+ end.
+
file_inspect(#core_search_conf{file = File}, Core) ->
FRes0 = os:cmd(File ++ " " ++ Core),
FRes = case string:split(FRes0, Core) of
@@ -186,15 +189,14 @@ dump_core(#core_search_conf{ cerl = false }, _) ->
dump_core(_, {ignore, _Core}) ->
ok;
dump_core(#core_search_conf{ cerl = Cerl }, Core) ->
- Dump = case test_server:is_debug() of
- true ->
- os:cmd(Cerl ++ " -debug -dump " ++ Core);
- _ ->
- os:cmd(Cerl ++ " -dump " ++ Core)
- end,
+ Dump = case erlang:system_info(build_type) of
+ opt ->
+ os:cmd(Cerl ++ " -dump " ++ Core);
+ Type ->
+ os:cmd(lists:concat([Cerl," -",Type," -dump ",Core]))
+ end,
ct:log("~ts~n~n~ts",[Core,Dump]).
-
format_core(Conf, {ignore, Core}) ->
format_core(Conf, Core, "[ignored] ");
format_core(Conf, Core) ->
@@ -230,17 +232,24 @@ core_file_search(#core_search_conf{search_dir = Base,
extra_search_dir = XBase,
cerl = Cerl,
run_by_ts = RunByTS} = Conf) ->
- case {Cerl,test_server:is_debug()} of
+ case {Cerl,erlang:system_info(build_type)} of
{false,_} -> ok;
- {_,true} ->
+ {_,opt} ->
catch io:format("A cerl script that probably can be used for "
- "inspection of emulator cores:~n ~s -debug~n",
+ "inspection of emulator cores:~n ~s~n",
[Cerl]);
- _ ->
+ {_,Type} ->
catch io:format("A cerl script that probably can be used for "
- "inspection of emulator cores:~n ~s~n",
- [Cerl])
+ "inspection of emulator cores:~n ~s -emu_type ~p~n",
+ [Cerl,Type])
end,
+
+ case os:getenv("DOCKER_BUILD_INFO") of
+ false -> ok;
+ Info ->
+ io:format(Info)
+ end,
+
io:format("Searching for core-files in: ~s~s~n",
[case XBase of
false -> "";
@@ -321,3 +330,39 @@ core_file_search(#core_search_conf{search_dir = Base,
_ -> Res
end
end.
+
+win32_search(RunByTS, DBTop) ->
+ case os:getenv("WSLENV") of
+ false when RunByTS ->
+ {skipped, "No idea searching for core-files on old windows"};
+ false ->
+ io:format("No idea searching for core-files on old windows");
+ _ ->
+ win32_search_2(RunByTS, DBTop)
+ end.
+
+win32_search_2(true, DBTop0) ->
+ DBTop = search_dir(DBTop0),
+ Dir = "c:/ldisk/daily_build",
+ io:format("Find and move 'dmp' files in: ~s to ~s~n",[Dir, DBTop]),
+ case filelib:wildcard("*.dmp", Dir) of
+ [] -> ok;
+ Dumps ->
+ %% We move the "daily" dmp files to this test-run
+ Str = lists:flatten(["Core-files found:", lists:join($\s, lists:reverse(Dumps))]),
+ Rename = fun(File) ->
+ FP = filename:join(Dir, File),
+ _ = file:rename(FP, filename:join(DBTop, File))
+ end,
+ [Rename(File) || File <- Dumps],
+ ct:fail(Str)
+ end;
+win32_search_2(false, _DBTop0) ->
+ DBTop = search_dir("c:/ldisk/daily_build"),
+ io:format("Search for 'dmp' files in: ~s~n",[DBTop]),
+ case filelib:wildcard("*.dmp", DBTop) of
+ [] -> "Core-files found: Ignored core-files found:";
+ Dumps ->
+ io:format("The dmp files must be removed manually\n", []),
+ lists:flatten(["Core-files found:", lists:join($\s, lists:reverse(Dumps))])
+ end.
diff --git a/lib/asn1/Makefile b/lib/asn1/Makefile
index 26e7e37924..10a8795703 100644
--- a/lib/asn1/Makefile
+++ b/lib/asn1/Makefile
@@ -54,12 +54,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
@@ -100,3 +95,5 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile
index 9c0d865884..c18f567240 100644
--- a/lib/asn1/doc/src/Makefile
+++ b/lib/asn1/doc/src/Makefile
@@ -21,6 +21,7 @@
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
@@ -29,11 +30,6 @@ VSN=$(ASN1_VSN)
APPLICATION=asn1
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -47,6 +43,7 @@ XML_PART_FILES = part.xml
XML_HTML_FILE = \
notes_history.xml
+
XML_CHAPTER_FILES = \
asn1_introduction.xml \
asn1_getting_started.xml \
@@ -60,77 +57,11 @@ XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
XML_GEN_FILES = $(GEN_XML:%=$(XMLDIR)/%)
-GIF_FILES = \
+IMAGE_FILES = \
exclusive_Win_But.gif \
selective_Window2.gif \
selective_TypeList.gif
-# ----------------------------------------------------
-
-ASN1_FILES = \
- Seq.asn \
- Seq.asn1config
-
-INFO_FILE = ../../info
-EXTRA_FILES = \
- $(DEFAULT_HTML_FILES) \
- $(ASN1_FILES) \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(GEN_XML:%.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 +=
-DVIPS_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)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(GEN_XML) errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+include $(ERL_TOP)/make/doc.mk
-release_spec:
+.SECONDARY: $(XML_GEN_FILES)
diff --git a/lib/asn1/doc/users_guide/Makefile b/lib/asn1/doc/users_guide/Makefile
index 38196f5e1c..079af7ac1f 100644
--- a/lib/asn1/doc/users_guide/Makefile
+++ b/lib/asn1/doc/users_guide/Makefile
@@ -40,8 +40,7 @@ PSFIG_FILES=
USERS_GUIDE = users_guide.sgml
EXTRA_GEN_FILES= $(SGML_FILES:.sgml=.html) \
- users_guide_frame.html users_guide_first.html \
- min_head.gif
+ users_guide_frame.html users_guide_first.html
HTML_FILES= $(USERS_GUIDE:.sgml=.html)
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index cd50d30aa8..5708d5cb7e 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -76,6 +76,11 @@
-define(ALTERNATIVE_UNDECODED,alt_undec).
-define(ALTERNATIVE_PARTS,alt_parts).
+%% Removed functions
+
+-removed({decode,'_',"use Mod:decode/2 instead"}).
+-removed({encode,'_',"use Mod:encode/2 instead"}).
+
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This is the interface to the compiler
diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl
index 8235b689f8..76ff60f9cf 100644
--- a/lib/asn1/src/asn1ct_tok.erl
+++ b/lib/asn1/src/asn1ct_tok.erl
@@ -239,16 +239,16 @@ skip_multiline_comment(Stream, [_|T], Lno, Level) ->
skip_multiline_comment(Stream, T, Lno, Level).
collect_quoted("'B"++T, Lno, L) ->
- case check_bin(L) of
- true ->
- {{bstring,Lno,lists:reverse(L)}, T};
+ case validate_bin(L) of
+ {ok, Bin} ->
+ {{bstring,Lno,Bin}, T};
false ->
throw({error,{invalid_binary_number,lists:reverse(L)}})
end;
collect_quoted("'H"++T, Lno, L) ->
- case check_hex(L) of
- true ->
- {{hstring,Lno,lists:reverse(L)}, T};
+ case validate_hex(L) of
+ {ok, Hex} ->
+ {{hstring,Lno,Hex}, T};
false ->
throw({error,{invalid_hex_number,lists:reverse(L)}})
end;
@@ -257,24 +257,31 @@ collect_quoted([H|T], Lno, L) ->
collect_quoted([], _, _) -> % This should be allowed FIX later
throw({error,eol_in_token}).
-check_bin([$0|T]) ->
- check_bin(T);
-check_bin([$1|T]) ->
- check_bin(T);
-check_bin([]) ->
- true;
-check_bin(_) ->
- false.
-
-check_hex([H|T]) when $0 =< H , H =< $9 ->
- check_hex(T);
-check_hex([H|T]) when $A =< H , H =< $F ->
- check_hex(T);
-check_hex([]) ->
- true;
-check_hex(_) ->
- false.
-
+validate_bin(L) ->
+ validate_bin(L,[]).
+
+validate_bin([H|T], A) when H =:= $0; H =:= $1 ->
+ validate_bin(T, [H|A]);
+validate_bin([$\s|T], A) ->
+ validate_bin(T, A);
+validate_bin([_|_], _) ->
+ false;
+validate_bin([], A) ->
+ {ok, A}.
+
+validate_hex(L) ->
+ validate_hex(L,[]).
+
+validate_hex([H|T], A) when $0 =< H , H =< $9 ->
+ validate_hex(T, [H|A]);
+validate_hex([H|T], A) when $A =< H , H =< $F ->
+ validate_hex(T, [H|A]);
+validate_hex([$\s|T], A) ->
+ validate_hex(T, A);
+validate_hex([_|_], _) ->
+ false;
+validate_hex([], A) ->
+ {ok, A}.
%% reserved_word(A) -> true|false|rstrtype
%% A = atom()
diff --git a/lib/asn1/src/asn1rt.erl b/lib/asn1/src/asn1rt.erl
new file mode 100644
index 0000000000..fc2e2c2d2a
--- /dev/null
+++ b/lib/asn1/src/asn1rt.erl
@@ -0,0 +1,34 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This module was removed entirely in OTP 20.0, it's only retained for its
+%% removal warning attribute.
+%%
+
+-module(asn1rt).
+
+-removed({decode,'_',"use Mod:decode/2 instead"}).
+-removed({encode,'_',"use Mod:encode/2 instead"}).
+
+-removed({utf8_binary_to_list,'_',
+ "use unicode:characters_to_list/1 instead"}).
+-removed({utf8_list_to_binary,'_',
+ "use unicode:characters_to_binary/1 instead"}).
diff --git a/lib/asn1/test/asn1_SUITE_data/ValueTest.asn b/lib/asn1/test/asn1_SUITE_data/ValueTest.asn
index b2c59d686a..0474386061 100644
--- a/lib/asn1/test/asn1_SUITE_data/ValueTest.asn
+++ b/lib/asn1/test/asn1_SUITE_data/ValueTest.asn
@@ -100,6 +100,7 @@ os-holder-1 OS-HOLDER ::= { ID 1 OS '4041FF'H }
OctetStringSeq ::= ParamSeq{OCTET STRING}
someOctetString OCTET STRING ::= '404142'H
+someOctetStringWhiteSpace OCTET STRING ::= '40 41 42'H
octetStringSeq1 OctetStringSeq ::= { a someOctetString }
octetStringSeq2 OctetStringSeq ::= { a otherOctetString }
@@ -128,6 +129,7 @@ BsSeq ::= SEQUENCE {
}
someBitString BIT STRING ::= '101101'B
+someBitStringWhiteSpace BIT STRING ::= '101 101'B
bsSeq1 BsSeq ::= { a someBitString, b someNamedBs }
bsSeq2 BsSeq ::= { a otherBitString, b someOtherNamedBs }
diff --git a/lib/asn1/test/testValueTest.erl b/lib/asn1/test/testValueTest.erl
index 6699c0094a..f09a9ee307 100644
--- a/lib/asn1/test/testValueTest.erl
+++ b/lib/asn1/test/testValueTest.erl
@@ -84,6 +84,7 @@ main() ->
{'OctetStringSeq',<<16#40,16#41,16#42>>} = M:octetStringSeq1(),
<<16#40,16#41,16#42>> = M:otherOctetString(),
<<16#40,16#41,16#42>> = M:someOctetString(),
+ <<16#40,16#41,16#42>> = M:someOctetStringWhiteSpace(),
{'OctetStringSeq',<<16#40,16#41,16#42>>} = M:octetStringSeq2(),
{'OctetStringSeq',<<16#40,16#41,16#FF>>} = M:octetStringSeq3(),
<<16#40,16#41,16#FF>> = M:'os-1'(),
@@ -94,6 +95,7 @@ main() ->
{'BsSeq',<<2#101101:6>>,[c]} = M:bsSeq2(),
{'BsSeq',<<2#101:3>>,[a,c]} = M:bsSeq3(),
<<2#101101:6>> = M:someBitString(),
+ <<2#101101:6>> = M:someBitStringWhiteSpace(),
<<2#101101:6>> = M:otherBitString(),
<<2#101:3>> = M:bsFromObject(),
<<2#101:3>> = M:bsFromObjectInd(),
diff --git a/lib/common_test/Makefile b/lib/common_test/Makefile
index f2065b8a0d..5ac76f0044 100644
--- a/lib/common_test/Makefile
+++ b/lib/common_test/Makefile
@@ -45,3 +45,7 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler tools crypto runtime_tools syntax_tools ftp inets \
+ debugger sasl snmp ssh reltool observer xmerl
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index ae06572752..3a37cac09c 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -27,10 +27,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
VSN=$(COMMON_TEST_VSN)
APPLICATION=common_test
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# ----------------------------------------------------
# Target Specs
@@ -39,7 +35,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
CT_XML_FILES = $(CT_MODULES:=.xml)
XML_APPLICATION_FILES = ref_man.xml
-XML_REF1_FILES = ct_run.xml
+XML_REF1_FILES = ct_run_cmd.xml
# REMEMBER: links to HTML files for these modules in ref_man.xml
XML_REF3_FILES = ct.xml \
ct_master.xml \
@@ -80,95 +76,18 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-GIF_FILES = \
+IMAGE_FILES = \
tc_execution.gif \
config.gif \
html_logs.gif
-INSTALL_NOTES = ../../notes.html
-
XML_FILES=$(XML_APPLICATION_FILES) $(XML_REF1_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES) $(BOOK_FILES)
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-SPECS_FLAGS = -I../../include -I../../../snmp/include \
- -I../../../kernel/include
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: man pdf html
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-man: $(MAN6_FILES) $(MAN3_FILES) $(MAN1_FILES)
+NO_CHUNKS = ct_hooks.xml
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-
-release_spec:
-release_tests_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index ea4b67be08..8d1c546fb1 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -506,9 +506,13 @@
If an event cannot be associated with a test case, it is printed in
the <c>Common Test</c> framework log.
This happens for test cases running in parallel and events occuring
- in-between test cases. You can configure the level of
- <seealso marker="sasl:sasl_app">SASL</seealso> reports
- using the normal SASL mechanisms.</p>
+ in-between test cases.</p>
+ <p>The log events are handled using a <seealso marker="kernel:logger">Logger</seealso>
+ handler called cth_log_redirect. The formatting and level is copied from
+ the current <c>default</c> handler when the cth is started. If you want to
+ use another level either change the <c>default</c> handler level before
+ starting common_test, or use the <seealso marker="kernel:logger#set_handler_config-3">
+ <c>logger:set_handler_config/3</c></seealso> API.</p>
</item>
<tag><c>cth_surefire</c></tag>
<item>
@@ -541,7 +545,3 @@ x86_64-unknown-linux-gnu.my_test.logs/run.2012-12-12_11.19.39/suite.log.html"</c
</section>
</chapter>
-
-
-
-
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run_cmd.xml
index 5b962ed5c7..3a9b8bbc03 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -226,4 +226,3 @@
</section>
</comref>
-
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index 1ac20db5c2..e916fc7cec 100644
--- a/lib/common_test/doc/src/ref_man.xml
+++ b/lib/common_test/doc/src/ref_man.xml
@@ -33,7 +33,7 @@
</description>
<xi:include href="common_test_app.xml"/>
- <xi:include href="ct_run.xml"/>
+ <xi:include href="ct_run_cmd.xml"/>
<xi:include href="ct.xml"/>
<xi:include href="ct_master.xml"/>
<xi:include href="ct_cover.xml"/>
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index a07e61199b..adb90c0869 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -24,6 +24,8 @@
%% Created : 15 February 2010
%%----------------------------------------------------------------------
-module(ct_config).
+-compile([{nowarn_deprecated_function,{crypto,block_decrypt,4}},
+ {nowarn_deprecated_function,{crypto,block_encrypt,4}}]).
-export([start/1, stop/0]).
@@ -600,7 +602,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) ->
0 -> Bin1;
N -> list_to_binary([Bin1,random_bytes(8-N)])
end,
- EncBin = crypto:block_encrypt(des3_cbc, CryptoKey, IVec, Bin2),
+ EncBin = crypto:crypto_one_time(des_ede3_cbc, CryptoKey, IVec, Bin2, true),
case file:write_file(EncryptFileName, EncBin) of
ok ->
io:format("~ts --(encrypt)--> ~ts~n",
@@ -634,7 +636,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) ->
{CryptoKey,IVec} = make_crypto_key(Key),
case file:read_file(EncryptFileName) of
{ok,Bin} ->
- DecBin = crypto:block_decrypt(des3_cbc, CryptoKey, IVec, Bin),
+ DecBin = crypto:crypto_one_time(des_ede3_cbc, CryptoKey, IVec, Bin, false),
case catch binary_to_term(DecBin) of
{'EXIT',_} ->
{error,bad_file};
@@ -706,7 +708,8 @@ get_crypt_key_from_file() ->
make_crypto_key(String) ->
<<K1:8/binary,K2:8/binary>> = First = erlang:md5(String),
<<K3:8/binary,IVec:8/binary>> = erlang:md5([First|lists:reverse(String)]),
- {[K1,K2,K3],IVec}.
+ Key = <<K1/binary,K2/binary,K3/binary>>,
+ {Key,IVec}.
random_bytes(N) ->
random_bytes_1(N, []).
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index 251a0a4896..b8bb890add 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -23,8 +23,8 @@
%%% API
%% Main functions
-export([init_per_suite/1,
- quickcheck/2
- ]).
+ init_tool/1,
+ quickcheck/2]).
%% Result presentation
-export([present_result/4, present_result/5,
@@ -50,34 +50,40 @@
%%% the property tests
%%%
init_per_suite(Config) ->
+ case init_tool(Config) of
+ {skip, _}=Skip ->
+ Skip;
+ Config1 ->
+ Path = property_tests_path("property_test", Config1),
+ case compile_tests(Path, Config1) of
+ error ->
+ {fail, "Property test compilation failed in "++Path};
+ {skip,Reason} ->
+ {skip,Reason};
+ up_to_date ->
+ add_code_pathz(Path),
+ [{property_dir, Path} | Config1]
+ end
+ end.
+
+init_tool(Config) ->
ToolsToCheck = proplists:get_value(prop_tools, Config, [eqc,proper,triq]),
case which_module_exists(ToolsToCheck) of
{ok,ToolModule} ->
case code:where_is_file(lists:concat([ToolModule,".beam"])) of
non_existing ->
- ct:log("Found ~p, but ~tp is not found",
+ ct:log("Found ~p, but ~tp~n is not found",
[ToolModule, lists:concat([ToolModule,".beam"])]),
{skip, "Strange Property testing tool installation"};
ToolPath ->
ct:pal("Found property tester ~p~n"
"at ~tp",
[ToolModule, ToolPath]),
- Path = property_tests_path("property_test", Config),
- case compile_tests(Path,ToolModule) of
- error ->
- {fail, "Property test compilation failed in "++Path};
- {skip,Reason} ->
- {skip,Reason};
- up_to_date ->
- add_code_pathz(Path),
- [{property_dir,Path},
- {property_test_tool,ToolModule} | Config]
- end
+ [{property_test_tool, ToolModule} | Config]
end;
-
- not_found ->
- ct:pal("No property tester found",[]),
- {skip, "No property testing tool found"}
+ not_found ->
+ ct:pal("No property tester found",[]),
+ {skip, "No property testing tool found"}
end.
%%%----------------------------------------------------------------
@@ -189,7 +195,8 @@ add_code_pathz(Dir) ->
ok
end.
-compile_tests(Path, ToolModule) ->
+compile_tests(Path, Config) ->
+ ToolModule = proplists:get_value(property_test_tool, Config),
MacroDefs = macro_def(ToolModule),
{ok,Cwd} = file:get_cwd(),
case file:set_cwd(Path) of
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index fe869a4373..9743611425 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -129,10 +129,16 @@ start_log_handler() ->
_Pid ->
ok
end,
+ {DefaultFormatter, DefaultLevel} =
+ case logger:get_handler_config(default) of
+ {ok, Default} ->
+ {maps:get(formatter, Default), maps:get(level, Default)};
+ _Else ->
+ {{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG},info}
+ end,
ok = logger:add_handler(?MODULE,?MODULE,
- #{level=>info,
- formatter=>{?DEFAULT_FORMATTER,
- ?DEFAULT_FORMAT_CONFIG}}).
+ #{level=>DefaultLevel,
+ formatter=>DefaultFormatter}).
init([]) ->
{ok, #eh_state{log_func = tc_log_async}}.
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index b87464f5e4..4c95fcbde3 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -32,6 +32,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
+-include_lib("kernel/src/logger_internal.hrl").
-define(eh, ct_test_support_eh).
@@ -60,6 +61,13 @@ end_per_suite(Config) ->
init_per_testcase(TestCase, Config) ->
ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(cth_log_formatter = TestCase, Config) ->
+ ct_test_support:ct_rpc(
+ {logger,set_handler_config,
+ [default, formatter,
+ {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}]}, Config),
+ ct_test_support:end_per_testcase(TestCase, Config);
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
@@ -92,7 +100,8 @@ all(suite) ->
fail_n_skip_with_minimal_cth, prio_cth, no_config,
no_init_suite_config, no_init_config, no_end_config,
failed_sequence, repeat_force_stop, config_clash,
- callbacks_on_skip, fallback, data_dir, cth_log
+ callbacks_on_skip, fallback, data_dir,
+ cth_log, cth_log_formatter, cth_log_unexpect
]
).
@@ -263,10 +272,79 @@ data_dir(Config) when is_list(Config) ->
cth_log(Config) when is_list(Config) ->
%% test that cth_log_redirect writes properly to
- %% unexpected I/O log
+ %% html I/O log
ct:timetrap({minutes,10}),
StartOpts = do_test(cth_log, "cth_log_SUITE.erl", [], Config),
Logdir = proplists:get_value(logdir, StartOpts),
+ TCLogs =
+ filelib:wildcard(
+ filename:join(Logdir,
+ "ct_run*/cth.tests*/run*/cth_log_suite.tc*.html")),
+ lists:foreach(
+ fun(TCLog) ->
+ {ok,Bin} = file:read_file(TCLog),
+ Ts = string:lexemes(binary_to_list(Bin),[$\n]),
+ Matches = lists:foldl(fun("=ERROR"++_, {E,I,N,L}) ->
+ {E+1,I,N,L};
+ ("=INFO"++_, {E,I,N,L}) ->
+ {E,I+1,N,L};
+ ("=NOTICE"++_, {E,I,N,L}) ->
+ {E,I,N+1,L};
+ ("Logger"++_, {E,I,N,L}) ->
+ {E,I,N,L+1};
+ (_, N) -> N
+ end, {0,0,0,0}, Ts),
+ ct:pal("~p ({Error,Info,Notice,Log}) matches in ~tp",
+ [Matches,TCLog]),
+ MatchList = tuple_to_list(Matches),
+ case [N || N <- MatchList, N<1] of
+ [] -> ok;
+ _ -> exit({missing_io,TCLog})
+ end
+ end, TCLogs),
+ ok.
+
+cth_log_formatter(Config) when is_list(Config) ->
+ %% test that cth_log_redirect writes properly to
+ %% html I/O log using the formatter
+ ct:timetrap({minutes,10}),
+ ct_test_support:ct_rpc({logger,set_handler_config,[default, formatter,
+ {logger_formatter,#{ template => [level,":",msg,"\n"] }}]},
+ Config),
+ StartOpts = do_test(cth_log_formatter, "cth_log_formatter_SUITE.erl", [], Config),
+ Logdir = proplists:get_value(logdir, StartOpts),
+ TCLogs =
+ filelib:wildcard(
+ filename:join(Logdir,
+ "ct_run*/cth.tests*/run*/cth_log_formatter_suite.tc*.html")),
+ lists:foreach(
+ fun(TCLog) ->
+ {ok,Bin} = file:read_file(TCLog),
+ Ts = string:lexemes(binary_to_list(Bin),[$\n]),
+ Matches = lists:foldl(fun("error:"++_, {E,N,L}) ->
+ {E+1,N,L};
+ ("notice:"++_, {E,N,L}) ->
+ {E,N+1,L};
+ ("Logger"++_, {E,N,L}) ->
+ {E,N,L+1};
+ (_, N) -> N
+ end, {0,0,0}, Ts),
+ ct:pal("~p ({Error,Notice,Log}) matches in ~tp",
+ [Matches,TCLog]),
+ MatchList = tuple_to_list(Matches),
+ case [N || N <- MatchList, N<1] of
+ [] -> ok;
+ _ -> exit({missing_io,TCLog})
+ end
+ end, TCLogs),
+ ok.
+
+cth_log_unexpect(Config) when is_list(Config) ->
+ %% test that cth_log_redirect writes properly to
+ %% unexpected I/O log
+ ct:timetrap({minutes,10}),
+ StartOpts = do_test(cth_log_unexpect, "cth_log_unexpect_SUITE.erl", [], Config),
+ Logdir = proplists:get_value(logdir, StartOpts),
UnexpIoLogs =
filelib:wildcard(
filename:join(Logdir,
@@ -275,11 +353,11 @@ cth_log(Config) when is_list(Config) ->
fun(UnexpIoLog) ->
{ok,Bin} = file:read_file(UnexpIoLog),
Ts = string:lexemes(binary_to_list(Bin),[$\n]),
- Matches = lists:foldl(fun([$=,$E,$R,$R,$O,$R|_], {E,I,L}) ->
+ Matches = lists:foldl(fun("=ERROR"++_, {E,I,L}) ->
{E+1,I,L};
- ([$=,$I,$N,$F,$O|_], {E,I,L}) ->
+ ("=INFO"++_, {E,I,L}) ->
{E,I+1,L};
- ([$L,$o,$g,$g,$e,$r|_], {E,I,L}) ->
+ ("Logger"++_, {E,I,L}) ->
{E,I,L+1};
(_, N) -> N
end, {0,0,0}, Ts),
@@ -1824,18 +1902,58 @@ test_events(cth_log) ->
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{cth_log_SUITE,init_per_suite}},
+ {?eh,tc_start,{ct_framework,{init_per_group,g1,
+ [{suite,cth_log_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g1,
+ [{suite,cth_log_SUITE}]},ok}},
+ {?eh,test_stats,{30,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,{end_per_group,g1,
+ [{suite,cth_log_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g1,
+ [{suite,cth_log_SUITE}]},ok}},
+
+ {?eh,tc_done,{cth_log_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(cth_log_formatter) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{cth_log_formatter_SUITE,init_per_suite}},
+
+ {?eh,tc_start,{ct_framework,{init_per_group,g1,
+ [{suite,cth_log_formatter_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g1,
+ [{suite,cth_log_formatter_SUITE}]},ok}},
+ {?eh,test_stats,{30,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,{end_per_group,g1,
+ [{suite,cth_log_formatter_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g1,
+ [{suite,cth_log_formatter_SUITE}]},ok}},
+
+ {?eh,tc_done,{cth_log_formatter_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(cth_log_unexpect) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{cth_log_unexpect_SUITE,init_per_suite}},
+
{parallel,
[{?eh,tc_start,{ct_framework,{init_per_group,g1,
- [{suite,cth_log_SUITE},parallel]}}},
+ [{suite,cth_log_unexpect_SUITE},parallel]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g1,
- [{suite,cth_log_SUITE},parallel]},ok}},
+ [{suite,cth_log_unexpect_SUITE},parallel]},ok}},
{?eh,test_stats,{30,0,{0,0}}},
{?eh,tc_start,{ct_framework,{end_per_group,g1,
- [{suite,cth_log_SUITE},parallel]}}},
+ [{suite,cth_log_unexpect_SUITE},parallel]}}},
{?eh,tc_done,{ct_framework,{end_per_group,g1,
- [{suite,cth_log_SUITE},parallel]},ok}}]},
+ [{suite,cth_log_unexpect_SUITE},parallel]},ok}}]},
- {?eh,tc_done,{cth_log_SUITE,end_per_suite,ok}},
+ {?eh,tc_done,{cth_log_unexpect_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
index eda190b682..0de234f77e 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_SUITE.erl
@@ -41,8 +41,8 @@ suite() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
application:start(sasl),
- Gen = spawn(fun() -> gen() end),
- [{gen,Gen}|Config].
+ do_log(?FUNCTION_NAME),
+ Config.
%%--------------------------------------------------------------------
%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
@@ -50,9 +50,7 @@ init_per_suite(Config) ->
%% @end
%%--------------------------------------------------------------------
end_per_suite(Config) ->
- Gen = proplists:get_value(gen, Config),
- exit(Gen, kill),
- ct:sleep(100),
+ do_log(?FUNCTION_NAME),
application:stop(sasl),
ok.
@@ -92,8 +90,7 @@ end_per_testcase(_TestCase, _Config) ->
%% @end
%%--------------------------------------------------------------------
groups() ->
- [{g1,[parallel,{repeat,10}],[tc1,tc2,tc3]},
- {g2,[{repeat,10}],[tc1,tc2,tc3]}].
+ [{g1,[{repeat,10}],[tc1,tc2,tc3]}].
%%--------------------------------------------------------------------
%% @spec all() -> GroupsAndTestCases | {skip,Reason}
@@ -104,26 +101,24 @@ groups() ->
%% @end
%%--------------------------------------------------------------------
all() ->
- [{group,g1},{group,g2}].
+ [{group,g1}].
tc1(_) ->
- ct:sleep(100),
+ do_log(?FUNCTION_NAME),
ok.
tc2(_) ->
- ct:sleep(100),
+ do_log(?FUNCTION_NAME),
ok.
tc3(_) ->
- ct:sleep(100),
+ do_log(?FUNCTION_NAME),
ok.
%%%-----------------------------------------------------------------
-gen() ->
- gen_loop(1).
-gen_loop(N) ->
- ct:log("Logger iteration: ~p", [N]),
- error_logger:error_report(N),
- error_logger:info_report(N),
- ct:sleep(150),
- gen_loop(N+1).
+do_log(What) ->
+ ct:log("Logger ~p", [What]),
+ error_logger:error_report(What),
+ error_logger:info_report(What),
+ logger:notice("~p",[What]),
+ timer:sleep(100).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_formatter_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_formatter_SUITE.erl
new file mode 100644
index 0000000000..26097f5eae
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_formatter_SUITE.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(cth_log_formatter_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ application:start(sasl),
+ do_log(?FUNCTION_NAME),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ do_log(?FUNCTION_NAME),
+ application:stop(sasl),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [{g1,[{repeat,10}],[tc1,tc2,tc3]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [{group,g1}].
+
+tc1(_) ->
+ do_log(?FUNCTION_NAME),
+ ok.
+tc2(_) ->
+ do_log(?FUNCTION_NAME),
+ ok.
+tc3(_) ->
+ do_log(?FUNCTION_NAME),
+ ok.
+
+%%%-----------------------------------------------------------------
+
+
+do_log(What) ->
+ ct:log("Logger ~p", [What]),
+ error_logger:error_report(What),
+ error_logger:info_report(What),
+ logger:notice("~p",[What]),
+ timer:sleep(100).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_unexpect_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_unexpect_SUITE.erl
new file mode 100644
index 0000000000..af482b8f27
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/cth_log_unexpect_SUITE.erl
@@ -0,0 +1,129 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(cth_log_unexpect_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ application:start(sasl),
+ Gen = spawn(fun() -> gen() end),
+ [{gen,Gen}|Config].
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ Gen = proplists:get_value(gen, Config),
+ exit(Gen, kill),
+ ct:sleep(100),
+ application:stop(sasl),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [{g1,[parallel,{repeat,10}],[tc1,tc2,tc3]},
+ {g2,[{repeat,10}],[tc1,tc2,tc3]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [{group,g1},{group,g2}].
+
+tc1(_) ->
+ ct:sleep(100),
+ ok.
+tc2(_) ->
+ ct:sleep(100),
+ ok.
+tc3(_) ->
+ ct:sleep(100),
+ ok.
+
+%%%-----------------------------------------------------------------
+
+gen() ->
+ gen_loop(1).
+
+gen_loop(N) ->
+ ct:log("Logger iteration: ~p", [N]),
+ error_logger:error_report(N),
+ error_logger:info_report(N),
+ ct:sleep(150),
+ gen_loop(N+1).
diff --git a/lib/common_test/test_server/conf_vars.in b/lib/common_test/test_server/conf_vars.in
index 7c55d7b9ed..2d1e78fc69 100644
--- a/lib/common_test/test_server/conf_vars.in
+++ b/lib/common_test/test_server/conf_vars.in
@@ -23,3 +23,4 @@ SSLEAY_ROOT:@SSLEAY_ROOT@
JAVAC:@JAVAC@
make_command:@make_command@
test_c_compiler:@test_c_compiler@
+WSL:@wsl@
diff --git a/lib/common_test/test_server/configure.in b/lib/common_test/test_server/configure.in
index a32d050081..cb9df0e717 100644
--- a/lib/common_test/test_server/configure.in
+++ b/lib/common_test/test_server/configure.in
@@ -371,7 +371,7 @@ HCC='$(CC)'
AC_SUBST(HCC)
#--------------------------------------------------------------------
-# ld is used for linking on vxworks
+# ld
#--------------------------------------------------------------------
LD='$(CC) $(CFLAGS)'
AC_SUBST(LD)
@@ -389,6 +389,12 @@ exe=''
AC_SUBST(exe)
#--------------------------------------------------------------------
+# wsl command (if on windows using wsl)
+#--------------------------------------------------------------------
+wsl=''
+AC_SUBST(wsl)
+
+#--------------------------------------------------------------------
# flags when linking for cross platform targets (yet 'tis useful with
# native builds)
#--------------------------------------------------------------------
diff --git a/lib/common_test/test_server/ts_autoconf_win32.erl b/lib/common_test/test_server/ts_autoconf_win32.erl
index 1179dfe0e5..33492a18fd 100644
--- a/lib/common_test/test_server/ts_autoconf_win32.erl
+++ b/lib/common_test/test_server/ts_autoconf_win32.erl
@@ -57,7 +57,9 @@ tests() ->
{"for C compiler", fun c_compiler/1},
{"for make program", fun make/1},
{"for location of SSL libraries", fun ssl/1},
- {"for location of Java compiler", fun javac/1}].
+ {"for location of Java compiler", fun javac/1},
+ {"if wsl is to used as shell", fun wsl/1}
+ ].
system_type(Vars) ->
Os = case os:type() of
@@ -168,7 +170,7 @@ visual_cxx(Vars) ->
{'DEFS', common_c_defs()},
{'SHLIB_SUFFIX', ".dll"},
{'ERTS_LIBS', ERTS_THR_LIB ++ LIBS},
- {'LIBS', DEFAULT_THR_LIB ++ DBG_LINK ++ LIBS},
+ {'LIBS', DBG_LINK ++ LIBS},
{obj,".obj"},
{exe, ".exe"},
{test_c_compiler, "{msc, undefined}"}
@@ -247,6 +249,14 @@ javac(Vars) ->
{Path, [{'JAVAC', "javac"} | Vars]}
end.
+wsl(Vars) ->
+ case os:getenv("WSLENV") of
+ false ->
+ {no, [{'wsl', ""} | Vars]};
+ _ ->
+ {"wsl.exe", [{'wsl', "wsl.exe"} | Vars]}
+ end.
+
is_debug_build() ->
case catch string:find(erlang:system_info(system_version), "debug") of
nomatch ->
diff --git a/lib/compiler/Makefile b/lib/compiler/Makefile
index b8b2f562a2..f18df11e9f 100644
--- a/lib/compiler/Makefile
+++ b/lib/compiler/Makefile
@@ -36,3 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto hipe
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index 2fb163b9e7..e1c445662c 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -28,94 +28,19 @@ VSN=$(COMPILER_VSN)
APPLICATION=compiler
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-COMPILER_DIR = $(ERL_TOP)/lib/compiler/src
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = compile.xml
+EDOC_REF3_FILES = cerl.xml cerl_trees.xml cerl_clauses.xml
XML_PART_FILES = internal.xml
-XML_CHAPTER_FILES = notes.xml
+XML_NOTES_FILES = notes.xml
BOOK_FILES = book.xml
-GIF_FILES =
-
XML_FILES = \
- $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(BOOK_FILES) $(XML_NOTES_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-XML_INTERNAL_FILES = \
- cerl.xml cerl_trees.xml cerl_clauses.xml
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%)
-
-# ----------------------------------------------------
-# 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)/%)
-
-$(XML_INTERNAL_FILES:%=$(XMLDIR)/%): $(COMPILER_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(COMPILER_VSN) -dir $(XMLDIR) $(COMPILER_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/compiler/scripts/.gitignore b/lib/compiler/scripts/.gitignore
index 4e4eba766d..444b0cea1e 100644
--- a/lib/compiler/scripts/.gitignore
+++ b/lib/compiler/scripts/.gitignore
@@ -1 +1,5 @@
-/smoke-build
+/smoke-build/*
+
+# The dependency lock file must be kept to ensure that the smoke
+# test won't be broken as time passes.
+!/smoke-build/mix.lock
diff --git a/lib/compiler/scripts/smoke-build/mix.lock b/lib/compiler/scripts/smoke-build/mix.lock
new file mode 100644
index 0000000000..9fb83798c4
--- /dev/null
+++ b/lib/compiler/scripts/smoke-build/mix.lock
@@ -0,0 +1,9 @@
+%{
+ "credentials_obfuscation": {:hex, :credentials_obfuscation, "1.1.0", "513793cc20c18afc9e03e584b436192a751a8344890e03a8741c65c8d6866fab", [:rebar3], [], "hexpm"},
+ "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"},
+ "jsx": {:hex, :jsx, "2.9.0", "d2f6e5f069c00266cad52fb15d87c428579ea4d7d73a33669e12679e203329dd", [:mix, :rebar3], [], "hexpm"},
+ "lager": {:hex, :lager, "3.8.0", "3402b9a7e473680ca179fc2f1d827cab88dd37dd1e6113090c6f45ef05228a1c", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
+ "rabbit_common": {:hex, :rabbit_common, "3.7.18", "4249efdf1fd96a81739ffad675582f980cc55aa0a02217e4907b4cd719c44822", [:make, :rebar3], [{:credentials_obfuscation, "1.1.0", [hex: :credentials_obfuscation, repo: "hexpm", optional: false]}, {:jsx, "2.9.0", [hex: :jsx, repo: "hexpm", optional: false]}, {:lager, "3.8.0", [hex: :lager, repo: "hexpm", optional: false]}, {:ranch, "1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}, {:recon, "2.5.0", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm"},
+ "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
+ "recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm"},
+}
diff --git a/lib/compiler/scripts/smoke-mix.exs b/lib/compiler/scripts/smoke-mix.exs
index ba0815e465..0bfb80b53c 100644
--- a/lib/compiler/scripts/smoke-mix.exs
+++ b/lib/compiler/scripts/smoke-mix.exs
@@ -45,6 +45,7 @@ defmodule Smoke.MixProject do
{:gpb, "~> 4.6"},
{:gproc, "~> 0.8.0"},
{:graphql, "~> 0.15.0", hex: :graphql_erl},
+ {:hut, "~> 1.3"},
{:hackney, "~> 1.15.0"},
{:ibrowse, "~> 4.4.1"},
{:jose, "~> 1.9.0"},
@@ -89,12 +90,13 @@ defmodule Smoke.MixProject do
defp build_wings do
# If the Erlang system is not installed, the build will
- # crash in plugins_src/accel when attempting to build
- # the accel driver. Since there is very little Erlang code in
- # the directory, skip the entire directory.
+ # crash in c_src or plugins_src/accel when attempting to
+ # build native code. Since there is very little Erlang
+ # code in these directories, skip them both.
"""
echo "all:\n\t" >plugins_src/accel/Makefile
+ echo "all:\n\t" >c_src/Makefile
git commit -a -m'Disable for smoke testing'
git tag -a -m'Smoke test' vsmoke_test
make
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 87b0d345f2..6ce354359c 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -49,16 +49,18 @@ MODULES = \
beam_a \
beam_asm \
beam_block \
+ beam_call_types \
beam_clean \
beam_dict \
+ beam_digraph \
beam_disasm \
- beam_except \
beam_flatten \
beam_jump \
beam_listing \
beam_opcodes \
beam_peep \
beam_ssa \
+ beam_ssa_bool \
beam_ssa_bsm \
beam_ssa_codegen \
beam_ssa_dead \
@@ -72,6 +74,7 @@ MODULES = \
beam_ssa_type \
beam_kernel_to_ssa \
beam_trim \
+ beam_types \
beam_utils \
beam_validator \
beam_z \
@@ -93,6 +96,7 @@ MODULES = \
sys_core_fold \
sys_core_fold_lists \
sys_core_inline \
+ sys_core_prepare \
sys_pre_attributes \
v3_core \
v3_kernel \
@@ -104,6 +108,7 @@ HRL_FILES= \
beam_disasm.hrl \
beam_ssa_opt.hrl \
beam_ssa.hrl \
+ beam_types.hrl \
core_parse.hrl \
v3_kernel.hrl
@@ -190,6 +195,7 @@ release_docs_spec:
# Dependencies -- alphabetically, please
# ----------------------------------------------------
+$(EBIN)/beam_call_types.beam: beam_types.hrl
$(EBIN)/beam_disasm.beam: $(EGEN)/beam_opcodes.hrl beam_disasm.hrl
$(EBIN)/beam_listing.beam: core_parse.hrl v3_kernel.hrl beam_ssa.hrl
$(EBIN)/beam_kernel_to_ssa.beam: v3_kernel.hrl beam_ssa.hrl
@@ -204,7 +210,9 @@ $(EBIN)/beam_ssa_pp.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_pre_codegen.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_recv.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_share.beam: beam_ssa.hrl
-$(EBIN)/beam_ssa_type.beam: beam_ssa.hrl
+$(EBIN)/beam_ssa_type.beam: beam_ssa.hrl beam_types.hrl
+$(EBIN)/beam_types.beam: beam_types.hrl
+$(EBIN)/beam_validator.beam: beam_types.hrl
$(EBIN)/cerl.beam: core_parse.hrl
$(EBIN)/compile.beam: core_parse.hrl ../../stdlib/include/erl_compile.hrl
$(EBIN)/core_lib.beam: core_parse.hrl
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index eadd858885..bd4be485bb 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -59,16 +59,6 @@ rename_instrs([{test,is_eq_exact,_,[Dst,Src]}=Test,
rename_instrs([{test,is_eq_exact,_,[Same,Same]}|Is]) ->
%% Same literal or same register. Will always succeed.
rename_instrs(Is);
-rename_instrs([{recv_set,_},
- {label,Lbl},
- {loop_rec,{f,Fail},{x,0}},
- {loop_rec_end,_},{label,Fail}|Is]) ->
- %% This instruction sequence does nothing. All we need to
- %% keep is the first label.
- [{label,Lbl}|rename_instrs(Is)];
-rename_instrs([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_},{label,Fail}|Is]) ->
- %% This instruction sequence does nothing.
- rename_instrs(Is);
rename_instrs([{apply_last,A,N}|Is]) ->
[{apply,A},{deallocate,N},return|rename_instrs(Is)];
rename_instrs([{call_last,A,F,N}|Is]) ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index df09dcb06c..60e19ec596 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -64,11 +64,30 @@ module(Code, ExtraChunks, CompileInfo, CompilerOpts) ->
assemble({Mod,Exp0,Attr0,Asm0,NumLabels}, ExtraChunks, CompileInfo, CompilerOpts) ->
{1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
{0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0),
+ Dict2 = shared_fun_wrappers(CompilerOpts, Dict1),
NumFuncs = length(Asm0),
{Asm,Attr} = on_load(Asm0, Attr0),
Exp = cerl_sets:from_list(Exp0),
- {Code,Dict2} = assemble_1(Asm, Exp, Dict1, []),
- build_file(Code, Attr, Dict2, NumLabels, NumFuncs, ExtraChunks, CompileInfo, CompilerOpts).
+ {Code,Dict} = assemble_1(Asm, Exp, Dict2, []),
+ build_file(Code, Attr, Dict, NumLabels, NumFuncs,
+ ExtraChunks, CompileInfo, CompilerOpts).
+
+shared_fun_wrappers(Opts, Dict) ->
+ case proplists:get_bool(no_shared_fun_wrappers, Opts) of
+ false ->
+ %% The compiler in OTP 23 depends on the on the loader
+ %% using the new indices in funs and being able to have
+ %% multiple make_fun2 instructions referring to the same
+ %% fun entry. Artificially set the highest opcode for the
+ %% module to ensure that it cannot be loaded in OTP 22
+ %% and earlier.
+ Swap = beam_opcodes:opcode(swap, 2),
+ beam_dict:opcode(Swap, Dict);
+ true ->
+ %% Fun wrappers are not shared for compatibility with a
+ %% previous OTP release.
+ Dict
+ end.
on_load(Fs0, Attr0) ->
case proplists:get_value(on_load, Attr0) of
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 707974b2c1..a734ca3a10 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -33,8 +33,9 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
function({function,Name,Arity,CLabel,Is0}) ->
try
- Is1 = blockify(Is0),
- Is = embed_lines(Is1),
+ Is1 = swap_opt(Is0),
+ Is2 = blockify(Is1),
+ Is = embed_lines(Is2),
{function,Name,Arity,CLabel,Is}
catch
Class:Error:Stack ->
@@ -42,6 +43,40 @@ function({function,Name,Arity,CLabel,Is0}) ->
erlang:raise(Class, Error, Stack)
end.
+%%%
+%%% Try to use a `swap` instruction instead of a sequence of moves.
+%%%
+%%% Note that beam_ssa_codegen generates `swap` instructions only for
+%%% the moves within a single SSA instruction (such as `call`), not
+%%% for the moves generated by a sequence of SSA instructions.
+%%% Therefore, this optimization is needed.
+%%%
+
+swap_opt([{move,Reg1,{x,X}=Temp}=Move1,
+ {move,Reg2,Reg1}=Move2,
+ {move,Temp,Reg2}=Move3|Is]) when Reg1 =/= Temp ->
+ case is_unused(X, Is) of
+ true ->
+ [{swap,Reg1,Reg2}|swap_opt(Is)];
+ false ->
+ [Move1|swap_opt([Move2,Move3|Is])]
+ end;
+swap_opt([I|Is]) ->
+ [I|swap_opt(Is)];
+swap_opt([]) -> [].
+
+is_unused(X, [{call,A,_}|_]) when A =< X -> true;
+is_unused(X, [{call_ext,A,_}|_]) when A =< X -> true;
+is_unused(X, [{make_fun2,_,_,_,A}|_]) when A =< X -> true;
+is_unused(X, [{move,Src,Dst}|Is]) ->
+ case {Src,Dst} of
+ {{x,X},_} -> false;
+ {_,{x,X}} -> true;
+ {_,_} -> is_unused(X, Is)
+ end;
+is_unused(X, [{line,_}|Is]) -> is_unused(X, Is);
+is_unused(_, _) -> false.
+
%% blockify(Instructions0) -> Instructions
%% Collect sequences of instructions to basic blocks.
%% Also do some simple optimations on instructions outside the blocks.
diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl
new file mode 100644
index 0000000000..290a31b8ba
--- /dev/null
+++ b/lib/compiler/src/beam_call_types.erl
@@ -0,0 +1,983 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_call_types).
+
+-include("beam_types.hrl").
+
+-import(lists, [duplicate/2,foldl/3]).
+
+-export([will_succeed/3, types/3]).
+
+%%
+%% Returns whether a call will succeed or not.
+%%
+%% Note that it only answers 'yes' for functions in the 'erlang' module as
+%% calls to other modules may fail due to not being loaded, even if we consider
+%% the module to be known.
+%%
+
+-spec will_succeed(Mod, Func, ArgTypes) -> Result when
+ Mod :: atom(),
+ Func :: atom(),
+ ArgTypes :: [normal_type()],
+ Result :: yes | no | maybe.
+
+will_succeed(erlang, '++', [LHS, _RHS]) ->
+ succeeds_if_type(LHS, proper_list());
+will_succeed(erlang, '--', [LHS, RHS]) ->
+ case {succeeds_if_type(LHS, proper_list()),
+ succeeds_if_type(RHS, proper_list())} of
+ {yes, yes} -> yes;
+ {no, _} -> no;
+ {_, no} -> no;
+ {_, _} -> maybe
+ end;
+will_succeed(erlang, BoolOp, [LHS, RHS]) when BoolOp =:= 'and';
+ BoolOp =:= 'or' ->
+ case {succeeds_if_type(LHS, beam_types:make_boolean()),
+ succeeds_if_type(RHS, beam_types:make_boolean())} of
+ {yes, yes} -> yes;
+ {no, _} -> no;
+ {_, no} -> no;
+ {_, _} -> maybe
+ end;
+will_succeed(erlang, bit_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, byte_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, hd, [Arg]) ->
+ succeeds_if_type(Arg, #t_cons{});
+will_succeed(erlang, is_map_key, [_Key, Map]) ->
+ succeeds_if_type(Map, #t_map{});
+will_succeed(erlang, length, [Arg]) ->
+ succeeds_if_type(Arg, proper_list());
+will_succeed(erlang, map_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_map{});
+will_succeed(erlang, 'not', [Arg]) ->
+ succeeds_if_type(Arg, beam_types:make_boolean());
+will_succeed(erlang, setelement, [#t_integer{elements={Min,Max}},
+ #t_tuple{exact=Exact,size=Size}, _]) ->
+ case Min >= 1 andalso Max =< Size of
+ true -> yes;
+ false when Exact -> no;
+ false -> maybe
+ end;
+will_succeed(erlang, size, [Arg]) ->
+ ArgType = beam_types:join(#t_tuple{}, #t_bitstring{}),
+ succeeds_if_type(Arg, ArgType);
+will_succeed(erlang, tuple_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_tuple{});
+will_succeed(erlang, tl, [Arg]) ->
+ succeeds_if_type(Arg, #t_cons{});
+will_succeed(Mod, Func, Args) ->
+ Arity = length(Args),
+ case erl_bifs:is_safe(Mod, Func, Arity) of
+ true ->
+ yes;
+ false ->
+ case erl_bifs:is_exit_bif(Mod, Func, Arity) of
+ true ->
+ no;
+ false ->
+ %% While we can't infer success for functions outside the
+ %% 'erlang' module (see above comment), it's safe to infer
+ %% failure when we know the arguments must have certain
+ %% types.
+ {_, ArgTypes, _} = types(Mod, Func, Args),
+ fails_on_conflict(Args, ArgTypes)
+ end
+ end.
+
+fails_on_conflict([ArgType | Args], [Required | Types]) ->
+ case beam_types:meet(ArgType, Required) of
+ none -> no;
+ _ -> fails_on_conflict(Args, Types)
+ end;
+fails_on_conflict([], []) ->
+ maybe.
+
+succeeds_if_type(ArgType, Required) ->
+ case beam_types:meet(ArgType, Required) of
+ ArgType -> yes;
+ none -> no;
+ _ -> maybe
+ end.
+
+%%
+%% Returns the inferred return and argument types for known functions, and
+%% whether it's safe to subtract argument types on failure.
+%%
+%% Note that the return type will be 'none' if we can statically determine that
+%% the function will fail at runtime.
+%%
+
+-spec types(Mod, Func, ArgTypes) -> {RetType, ArgTypes, CanSubtract} when
+ Mod :: atom(),
+ Func :: atom(),
+ ArgTypes :: [normal_type()],
+ RetType :: type(),
+ CanSubtract :: boolean().
+
+%% Functions that only fail due to bad argument *types*, meaning it's safe to
+%% subtract argument types on failure.
+%%
+%% Note that these are all from the erlang module; suitable functions in other
+%% modules could fail due to the module not being loaded.
+types(erlang, 'map_size', [_]) ->
+ sub_safe(#t_integer{}, [#t_map{}]);
+types(erlang, 'tuple_size', [_]) ->
+ sub_safe(#t_integer{}, [#t_tuple{}]);
+types(erlang, 'bit_size', [_]) ->
+ sub_safe(#t_integer{}, [#t_bitstring{}]);
+types(erlang, 'byte_size', [_]) ->
+ sub_safe(#t_integer{}, [#t_bitstring{}]);
+types(erlang, hd, [Src]) ->
+ RetType = erlang_hd_type(Src),
+ sub_safe(RetType, [#t_cons{}]);
+types(erlang, tl, [Src]) ->
+ RetType = erlang_tl_type(Src),
+ sub_safe(RetType, [#t_cons{}]);
+types(erlang, 'not', [_]) ->
+ Bool = beam_types:make_boolean(),
+ sub_safe(Bool, [Bool]);
+types(erlang, 'length', [_]) ->
+ sub_safe(#t_integer{}, [proper_list()]);
+
+%% Boolean ops
+types(erlang, 'and', [_,_]) ->
+ Bool = beam_types:make_boolean(),
+ sub_unsafe(Bool, [Bool, Bool]);
+types(erlang, 'or', [_,_]) ->
+ Bool = beam_types:make_boolean(),
+ sub_unsafe(Bool, [Bool, Bool]);
+types(erlang, 'xor', [_,_]) ->
+ Bool = beam_types:make_boolean(),
+ sub_unsafe(Bool, [Bool, Bool]);
+
+%% Bitwise ops
+types(erlang, 'band', [_,_]=Args) ->
+ sub_unsafe(erlang_band_type(Args), [#t_integer{}, #t_integer{}]);
+types(erlang, 'bor', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+types(erlang, 'bxor', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+types(erlang, 'bsl', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+types(erlang, 'bsr', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+types(erlang, 'bnot', [_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}]);
+
+%% Fixed-type arithmetic
+types(erlang, 'float', [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(erlang, 'round', [_]) ->
+ sub_unsafe(#t_integer{}, [number]);
+types(erlang, 'floor', [_]) ->
+ sub_unsafe(#t_integer{}, [number]);
+types(erlang, 'ceil', [_]) ->
+ sub_unsafe(#t_integer{}, [number]);
+types(erlang, 'trunc', [_]) ->
+ sub_unsafe(#t_integer{}, [number]);
+types(erlang, '/', [_,_]) ->
+ sub_unsafe(#t_float{}, [number, number]);
+types(erlang, 'div', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+types(erlang, 'rem', [_,_]) ->
+ sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]);
+
+%% Mixed-type arithmetic; '+'/2 and friends are handled in the catch-all
+%% clause for the 'erlang' module.
+types(erlang, 'abs', [_]=Args) ->
+ mixed_arith_types(Args);
+
+%% List operations
+types(erlang, '++', [LHS, RHS]) ->
+ %% `[] ++ RHS` yields RHS, even if RHS is not a list.
+ ListType = copy_list(LHS, same_length, proper),
+ RetType = beam_types:join(ListType, RHS),
+ sub_unsafe(RetType, [proper_list(), any]);
+types(erlang, '--', [LHS, _]) ->
+ RetType = copy_list(LHS, new_length, proper),
+ sub_unsafe(RetType, [proper_list(), proper_list()]);
+
+types(erlang, 'iolist_to_binary', [_]) ->
+ %% Arg is an iodata(), despite its name.
+ ArgType = beam_types:join(#t_list{}, #t_bitstring{size_unit=8}),
+ sub_unsafe(#t_bitstring{size_unit=8}, [ArgType]);
+types(erlang, 'list_to_binary', [_]) ->
+ %% Arg is an iolist(), despite its name.
+ sub_unsafe(#t_bitstring{size_unit=8}, [#t_list{}]);
+types(erlang, 'list_to_bitstring', [_]) ->
+ %% As list_to_binary but with bitstrings rather than binaries.
+ sub_unsafe(#t_bitstring{}, [proper_list()]);
+
+%% Misc ops.
+types(erlang, 'binary_part', [_, _]) ->
+ PosLen = make_two_tuple(#t_integer{}, #t_integer{}),
+ Binary = #t_bitstring{size_unit=8},
+ sub_unsafe(Binary, [Binary, PosLen]);
+types(erlang, 'binary_part', [_, _, _]) ->
+ Binary = #t_bitstring{size_unit=8},
+ sub_unsafe(Binary, [Binary, #t_integer{}, #t_integer{}]);
+types(erlang, 'is_map_key', [Key, Map]) ->
+ RetType = case erlang_map_get_type(Key, Map) of
+ none -> beam_types:make_atom(false);
+ _ -> beam_types:make_boolean()
+ end,
+ sub_unsafe(RetType, [any, #t_map{}]);
+types(erlang, 'map_get', [Key, Map]) ->
+ RetType = erlang_map_get_type(Key, Map),
+ sub_unsafe(RetType, [any, #t_map{}]);
+types(erlang, 'node', [_]) ->
+ sub_unsafe(#t_atom{}, [any]);
+types(erlang, 'node', []) ->
+ sub_unsafe(#t_atom{}, []);
+types(erlang, 'size', [_]) ->
+ ArgType = beam_types:join(#t_tuple{}, #t_bitstring{}),
+ sub_unsafe(#t_integer{}, [ArgType]);
+
+%% Tuple element ops
+types(erlang, element, [PosType, TupleType]) ->
+ Index = case PosType of
+ #t_integer{elements={Same,Same}} when is_integer(Same) ->
+ Same;
+ _ ->
+ 0
+ end,
+
+ RetType = case TupleType of
+ #t_tuple{size=Sz,elements=Es} when Index =< Sz,
+ Index >= 1 ->
+ beam_types:get_tuple_element(Index, Es);
+ _ ->
+ any
+ end,
+
+ sub_unsafe(RetType, [#t_integer{}, #t_tuple{size=Index}]);
+types(erlang, setelement, [PosType, TupleType, ArgType]) ->
+ RetType = case {PosType,TupleType} of
+ {#t_integer{elements={Index,Index}},
+ #t_tuple{elements=Es0,size=Size}=T} when Index >= 1 ->
+ %% This is an exact index, update the type of said
+ %% element or return 'none' if it's known to be out of
+ %% bounds.
+ Es = beam_types:set_tuple_element(Index, ArgType, Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{size=max(Index, Size),elements=Es};
+ true when Index =< Size ->
+ T#t_tuple{elements=Es};
+ true ->
+ none
+ end;
+ {#t_integer{elements={Min,Max}},
+ #t_tuple{elements=Es0,size=Size}=T} when Min >= 1 ->
+ %% We know this will land between Min and Max, so kill
+ %% the types for those indexes.
+ Es = discard_tuple_element_info(Min, Max, Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{elements=Es,size=max(Min, Size)};
+ true when Min =< Size ->
+ T#t_tuple{elements=Es,size=Size};
+ true ->
+ none
+ end;
+ {_,#t_tuple{}=T} ->
+ %% Position unknown, so we have to discard all element
+ %% information.
+ T#t_tuple{elements=#{}};
+ {#t_integer{elements={Min,_Max}},_} ->
+ #t_tuple{size=Min};
+ {_,_} ->
+ #t_tuple{}
+ end,
+ sub_unsafe(RetType, [#t_integer{}, #t_tuple{}, any]);
+
+types(erlang, make_fun, [_,_,Arity0]) ->
+ Type = case Arity0 of
+ #t_integer{elements={Arity,Arity}} when Arity >= 0 ->
+ #t_fun{arity=Arity};
+ _ ->
+ #t_fun{}
+ end,
+ sub_unsafe(Type, [#t_atom{}, #t_atom{}, #t_integer{}]);
+
+types(erlang, Name, Args) ->
+ Arity = length(Args),
+
+ case erl_bifs:is_exit_bif(erlang, Name, Arity) of
+ true ->
+ {none, Args, false};
+ false ->
+ case erl_internal:arith_op(Name, Arity) of
+ true ->
+ mixed_arith_types(Args);
+ false ->
+ IsTest =
+ erl_internal:new_type_test(Name, Arity) orelse
+ erl_internal:comp_op(Name, Arity),
+
+ RetType = case IsTest of
+ true -> beam_types:make_boolean();
+ false -> any
+ end,
+
+ sub_unsafe(RetType, duplicate(Arity, any))
+ end
+ end;
+
+%%
+%% Math BIFs
+%%
+
+types(math, cos, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, cosh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, sin, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, sinh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, tan, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, tanh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, acos, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, acosh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, asin, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, asinh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, atan, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, atanh, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, erf, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, erfc, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, exp, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, log, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, log2, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, log10, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, sqrt, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, atan2, [_,_]) ->
+ sub_unsafe(#t_float{}, [number, number]);
+types(math, pow, [_,_]) ->
+ sub_unsafe(#t_float{}, [number, number]);
+types(math, ceil, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, floor, [_]) ->
+ sub_unsafe(#t_float{}, [number]);
+types(math, fmod, [_,_]) ->
+ sub_unsafe(#t_float{}, [number, number]);
+types(math, pi, []) ->
+ sub_unsafe(#t_float{}, []);
+
+%%
+%% List functions
+%%
+%% These tend to have tricky edge cases around nil and proper lists, be very
+%% careful and try not to narrow the types needlessly. Keep in mind that they
+%% need to be safe regardless of how the function is implemented, so it's best
+%% not to say that a list is proper unless every element must be visited to
+%% succeed.
+%%
+
+%% Operator aliases.
+types(lists, append, [_,_]=Args) ->
+ types(erlang, '++', Args);
+types(lists, append, [_]) ->
+ %% This is implemented through folding the list over erlang:'++'/2, so it
+ %% can hypothetically return anything, but we can infer that its argument
+ %% is a proper list on success.
+ sub_unsafe(any, [proper_list()]);
+types(lists, subtract, [_,_]=Args) ->
+ types(erlang, '--', Args);
+
+%% Functions returning booleans.
+types(lists, all, [_,_]) ->
+ %% This can succeed on improper lists if the fun returns 'false' for an
+ %% element before reaching the end.
+ sub_unsafe(beam_types:make_boolean(), [#t_fun{arity=1}, #t_list{}]);
+types(lists, any, [_,_]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ sub_unsafe(beam_types:make_boolean(), [#t_fun{arity=1}, #t_list{}]);
+types(lists, keymember, [_,_,_]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ sub_unsafe(beam_types:make_boolean(), [any, #t_integer{}, #t_list{}]);
+types(lists, member, [_,_]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ sub_unsafe(beam_types:make_boolean(), [any, #t_list{}]);
+types(lists, prefix, [_,_]) ->
+ %% This function doesn't need to reach the end of either list to return
+ %% false, so we can succeed even when both are improper lists.
+ sub_unsafe(beam_types:make_boolean(), [#t_list{}, #t_list{}]);
+types(lists, suffix, [_,_]) ->
+ %% A different implementation could return true when the first list is nil,
+ %% so we can't tell if either is proper.
+ sub_unsafe(beam_types:make_boolean(), [#t_list{}, #t_list{}]);
+
+%% Simple folds
+types(lists, foldl, [Fun, Init, List]) ->
+ RetType = lists_fold_type(Fun, Init, List),
+ sub_unsafe(RetType, [#t_fun{arity=2}, any, proper_list()]);
+types(lists, foldr, [Fun, Init, List]) ->
+ RetType = lists_fold_type(Fun, Init, List),
+ sub_unsafe(RetType, [#t_fun{arity=2}, any, proper_list()]);
+
+%% Functions returning plain lists.
+types(lists, droplast, [List]) ->
+ RetType = copy_list(List, new_length, proper),
+ sub_unsafe(RetType, [proper_list()]);
+types(lists, dropwhile, [_Fun, List]) ->
+ %% If the element is found before the end of the list, we could return an
+ %% improper list.
+ RetType = copy_list(List, new_length, maybe_improper),
+ sub_unsafe(RetType, [#t_fun{arity=1}, #t_list{}]);
+types(lists, duplicate, [_Count, Element]) ->
+ sub_unsafe(proper_list(Element), [#t_integer{}, any]);
+types(lists, filter, [_Fun, List]) ->
+ RetType = copy_list(List, new_length, proper),
+ sub_unsafe(RetType, [#t_fun{arity=1}, proper_list()]);
+types(lists, flatten, [_]) ->
+ sub_unsafe(proper_list(), [proper_list()]);
+types(lists, map, [Fun, List]) ->
+ RetType = lists_map_type(Fun, List),
+ sub_unsafe(RetType, [#t_fun{arity=1}, proper_list()]);
+types(lists, reverse, [List]) ->
+ RetType = copy_list(List, same_length, proper),
+ sub_unsafe(RetType, [proper_list()]);
+types(lists, sort, [List]) ->
+ RetType = copy_list(List, same_length, proper),
+ sub_unsafe(RetType, [proper_list()]);
+types(lists, takewhile, [_Fun, List]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ RetType = copy_list(List, new_length, proper),
+ sub_unsafe(RetType, [#t_fun{arity=1}, #t_list{}]);
+types(lists, usort, [List]) ->
+ %% The result is not quite the same length, but a non-empty list will stay
+ %% non-empty.
+ RetType = copy_list(List, same_length, proper),
+ sub_unsafe(RetType, [proper_list()]);
+types(lists, zip, [_,_]=Lists) ->
+ {RetType, ArgType} = lists_zip_types(Lists),
+ sub_unsafe(RetType, [ArgType, ArgType]);
+types(lists, zipwith, [Fun | [_,_]=Lists]) ->
+ {RetType, ArgType} = lists_zipwith_types(Fun, Lists),
+ sub_unsafe(RetType, [#t_fun{arity=2}, ArgType, ArgType]);
+
+%% Functions with complex return values.
+types(lists, keyfind, [KeyType,PosType,_]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ TupleType = case PosType of
+ #t_integer{elements={Index,Index}} when is_integer(Index),
+ Index >= 1 ->
+ Es = beam_types:set_tuple_element(Index, KeyType, #{}),
+ #t_tuple{size=Index,elements=Es};
+ _ ->
+ #t_tuple{}
+ end,
+ RetType = beam_types:join(TupleType, beam_types:make_atom(false)),
+ sub_unsafe(RetType, [any, #t_integer{}, #t_list{}]);
+types(lists, MapFold, [Fun, Init, List])
+ when MapFold =:= mapfoldl; MapFold =:= mapfoldr ->
+ RetType = lists_mapfold_type(Fun, Init, List),
+ sub_unsafe(RetType, [#t_fun{arity=2}, any, proper_list()]);
+types(lists, partition, [_Fun, List]) ->
+ ListType = copy_list(List, new_length, proper),
+ RetType = make_two_tuple(ListType, ListType),
+ sub_unsafe(RetType, [#t_fun{arity=1}, proper_list()]);
+types(lists, search, [_,_]) ->
+ %% Doesn't imply that the argument is a proper list; see lists:all/2
+ TupleType = make_two_tuple(beam_types:make_atom(value), any),
+ RetType = beam_types:join(TupleType, beam_types:make_atom(false)),
+ sub_unsafe(RetType, [#t_fun{arity=1}, #t_list{}]);
+types(lists, splitwith, [_Fun, List]) ->
+ %% Only the elements in the left list are guaranteed to be visited, so both
+ %% the argument and the right list may be improper.
+ Left = copy_list(List, new_length, proper),
+ Right = copy_list(List, new_length, maybe_improper),
+ sub_unsafe(make_two_tuple(Left, Right), [#t_fun{arity=1}, #t_list{}]);
+types(lists, unzip, [List]) ->
+ RetType = lists_unzip_type(2, List),
+ sub_unsafe(RetType, [proper_list()]);
+
+%%
+%% Map functions
+%%
+
+types(maps, filter, [_Fun, Map]) ->
+ %% Conservatively assume that key/value types are unchanged.
+ RetType = case Map of
+ #t_map{}=T -> T;
+ _ -> #t_map{}
+ end,
+ sub_unsafe(RetType, [#t_fun{arity=2}, #t_map{}]);
+types(maps, find, [Key, Map]) ->
+ TupleType = case erlang_map_get_type(Key, Map) of
+ none ->
+ none;
+ ValueType ->
+ make_two_tuple(beam_types:make_atom(ok), ValueType)
+ end,
+ %% error | {ok, Value}
+ RetType = beam_types:join(beam_types:make_atom(error), TupleType),
+ sub_unsafe(RetType, [any, #t_map{}]);
+types(maps, fold, [Fun, Init, _Map]) ->
+ RetType = case Fun of
+ #t_fun{type=Type} ->
+ %% The map is potentially empty, so we have to assume it
+ %% can return the initial value.
+ beam_types:join(Type, Init);
+ _ ->
+ any
+ end,
+ sub_unsafe(RetType, [#t_fun{arity=3}, any, #t_map{}]);
+types(maps, from_list, [Pairs]) ->
+ PairType = erlang_hd_type(Pairs),
+ RetType = case beam_types:normalize(PairType) of
+ #t_tuple{elements=Es} ->
+ SKey = beam_types:get_tuple_element(1, Es),
+ SValue = beam_types:get_tuple_element(2, Es),
+ #t_map{super_key=SKey,super_value=SValue};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [proper_list()]);
+types(maps, get, [_Key, _Map]=Args) ->
+ types(erlang, map_get, Args);
+types(maps, get, [Key, Map, Default]) ->
+ RetType = case erlang_map_get_type(Key, Map) of
+ none -> Default;
+ ValueType -> beam_types:join(ValueType, Default)
+ end,
+ sub_unsafe(RetType, [any, #t_map{}, any]);
+types(maps, is_key, [_Key, _Map]=Args) ->
+ types(erlang, is_map_key, Args);
+types(maps, keys, [Map]) ->
+ RetType = case Map of
+ #t_map{super_key=none} -> nil;
+ #t_map{super_key=SKey} -> proper_list(SKey);
+ _ -> proper_list()
+ end,
+ sub_unsafe(RetType, [#t_map{}]);
+types(maps, map, [Fun, Map]) ->
+ RetType = case {Fun, Map} of
+ {#t_fun{type=FunRet}, #t_map{super_value=SValue0}} ->
+ SValue = beam_types:join(FunRet, SValue0),
+ Map#t_map{super_value=SValue};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [#t_fun{arity=2}, #t_map{}]);
+types(maps, merge, [A, B]) ->
+ RetType = case {A, B} of
+ {#t_map{super_key=SKeyA,super_value=SValueA},
+ #t_map{super_key=SKeyB,super_value=SValueB}} ->
+ SKey = beam_types:join(SKeyA, SKeyB),
+ SValue = beam_types:join(SValueA, SValueB),
+ #t_map{super_key=SKey,super_value=SValue};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [#t_map{}, #t_map{}]);
+types(maps, new, []) ->
+ RetType = #t_map{super_key=none,super_value=none},
+ sub_unsafe(RetType, []);
+types(maps, put, [Key, Value, Map]) ->
+ RetType = case Map of
+ #t_map{super_key=SKey0,super_value=SValue0} ->
+ SKey = beam_types:join(Key, SKey0),
+ SValue = beam_types:join(Value, SValue0),
+ #t_map{super_key=SKey,super_value=SValue};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [any, any, #t_map{}]);
+types(maps, remove, [Key, Map]) ->
+ RetType = maps_remove_type(Key, Map),
+ sub_unsafe(RetType, [any, #t_map{}]);
+types(maps, take, [Key, Map]) ->
+ TupleType = case erlang_map_get_type(Key, Map) of
+ none ->
+ none;
+ ValueType ->
+ MapType = beam_types:meet(Map, #t_map{}),
+ make_two_tuple(ValueType, MapType)
+ end,
+ %% error | {Value, Map}
+ RetType = beam_types:join(beam_types:make_atom(error), TupleType),
+ sub_unsafe(RetType, [any, #t_map{}]);
+types(maps, to_list, [Map]) ->
+ RetType = case Map of
+ #t_map{super_key=SKey,super_value=SValue} ->
+ proper_list(make_two_tuple(SKey, SValue));
+ _ ->
+ proper_list()
+ end,
+ sub_unsafe(RetType, [#t_map{}]);
+types(maps, update_with, [_Key, Fun, Map]) ->
+ RetType = case {Fun, Map} of
+ {#t_fun{type=FunRet}, #t_map{super_value=SValue0}} ->
+ SValue = beam_types:join(FunRet, SValue0),
+ Map#t_map{super_value=SValue};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [any, #t_fun{arity=1}, #t_map{}]);
+types(maps, values, [Map]) ->
+ RetType = case Map of
+ #t_map{super_value=none} -> nil;
+ #t_map{super_value=SValue} -> proper_list(SValue);
+ _ -> proper_list()
+ end,
+ sub_unsafe(RetType, [#t_map{}]);
+types(maps, with, [Keys, Map]) ->
+ RetType = case Map of
+ #t_map{super_key=SKey0} ->
+ %% Since we know that the Map will only contain the pairs
+ %% pointed out by Keys, we can restrict the types to
+ %% those in the list.
+ SKey = beam_types:meet(erlang_hd_type(Keys), SKey0),
+ Map#t_map{super_key=SKey};
+ _ ->
+ #t_map{}
+ end,
+ sub_unsafe(RetType, [proper_list(), #t_map{}]);
+types(maps, without, [Keys, Map]) ->
+ RetType = maps_remove_type(erlang_hd_type(Keys), Map),
+ sub_unsafe(RetType, [proper_list(), #t_map{}]);
+
+%% Catch-all clause for unknown functions.
+
+types(_, _, Args) ->
+ sub_unsafe(any, [any || _ <- Args]).
+
+%%
+%% Function-specific helpers.
+%%
+
+mixed_arith_types([FirstType | _]=Args0) ->
+ RetType = foldl(fun(#t_integer{}, #t_integer{}) -> #t_integer{};
+ (#t_integer{}, number) -> number;
+ (#t_integer{}, #t_float{}) -> #t_float{};
+ (#t_float{}, #t_integer{}) -> #t_float{};
+ (#t_float{}, number) -> #t_float{};
+ (#t_float{}, #t_float{}) -> #t_float{};
+ (number, #t_integer{}) -> number;
+ (number, #t_float{}) -> #t_float{};
+ (number, number) -> number;
+ (any, _) -> number;
+ (_, _) -> none
+ end, FirstType, Args0),
+ sub_unsafe(RetType, [number || _ <- Args0]).
+
+erlang_hd_type(Src) ->
+ case beam_types:meet(Src, #t_cons{}) of
+ #t_cons{type=Type} -> Type;
+ _ -> any
+ end.
+
+erlang_tl_type(Src) ->
+ case beam_types:meet(Src, #t_cons{}) of
+ #t_cons{terminator=Term}=Cons -> beam_types:join(Cons, Term);
+ _ -> any
+ end.
+
+erlang_band_type([#t_integer{elements={Int,Int}}, RHS]) when is_integer(Int) ->
+ erlang_band_type_1(RHS, Int);
+erlang_band_type([LHS, #t_integer{elements={Int,Int}}]) when is_integer(Int) ->
+ erlang_band_type_1(LHS, Int);
+erlang_band_type(_) ->
+ #t_integer{}.
+
+erlang_band_type_1(LHS, Int) ->
+ case LHS of
+ #t_integer{elements={Min0,Max0}} when Max0 - Min0 < 1 bsl 256 ->
+ {Intersection, Union} = range_masks(Min0, Max0),
+
+ Min = Intersection band Int,
+ Max = min(Max0, Union band Int),
+
+ #t_integer{elements={Min,Max}};
+ _ when Int >= 0 ->
+ %% The range is either unknown or too wide, conservatively assume
+ %% that the new range is 0 .. Int.
+ beam_types:meet(LHS, #t_integer{elements={0,Int}});
+ _ ->
+ %% We can't infer boundaries when LHS is not an integer or
+ %% the range is unknown and the other operand is a
+ %% negative number, as the latter sign-extends to infinity
+ %% and we can't express an inverted range at the moment
+ %% (cf. X band -8; either less than -7 or greater than 7).
+ beam_types:meet(LHS, #t_integer{})
+ end.
+
+erlang_map_get_type(Key, Map) ->
+ case Map of
+ #t_map{super_key=SKey,super_value=SValue} ->
+ case beam_types:meet(SKey, Key) of
+ none -> none;
+ _ -> SValue
+ end;
+ _ ->
+ any
+ end.
+
+lists_fold_type(_Fun, Init, nil) ->
+ Init;
+lists_fold_type(#t_fun{type=Type}, _Init, #t_cons{}) ->
+ %% The list is non-empty so it's safe to ignore Init.
+ Type;
+lists_fold_type(#t_fun{type=Type}, Init, #t_list{}) ->
+ %% The list is possibly empty so we have to assume it can return the
+ %% initial value, whose type can differ significantly from the fun's
+ %% return value.
+ beam_types:join(Type, Init);
+lists_fold_type(_Fun, _Init, _List) ->
+ any.
+
+lists_map_type(#t_fun{type=Type}, Types) ->
+ lists_map_type_1(Types, Type);
+lists_map_type(_Fun, Types) ->
+ lists_map_type_1(Types, any).
+
+lists_map_type_1(nil, _ElementType) ->
+ nil;
+lists_map_type_1(#t_cons{}, none) ->
+ %% The list is non-empty and the fun never returns.
+ none;
+lists_map_type_1(#t_cons{}, ElementType) ->
+ proper_cons(ElementType);
+lists_map_type_1(_, none) ->
+ %% The fun never returns, so the only way we could return normally is
+ %% if the list is empty.
+ nil;
+lists_map_type_1(_, ElementType) ->
+ proper_list(ElementType).
+
+lists_mapfold_type(#t_fun{type=#t_tuple{size=2,elements=Es}}, Init, List) ->
+ ElementType = beam_types:get_tuple_element(1, Es),
+ AccType = beam_types:get_tuple_element(2, Es),
+ lists_mapfold_type_1(List, ElementType, Init, AccType);
+lists_mapfold_type(#t_fun{type=none}, _Init, #t_cons{}) ->
+ %% The list is non-empty and the fun never returns.
+ none;
+lists_mapfold_type(#t_fun{type=none}, Init, _List) ->
+ %% The fun never returns, so the only way we could return normally is
+ %% if the list is empty, in which case we'll return [] and the initial
+ %% value.
+ make_two_tuple(nil, Init);
+lists_mapfold_type(_Fun, Init, List) ->
+ lists_mapfold_type_1(List, any, Init, any).
+
+lists_mapfold_type_1(nil, _ElementType, Init, _AccType) ->
+ make_two_tuple(nil, Init);
+lists_mapfold_type_1(#t_cons{}, ElementType, _Init, AccType) ->
+ %% The list has at least one element, so it's safe to ignore Init.
+ make_two_tuple(proper_cons(ElementType), AccType);
+lists_mapfold_type_1(_, ElementType, Init, AccType0) ->
+ %% We can only rely on AccType when we know the list is non-empty, so we
+ %% have to join it with the initial value in case the list is empty.
+ AccType = beam_types:join(AccType0, Init),
+ make_two_tuple(proper_list(ElementType), AccType).
+
+lists_unzip_type(Size, List) ->
+ Es = lut_make_elements(lut_list_types(Size, List), 1, #{}),
+ #t_tuple{size=Size,exact=true,elements=Es}.
+
+lut_make_elements([Type | Types], Index, Es0) ->
+ Es = beam_types:set_tuple_element(Index, Type, Es0),
+ lut_make_elements(Types, Index + 1, Es);
+lut_make_elements([], _Index, Es) ->
+ Es.
+
+lut_list_types(Size, #t_cons{type=#t_tuple{size=Size,elements=Es}}) ->
+ Types = lut_element_types(1, Size, Es),
+ [proper_cons(T) || T <- Types];
+lut_list_types(Size, #t_list{type=#t_tuple{size=Size,elements=Es}}) ->
+ Types = lut_element_types(1, Size, Es),
+ [proper_list(T) || T <- Types];
+lut_list_types(Size, nil) ->
+ lists:duplicate(Size, nil);
+lut_list_types(Size, _) ->
+ lists:duplicate(Size, proper_list()).
+
+lut_element_types(Index, Max, #{}) when Index > Max ->
+ [];
+lut_element_types(Index, Max, Es) ->
+ ElementType = beam_types:get_tuple_element(Index, Es),
+ [ElementType | lut_element_types(Index + 1, Max, Es)].
+
+%% lists:zip/2 and friends only succeed when all arguments have the same
+%% length, so if one of them is #t_cons{}, we can infer that all of them are
+%% #t_cons{} on success.
+
+lists_zip_types(Types) ->
+ lists_zip_types_1(Types, false, #{}, 1).
+
+lists_zip_types_1([nil | _], _AnyCons, _Es, _N) ->
+ %% Early exit; we know the result is [] on success.
+ {nil, nil};
+lists_zip_types_1([#t_cons{type=Type,terminator=nil} | Lists],
+ _AnyCons, Es0, N) ->
+ Es = beam_types:set_tuple_element(N, Type, Es0),
+ lists_zip_types_1(Lists, true, Es, N + 1);
+lists_zip_types_1([#t_list{type=Type,terminator=nil} | Lists],
+ AnyCons, Es0, N) ->
+ Es = beam_types:set_tuple_element(N, Type, Es0),
+ lists_zip_types_1(Lists, AnyCons, Es, N + 1);
+lists_zip_types_1([_ | Lists], AnyCons, Es, N) ->
+ lists_zip_types_1(Lists, AnyCons, Es, N + 1);
+lists_zip_types_1([], true, Es, N) ->
+ %% At least one element was cons, so we know it's non-empty on success.
+ ElementType = #t_tuple{exact=true,size=(N - 1),elements=Es},
+ RetType = proper_cons(ElementType),
+ ArgType = proper_cons(),
+ {RetType, ArgType};
+lists_zip_types_1([], false, Es, N) ->
+ ElementType = #t_tuple{exact=true,size=(N - 1),elements=Es},
+ RetType = proper_list(ElementType),
+ ArgType = proper_list(),
+ {RetType, ArgType}.
+
+lists_zipwith_types(#t_fun{type=Type}, Types) ->
+ lists_zipwith_type_1(Types, Type);
+lists_zipwith_types(_Fun, Types) ->
+ lists_zipwith_type_1(Types, any).
+
+lists_zipwith_type_1([nil | _], _ElementType) ->
+ %% Early exit; we know the result is [] on success.
+ {nil, nil};
+lists_zipwith_type_1([#t_cons{} | _Lists], none) ->
+ %% Early exit; the list is non-empty and we know the fun never
+ %% returns.
+ {none, any};
+lists_zipwith_type_1([#t_cons{} | _Lists], ElementType) ->
+ %% Early exit; we know the result is cons on success.
+ RetType = proper_cons(ElementType),
+ ArgType = proper_cons(),
+ {RetType, ArgType};
+lists_zipwith_type_1([_ | Lists], ElementType) ->
+ lists_zipwith_type_1(Lists, ElementType);
+lists_zipwith_type_1([], none) ->
+ %% Since we know the fun won't return, the only way we could return
+ %% normally is if all lists are empty.
+ {nil, nil};
+lists_zipwith_type_1([], ElementType) ->
+ RetType = proper_list(ElementType),
+ ArgType = proper_list(),
+ {RetType, ArgType}.
+
+maps_remove_type(Key, #t_map{super_key=SKey0}=Map) ->
+ case beam_types:is_singleton_type(Key) of
+ true ->
+ SKey = beam_types:subtract(SKey0, Key),
+ Map#t_map{super_key=SKey};
+ false ->
+ Map
+ end;
+maps_remove_type(_Key, _Map) ->
+ #t_map{}.
+
+%%%
+%%% Generic helpers
+%%%
+
+sub_unsafe(RetType, ArgTypes) ->
+ {RetType, ArgTypes, false}.
+
+sub_safe(RetType, ArgTypes) ->
+ {RetType, ArgTypes, true}.
+
+discard_tuple_element_info(Min, Max, Es) ->
+ foldl(fun(El, Acc) when Min =< El, El =< Max ->
+ maps:remove(El, Acc);
+ (_El, Acc) -> Acc
+ end, Es, maps:keys(Es)).
+
+%% Returns two bitmasks describing all possible values between From and To.
+%%
+%% The first contains the bits that are common to all values, and the second
+%% contains the bits that are set by any value in the range.
+range_masks(From, To) when From =< To ->
+ range_masks_1(From, To, 0, -1, 0).
+
+range_masks_1(From, To, BitPos, Intersection, Union) when From < To ->
+ range_masks_1(From + (1 bsl BitPos), To, BitPos + 1,
+ Intersection band From, Union bor From);
+range_masks_1(_From, To, _BitPos, Intersection0, Union0) ->
+ Intersection = To band Intersection0,
+ Union = To bor Union0,
+ {Intersection, Union}.
+
+proper_cons() ->
+ #t_cons{terminator=nil}.
+
+proper_cons(ElementType) ->
+ #t_cons{type=ElementType,terminator=nil}.
+
+proper_list() ->
+ #t_list{terminator=nil}.
+
+proper_list(ElementType) ->
+ #t_list{type=ElementType,terminator=nil}.
+
+%% Constructs a new list type based on another, optionally keeping the same
+%% length and/or making it proper.
+-spec copy_list(List, Length, Proper) -> type() when
+ List :: type(),
+ Length :: same_length | new_length,
+ Proper :: proper | maybe_improper.
+copy_list(#t_cons{terminator=Term}=T, Length, maybe_improper) ->
+ copy_list_1(T, Length, Term);
+copy_list(#t_list{terminator=Term}=T, Length, maybe_improper) ->
+ copy_list_1(T, Length, Term);
+copy_list(T, Length, proper) ->
+ copy_list_1(T, Length, nil);
+copy_list(T, Length, _Proper) ->
+ copy_list_1(T, Length, any).
+
+copy_list_1(#t_cons{}=T, same_length, Terminator) ->
+ T#t_cons{terminator=Terminator};
+copy_list_1(#t_cons{type=Type}, new_length, Terminator) ->
+ #t_list{type=Type,terminator=Terminator};
+copy_list_1(#t_list{}=T, _Length, Terminator) ->
+ T#t_list{terminator=Terminator};
+copy_list_1(nil, _Length, _Terminator) ->
+ nil;
+copy_list_1(_, _Length, Terminator) ->
+ #t_list{terminator=Terminator}.
+
+make_two_tuple(Type1, Type2) ->
+ Es0 = beam_types:set_tuple_element(1, Type1, #{}),
+ Es = beam_types:set_tuple_element(2, Type2, Es0),
+ #t_tuple{size=2,exact=true,elements=Es}.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 7299654476..6b2b2ce085 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -34,7 +34,8 @@ module({Mod,Exp,Attr,Fs0,_}, Opts) ->
Used = find_all_used(WorkList, All, cerl_sets:from_list(WorkList)),
Fs1 = remove_unused(Order, Used, All),
{Fs2,Lc} = clean_labels(Fs1),
- Fs = maybe_remove_lines(Fs2, Opts),
+ Fs3 = fix_swap(Fs2, Opts),
+ Fs = maybe_remove_lines(Fs3, Opts),
{ok,{Mod,Exp,Attr,Fs,Lc}}.
%% Determine the rootset, i.e. exported functions and
@@ -137,31 +138,54 @@ function_replace([{function,Name,Arity,Entry,Asm0}|Fs], Dict, Acc) ->
function_replace([], _, Acc) -> Acc.
%%%
+%%% If compatibility with a previous release (OTP 22 or earlier) has
+%%% been requested, replace swap instructions with a sequence of moves.
+%%%
+
+fix_swap(Fs, Opts) ->
+ case proplists:get_bool(no_swap, Opts) of
+ false -> Fs;
+ true -> fold_functions(fun swap_moves/1, Fs)
+ end.
+
+swap_moves([{swap,Reg1,Reg2}|Is]) ->
+ Temp = {x,1022},
+ [{move,Reg1,Temp},{move,Reg2,Reg1},{move,Temp,Reg2}|swap_moves(Is)];
+swap_moves([I|Is]) ->
+ [I|swap_moves(Is)];
+swap_moves([]) -> [].
+
+%%%
%%% Remove line instructions if requested.
%%%
maybe_remove_lines(Fs, Opts) ->
case proplists:get_bool(no_line_info, Opts) of
false -> Fs;
- true -> remove_lines(Fs)
+ true -> fold_functions(fun remove_lines/1, Fs)
end.
-remove_lines([{function,N,A,Lbl,Is0}|T]) ->
- Is = remove_lines_fun(Is0),
- [{function,N,A,Lbl,Is}|remove_lines(T)];
-remove_lines([]) -> [].
-
-remove_lines_fun([{line,_}|Is]) ->
- remove_lines_fun(Is);
-remove_lines_fun([{block,Bl0}|Is]) ->
+remove_lines([{line,_}|Is]) ->
+ remove_lines(Is);
+remove_lines([{block,Bl0}|Is]) ->
Bl = remove_lines_block(Bl0),
- [{block,Bl}|remove_lines_fun(Is)];
-remove_lines_fun([I|Is]) ->
- [I|remove_lines_fun(Is)];
-remove_lines_fun([]) -> [].
+ [{block,Bl}|remove_lines(Is)];
+remove_lines([I|Is]) ->
+ [I|remove_lines(Is)];
+remove_lines([]) -> [].
remove_lines_block([{set,_,_,{line,_}}|Is]) ->
remove_lines_block(Is);
remove_lines_block([I|Is]) ->
[I|remove_lines_block(Is)];
remove_lines_block([]) -> [].
+
+
+%%%
+%%% Helpers.
+%%%
+
+fold_functions(F, [{function,N,A,Lbl,Is0}|T]) ->
+ Is = F(Is0),
+ [{function,N,A,Lbl,Is}|fold_functions(F, T)];
+fold_functions(_F, []) -> [].
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index b2056332e6..b6f8c5a6e7 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -36,10 +36,11 @@
-type import_tab() :: gb_trees:tree(mfa(), index()).
-type fname_tab() :: #{Name :: term() => index()}.
-type line_tab() :: #{{Fname :: index(), Line :: term()} => index()}.
--type literal_tab() :: dict:dict(Literal :: term(), index()).
+-type literal_tab() :: #{Literal :: term() => index()}.
-type lambda_info() :: {label(),{index(),label(),non_neg_integer()}}.
-type lambda_tab() :: {non_neg_integer(),[lambda_info()]}.
+-type wrapper() :: #{label() => index()}.
-record(asm,
{atoms = #{} :: atom_tab(),
@@ -48,7 +49,8 @@
imports = gb_trees:empty() :: import_tab(),
strings = <<>> :: binary(), %String pool
lambdas = {0,[]} :: lambda_tab(),
- literals = dict:new() :: literal_tab(),
+ wrappers = #{} :: wrapper(),
+ literals = #{} :: literal_tab(),
fnames = #{} :: fname_tab(),
lines = #{} :: line_tab(),
num_lines = 0 :: non_neg_integer(), %Number of line instructions
@@ -147,22 +149,32 @@ string(BinString, Dict) when is_binary(BinString) ->
-spec lambda(label(), non_neg_integer(), bdict()) ->
{non_neg_integer(), bdict()}.
-lambda(Lbl, NumFree, #asm{lambdas={OldIndex,Lambdas0}}=Dict) ->
- %% Set Index the same as OldIndex.
- Index = OldIndex,
- Lambdas = [{Lbl,{Index,Lbl,NumFree}}|Lambdas0],
- {OldIndex,Dict#asm{lambdas={OldIndex+1,Lambdas}}}.
+lambda(Lbl, NumFree, #asm{wrappers=Wrappers0,
+ lambdas={OldIndex,Lambdas0}}=Dict) ->
+ case Wrappers0 of
+ #{Lbl:=Index} ->
+ %% OTP 23: There old is a fun entry for this wrapper function.
+ %% Share the fun entry.
+ {Index,Dict};
+ #{} ->
+ %% Set Index the same as OldIndex.
+ Index = OldIndex,
+ Wrappers = Wrappers0#{Lbl=>Index},
+ Lambdas = [{Lbl,{Index,Lbl,NumFree}}|Lambdas0],
+ {OldIndex,Dict#asm{wrappers=Wrappers,
+ lambdas={OldIndex+1,Lambdas}}}
+ end.
%% Returns the index for a literal (adding it to the literal table if necessary).
%% literal(Literal, Dict) -> {Index,Dict'}
-spec literal(term(), bdict()) -> {non_neg_integer(), bdict()}.
literal(Lit, #asm{literals=Tab0,next_literal=NextIndex}=Dict) ->
- case dict:find(Lit, Tab0) of
- {ok,Index} ->
+ case Tab0 of
+ #{Lit:=Index} ->
{Index,Dict};
- error ->
- Tab = dict:store(Lit, NextIndex, Tab0),
+ #{} ->
+ Tab = Tab0#{Lit=>NextIndex},
{NextIndex,Dict#asm{literals=Tab,next_literal=NextIndex+1}}
end.
@@ -253,7 +265,7 @@ lambda_table(#asm{locals=Loc0,lambdas={NumLambdas,Lambdas0}}) ->
-spec literal_table(bdict()) -> {non_neg_integer(), [[binary(),...]]}.
literal_table(#asm{literals=Tab,next_literal=NumLiterals}) ->
- L0 = dict:fold(fun(Lit, Num, Acc) ->
+ L0 = maps:fold(fun(Lit, Num, Acc) ->
[{Num,my_term_to_binary(Lit)}|Acc]
end, [], Tab),
L1 = lists:sort(L0),
@@ -261,7 +273,12 @@ literal_table(#asm{literals=Tab,next_literal=NumLiterals}) ->
{NumLiterals,L}.
my_term_to_binary(Term) ->
- term_to_binary(Term, [{minor_version,1}]).
+ %% Use the latest possible minor version. Minor version 2 can be
+ %% be decoded by OTP 16, which is as far back as we have compatibility
+ %% options for the compiler. (When this comment was written, some time
+ %% after the release of OTP 22, the default minor version was 1.)
+
+ term_to_binary(Term, [{minor_version,2}]).
%% Return the line table.
-spec line_table(bdict()) ->
diff --git a/lib/compiler/src/beam_digraph.erl b/lib/compiler/src/beam_digraph.erl
new file mode 100644
index 0000000000..800fcf4c22
--- /dev/null
+++ b/lib/compiler/src/beam_digraph.erl
@@ -0,0 +1,308 @@
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Digraph data type. Similar to the digraph module, but provides a
+%% functional API. The functional API allows us to revert to a
+%% previous version of the digraph when an optimization that may have
+%% damaged the digraph has failed.
+%%
+
+-module(beam_digraph).
+
+-export([new/0,
+ add_vertex/2, add_vertex/3, add_edge/3, add_edge/4,
+ del_edge/2, del_edges/2,
+ has_vertex/2,
+ is_path/3,
+ in_degree/2, in_edges/2, in_neighbours/2,
+ out_degree/2, out_edges/2, out_neighbours/2,
+ vertex/2, vertices/1,
+ reverse_postorder/2,
+ roots/1,
+ topsort/1,
+ strong_components/2]).
+
+%% Debugging.
+-define(DEBUG, false).
+-if(?DEBUG).
+-export([dump/1,dump/2,dump/3]).
+-endif.
+
+-import(lists, [foldl/3, reverse/1]).
+
+-type edge_map() :: #{ vertex() => ordsets:ordset(vertex()) }.
+-type vertice_map() :: #{ vertex() => label() }.
+
+-record(dg, {vs = #{} :: vertice_map(),
+ in_es = #{} :: edge_map(),
+ out_es = #{} :: edge_map()}).
+
+-type graph() :: #dg{}.
+
+-type vertex() :: term().
+-type label() :: term().
+-type edge() :: {vertex(), vertex(), label()}.
+
+-spec new() -> graph().
+new() -> #dg{}.
+
+-spec add_vertex(graph(), vertex()) -> graph().
+add_vertex(Dg, V) ->
+ add_vertex(Dg, V, vertex).
+
+-spec add_vertex(graph(), vertex(), label()) -> graph().
+add_vertex(Dg, V, Label) ->
+ #dg{in_es=InEsMap0,out_es=OutEsMap0,vs=Vs0} = Dg,
+ InEsMap = init_edge_map(V, InEsMap0),
+ OutEsMap = init_edge_map(V, OutEsMap0),
+ Vs = Vs0#{V=>Label},
+ Dg#dg{vs=Vs,in_es=InEsMap,out_es=OutEsMap}.
+
+init_edge_map(V, EsMap) ->
+ case is_map_key(V, EsMap) of
+ true ->
+ EsMap;
+ false ->
+ EsMap#{V=>ordsets:new()}
+ end.
+
+-spec add_edge(graph(), vertex(), vertex()) -> graph().
+add_edge(Dg, From, To) ->
+ add_edge(Dg, From, To, edge).
+
+-spec add_edge(graph(), vertex(), vertex(), label()) -> graph().
+add_edge(Dg, From, To, Label) ->
+ #dg{in_es=InEsMap0,out_es=OutEsMap0} = Dg,
+ Name = {From,To,Label},
+ InEsMap = edge_map_add(To, Name, InEsMap0),
+ OutEsMap = edge_map_add(From, Name, OutEsMap0),
+ Dg#dg{in_es=InEsMap,out_es=OutEsMap}.
+
+edge_map_add(V, E, EsMap) ->
+ Es0 = map_get(V, EsMap),
+ Es = ordsets:add_element(E, Es0),
+ EsMap#{V:=Es}.
+
+-spec del_edge(graph(), edge()) -> graph().
+del_edge(Dg, {From,To,_}=E) ->
+ #dg{in_es=InEsMap0,out_es=OutEsMap0} = Dg,
+ InEsMap = edge_map_del(To, E, InEsMap0),
+ OutEsMap = edge_map_del(From, E, OutEsMap0),
+ Dg#dg{in_es=InEsMap,out_es=OutEsMap}.
+
+edge_map_del(V, E, EsMap) ->
+ Es0 = map_get(V, EsMap),
+ Es = Es0 -- [E],
+ EsMap#{V:=Es}.
+
+-spec del_edges(graph(), [edge()]) -> graph().
+del_edges(G, Es) when is_list(Es) ->
+ foldl(fun(E, A) -> del_edge(A, E) end, G, Es).
+
+-spec has_vertex(graph(), vertex()) -> boolean().
+has_vertex(#dg{vs=Vs}, V) ->
+ is_map_key(V, Vs).
+
+-spec in_degree(graph(), vertex()) -> non_neg_integer().
+in_degree(#dg{in_es=InEsMap}, V) ->
+ length(map_get(V, InEsMap)).
+
+-spec in_edges(graph(), vertex()) -> [edge()].
+in_edges(#dg{in_es=InEsMap}, V) ->
+ map_get(V, InEsMap).
+
+-spec in_neighbours(graph(), vertex()) -> [vertex()].
+in_neighbours(#dg{in_es=InEsMap}, V) ->
+ [From || {From,_,_} <- map_get(V, InEsMap)].
+
+-spec is_path(graph(), vertex(), vertex()) -> boolean().
+is_path(G, From, To) ->
+ Seen = cerl_sets:new(),
+ try
+ _ = is_path_1([From], To, G, Seen),
+ false
+ catch
+ throw:true ->
+ true
+ end.
+
+is_path_1([To|_], To, _G, _Seen) ->
+ throw(true);
+is_path_1([V|Vs], To, G, Seen0) ->
+ case cerl_sets:is_element(V, Seen0) of
+ true ->
+ is_path_1(Vs, To, G, Seen0);
+ false ->
+ Seen1 = cerl_sets:add_element(V, Seen0),
+ Successors = out_neighbours(G, V),
+ Seen = is_path_1(Successors, To, G, Seen1),
+ is_path_1(Vs, To, G, Seen)
+ end;
+is_path_1([], _To, _G, Seen) ->
+ Seen.
+
+-spec out_degree(graph(), vertex()) -> non_neg_integer().
+out_degree(#dg{out_es=OutEsMap}, V) ->
+ length(map_get(V, OutEsMap)).
+
+-spec out_edges(graph(), vertex()) -> [edge()].
+out_edges(#dg{out_es=OutEsMap}, V) ->
+ map_get(V, OutEsMap).
+
+-spec out_neighbours(graph(), vertex()) -> [vertex()].
+out_neighbours(#dg{out_es=OutEsMap}, V) ->
+ [To || {_,To,_} <- map_get(V, OutEsMap)].
+
+-spec vertex(graph(), vertex()) -> label().
+vertex(#dg{vs=Vs}, V) ->
+ map_get(V, Vs).
+
+-spec vertices(graph()) -> [{vertex(), label()}].
+vertices(#dg{vs=Vs}) ->
+ maps:to_list(Vs).
+
+-spec reverse_postorder(graph(), [vertex()]) -> [vertex()].
+reverse_postorder(G, Vs) ->
+ Seen = cerl_sets:new(),
+ {RPO, _} = reverse_postorder_1(Vs, G, Seen, []),
+ RPO.
+
+reverse_postorder_1([V|Vs], G, Seen0, Acc0) ->
+ case cerl_sets:is_element(V, Seen0) of
+ true ->
+ reverse_postorder_1(Vs, G, Seen0, Acc0);
+ false ->
+ Seen1 = cerl_sets:add_element(V, Seen0),
+ Successors = out_neighbours(G, V),
+ {Acc,Seen} = reverse_postorder_1(Successors, G, Seen1, Acc0),
+ reverse_postorder_1(Vs, G, Seen, [V|Acc])
+ end;
+reverse_postorder_1([], _, Seen, Acc) ->
+ {Acc, Seen}.
+
+-spec roots(graph()) -> [vertex()].
+roots(G) ->
+ roots_1(vertices(G), G).
+
+roots_1([{V,_}|Vs], G) ->
+ case in_degree(G, V) of
+ 0 ->
+ [V|roots_1(Vs, G)];
+ _ ->
+ roots_1(Vs, G)
+ end;
+roots_1([], _G) -> [].
+
+-spec topsort(graph()) -> [vertex()].
+topsort(G) ->
+ Seen = roots(G),
+ reverse_postorder(G, Seen).
+
+%%
+%% Kosaraju's algorithm
+%%
+%% Visit each node in reverse post order. If the node has not been assigned to
+%% a component yet, start a new component and add all of its in-neighbors to it
+%% if they don't yet belong to one. Keep going until all nodes have been
+%% visited.
+%%
+%% https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
+%%
+
+-spec strong_components(graph(), [vertex()]) -> ComponentMap when
+ %% Vertices together with their components.
+ ComponentMap :: #{ vertex() => [vertex()] }.
+strong_components(G, Vs) ->
+ sc_1(Vs, G, #{}, #{}).
+
+sc_1([V | Vs], G, Roots0, Components) when not is_map_key(V, Roots0) ->
+ %% V has not been assigned to a component, start a new one with this one as
+ %% the root.
+ {Roots, Component} = sc_2([V], G, V, Roots0, []),
+ sc_1(Vs, G, Roots, Components#{ V => Component });
+sc_1([V | Vs], G, Roots, Components0) ->
+ %% V is already part of a component, copy it over.
+ Root = map_get(V, Roots),
+ Components = Components0#{ V => map_get(Root, Components0) },
+
+ sc_1(Vs, G, Roots, Components);
+sc_1([], _G, _Roots, Components) ->
+ Components.
+
+sc_2([V | Vs], G, Root, Roots, Acc) when not is_map_key(V, Roots) ->
+ %% V has not been assigned to a component, so assign it to the current one.
+ sc_2(in_neighbours(G, V) ++ Vs, G, Root, Roots#{ V => Root }, [V | Acc]);
+sc_2([_V | Vs], G, Root, Roots, Acc) ->
+ %% V is already part of a component, skip it.
+ sc_2(Vs, G, Root, Roots, Acc);
+sc_2([], _G, _Root, Roots, Acc) ->
+ {Roots, reverse(Acc)}.
+
+-if(?DEBUG).
+
+%%
+%% Dumps the graph as a string in dot (graphviz) format.
+%%
+%% Use dot(1) to convert to an image:
+%%
+%% dot [input] -T[format]
+%% dot graph_file -Tsvg > graph.svg
+
+-spec dump(any()) -> any().
+dump(G) ->
+ Formatter = fun(Node) -> io_lib:format("~p", [Node]) end,
+ io:format("~s", [dump_1(G, Formatter)]).
+
+-spec dump(any(), any()) -> any().
+dump(G, FileName) ->
+ Formatter = fun(Node) -> io_lib:format("~p", [Node]) end,
+ dump(G, FileName, Formatter).
+
+-spec dump(any(), any(), any()) -> any().
+dump(G, FileName, Formatter) ->
+ {ok, Fd} = file:open(FileName, [write]),
+ io:fwrite(Fd, "~s", [dump_1(G, Formatter)]),
+ file:close(Fd).
+
+dump_1(G, Formatter) ->
+ Vs = maps:keys(G#dg.vs),
+
+ {Map, Vertices} = dump_vertices(Vs, 0, Formatter,#{}, []),
+ Edges = dump_edges(Vs, G, Map, []),
+
+ io_lib:format("digraph g {~n~s~n~s~n}~n", [Vertices, Edges]).
+
+dump_vertices([V | Vs], Counter, Formatter, Map, Acc) ->
+ VerticeSlug = io_lib:format(" ~p [label=\"~s\"]~n",
+ [Counter, Formatter(V)]),
+ dump_vertices(Vs, Counter + 1, Formatter,
+ Map#{ V => Counter }, [VerticeSlug | Acc]);
+dump_vertices([], _Counter, _Formatter, Map, Acc) ->
+ {Map, Acc}.
+
+dump_edges([V | Vs], G, Map, Acc) ->
+ SelfId = map_get(V, Map),
+ EdgeSlug = [io_lib:format(" ~p -> ~p~n", [SelfId, map_get(To, Map)]) ||
+ {_, To, _} <- out_edges(G, V)],
+ dump_edges(Vs, G, Map, [EdgeSlug | Acc]);
+dump_edges([], _G, _Map, Acc) ->
+ Acc.
+
+-endif.
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 7d048716e4..c52edd6635 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1123,6 +1123,15 @@ resolve_inst({put_tuple2,[Dst,{{z,1},{u,_},List0}]},_,_,_) ->
{put_tuple2,Dst,{list,List}};
%%
+%% OTP 23.
+%%
+resolve_inst({bs_start_match4,[Fail,Live,Src,Dst]},_,_,_) ->
+ {bs_start_match4,Fail,Live,Src,Dst};
+resolve_inst({swap,[_,_]=List},_,_,_) ->
+ [R1,R2] = resolve_args(List),
+ {swap,R1,R2};
+
+%%
%% Catches instructions that are not yet handled.
%%
resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}).
diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl
deleted file mode 100644
index 04a5e3430a..0000000000
--- a/lib/compiler/src/beam_except.erl
+++ /dev/null
@@ -1,256 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2011-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(beam_except).
--export([module/2]).
-
-%%% Rewrite certain calls to erlang:error/{1,2} to specialized
-%%% instructions:
-%%%
-%%% erlang:error({badmatch,Value}) => badmatch Value
-%%% erlang:error({case_clause,Value}) => case_end Value
-%%% erlang:error({try_clause,Value}) => try_case_end Value
-%%% erlang:error(if_clause) => if_end
-%%% erlang:error(function_clause, Args) => jump FuncInfoLabel
-%%%
-
--import(lists, [reverse/1,reverse/2,seq/2,splitwith/2]).
-
--spec module(beam_utils:module_code(), [compile:option()]) ->
- {'ok',beam_utils:module_code()}.
-
-module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
- Fs = [function(F) || F <- Fs0],
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-function({function,Name,Arity,CLabel,Is0}) ->
- try
- Is = function_1(Is0),
- {function,Name,Arity,CLabel,Is}
- catch
- Class:Error:Stack ->
- io:fwrite("Function: ~w/~w\n", [Name,Arity]),
- erlang:raise(Class, Error, Stack)
- end.
-
--record(st,
- {lbl :: beam_asm:label(), %func_info label
- loc :: [_], %location for func_info
- arity :: arity() %arity for function
- }).
-
-function_1(Is0) ->
- case Is0 of
- [{label,Lbl},{line,Loc},{func_info,_,_,Arity}|_] ->
- St = #st{lbl=Lbl,loc=Loc,arity=Arity},
- translate(Is0, St, []);
- [{label,_}|_] ->
- %% No line numbers. The source must be a .S file.
- %% There is no need to do anything.
- Is0
- end.
-
-translate([{call_ext,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) ->
- translate_1(Ar, I, Is, St, Acc);
-translate([I|Is], St, Acc) ->
- translate(Is, St, [I|Acc]);
-translate([], _, Acc) ->
- reverse(Acc).
-
-translate_1(Ar, I, Is, #st{arity=Arity}=St, [{line,_}=Line|Acc1]=Acc0) ->
- case dig_out(Ar, Arity, Acc1) of
- no ->
- translate(Is, St, [I|Acc0]);
- {yes,function_clause,Acc2} ->
- case {Is,Line,St} of
- {[return|_],{line,Loc},#st{lbl=Fi,loc=Loc}} ->
- Instr = {jump,{f,Fi}},
- translate(Is, St, [Instr|Acc2]);
- {_,_,_} ->
- %% Not a call_only instruction, or not the same
- %% location information as in in the line instruction
- %% before the func_info instruction. Not safe
- %% to translate to a jump.
- translate(Is, St, [I|Acc0])
- end;
- {yes,Instr,Acc2} ->
- translate(Is, St, [Instr,Line|Acc2])
- end.
-
-dig_out(1, _Arity, Is) ->
- dig_out(Is);
-dig_out(2, Arity, Is) ->
- dig_out_fc(Arity, Is);
-dig_out(_, _, _) -> no.
-
-dig_out([{block,Bl0}|Is]) ->
- case dig_out_block(reverse(Bl0)) of
- no -> no;
- {yes,What,[]} ->
- {yes,What,Is};
- {yes,What,Bl} ->
- {yes,What,[{block,Bl}|Is]}
- end;
-dig_out(_) -> no.
-
-dig_out_block([{set,[{x,0}],[{atom,if_clause}],move}]) ->
- {yes,if_end,[]};
-dig_out_block([{set,[{x,0}],[{literal,{Exc,Value}}],move}|Is]) ->
- translate_exception(Exc, {literal,Value}, Is, 0);
-dig_out_block([{set,[{x,0}],[{atom,Exc},Value],put_tuple2}|Is]) ->
- translate_exception(Exc, Value, Is, 3);
-dig_out_block(_) -> no.
-
-translate_exception(badmatch, Val, Is, Words) ->
- {yes,{badmatch,Val},fix_block(Is, Words)};
-translate_exception(case_clause, Val, Is, Words) ->
- {yes,{case_end,Val},fix_block(Is, Words)};
-translate_exception(try_clause, Val, Is, Words) ->
- {yes,{try_case_end,Val},fix_block(Is, Words)};
-translate_exception(_, _, _, _) -> no.
-
-fix_block(Is, 0) ->
- reverse(Is);
-fix_block(Is, Words) ->
- reverse(fix_block_1(Is, Words)).
-
-fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words)
- when is_integer(Needed0) ->
- case Needed0 - Words of
- 0 ->
- Is;
- Needed ->
- true = Needed >= 0, %Assertion.
- [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is]
- end;
-fix_block_1([I|Is], Words) ->
- [I|fix_block_1(Is, Words)];
-fix_block_1([], _Words) ->
- %% Rare. The heap allocation was probably done by a binary
- %% construction instruction.
- [].
-
-dig_out_fc(Arity, Is0) ->
- Regs0 = maps:from_list([{{x,X},{arg,X}} || X <- seq(0, Arity-1)]),
- {Is,Acc0} = splitwith(fun({label,_}) -> false;
- ({test,_,_,_}) -> false;
- (_) -> true
- end, Is0),
- {Regs,Acc} = dig_out_fc_1(reverse(Is), Arity, Regs0, Acc0),
- case Regs of
- #{{x,0}:={atom,function_clause},{x,1}:=Args} ->
- case moves_from_stack(Args, 0, []) of
- {Moves,Arity} ->
- {yes,function_clause,reverse(Moves, Acc)};
- {_,_} ->
- no
- end;
- #{} ->
- no
- end.
-
-dig_out_fc_1([{block,Bl}|Is], Arity, Regs0, Acc) ->
- Regs = dig_out_fc_block(Bl, Regs0),
- dig_out_fc_1(Is, Arity, Regs, Acc);
-dig_out_fc_1([{bs_set_position,_,_}=I|Is], Arity, Regs, Acc) ->
- dig_out_fc_1(Is, Arity, Regs, [I|Acc]);
-dig_out_fc_1([{bs_get_tail,Src,Dst,Live0}|Is], Arity, Regs0, Acc) ->
- case Src of
- {x,X} when X < Arity ->
- %% The heuristic for determining the number of live
- %% registers is likely to give an incorrect result.
- %% Give up.
- {#{},[]};
- _ ->
- Regs = prune_xregs(Live0, Regs0),
- Live = dig_out_stack_live(Regs, Live0),
- I = {bs_get_tail,Src,Dst,Live},
- dig_out_fc_1(Is, Arity, Regs, [I|Acc])
- end;
-dig_out_fc_1([_|_], _Arity, _Regs, _Acc) ->
- {#{},[]};
-dig_out_fc_1([], _Arity, Regs, Acc) ->
- {Regs,Acc}.
-
-dig_out_fc_block([{set,[],[],{alloc,Live,_}}|Is], Regs0) ->
- Regs = prune_xregs(Live, Regs0),
- dig_out_fc_block(Is, Regs);
-dig_out_fc_block([{set,[Dst],[Hd,Tl],put_list}|Is], Regs0) ->
- Regs = Regs0#{Dst=>{cons,get_reg(Hd, Regs0),get_reg(Tl, Regs0)}},
- dig_out_fc_block(Is, Regs);
-dig_out_fc_block([{set,[Dst],[Src],move}|Is], Regs0) ->
- Regs = Regs0#{Dst=>get_reg(Src, Regs0)},
- dig_out_fc_block(Is, Regs);
-dig_out_fc_block([{set,_,_,_}|_], _Regs) ->
- %% Unknown instruction. Fail.
- #{};
-dig_out_fc_block([], Regs) -> Regs.
-
-dig_out_stack_live(Regs, Default) ->
- Reg = {x,2},
- case Regs of
- #{Reg:=List} ->
- dig_out_stack_live_1(List, Default);
- #{} ->
- Default
- end.
-
-dig_out_stack_live_1({cons,{arg,N},T}, Live) ->
- dig_out_stack_live_1(T, max(N + 1, Live));
-dig_out_stack_live_1({cons,_,T}, Live) ->
- dig_out_stack_live_1(T, Live);
-dig_out_stack_live_1(nil, Live) ->
- Live;
-dig_out_stack_live_1(_, Live) -> Live.
-
-prune_xregs(Live, Regs) ->
- maps:filter(fun({x,X}, _) -> X < Live end, Regs).
-
-moves_from_stack({cons,{arg,N},_}, I, _Acc) when N =/= I ->
- %% Wrong argument. Give up.
- {[],-1};
-moves_from_stack({cons,H,T}, I, Acc) ->
- case H of
- {arg,I} ->
- moves_from_stack(T, I+1, Acc);
- _ ->
- moves_from_stack(T, I+1, [{move,H,{x,I}}|Acc])
- end;
-moves_from_stack(nil, I, Acc) ->
- {reverse(Acc),I};
-moves_from_stack({literal,[H|T]}, I, Acc) ->
- Cons = {cons,tag_literal(H),tag_literal(T)},
- moves_from_stack(Cons, I, Acc);
-moves_from_stack(_, _, _) ->
- %% Not understood. Give up.
- {[],-1}.
-
-
-get_reg(R, Regs) ->
- case Regs of
- #{R:=Val} -> Val;
- #{} -> R
- end.
-
-tag_literal([]) -> nil;
-tag_literal(T) when is_atom(T) -> {atom,T};
-tag_literal(T) when is_float(T) -> {float,T};
-tag_literal(T) when is_integer(T) -> {integer,T};
-tag_literal(T) -> {literal,T}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 2d5d3dc457..61738e4435 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -136,6 +136,8 @@
-type instruction() :: beam_utils:instruction().
+-include("beam_types.hrl").
+
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
@@ -189,6 +191,15 @@ eliminate_moves([{test,is_eq_exact,_,[Reg,Val]}=I,
RegVal = {Reg,Val},
BlkIs = eliminate_moves_blk(BlkIs0, RegVal),
eliminate_moves([{block,BlkIs}|Is], D, [I|Acc]);
+eliminate_moves([{test,is_nonempty_list,Fail,[Reg]}=I|Is], D0, Acc) ->
+ case is_proper_list(Reg, Acc) of
+ true ->
+ D = update_value_dict([nil,Fail], Reg, D0),
+ eliminate_moves(Is, D, [I|Acc]);
+ false ->
+ D = update_unsafe_labels(I, D0),
+ eliminate_moves(Is, D, [I|Acc])
+ end;
eliminate_moves([{label,Lbl},{block,BlkIs0}=Blk|Is], D, Acc0) ->
Acc = [{label,Lbl}|Acc0],
case {no_fallthrough(Acc0),D} of
@@ -198,6 +209,10 @@ eliminate_moves([{label,Lbl},{block,BlkIs0}=Blk|Is], D, Acc0) ->
{_,_} ->
eliminate_moves([Blk|Is], D, Acc)
end;
+eliminate_moves([{call,_,_}=I|Is], D, Acc) ->
+ eliminate_moves_call(Is, D, [I | Acc]);
+eliminate_moves([{call_ext,_,_}=I|Is], D, Acc) ->
+ eliminate_moves_call(Is, D, [I | Acc]);
eliminate_moves([{block,[]}|Is], D, Acc) ->
%% Empty blocks can prevent further jump optimizations.
eliminate_moves(Is, D, Acc);
@@ -206,6 +221,21 @@ eliminate_moves([I|Is], D0, Acc) ->
eliminate_moves(Is, D, [I|Acc]);
eliminate_moves([], _, Acc) -> reverse(Acc).
+eliminate_moves_call([{'%',{var_info,{x,0},Info}}=Anno,
+ {block,BlkIs0}=Blk | Is], D, Acc0) ->
+ Acc = [Anno | Acc0],
+ RetType = proplists:get_value(type, Info, none),
+ case beam_types:get_singleton_value(RetType) of
+ {ok, Value} ->
+ RegVal = {{x,0}, value_to_literal(Value)},
+ BlkIs = eliminate_moves_blk(BlkIs0, RegVal),
+ eliminate_moves([{block,BlkIs}|Is], D, Acc);
+ error ->
+ eliminate_moves(Is, D, [Blk | Acc])
+ end;
+eliminate_moves_call(Is, D, Acc) ->
+ eliminate_moves(Is, D, Acc).
+
eliminate_moves_blk([{set,[Dst],[_],move}|_]=Is, {_,Dst}) ->
Is;
eliminate_moves_blk([{set,[Dst],[Lit],move}|Is], {Dst,Lit}) ->
@@ -217,9 +247,29 @@ eliminate_moves_blk([{set,[_],[_],move}=I|Is], {_,_}=RegVal) ->
[I|eliminate_moves_blk(Is, RegVal)];
eliminate_moves_blk(Is, _) -> Is.
+no_fallthrough([{'%',_} | Is]) ->
+ no_fallthrough(Is);
no_fallthrough([I|_]) ->
is_unreachable_after(I).
+is_proper_list(Reg, [{'%',{var_info,Reg,Info}}|_]) ->
+ case proplists:get_value(type, Info) of
+ #t_list{terminator=nil} ->
+ true;
+ _ ->
+ %% Unknown type or not a proper list.
+ false
+ end;
+is_proper_list(Reg, [{'%',{var_info,_,_}}|Is]) ->
+ is_proper_list(Reg, Is);
+is_proper_list(_, _) -> false.
+
+value_to_literal([]) -> nil;
+value_to_literal(A) when is_atom(A) -> {atom,A};
+value_to_literal(F) when is_float(F) -> {float,F};
+value_to_literal(I) when is_integer(I) -> {integer,I};
+value_to_literal(Other) -> {literal,Other}.
+
update_value_dict([Lit,{f,Lbl}|T], Reg, D0) ->
D = case D0 of
#{Lbl:=unsafe} -> D0;
@@ -267,55 +317,84 @@ insert_labels([], Lc, Acc) ->
%%% (1) We try to share the code for identical code segments by replacing all
%%% occurrences except the last with jumps to the last occurrence.
%%%
+%%% We must not share code that raises an exception from outside a
+%%% try/catch block with code inside a try/catch block and vice versa,
+%%% because beam_validator will probably flag it as unsafe
+%%% (ambiguous_catch_try_state). The same goes for a plain catch.
+%%%
share(Is0) ->
Is1 = eliminate_fallthroughs(Is0, []),
Is2 = find_fixpoint(fun(Is) ->
- share_1(Is, #{}, #{}, [], [])
+ share_1(Is)
end, Is1),
reverse(Is2).
-share_1([{label,L}=Lbl|Is], Dict0, Lbls0, [_|_]=Seq, Acc) ->
- case maps:find(Seq, Dict0) of
- error ->
- Dict = case is_shareable(Seq) of
- true ->
- maps:put(Seq, L, Dict0);
- false ->
- Dict0
- end,
- share_1(Is, Dict, Lbls0, [], [[Lbl|Seq]|Acc]);
- {ok,Label} ->
- Lbls = maps:put(L, Label, Lbls0),
- share_1(Is, Dict0, Lbls, [], [[Lbl,{jump,{f,Label}}]|Acc])
+share_1(Is) ->
+ Safe = classify_labels(Is),
+ share_1(Is, Safe, #{}, #{}, [], []).
+
+%% Note that we examine the instructions in reverse execution order.
+share_1([{label,L}=Lbl|Is], Safe, Dict0, Lbls0, [_|_]=Seq, Acc) ->
+ case Dict0 of
+ #{Seq := Label} ->
+ %% This sequence of instructions has been seen previously. Find out
+ %% whether it would be safe to jump the label for previous occurrence.
+ case is_safely_shareable(L, Label, Seq, Safe) of
+ true ->
+ %% Safe, because either the sequence never raises an exception
+ %% or the jump to the label will not pass a try/catch or catch
+ %% boundary.
+ Lbls = Lbls0#{L => Label},
+ share_1(Is, Safe, Dict0, Lbls, [],
+ [[Lbl,{jump,{f,Label}}]|Acc]);
+ false ->
+ %% Not safe, because the sequence can raise an exception
+ %% and the jump would pass the boundary going in
+ %% or out of a try/catch or catch block.
+ share_1(Is, Safe, Dict0, Lbls0, [], [[Lbl|Seq]|Acc])
+ end;
+ #{} ->
+ %% This is first time we have seen this sequence of instructions.
+ case is_shareable(Seq) of
+ true ->
+ Dict = Dict0#{Seq => L},
+ share_1(Is, Safe, Dict, Lbls0, [], [[Lbl|Seq]|Acc]);
+ false ->
+ %% The sequence begins with an inappropriate instruction.
+ share_1(Is, Safe, Dict0, Lbls0, [], [[Lbl|Seq]|Acc])
+ end
end;
-share_1([{func_info,_,_,_}|_]=Is0, _, Lbls, [], Acc0) when Lbls =/= #{} ->
- lists:foldl(fun(Is, Acc) ->
- beam_utils:replace_labels(Is, Acc, Lbls, fun(Old) -> Old end)
- end, Is0, Acc0);
-share_1([{func_info,_,_,_}|_]=Is, _, Lbls, [], Acc) when Lbls =:= #{} ->
- lists:foldl(fun lists:reverse/2, Is, Acc);
-share_1([{'catch',_,_}=I|Is], Dict0, Lbls0, Seq, Acc) ->
- {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0),
- share_1(Is, Dict, Lbls, [I|Seq], Acc);
-share_1([{'try',_,_}=I|Is], Dict0, Lbls0, Seq, Acc) ->
- {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0),
- share_1(Is, Dict, Lbls, [I|Seq], Acc);
-share_1([{try_case,_}=I|Is], Dict0, Lbls0, Seq, Acc) ->
- {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0),
- share_1(Is, Dict, Lbls, [I|Seq], Acc);
-share_1([{catch_end,_}=I|Is], Dict0, Lbls0, Seq, Acc) ->
- {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0),
- share_1(Is, Dict, Lbls, [I|Seq], Acc);
-share_1([{jump,{f,To}}=I,{label,L}=Lbl|Is], Dict0, Lbls0, _Seq, Acc) ->
- Lbls = maps:put(L, To, Lbls0),
- share_1(Is, Dict0, Lbls, [], [[Lbl,I]|Acc]);
-share_1([I|Is], Dict, Lbls, Seq, Acc) ->
+share_1([{func_info,_,_,_}|_]=Is0, _Safe, _, Lbls, [], Acc0) ->
+ %% Replace jumps to jumps with a jump to the final destination
+ %% (jump threading). This optimization is done in the main
+ %% optimization pass of this module, but we do it here too because
+ %% it can give more opportunities for sharing code.
+ F = case Lbls =:= #{} of
+ true ->
+ fun lists:reverse/2;
+ false ->
+ fun(Is, Acc) ->
+ beam_utils:replace_labels(Is, Acc, Lbls,
+ fun(Old) -> Old end)
+ end
+ end,
+ foldl(F, Is0, Acc0);
+share_1([{'catch',_,_}=I|Is], Safe, Dict, _Lbls0, Seq, Acc) ->
+ %% Disable the jump threading optimization because it may be unsafe.
+ share_1(Is, Safe, Dict, #{}, [I|Seq], Acc);
+share_1([{'try',_,_}=I|Is], Safe, Dict, _Lbls, Seq, Acc) ->
+ %% Disable the jump threading optimization because it may be unsafe.
+ share_1(Is, Safe, Dict, #{}, [I|Seq], Acc);
+share_1([{jump,{f,To}}=I,{label,From}=Lbl|Is], Safe, Dict0, Lbls0, _Seq, Acc) ->
+ Lbls = Lbls0#{From => To},
+ share_1(Is, Safe, Dict0, Lbls, [], [[Lbl,I]|Acc]);
+share_1([I|Is], Safe, Dict, Lbls, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
- share_1(Is, Dict, Lbls, [I|Seq], Acc);
+ share_1(Is, Safe, Dict, Lbls, [I|Seq], Acc);
true ->
- share_1(Is, Dict, Lbls, [I], Acc)
+ share_1(Is, Safe, Dict, Lbls, [I], Acc)
end.
is_shareable([{'catch',_,_}|_]) -> false;
@@ -325,34 +404,73 @@ is_shareable([{try_case,_}|_]) -> false;
is_shareable([{try_end,_}|_]) -> false;
is_shareable(_) -> true.
-clean_non_sharable(Dict0, Lbls0) ->
- %% We are passing in or out of a 'catch' or 'try' block. Remove
- %% sequences that should not be shared over the boundaries of the
- %% block. Since the end of the sequence must match, the only
- %% possible match between a sequence outside and a sequence inside
- %% the 'catch'/'try' block is a sequence that ends with an
- %% instruction that causes an exception. Any sequence that causes
- %% an exception must contain a line/1 instruction.
- Dict1 = maps:to_list(Dict0),
- Lbls1 = maps:to_list(Lbls0),
- {Dict2,Lbls2} = foldl(fun({K, V}, {Dict,Lbls}) ->
- case sharable_with_try(K) of
- true ->
- {[{K,V}|Dict],lists:keydelete(V, 2, Lbls)};
- false ->
- {Dict,Lbls}
- end
- end, {[],Lbls1}, Dict1),
- {maps:from_list(Dict2),maps:from_list(Lbls2)}.
-
-sharable_with_try([{line,_}|_]) ->
- %% This sequence may cause an exception and may potentially
+%% There are identical code sequences Seq at labels Lbl1 and Lbl2. Is it
+%% safe to replace the sequence at label Lbl1 with a jump to Lbl2?
+
+is_safely_shareable(Lbl1, Lbl2, Seq, Safe) ->
+ case no_exception(Seq) of
+ true ->
+ %% Safe, because the sequence Seq can't raise an exception.
+ true;
+ false ->
+ %% Safe if both labels are either ouside try/catch or inside
+ %% the same part of the same try/catch or catch block.
+ case Safe of
+ #{Lbl1 := Scope, Lbl2 := Scope} -> true;
+ #{} -> false
+ end
+ end.
+
+no_exception([{line,_}|_]) ->
+ %% This sequence may raise an exception and may potentially
%% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
-sharable_with_try([_|Is]) ->
- sharable_with_try(Is);
-sharable_with_try([]) -> true.
+no_exception([_|Is]) ->
+ no_exception(Is);
+no_exception([]) -> true.
+
+%%
+%% Classify labels according to where the instructions that branch to
+%% the labels are located. Each label is assigned a scope identifer.
+%% If two labels have different scope identfiers, sharing a sequence
+%% that raises an exception between the labels may not be safe, because
+%% one label is inside a try/catch, and the other label is outside.
+%%
+%% Note that we don't care where the labels themselves are located,
+%% only from where the branches to them are located. This is essential
+%% to ensure that beam_jump is idempotent, ensuring that beam_jump
+%% will not do any unsafe optimizations when when compiling from a .S
+%% file. The move/1 optimization pass below (2) will move instruction
+%% sequences that end in an exception raising instruction to the end
+%% of the function. Thus instruction sequences initially being in
+%% different scopes could be placed next to each other.
+%%
+
+classify_labels(Is) ->
+ classify_labels(Is, 0, #{}).
+
+classify_labels([{'catch',_,_}|Is], Scope, Safe) ->
+ classify_labels(Is, Scope+1, Safe);
+classify_labels([{catch_end,_}|Is], Scope, Safe) ->
+ classify_labels(Is, Scope+1, Safe);
+classify_labels([{'try',_,_}|Is], Scope, Safe) ->
+ classify_labels(Is, Scope+1, Safe);
+classify_labels([{'try_end',_}|Is], Scope, Safe) ->
+ classify_labels(Is, Scope+1, Safe);
+classify_labels([{'try_case',_}|Is], Scope, Safe) ->
+ classify_labels(Is, Scope+1, Safe);
+classify_labels([I|Is], Scope, Safe0) ->
+ Labels = instr_labels(I),
+ Safe = foldl(fun(L, A) ->
+ case A of
+ #{L := Scope} -> A;
+ #{L := _} -> maps:remove(L, A);
+ #{} -> A#{L => Scope}
+ end
+ end, Safe0, Labels),
+ classify_labels(Is, Scope, Safe);
+classify_labels([], _Scope, Safe) -> Safe.
%% Eliminate all fallthroughs. Return the result reversed.
@@ -592,8 +710,6 @@ is_unreachable_after(I) -> is_exit_instruction(I).
-spec is_exit_instruction(instruction()) -> boolean().
-is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
- erl_bifs:is_exit_bif(M, F, A);
is_exit_instruction(if_end) -> true;
is_exit_instruction({case_end,_}) -> true;
is_exit_instruction({try_case_end,_}) -> true;
@@ -736,7 +852,13 @@ instr_labels({recv_set,Lbl}) ->
do_instr_labels(Lbl);
instr_labels({fcheckerror,Lbl}) ->
do_instr_labels(Lbl);
-instr_labels(_) -> [].
+instr_labels({bs_start_match4,Fail,_,_,_}) ->
+ case Fail of
+ {f,L} -> [L];
+ {atom,_} -> []
+ end;
+instr_labels(_) ->
+ [].
do_instr_labels({f,0}) -> [];
do_instr_labels({f,F}) -> [F].
diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl
index aa04a75804..6b552ae38c 100644
--- a/lib/compiler/src/beam_kernel_to_ssa.erl
+++ b/lib/compiler/src/beam_kernel_to_ssa.erl
@@ -24,7 +24,7 @@
%% The main interface.
-export([module/2]).
--import(lists, [append/1,duplicate/2,flatmap/2,foldl/3,
+-import(lists, [all/2,append/1,flatmap/2,foldl/3,
keysort/2,mapfoldl/3,map/2,member/2,
reverse/1,reverse/2,sort/1]).
@@ -34,13 +34,14 @@
-type label() :: beam_ssa:label().
%% Main codegen structure.
--record(cg, {lcount=1 :: label(), %Label counter
+-record(cg, {lcount=1 :: label(), %Label counter
bfail=1 :: label(),
catch_label=none :: 'none' | label(),
vars=#{} :: map(), %Defined variables.
break=0 :: label(), %Break label
recv=0 :: label(), %Receive label
- ultimate_failure=0 :: label() %Label for ultimate match failure.
+ ultimate_failure=0 :: label(), %Label for ultimate match failure.
+ labels=#{} :: #{atom() => label()}
}).
%% Internal records.
@@ -83,6 +84,7 @@ function(#k_fdef{anno=Anno0,func=Name,arity=Arity,
cg_fun(Ke, St0) ->
{UltimateFail,FailIs,St1} = make_failure(badarg, St0),
+ ?EXCEPTION_BLOCK = UltimateFail, %Assertion.
St2 = St1#cg{bfail=UltimateFail,ultimate_failure=UltimateFail},
{B,St} = cg(Ke, St2),
Asm = [{label,0}|B++FailIs],
@@ -105,8 +107,6 @@ make_failure(Reason, St0) ->
cg(#k_match{body=M,ret=Rs}, St) ->
do_match_cg(M, Rs, St);
-cg(#k_guard_match{body=M,ret=Rs}, St) ->
- do_match_cg(M, Rs, St);
cg(#k_seq{arg=Arg,body=Body}, St0) ->
{ArgIs,St1} = cg(Arg, St0),
{BodyIs,St} = cg(Body, St1),
@@ -123,14 +123,6 @@ cg(#k_try_enter{arg=Ta,vars=Vs,body=Tb,evars=Evs,handler=Th}, St) ->
try_enter_cg(Ta, Vs, Tb, Evs, Th, St);
cg(#k_catch{body=Cb,ret=[R]}, St) ->
do_catch_cg(Cb, R, St);
-cg(#k_receive{anno=Le,timeout=Te,var=Rvar,body=Rm,action=Tes,ret=Rs}, St) ->
- recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, St);
-cg(#k_receive_next{}, #cg{recv=Recv}=St) ->
- Is = [#b_set{op=recv_next},make_uncond_branch(Recv)],
- {Is,St};
-cg(#k_receive_accept{}, St) ->
- Remove = #b_set{op=remove_message},
- {[Remove],St};
cg(#k_put{anno=Le,arg=Con,ret=Var}, St) ->
put_cg(Var, Con, Le, St);
cg(#k_return{args=[Ret0]}, St) ->
@@ -139,18 +131,30 @@ cg(#k_return{args=[Ret0]}, St) ->
cg(#k_break{args=Bs}, #cg{break=Br}=St) ->
Args = ssa_args(Bs, St),
{[#cg_break{args=Args,phi=Br}],St};
-cg(#k_guard_break{args=Bs}, St) ->
- cg(#k_break{args=Bs}, St).
+cg(#k_letrec_goto{label=Label,first=First,then=Then,ret=Rs},
+ #cg{break=OldBreak,labels=Labels0}=St0) ->
+ {Tf,St1} = new_label(St0),
+ {B,St2} = new_label(St1),
+ Labels = Labels0#{Label=>Tf},
+ {Fis,St3} = cg(First, St2#cg{labels=Labels,break=B}),
+ {Sis,St4} = cg(Then, St3),
+ St5 = St4#cg{labels=Labels0},
+ {BreakVars,St} = new_ssa_vars(Rs, St5),
+ Phi = #cg_phi{vars=BreakVars},
+ {Fis ++ [{label,Tf}] ++ Sis ++ [{label,B},Phi],St#cg{break=OldBreak}};
+cg(#k_goto{label=Label}, #cg{labels=Labels}=St) ->
+ Branch = map_get(Label, Labels),
+ {[make_uncond_branch(Branch)],St}.
%% match_cg(Matc, [Ret], State) -> {[Ainstr],State}.
%% Generate code for a match.
-do_match_cg(M, Rs, St0) ->
+do_match_cg(M, Rs, #cg{bfail=Bfail,break=OldBreak}=St0) ->
{B,St1} = new_label(St0),
- {Mis,St2} = match_cg(M, St1#cg.bfail, St1#cg{break=B}),
- {BreakVars,St} = new_ssa_vars(Rs, St2),
- {Mis ++ [{label,B},#cg_phi{vars=BreakVars}],
- St#cg{bfail=St0#cg.bfail,break=St1#cg.break}}.
+ {Mis,St2} = match_cg(M, Bfail, St1#cg{break=B}),
+ St3 = St2#cg{break=OldBreak},
+ {BreakVars,St} = new_ssa_vars(Rs, St3),
+ {Mis ++ [{label,B},#cg_phi{vars=BreakVars}],St}.
%% match_cg(Match, Fail, State) -> {[Ainstr],State}.
%% Generate code for a match tree.
@@ -206,6 +210,22 @@ select_cg(#k_type_clause{type=Type,values=Scs}, Var, Tf, Vf, St0) ->
{Is,St} = select_val_cg(Type, Arg, Vls, Tf, Vf, Sis, St2),
{Is,St}.
+select_val_cg(k_atom, {succeeded,Dst}, Vls, _Tf, _Vf, Sis, St0) ->
+ [{#b_literal{val=false},Fail},{#b_literal{val=true},Succ}] = sort(Vls),
+ case Dst of
+ #b_var{} ->
+ %% Generate a `succeeded` instruction and two-way branch
+ %% following the `peek_message` and `wait_timeout`
+ %% instructions.
+ {Bool,St} = new_ssa_var('@ssa_bool', St0),
+ Succeeded = #b_set{op=succeeded,dst=Bool,args=[Dst]},
+ Br = #b_br{bool=Bool,succ=Succ,fail=Fail},
+ {[Succeeded,Br|Sis],St};
+ #b_literal{val=true}=Bool ->
+ %% A 'wait_timeout 0' instruction was optimized away.
+ Br = #b_br{bool=Bool,succ=Succ,fail=Succ},
+ {[Br|Sis],St0}
+ end;
select_val_cg(k_tuple, Tuple, Vls, Tf, Vf, Sis, St0) ->
{Is0,St1} = make_cond_branch({bif,is_tuple}, [Tuple], Tf, St0),
{Arity,St2} = new_ssa_var('@ssa_arity', St1),
@@ -269,7 +289,7 @@ select_cons(#k_val_clause{val=#k_cons{hd=Hd,tl=Tl},body=B},
{Is,St} = make_cond_branch(is_nonempty_list, [Src], Tf, St2),
{Is ++ Eis ++ Bis,St}.
-select_nil(#k_val_clause{val=#k_nil{},body=B}, V, Tf, Vf, St0) ->
+select_nil(#k_val_clause{val=#k_literal{val=[]},body=B}, V, Tf, Vf, St0) ->
{Bis,St1} = match_cg(B, Vf, St0),
Src = ssa_arg(V, St1),
{Is,St} = make_cond_branch({bif,'=:='}, [Src,#b_literal{val=[]}], Tf, St1),
@@ -279,9 +299,10 @@ select_binary(#k_val_clause{val=#k_binary{segs=#k_var{name=Ctx0}},body=B},
#k_var{}=Src, Tf, Vf, St0) ->
{Ctx,St1} = new_ssa_var(Ctx0, St0),
{Bis0,St2} = match_cg(B, Vf, St1),
- {TestIs,St} = make_cond_branch(succeeded, [Ctx], Tf, St2),
+ {TestIs,St} = make_succeeded(Ctx, {guard, Tf}, St2),
Bis1 = [#b_set{op=bs_start_match,dst=Ctx,
- args=[ssa_arg(Src, St)]}] ++ TestIs ++ Bis0,
+ args=[#b_literal{val=new},
+ ssa_arg(Src, St)]}] ++ TestIs ++ Bis0,
Bis = finish_bs_matching(Bis1),
{Bis,St}.
@@ -311,6 +332,35 @@ make_cond_branch(Cond, Args, Fail, St0) ->
make_uncond_branch(Fail) ->
#b_br{bool=#b_literal{val=true},succ=Fail,fail=Fail}.
+%%
+%% The 'succeeded' instruction needs special treatment in catch blocks to
+%% prevent the checked operation from being optimized away if a later pass
+%% determines that it always fails.
+%%
+
+make_succeeded(Var, {in_catch, CatchLbl}, St0) ->
+ {Bool, St1} = new_ssa_var('@ssa_bool', St0),
+ {Succ, St2} = new_label(St1),
+ {Fail, St} = new_label(St2),
+
+ Check = [#b_set{op=succeeded,dst=Bool,args=[Var]},
+ #b_br{bool=Bool,succ=Succ,fail=Fail}],
+
+ %% Add a dummy block that references the checked variable, ensuring it
+ %% stays alive and that it won't be merged with the landing pad.
+ Trampoline = [{label,Fail},
+ #b_set{op=exception_trampoline,args=[Var]},
+ make_uncond_branch(CatchLbl)],
+
+ {Check ++ Trampoline ++ [{label,Succ}], St};
+make_succeeded(Var, {no_catch, Fail}, St) ->
+ %% Ultimate failure raises an exception, so we must treat it as if it were
+ %% in a catch to keep it from being optimized out.
+ #cg{ultimate_failure=Fail} = St, %Assertion
+ make_succeeded(Var, {in_catch, Fail}, St);
+make_succeeded(Var, {guard, Fail}, St) ->
+ make_cond_branch(succeeded, [Var], Fail, St).
+
%% Instructions for selection of binary segments.
select_bin_segs(Scs, Ivar, Tf, St) ->
@@ -375,15 +425,23 @@ select_bin_end(#k_val_clause{val=#k_bin_end{},body=B}, Src, Tf, St0) ->
select_extract_bin(#k_var{name=Hd}, Size0, Unit, Type, Flags, Vf,
Ctx, Anno, St0) ->
{Dst,St1} = new_ssa_var(Hd, St0),
- Size = ssa_arg(Size0, St0),
+ Size = case {Size0,ssa_arg(Size0, St0)} of
+ {#k_var{},#b_literal{val=all}} ->
+ %% The size `all` is used for the size of the final binary
+ %% segment in a pattern. Using `all` explicitly is not allowed,
+ %% so we convert it to an obvious invalid size.
+ #b_literal{val=bad_size};
+ {_,Size1} ->
+ Size1
+ end,
build_bs_instr(Anno, Type, Vf, Ctx, Size, Unit, Flags, Dst, St1).
-select_extract_int(#k_var{name=Tl}, 0, #k_int{val=0}, _U, _Fs, _Vf,
+select_extract_int(#k_var{name=Tl}, 0, #k_literal{val=0}, _U, _Fs, _Vf,
Ctx, St0) ->
St = set_ssa_var(Tl, Ctx, St0),
{[],St};
-select_extract_int(#k_var{name=Tl}, Val, #k_int{val=Sz}, U, Fs, Vf,
- Ctx, St0) ->
+select_extract_int(#k_var{name=Tl}, Val, #k_literal{val=Sz}, U, Fs, Vf,
+ Ctx, St0) when is_integer(Sz) ->
{Dst,St1} = new_ssa_var(Tl, St0),
Bits = U*Sz,
Bin = case member(big, Fs) of
@@ -394,7 +452,7 @@ select_extract_int(#k_var{name=Tl}, Val, #k_int{val=Sz}, U, Fs, Vf,
<<Val:Bits/little>>
end,
Bits = bit_size(Bin), %Assertion.
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Vf, St1),
+ {TestIs,St} = make_succeeded(Dst, {guard, Vf}, St1),
Set = #b_set{op=bs_match,dst=Dst,
args=[#b_literal{val=string},Ctx,#b_literal{val=Bin}]},
{[Set|TestIs],St}.
@@ -412,21 +470,14 @@ build_bs_instr(Anno, Type, Fail, Ctx, Size, Unit0, Flags0, Dst, St0) ->
#b_set{anno=Anno,op=bs_match,dst=Dst,
args=[TypeArg,Ctx,Flags]}
end,
- {Is,St} = make_cond_branch(succeeded, [Dst], Fail, St0),
+ {Is,St} = make_succeeded(Dst, {guard, Fail}, St0),
{[Get|Is],St}.
select_val(#k_val_clause{val=#k_tuple{es=Es},body=B}, V, Vf, St0) ->
- #k{us=Used} = k_get_anno(B),
- {Eis,St1} = select_extract_tuple(V, Es, Used, St0),
+ {Eis,St1} = select_extract_tuple(V, Es, St0),
{Bis,St2} = match_cg(B, Vf, St1),
{length(Es),Eis ++ Bis,St2};
-select_val(#k_val_clause{val=Val0,body=B}, _V, Vf, St0) ->
- Val = case Val0 of
- #k_atom{val=Lit} -> Lit;
- #k_float{val=Lit} -> Lit;
- #k_int{val=Lit} -> Lit;
- #k_literal{val=Lit} -> Lit
- end,
+select_val(#k_val_clause{val=#k_literal{val=Val},body=B}, _V, Vf, St0) ->
{Bis,St1} = match_cg(B, Vf, St0),
{Val,Bis,St1}.
@@ -438,17 +489,18 @@ select_val(#k_val_clause{val=Val0,body=B}, _V, Vf, St0) ->
%% It is probably worthwhile because it is common to extract only a
%% few elements from a huge record.
-select_extract_tuple(Src, Vs, Used, St0) ->
+select_extract_tuple(Src, Vs, St0) ->
Tuple = ssa_arg(Src, St0),
- F = fun (#k_var{name=V}, {Elem,S0}) ->
- case member(V, Used) of
+ F = fun (#k_var{anno=Anno,name=V}, {Elem,S0}) ->
+ case member(unused, Anno) of
true ->
+ {[],{Elem+1,S0}};
+ false ->
Args = [Tuple,#b_literal{val=Elem}],
{Dst,S} = new_ssa_var(V, S0),
- Get = #b_set{op=get_tuple_element,dst=Dst,args=Args},
- {[Get],{Elem+1,S}};
- false ->
- {[],{Elem+1,S0}}
+ Get = #b_set{op=get_tuple_element,
+ dst=Dst,args=Args},
+ {[Get],{Elem+1,S}}
end
end,
{Es,{_,St}} = flatmapfoldl(F, {0,St0}, Vs),
@@ -475,7 +527,7 @@ select_extract_map([P|Ps], Src, Fail, St0) ->
Key = ssa_arg(Key0, St0),
{Dst,St1} = new_ssa_var(Dst0, St0),
Set = #b_set{op=get_map_element,dst=Dst,args=[MapSrc,Key]},
- {TestIs,St2} = make_cond_branch(succeeded, [Dst], Fail, St1),
+ {TestIs,St2} = make_succeeded(Dst, {guard, Fail}, St1),
{Is,St} = select_extract_map(Ps, Src, Fail, St2),
{[Set|TestIs]++Is,St};
select_extract_map([], _, _, St) ->
@@ -501,11 +553,20 @@ guard_clause_cg(#k_guard_clause{guard=G,body=B}, Fail, St0) ->
%% the correct exit point. Primops and tests all go to the next
%% instruction on success or jump to a failure label.
-guard_cg(#k_protected{arg=Ts,ret=Rs,inner=Inner}, Fail, St) ->
- protected_cg(Ts, Rs, Inner, Fail, St);
-guard_cg(#k_test{op=Test0,args=As,inverted=Inverted}, Fail, St0) ->
- #k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=Test}} = Test0,
- test_cg(Test, Inverted, As, Fail, St0);
+guard_cg(#k_try{arg=Ts,vars=[],body=#k_break{args=[]},
+ evars=[],handler=#k_break{args=[]}},
+ Fail,
+ #cg{bfail=OldBfail,break=OldBreak}=St0) ->
+ %% Do a try/catch without return value for effect. The return
+ %% value is not checked; success passes on to the next instruction
+ %% and failure jumps to Fail.
+ {Next,St1} = new_label(St0),
+ {Tis,St2} = guard_cg(Ts, Fail, St1#cg{bfail=Fail,break=Next}),
+ Is = Tis ++ [{label,Next},#cg_phi{vars=[]}],
+ {Is,St2#cg{bfail=OldBfail,break=OldBreak}};
+guard_cg(#k_test{op=Test0,args=As}, Fail, St0) ->
+ #k_remote{mod=#k_literal{val=erlang},name=#k_literal{val=Test}} = Test0,
+ test_cg(Test, false, As, Fail, St0);
guard_cg(#k_seq{arg=Arg,body=Body}, Fail, St0) ->
{ArgIs,St1} = guard_cg(Arg, Fail, St0),
{BodyIs,St} = guard_cg(Body, Fail, St1),
@@ -522,7 +583,8 @@ test_cg(Test, Inverted, As0, Fail, St0) ->
case {Test,ssa_args(As0, St0)} of
{is_record,[Tuple,#b_literal{val=Atom}=Tag,#b_literal{val=Int}=Arity]}
when is_atom(Atom), is_integer(Int) ->
- test_is_record_cg(Inverted, Fail, Tuple, Tag, Arity, St0);
+ false = Inverted, %Assertion.
+ test_is_record_cg(Fail, Tuple, Tag, Arity, St0);
{_,As} ->
{Bool,St1} = new_ssa_var('@ssa_bool', St0),
{Succ,St} = new_label(St1),
@@ -534,7 +596,7 @@ test_cg(Test, Inverted, As0, Fail, St0) ->
{[Bif,Br,{label,Succ}],St}
end.
-test_is_record_cg(false, Fail, Tuple, TagVal, ArityVal, St0) ->
+test_is_record_cg(Fail, Tuple, TagVal, ArityVal, St0) ->
{Arity,St1} = new_ssa_var('@ssa_arity', St0),
{Tag,St2} = new_ssa_var('@ssa_tag', St1),
{Is0,St3} = make_cond_branch({bif,is_tuple}, [Tuple], Fail, St2),
@@ -544,44 +606,8 @@ test_is_record_cg(false, Fail, Tuple, TagVal, ArityVal, St0) ->
args=[Tuple,#b_literal{val=0}]},
{Is2,St} = make_cond_branch({bif,'=:='}, [Tag,TagVal], Fail, St4),
Is = Is0 ++ [GetArity] ++ Is1 ++ [GetTag] ++ Is2,
- {Is,St};
-test_is_record_cg(true, Fail, Tuple, TagVal, ArityVal, St0) ->
- {Succ,St1} = new_label(St0),
- {Arity,St2} = new_ssa_var('@ssa_arity', St1),
- {Tag,St3} = new_ssa_var('@ssa_tag', St2),
- {Is0,St4} = make_cond_branch({bif,is_tuple}, [Tuple], Succ, St3),
- GetArity = #b_set{op={bif,tuple_size},dst=Arity,args=[Tuple]},
- {Is1,St5} = make_cond_branch({bif,'=:='}, [Arity,ArityVal], Succ, St4),
- GetTag = #b_set{op=get_tuple_element,dst=Tag,
- args=[Tuple,#b_literal{val=0}]},
- {Is2,St} = make_cond_branch({bif,'=:='}, [Tag,TagVal], Succ, St5),
- Is3 = [make_uncond_branch(Fail),{label,Succ}],
- Is = Is0 ++ [GetArity] ++ Is1 ++ [GetTag] ++ Is2 ++ Is3,
{Is,St}.
-%% protected_cg([Kexpr], [Ret], Fail, St) -> {[Ainstr],St}.
-%% Do a protected. Protecteds without return values are just done
-%% for effect, the return value is not checked, success passes on to
-%% the next instruction and failure jumps to Fail. If there are
-%% return values then these must be set to 'false' on failure,
-%% control always passes to the next instruction.
-
-protected_cg(Ts, [], _, Fail, St0) ->
- %% Protect these calls, revert when done.
- {Tis,St1} = guard_cg(Ts, Fail, St0#cg{bfail=Fail}),
- {Tis,St1#cg{bfail=St0#cg.bfail}};
-protected_cg(Ts, Rs, Inner0, _Fail, St0) ->
- {Pfail,St1} = new_label(St0),
- {Br,St2} = new_label(St1),
- Prot = duplicate(length(Rs), #b_literal{val=false}),
- {Tis,St3} = guard_cg(Ts, Pfail, St2#cg{break=Pfail,bfail=Pfail}),
- Inner = ssa_args(Inner0, St3),
- {BreakVars,St} = new_ssa_vars(Rs, St3),
- Is = Tis ++ [#cg_break{args=Inner,phi=Br},
- {label,Pfail},#cg_break{args=Prot,phi=Br},
- {label,Br},#cg_phi{vars=BreakVars}],
- {Is,St#cg{break=St0#cg.break,bfail=St0#cg.bfail}}.
-
%% match_fmf(Fun, LastFail, State, [Clause]) -> {Is,State}.
%% This is a special flatmapfoldl for match code gen where we
%% generate a "failure" label for each clause. The last clause uses
@@ -596,7 +622,7 @@ match_fmf(F, LastFail, St0, [H|T]) ->
{Rs,St3} = match_fmf(F, LastFail, St2, T),
{R ++ [{label,Fail}] ++ Rs,St3}.
-%% fail_label(State) -> {Where,FailureLabel}.
+%% fail_context(State) -> {Where,FailureLabel}.
%% Where = guard | no_catch | in_catch
%% Return an indication of which part of a function code is
%% being generated for and the appropriate failure label to
@@ -609,7 +635,7 @@ match_fmf(F, LastFail, St0, [H|T]) ->
%% a try/catch or catch.
%% in_catch - In the scope of a try/catch or catch.
-fail_label(#cg{catch_label=Catch,bfail=Fail,ultimate_failure=Ult}) ->
+fail_context(#cg{catch_label=Catch,bfail=Fail,ultimate_failure=Ult}) ->
if
Fail =/= Ult ->
{guard,Fail};
@@ -619,14 +645,6 @@ fail_label(#cg{catch_label=Catch,bfail=Fail,ultimate_failure=Ult}) ->
{in_catch,Catch}
end.
-%% bif_fail_label(State) -> FailureLabel.
-%% Return the appropriate failure label for a guard BIF call or
-%% primop that fails.
-
-bif_fail_label(St) ->
- {_,Fail} = fail_label(St),
- Fail.
-
%% call_cg(Func, [Arg], [Ret], Le, State) ->
%% {[Ainstr],State}.
%% enter_cg(Func, [Arg], Le, St) -> {[Ainstr],St}.
@@ -634,96 +652,58 @@ bif_fail_label(St) ->
call_cg(Func, As, [], Le, St) ->
call_cg(Func, As, [#k_var{name='@ssa_ignored'}], Le, St);
-call_cg(Func0, As, [#k_var{name=R}|MoreRs]=Rs, Le, St0) ->
- case fail_label(St0) of
+call_cg(Func, As, [#k_var{name=R}|MoreRs]=Rs, Le, St0) ->
+ case fail_context(St0) of
{guard,Fail} ->
%% Inside a guard. The only allowed function call is to
%% erlang:error/1,2. We will generate a branch to the
%% failure branch.
- #k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=error}} = Func0, %Assertion.
+ #k_remote{mod=#k_literal{val=erlang},
+ name=#k_literal{val=error}} = Func, %Assertion.
[#k_var{name=DestVar}] = Rs,
St = set_ssa_var(DestVar, #b_literal{val=unused}, St0),
{[make_uncond_branch(Fail),#cg_unreachable{}],St};
- {Catch,Fail} ->
+ FailCtx ->
%% Ordinary function call in a function body.
- Args = ssa_args(As, St0),
+ Args = ssa_args([Func|As], St0),
{Ret,St1} = new_ssa_var(R, St0),
- Func = call_target(Func0, Args, St0),
- Call = #b_set{anno=line_anno(Le),op=call,dst=Ret,args=[Func|Args]},
+ Call = #b_set{anno=line_anno(Le),op=call,dst=Ret,args=Args},
%% If this is a call to erlang:error(), MoreRs could be a
%% nonempty list of variables that each need a value.
St2 = foldl(fun(#k_var{name=Dummy}, S) ->
set_ssa_var(Dummy, #b_literal{val=unused}, S)
end, St1, MoreRs),
- case Catch of
- no_catch ->
- {[Call],St2};
- in_catch ->
- {TestIs,St} = make_cond_branch(succeeded, [Ret], Fail, St2),
- {[Call|TestIs],St}
- end
+
+ {TestIs,St} = make_succeeded(Ret, FailCtx, St2),
+ {[Call|TestIs],St}
end.
-enter_cg(Func0, As0, Le, St0) ->
- Anno = line_anno(Le),
- Func = call_target(Func0, As0, St0),
- As = ssa_args(As0, St0),
+enter_cg(Func, As0, Le, St0) ->
+ %% Adding a trampoline here would give us greater freedom in rewriting
+ %% calls, but doing so makes it difficult to tell tail calls apart from
+ %% body calls during code generation.
+ %%
+ %% We therefore skip the trampoline, reasoning that we've already left the
+ %% current function by the time an exception is thrown.
+ As = ssa_args([Func|As0], St0),
{Ret,St} = new_ssa_var('@ssa_ret', St0),
- Call = #b_set{anno=Anno,op=call,dst=Ret,args=[Func|As]},
+ Call = #b_set{anno=line_anno(Le),op=call,dst=Ret,args=As},
{[Call,#b_ret{arg=Ret}],St}.
-call_target(Func, As, St) ->
- Arity = length(As),
- case Func of
- #k_remote{mod=Mod0,name=Name0} ->
- Mod = ssa_arg(Mod0, St),
- Name = ssa_arg(Name0, St),
- #b_remote{mod=Mod,name=Name,arity=Arity};
- #k_local{name=Name} when is_atom(Name) ->
- #b_local{name=#b_literal{val=Name},arity=Arity};
- #k_var{}=Var ->
- ssa_arg(Var, St)
- end.
-
%% bif_cg(#k_bif{}, Le,State) -> {[Ainstr],State}.
%% Generate code for a guard BIF or primop.
-bif_cg(#k_bif{op=#k_internal{name=Name},args=As,ret=Rs}, Le, St) ->
- internal_cg(Name, As, Rs, Le, St);
-bif_cg(#k_bif{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=Name}},
+bif_cg(#k_bif{op=#k_internal{name=Name},args=As,ret=Rs}, _Le, St) ->
+ internal_cg(Name, As, Rs, St);
+bif_cg(#k_bif{op=#k_remote{mod=#k_literal{val=erlang},name=#k_literal{val=Name}},
args=As,ret=Rs}, Le, St) ->
bif_cg(Name, As, Rs, Le, St).
%% internal_cg(Bif, [Arg], [Ret], Le, State) ->
%% {[Ainstr],State}.
-internal_cg(make_fun, [Name0,Arity0|As], Rs, _Le, St0) ->
- #k_atom{val=Name} = Name0,
- #k_int{val=Arity} = Arity0,
- case Rs of
- [#k_var{name=Dst0}] ->
- {Dst,St} = new_ssa_var(Dst0, St0),
- Args = ssa_args(As, St),
- Local = #b_local{name=#b_literal{val=Name},arity=Arity},
- MakeFun = #b_set{op=make_fun,dst=Dst,args=[Local|Args]},
- {[MakeFun],St};
- [] ->
- {[],St0}
- end;
-internal_cg(bs_init_writable=I, As, [#k_var{name=Dst0}], _Le, St0) ->
- %% This behaves like a function call.
- {Dst,St} = new_ssa_var(Dst0, St0),
- Args = ssa_args(As, St),
- Set = #b_set{op=I,dst=Dst,args=Args},
- {[Set],St};
-internal_cg(build_stacktrace=I, As, [#k_var{name=Dst0}], _Le, St0) ->
- {Dst,St} = new_ssa_var(Dst0, St0),
- Args = ssa_args(As, St),
- Set = #b_set{op=I,dst=Dst,args=Args},
- {[Set],St};
-internal_cg(raise, As, [#k_var{name=Dst0}], _Le, St0) ->
+internal_cg(raise, As, [#k_var{name=Dst0}], St0) ->
Args = ssa_args(As, St0),
{Dst,St} = new_ssa_var(Dst0, St0),
Resume = #b_set{op=resume,dst=Dst,args=Args},
@@ -734,11 +714,41 @@ internal_cg(raise, As, [#k_var{name=Dst0}], _Le, St0) ->
Is = [Resume,make_uncond_branch(Catch),#cg_unreachable{}],
{Is,St}
end;
-internal_cg(raw_raise=I, As, [#k_var{name=Dst0}], _Le, St0) ->
+internal_cg(recv_peek_message, [], [#k_var{name=Succeeded0},
+ #k_var{name=Dst0}], St0) ->
+ {Dst,St1} = new_ssa_var(Dst0, St0),
+ St = new_succeeded_value(Succeeded0, Dst, St1),
+ Set = #b_set{op=peek_message,dst=Dst,args=[]},
+ {[Set],St};
+internal_cg(recv_wait_timeout, As, [#k_var{name=Succeeded0}], St0) ->
+ case ssa_args(As, St0) of
+ [#b_literal{val=0}] ->
+ %% If beam_ssa_opt is run (which is default), the
+ %% `wait_timeout` instruction will be removed if the
+ %% operand is a literal 0. However, if optimizations have
+ %% been turned off, we must not not generate a
+ %% `wait_timeout` instruction with a literal 0 timeout,
+ %% because the BEAM instruction will not handle it
+ %% correctly.
+ St = new_succeeded_value(Succeeded0, #b_literal{val=true}, St0),
+ {[],St};
+ Args ->
+ {Wait,St1} = new_ssa_var('@ssa_wait', St0),
+ St = new_succeeded_value(Succeeded0, Wait, St1),
+ Set = #b_set{op=wait_timeout,dst=Wait,args=Args},
+ {[Set],St}
+ end;
+internal_cg(Op, As, [#k_var{name=Dst0}], St0) when is_atom(Op) ->
%% This behaves like a function call.
{Dst,St} = new_ssa_var(Dst0, St0),
Args = ssa_args(As, St),
- Set = #b_set{op=I,dst=Dst,args=Args},
+ Set = #b_set{op=Op,dst=Dst,args=Args},
+ {[Set],St};
+internal_cg(Op, As, [], St0) when is_atom(Op) ->
+ %% This behaves like a function call.
+ {Dst,St} = new_ssa_var('@ssa_ignored', St0),
+ Args = ssa_args(As, St),
+ Set = #b_set{op=Op,dst=Dst,args=Args},
{[Set],St}.
bif_cg(Bif, As0, [#k_var{name=Dst0}], Le, St0) ->
@@ -752,8 +762,8 @@ bif_cg(Bif, As0, [#k_var{name=Dst0}], Le, St0) ->
I = #b_set{anno=line_anno(Le),op={bif,Bif},dst=Dst,args=As},
case erl_bifs:is_safe(erlang, Bif, length(As)) of
false ->
- Fail = bif_fail_label(St1),
- {Is,St} = make_cond_branch(succeeded, [Dst], Fail, St1),
+ FailCtx = fail_context(St1),
+ {Is,St} = make_succeeded(Dst, FailCtx, St1),
{[I|Is],St};
true->
{[I],St1}
@@ -779,50 +789,6 @@ bif_is_record_cg(Dst, Tuple, TagVal, ArityVal, St0) ->
Is = Is0 ++ [GetArity] ++ Is1 ++ [GetTag] ++ Is2 ++ Is3,
{Is,St}.
-%% recv_loop_cg(TimeOut, ReceiveVar, ReceiveMatch, TimeOutExprs,
-%% [Ret], Le, St) -> {[Ainstr],St}.
-
-recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, St0) ->
- %% Get labels.
- {Rl,St1} = new_label(St0),
- {Tl,St2} = new_label(St1),
- {Bl,St3} = new_label(St2),
- St4 = St3#cg{break=Bl,recv=Rl},
- {Ris,St5} = cg_recv_mesg(Rvar, Rm, Tl, Le, St4),
- {Wis,St6} = cg_recv_wait(Te, Tes, St5),
- {BreakVars,St} = new_ssa_vars(Rs, St6),
- {Ris ++ [{label,Tl}] ++ Wis ++
- [{label,Bl},#cg_phi{vars=BreakVars}],
- St#cg{break=St0#cg.break,recv=St0#cg.recv}}.
-
-%% cg_recv_mesg( ) -> {[Ainstr],St}.
-
-cg_recv_mesg(#k_var{name=R}, Rm, Tl, Le, St0) ->
- {Dst,St1} = new_ssa_var(R, St0),
- {Mis,St2} = match_cg(Rm, none, St1),
- RecvLbl = St1#cg.recv,
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Tl, St2),
- Is = [#b_br{anno=line_anno(Le),bool=#b_literal{val=true},
- succ=RecvLbl,fail=RecvLbl},
- {label,RecvLbl},
- #b_set{op=peek_message,dst=Dst}|TestIs],
- {Is++Mis,St}.
-
-%% cg_recv_wait(Te, Tes, St) -> {[Ainstr],St}.
-
-cg_recv_wait(#k_int{val=0}, Es, St0) ->
- {Tis,St} = cg(Es, St0),
- {[#b_set{op=timeout}|Tis],St};
-cg_recv_wait(Te, Es, St0) ->
- {Tis,St1} = cg(Es, St0),
- Args = [ssa_arg(Te, St1)],
- {WaitDst,St2} = new_ssa_var('@ssa_wait', St1),
- {WaitIs,St} = make_cond_branch(succeeded, [WaitDst], St1#cg.recv, St2),
- %% Infinite timeout will be optimized later.
- Is = [#b_set{op=wait_timeout,dst=WaitDst,args=Args}] ++ WaitIs ++
- [#b_set{op=timeout}] ++ Tis,
- {Is,St}.
-
%% try_cg(TryBlock, [BodyVar], TryBody, [ExcpVar], TryHandler, [Ret], St) ->
%% {[Ainstr],St}.
@@ -835,24 +801,106 @@ try_cg(Ta, Vs, Tb, Evs, Th, Rs, St0) ->
{SsaVs,St6} = new_ssa_vars(Vs, St5),
{SsaEvs,St7} = new_ssa_vars(Evs, St6),
{Ais,St8} = cg(Ta, St7#cg{break=B,catch_label=H}),
- St9 = St8#cg{break=E,catch_label=St7#cg.catch_label},
- {Bis,St10} = cg(Tb, St9),
- {His,St11} = cg(Th, St10),
- {BreakVars,St12} = new_ssa_vars(Rs, St11),
- {CatchedAgg,St} = new_ssa_var('@ssa_agg', St12),
- ExtractVs = extract_vars(SsaEvs, CatchedAgg, 0),
- KillTryTag = #b_set{op=kill_try_tag,args=[TryTag]},
- Args = [#b_literal{val='try'},TryTag],
- Handler = [{label,H},
- #b_set{op=landingpad,dst=CatchedAgg,args=Args}] ++
- ExtractVs ++ [KillTryTag],
- {[#b_set{op=new_try_tag,dst=TryTag,args=[#b_literal{val='try'}]},
- #b_br{bool=TryTag,succ=Next,fail=H},
- {label,Next}] ++ Ais ++
- [{label,B},#cg_phi{vars=SsaVs},KillTryTag] ++ Bis ++
- Handler ++ His ++
- [{label,E},#cg_phi{vars=BreakVars}],
- St#cg{break=St0#cg.break}}.
+
+ %% We try to avoid constructing a try/catch if the expression to
+ %% be evaluated don't have any side effects and if the error
+ %% reason is not explicitly matched.
+ %%
+ %% Starting in OTP 23, segment sizes in binary matching and keys
+ %% in map matching are allowed to be arbitrary guard
+ %% expressions. Those expressions are evaluated in a try/catch
+ %% so that matching can continue with the next clause if the evaluation
+ %% of such expression fails.
+ %%
+ %% It is not allowed to use try/catch during matching in a receive
+ %% (the try/catch would force the saving of fragile message references
+ %% to the stack frame). Therefore, avoiding creating try/catch is
+ %% not merely an optimization but necessary for correctness.
+
+ case {Vs,Tb,Th,is_guard_cg_safe_list(Ais)} of
+ {[#k_var{name=X}],#k_break{args=[#k_var{name=X}]},
+ #k_break{args=[#k_literal{}]},true} ->
+ %% There are no instructions that will clobber X registers
+ %% and the exception is not matched. Therefore, a
+ %% try/catch is not needed. This code is probably located
+ %% in a guard.
+ {ProtIs,St9} = guard_cg(Ta, H, St7#cg{break=B,bfail=H}),
+ {His,St10} = cg(Th, St9),
+ {RetVars,St} = new_ssa_vars(Rs, St10),
+ Is = ProtIs ++ [{label,H}] ++ His ++
+ [{label,B},#cg_phi{vars=RetVars}],
+ {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}};
+ {[#k_var{name=X}],#k_break{args=[#k_literal{}=SuccLit0,#k_var{name=X}]},
+ #k_break{args=[#k_literal{val=false},#k_literal{}]},true} ->
+ %% There are no instructions that will clobber X registers
+ %% and the exception is not matched. Therefore, a
+ %% try/catch is not needed. This code probably evaluates
+ %% a key expression in map matching.
+ {FinalLabel,St9} = new_label(St7),
+ {ProtIs,St10} = guard_cg(Ta, H, St9#cg{break=B,bfail=H}),
+ {His,St11} = cg(Th, St10#cg{break=FinalLabel}),
+ {RetVars,St12} = new_ssa_vars(Rs, St11),
+ {Result,St} = new_ssa_var('@ssa_result', St12),
+ SuccLit = ssa_arg(SuccLit0, St),
+ Is = ProtIs ++ [{label,H}] ++ His ++
+ [{label,B},
+ #cg_phi{vars=[Result]},
+ #cg_break{args=[SuccLit,Result],phi=FinalLabel},
+ {label,FinalLabel},
+ #cg_phi{vars=RetVars}],
+ {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}};
+ {_,#k_break{args=[]},#k_break{args=[]},true} ->
+ %% There are no instructions that will clobber X registers
+ %% and the exception is not matched. Therefore, a
+ %% try/catch is not needed. This code probably does the
+ %% size calculation for a segment in binary matching.
+ {ProtIs,St9} = guard_cg(Ta, H, St7#cg{break=B,bfail=H}),
+ {His,St10} = cg(Th, St9),
+ {RetVars,St} = new_ssa_vars(Rs, St10),
+ Is = ProtIs ++ [{label,H}] ++ His ++
+ [{label,B},#cg_phi{vars=RetVars}],
+ {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}};
+ {_,_,_,_} ->
+ %% The general try/catch (not in a guard).
+ St9 = St8#cg{break=E,catch_label=St7#cg.catch_label},
+ {Bis,St10} = cg(Tb, St9),
+ {His,St11} = cg(Th, St10),
+ {BreakVars,St12} = new_ssa_vars(Rs, St11),
+ {CatchedAgg,St13} = new_ssa_var('@ssa_agg', St12),
+ ExtractVs = extract_vars(SsaEvs, CatchedAgg, 0),
+ KillTryTag = #b_set{op=kill_try_tag,args=[TryTag]},
+ Args = [#b_literal{val='try'},TryTag],
+ Handler = [{label,H},
+ #b_set{op=landingpad,dst=CatchedAgg,args=Args}] ++
+ ExtractVs ++ [KillTryTag],
+ {[#b_set{op=new_try_tag,dst=TryTag,args=[#b_literal{val='try'}]},
+ #b_br{bool=TryTag,succ=Next,fail=H},
+ {label,Next}] ++ Ais ++
+ [{label,B},#cg_phi{vars=SsaVs},KillTryTag] ++ Bis ++
+ Handler ++ His ++
+ [{label,E},#cg_phi{vars=BreakVars}],
+ St13#cg{break=St0#cg.break}}
+ end.
+
+is_guard_cg_safe_list(Is) ->
+ all(fun is_guard_cg_safe/1, Is).
+
+is_guard_cg_safe(#b_set{op=call,args=Args}) ->
+ case Args of
+ [#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error},
+ arity=1}|_] ->
+ true;
+ _ ->
+ false
+ end;
+is_guard_cg_safe(#b_set{}=I) -> not beam_ssa:clobbers_xregs(I);
+is_guard_cg_safe(#b_br{}) -> true;
+is_guard_cg_safe(#b_switch{}) -> true;
+is_guard_cg_safe(#cg_break{}) -> true;
+is_guard_cg_safe(#cg_phi{}) -> true;
+is_guard_cg_safe({label,_}) -> true;
+is_guard_cg_safe(#cg_unreachable{}) -> false.
try_enter_cg(Ta, Vs, Tb, Evs, Th, St0) ->
{B,St1} = new_label(St0), %Body label
@@ -928,9 +976,9 @@ put_cg([#k_var{name=R}], #k_tuple{es=Es}, _Le, St0) ->
PutTuple = #b_set{op=put_tuple,dst=Ret,args=Args},
{[PutTuple],St};
put_cg([#k_var{name=R}], #k_binary{segs=Segs}, Le, St0) ->
- Fail = bif_fail_label(St0),
+ FailCtx = fail_context(St0),
{Dst,St1} = new_ssa_var(R, St0),
- cg_binary(Dst, Segs, Fail, Le, St1);
+ cg_binary(Dst, Segs, FailCtx, Le, St1);
put_cg([#k_var{name=R}], #k_map{op=Op,var=Map,
es=[#k_map_pair{key=#k_var{}=K,val=V}]},
Le, St0) ->
@@ -959,14 +1007,14 @@ put_cg([#k_var{name=R}], Con0, _Le, St0) ->
{[],St}.
put_cg_map(LineAnno, Op, SrcMap, Dst, List, St0) ->
- Fail = bif_fail_label(St0),
Args = [#b_literal{val=Op},SrcMap|List],
PutMap = #b_set{anno=LineAnno,op=put_map,dst=Dst,args=Args},
if
Op =:= assoc ->
{[PutMap],St0};
true ->
- {Is,St} = make_cond_branch(succeeded, [Dst], Fail, St0),
+ FailCtx = fail_context(St0),
+ {Is,St} = make_succeeded(Dst, FailCtx, St0),
{[PutMap|Is],St}
end.
@@ -974,18 +1022,18 @@ put_cg_map(LineAnno, Op, SrcMap, Dst, List, St0) ->
%%% Code generation for constructing binaries.
%%%
-cg_binary(Dst, Segs0, Fail, Le, St0) ->
- {PutCode0,SzCalc0,St1} = cg_bin_put(Segs0, Fail, St0),
+cg_binary(Dst, Segs0, FailCtx, Le, St0) ->
+ {PutCode0,SzCalc0,St1} = cg_bin_put(Segs0, FailCtx, St0),
LineAnno = line_anno(Le),
- Anno = Le#k.a,
+ Anno = Le,
case PutCode0 of
[#b_set{op=bs_put,dst=Bool,args=[_,_,Src,#b_literal{val=all}|_]},
#b_br{bool=Bool},
{label,_}|_] ->
#k_bin_seg{unit=Unit0,next=Segs} = Segs0,
Unit = #b_literal{val=Unit0},
- {PutCode,SzCalc1,St2} = cg_bin_put(Segs, Fail, St1),
- {_,SzVar,SzCode0,St3} = cg_size_calc(1, SzCalc1, Fail, St2),
+ {PutCode,SzCalc1,St2} = cg_bin_put(Segs, FailCtx, St1),
+ {_,SzVar,SzCode0,St3} = cg_size_calc(1, SzCalc1, FailCtx, St2),
SzCode = cg_bin_anno(SzCode0, LineAnno),
Args = case member(single_use, Anno) of
true ->
@@ -994,14 +1042,14 @@ cg_binary(Dst, Segs0, Fail, Le, St0) ->
[#b_literal{val=append},Src,SzVar,Unit]
end,
BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args},
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Fail, St3),
+ {TestIs,St} = make_succeeded(Dst, FailCtx, St3),
{SzCode ++ [BsInit] ++ TestIs ++ PutCode,St};
[#b_set{op=bs_put}|_] ->
- {Unit,SzVar,SzCode0,St2} = cg_size_calc(8, SzCalc0, Fail, St1),
+ {Unit,SzVar,SzCode0,St2} = cg_size_calc(8, SzCalc0, FailCtx, St1),
SzCode = cg_bin_anno(SzCode0, LineAnno),
Args = [#b_literal{val=new},SzVar,Unit],
BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args},
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Fail, St2),
+ {TestIs,St} = make_succeeded(Dst, FailCtx, St2),
{SzCode ++ [BsInit] ++ TestIs ++ PutCode0,St}
end.
@@ -1009,18 +1057,18 @@ cg_bin_anno([Set|Sets], Anno) ->
[Set#b_set{anno=Anno}|Sets];
cg_bin_anno([], _) -> [].
-%% cg_size_calc(PreferredUnit, SzCalc, Fail, St0) ->
+%% cg_size_calc(PreferredUnit, SzCalc, FailCtx, St0) ->
%% {ActualUnit,SizeVariable,SizeCode,St}.
%% Generate size calculation code.
-cg_size_calc(Unit, error, _Fail, St) ->
+cg_size_calc(Unit, error, _FailCtx, St) ->
{#b_literal{val=Unit},#b_literal{val=badarg},[],St};
-cg_size_calc(8, [{1,_}|_]=SzCalc, Fail, St) ->
- cg_size_calc(1, SzCalc, Fail, St);
-cg_size_calc(8, SzCalc, Fail, St0) ->
- {Var,Pre,St} = cg_size_calc_1(SzCalc, Fail, St0),
+cg_size_calc(8, [{1,_}|_]=SzCalc, FailCtx, St) ->
+ cg_size_calc(1, SzCalc, FailCtx, St);
+cg_size_calc(8, SzCalc, FailCtx, St0) ->
+ {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0),
{#b_literal{val=8},Var,Pre,St};
-cg_size_calc(1, SzCalc0, Fail, St0) ->
+cg_size_calc(1, SzCalc0, FailCtx, St0) ->
SzCalc = map(fun({8,#b_literal{val=Size}}) ->
{1,#b_literal{val=8*Size}};
({8,{{bif,byte_size},Src}}) ->
@@ -1030,54 +1078,54 @@ cg_size_calc(1, SzCalc0, Fail, St0) ->
({_,_}=Pair) ->
Pair
end, SzCalc0),
- {Var,Pre,St} = cg_size_calc_1(SzCalc, Fail, St0),
+ {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0),
{#b_literal{val=1},Var,Pre,St}.
-cg_size_calc_1(SzCalc, Fail, St0) ->
- cg_size_calc_2(SzCalc, #b_literal{val=0}, Fail, St0).
+cg_size_calc_1(SzCalc, FailCtx, St0) ->
+ cg_size_calc_2(SzCalc, #b_literal{val=0}, FailCtx, St0).
-cg_size_calc_2([{_,{'*',Unit,{_,_}=Bif}}|T], Sum0, Fail, St0) ->
- {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, Fail, St0),
- {BifDst,Pre1,St2} = cg_size_bif(Bif, Fail, St1),
- {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, Unit, Fail, St2),
+cg_size_calc_2([{_,{'*',Unit,{_,_}=Bif}}|T], Sum0, FailCtx, St0) ->
+ {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0),
+ {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1),
+ {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, Unit, FailCtx, St2),
{Sum,Pre0++Pre1++Pre2,St};
-cg_size_calc_2([{_,#b_literal{}=Sz}|T], Sum0, Fail, St0) ->
- {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, Fail, St0),
- {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, Fail, St1),
+cg_size_calc_2([{_,#b_literal{}=Sz}|T], Sum0, FailCtx, St0) ->
+ {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0),
+ {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1),
{Sum,Pre0++Pre,St};
-cg_size_calc_2([{_,#b_var{}=Sz}|T], Sum0, Fail, St0) ->
- {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, Fail, St0),
- {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, Fail, St1),
+cg_size_calc_2([{_,#b_var{}=Sz}|T], Sum0, FailCtx, St0) ->
+ {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0),
+ {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1),
{Sum,Pre0++Pre,St};
-cg_size_calc_2([{_,{_,_}=Bif}|T], Sum0, Fail, St0) ->
- {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, Fail, St0),
- {BifDst,Pre1,St2} = cg_size_bif(Bif, Fail, St1),
- {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, #b_literal{val=1}, Fail, St2),
+cg_size_calc_2([{_,{_,_}=Bif}|T], Sum0, FailCtx, St0) ->
+ {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0),
+ {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1),
+ {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, #b_literal{val=1}, FailCtx, St2),
{Sum,Pre0++Pre1++Pre2,St};
-cg_size_calc_2([], Sum, _Fail, St) ->
+cg_size_calc_2([], Sum, _FailCtx, St) ->
{Sum,[],St}.
-cg_size_bif(#b_var{}=Var, _Fail, St) ->
+cg_size_bif(#b_var{}=Var, _FailCtx, St) ->
{Var,[],St};
-cg_size_bif({Name,Src}, Fail, St0) ->
+cg_size_bif({Name,Src}, FailCtx, St0) ->
{Dst,St1} = new_ssa_var('@ssa_bif', St0),
Bif = #b_set{op=Name,dst=Dst,args=[Src]},
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Fail, St1),
+ {TestIs,St} = make_succeeded(Dst, FailCtx, St1),
{Dst,[Bif|TestIs],St}.
-cg_size_add(#b_literal{val=0}, Val, #b_literal{val=1}, _Fail, St) ->
+cg_size_add(#b_literal{val=0}, Val, #b_literal{val=1}, _FailCtx, St) ->
{Val,[],St};
-cg_size_add(A, B, Unit, Fail, St0) ->
+cg_size_add(A, B, Unit, FailCtx, St0) ->
{Dst,St1} = new_ssa_var('@ssa_sum', St0),
- {TestIs,St} = make_cond_branch(succeeded, [Dst], Fail, St1),
+ {TestIs,St} = make_succeeded(Dst, FailCtx, St1),
BsAdd = #b_set{op=bs_add,dst=Dst,args=[A,B,Unit]},
{Dst,[BsAdd|TestIs],St}.
-cg_bin_put(Seg, Fail, St) ->
- cg_bin_put_1(Seg, Fail, [], [], St).
+cg_bin_put(Seg, FailCtx, St) ->
+ cg_bin_put_1(Seg, FailCtx, [], [], St).
cg_bin_put_1(#k_bin_seg{size=Size0,unit=U,type=T,flags=Fs,seg=Src0,next=Next},
- Fail, Acc, SzCalcAcc, St0) ->
+ FailCtx, Acc, SzCalcAcc, St0) ->
[Src,Size] = ssa_args([Src0,Size0], St0),
NeedSize = bs_need_size(T),
TypeArg = #b_literal{val=T},
@@ -1087,9 +1135,12 @@ cg_bin_put_1(#k_bin_seg{size=Size0,unit=U,type=T,flags=Fs,seg=Src0,next=Next},
true -> [TypeArg,Flags,Src,Size,Unit];
false -> [TypeArg,Flags,Src]
end,
- {Is,St} = make_cond_branch(bs_put, Args, Fail, St0),
+ %% bs_put has its own 'succeeded' logic, and should always jump directly to
+ %% the fail label regardless of whether it's in a catch or not.
+ {_, FailLbl} = FailCtx,
+ {Is,St} = make_cond_branch(bs_put, Args, FailLbl, St0),
SzCalc = bin_size_calc(T, Src, Size, U),
- cg_bin_put_1(Next, Fail, reverse(Is, Acc), [SzCalc|SzCalcAcc], St);
+ cg_bin_put_1(Next, FailCtx, reverse(Is, Acc), [SzCalc|SzCalcAcc], St);
cg_bin_put_1(#k_bin_end{}, _, Acc, SzCalcAcc, St) ->
SzCalc = fold_size_calc(SzCalcAcc, 0, []),
{reverse(Acc),SzCalc,St}.
@@ -1139,12 +1190,18 @@ fold_size_calc([], Bits, Acc) ->
ssa_args(As, St) ->
[ssa_arg(A, St) || A <- As].
-ssa_arg(#k_var{name=V}, #cg{vars=Vars}) -> maps:get(V, Vars);
+ssa_arg(#k_var{name=V}, #cg{vars=Vars}) -> map_get(V, Vars);
ssa_arg(#k_literal{val=V}, _) -> #b_literal{val=V};
-ssa_arg(#k_atom{val=V}, _) -> #b_literal{val=V};
-ssa_arg(#k_float{val=V}, _) -> #b_literal{val=V};
-ssa_arg(#k_int{val=V}, _) -> #b_literal{val=V};
-ssa_arg(#k_nil{}, _) -> #b_literal{val=[]}.
+ssa_arg(#k_remote{mod=Mod0,name=Name0,arity=Arity}, St) ->
+ Mod = ssa_arg(Mod0, St),
+ Name = ssa_arg(Name0, St),
+ #b_remote{mod=Mod,name=Name,arity=Arity};
+ssa_arg(#k_local{name=Name,arity=Arity}, _) when is_atom(Name) ->
+ #b_local{name=#b_literal{val=Name},arity=Arity}.
+
+new_succeeded_value(VarBase, Var, #cg{vars=Vars0}=St) ->
+ Vars = Vars0#{VarBase=>{succeeded,Var}},
+ St#cg{vars=Vars}.
new_ssa_vars(Vs, St) ->
mapfoldl(fun(#k_var{name=V}, S) ->
@@ -1178,23 +1235,20 @@ new_label(#cg{lcount=Next}=St) ->
%% current filename and line number. The annotation should be
%% included in any operation that could cause an exception.
-line_anno(#k{a=Anno}) ->
- line_anno_1(Anno).
-
-line_anno_1([Line,{file,Name}]) when is_integer(Line) ->
- line_anno_2(Name, Line);
-line_anno_1([_|_]=A) ->
+line_anno([Line,{file,Name}]) when is_integer(Line) ->
+ line_anno_1(Name, Line);
+line_anno([_|_]=A) ->
{Name,Line} = find_loc(A, no_file, 0),
- line_anno_2(Name, Line);
-line_anno_1([]) ->
+ line_anno_1(Name, Line);
+line_anno([]) ->
#{}.
-line_anno_2(no_file, _) ->
+line_anno_1(no_file, _) ->
#{};
-line_anno_2(_, 0) ->
+line_anno_1(_, 0) ->
%% Missing line number or line number 0.
#{};
-line_anno_2(Name, Line) ->
+line_anno_1(Name, Line) ->
#{location=>{Name,Line}}.
find_loc([Line|T], File, _) when is_integer(Line) ->
@@ -1220,27 +1274,37 @@ finalize(Asm0, St0) ->
{Asm,St} = fix_sets(Asm1, [], St0),
{build_map(Asm),St}.
+%% fix_phis(Is0) -> Is.
+%% Rewrite #cg_break{} and #cg_phi{} records to #b_set{} records.
+%% A #cg_break{} is rewritten to an unconditional branch, and
+%% and a #cg_phi{} is rewritten to one or more phi nodes.
+
fix_phis(Is) ->
fix_phis_1(Is, none, #{}).
-fix_phis_1([{label,L},#cg_phi{vars=[]}=Phi|Is0], _Lbl, Map0) ->
- case maps:is_key(L, Map0) of
- false ->
- %% No #cg_break{} references this label. Nothing else can
- %% reference it, so it can be safely be removed.
- {Is,Map} = drop_upto_label(Is0, Map0),
- fix_phis_1(Is, none, Map);
- true ->
- %% There is a break referencing this label; probably caused
- %% by a try/catch whose return value is ignored.
- [{label,L}|fix_phis_1([Phi|Is0], L, Map0)]
+fix_phis_1([{label,Lbl},#cg_phi{vars=Vars}|Is0], _Lbl, Map0) ->
+ case Map0 of
+ #{Lbl:=Pairs} ->
+ %% This phi node was referenced by at least one #cg_break{}.
+ %% Create the phi nodes.
+ Phis = gen_phis(Vars, Pairs),
+ Map = maps:remove(Lbl, Map0),
+ [{label,Lbl}] ++ Phis ++ fix_phis_1(Is0, Lbl, Map);
+ #{} ->
+ %% No #cg_break{} instructions reference this label.
+ %% #cg_break{} instructions must reference the labels for
+ %% #cg_phi{} instructions; therefore this label is
+ %% unreachable and can be dropped.
+ Is = drop_upto_label(Is0),
+ fix_phis_1(Is, none, Map0)
end;
fix_phis_1([{label,L}=I|Is], _Lbl, Map) ->
[I|fix_phis_1(Is, L, Map)];
-fix_phis_1([#cg_unreachable{}|Is0], _Lbl, Map0) ->
- {Is,Map} = drop_upto_label(Is0, Map0),
+fix_phis_1([#cg_unreachable{}|Is0], _Lbl, Map) ->
+ Is = drop_upto_label(Is0),
fix_phis_1(Is, none, Map);
fix_phis_1([#cg_break{args=Args,phi=Target}|Is], Lbl, Map) when is_integer(Lbl) ->
+ %% Pair each argument with the label for this block and save in the map.
Pairs1 = case Map of
#{Target:=Pairs0} -> Pairs0;
#{} -> []
@@ -1248,17 +1312,6 @@ fix_phis_1([#cg_break{args=Args,phi=Target}|Is], Lbl, Map) when is_integer(Lbl)
Pairs = [[{Arg,Lbl} || Arg <- Args]|Pairs1],
I = make_uncond_branch(Target),
[I|fix_phis_1(Is, none, Map#{Target=>Pairs})];
-fix_phis_1([#cg_phi{vars=Vars}|Is0], Lbl, Map0) ->
- Pairs = maps:get(Lbl, Map0),
- Map1 = maps:remove(Lbl, Map0),
- case gen_phis(Vars, Pairs) of
- [#b_set{op=phi,args=[]}] ->
- {Is,Map} = drop_upto_label(Is0, Map1),
- Ret = #b_ret{arg=#b_literal{val=unreachable}},
- [Ret|fix_phis_1(Is, none, Map)];
- Phis ->
- Phis ++ fix_phis_1(Is0, Lbl, Map1)
- end;
fix_phis_1([I|Is], Lbl, Map) ->
[I|fix_phis_1(Is, Lbl, Map)];
fix_phis_1([], _, Map) ->
@@ -1267,6 +1320,7 @@ fix_phis_1([], _, Map) ->
gen_phis([V|Vs], Preds0) ->
{Pairs,Preds} = collect_preds(Preds0, [], []),
+ [_|_] = Pairs, %Assertion.
[#b_set{op=phi,dst=V,args=Pairs}|gen_phis(Vs, Preds)];
gen_phis([], _) -> [].
@@ -1275,6 +1329,36 @@ collect_preds([[First|Rest]|T], ColAcc, RestAcc) ->
collect_preds([], ColAcc, RestAcc) ->
{keysort(2, ColAcc),RestAcc}.
+drop_upto_label([{label,_}|_]=Is) -> Is;
+drop_upto_label([_|Is]) -> drop_upto_label(Is).
+
+%% fix_sets(Is0, Acc, St0) -> {Is,St}.
+%% Ensure that #b_set.dst is filled in with a proper variable.
+%% (For convenience, for instructions that don't have a useful return value,
+%% the code generator would set #b_set.dst to `none`.)
+
+fix_sets([#b_set{op=Op,dst=Dst}=Set,#b_ret{arg=Dst}=Ret|Is], Acc, St) ->
+ NoValue = case Op of
+ remove_message -> true;
+ timeout -> true;
+ _ -> false
+ end,
+ case NoValue of
+ true ->
+ %% An instruction without value was used in effect
+ %% context in `after` block. Example:
+ %%
+ %% try
+ %% ...
+ %% after
+ %% receive _ -> ignored end
+ %% end,
+ %% ok.
+ %%
+ fix_sets(Is, [Ret#b_ret{arg=#b_literal{val=ok}},Set|Acc], St);
+ false ->
+ fix_sets(Is, [Ret,Set|Acc], St)
+ end;
fix_sets([#b_set{dst=none}=Set|Is], Acc, St0) ->
{Dst,St} = new_ssa_var('@ssa_ignored', St0),
I = Set#b_set{dst=Dst},
@@ -1284,6 +1368,10 @@ fix_sets([I|Is], Acc, St) ->
fix_sets([], Acc, St) ->
{reverse(Acc),St}.
+%% build_map(Is) -> #{}.
+%% Split up the sequential instruction stream into blocks and
+%% store them in a map.
+
build_map(Is) ->
Blocks = build_graph_1(Is, [], []),
maps:from_list(Blocks).
@@ -1301,16 +1389,3 @@ make_blocks(Lbls, [Last|Is0]) ->
Is = reverse(Is0),
Block = #b_blk{is=Is,last=Last},
[{L,Block} || L <- Lbls].
-
-drop_upto_label([{label,_}|_]=Is, Map) ->
- {Is,Map};
-drop_upto_label([#cg_break{phi=Target}|Is], Map) ->
- Pairs = case Map of
- #{Target:=Pairs0} -> Pairs0;
- #{} -> []
- end,
- drop_upto_label(Is, Map#{Target=>Pairs});
-drop_upto_label([_|Is], Map) ->
- drop_upto_label(Is, Map).
-
-k_get_anno(Thing) -> element(2, Thing).
diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl
index 6492d1e1bf..9a8ae9b407 100644
--- a/lib/compiler/src/beam_ssa.erl
+++ b/lib/compiler/src/beam_ssa.erl
@@ -21,12 +21,13 @@
-module(beam_ssa).
-export([add_anno/3,get_anno/2,get_anno/3,
- clobbers_xregs/1,def/2,def_used/2,
+ clobbers_xregs/1,def/2,def_unused/3,
definitions/1,
dominators/1,common_dominators/3,
flatmapfold_instrs_rpo/4,
fold_po/3,fold_po/4,fold_rpo/3,fold_rpo/4,
fold_instrs_rpo/4,
+ is_loop_header/1,
linearize/1,
mapfold_blocks_rpo/4,
mapfold_instrs_rpo/4,
@@ -79,7 +80,7 @@
-type var_base() :: atom() | non_neg_integer().
-type literal_value() :: atom() | integer() | float() | list() |
- nil() | tuple() | map() | binary().
+ nil() | tuple() | map() | binary() | fun().
-type op() :: {'bif',atom()} | {'float',float_op()} | prim_op() | cg_prim_op().
-type anno() :: #{atom() := any()}.
@@ -101,7 +102,7 @@
'bs_match' | 'bs_put' | 'bs_start_match' | 'bs_test_tail' |
'bs_utf16_size' | 'bs_utf8_size' | 'build_stacktrace' |
'call' | 'catch_end' |
- 'extract' |
+ 'extract' | 'exception_trampoline' |
'get_hd' | 'get_map_element' | 'get_tl' | 'get_tuple_element' |
'has_map_field' |
'is_nonempty_list' | 'is_tagged_tuple' |
@@ -120,10 +121,11 @@
%% Primops only used internally during code generation.
-type cg_prim_op() :: 'bs_get' | 'bs_get_position' | 'bs_match_string' |
'bs_restore' | 'bs_save' | 'bs_set_position' | 'bs_skip' |
- 'copy' | 'put_tuple_arity' | 'put_tuple_element' |
- 'put_tuple_elements' | 'set_tuple_element'.
+ 'copy' | 'match_fail' | 'put_tuple_arity' |
+ 'put_tuple_element' | 'put_tuple_elements' |
+ 'set_tuple_element'.
--import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1,umerge/1]).
+-import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1,sort/1]).
-spec add_anno(Key, Value, Construct) -> Construct when
Key :: atom(),
@@ -174,6 +176,8 @@ clobbers_xregs(#b_set{op=Op}) ->
make_fun -> true;
peek_message -> true;
raw_raise -> true;
+ timeout -> true;
+ wait_timeout -> true;
_ -> false
end.
@@ -188,13 +192,17 @@ no_side_effect(#b_set{op=Op}) ->
case Op of
{bif,_} -> true;
{float,get} -> true;
+ bs_add -> true;
bs_init -> true;
+ bs_init_writable -> true;
bs_extract -> true;
bs_match -> true;
bs_start_match -> true;
bs_test_tail -> true;
bs_get_tail -> true;
bs_put -> true;
+ bs_utf16_size -> true;
+ bs_utf8_size -> true;
extract -> true;
get_hd -> true;
get_tl -> true;
@@ -211,6 +219,18 @@ no_side_effect(#b_set{op=Op}) ->
_ -> false
end.
+%% is_loop_header(#b_set{}) -> true|false.
+%% Test whether this instruction is a loop header.
+
+-spec is_loop_header(b_set()) -> boolean().
+
+is_loop_header(#b_set{op=Op}) ->
+ case Op of
+ peek_message -> true;
+ wait_timeout -> true;
+ _ -> false
+ end.
+
-spec predecessors(Blocks) -> #{BlockNumber:=[Predecessor]} when
Blocks :: block_map(),
BlockNumber :: label(),
@@ -299,7 +319,7 @@ normalize(#b_switch{arg=Arg,fail=Fail,list=List}=Sw) ->
#b_var{} when List =:= [] ->
#b_br{bool=#b_literal{val=true},succ=Fail,fail=Fail};
#b_var{} ->
- Sw
+ Sw#b_switch{list=sort(List)}
end;
normalize(#b_ret{}=Ret) ->
Ret.
@@ -319,17 +339,18 @@ def(Ls, Blocks) ->
Blks = [map_get(L, Blocks) || L <- Top],
def_1(Blks, []).
--spec def_used(Ls, Blocks) -> {Def,Used} when
+-spec def_unused(Ls, Used, Blocks) -> {Def,Unused} when
Ls :: [label()],
+ Used :: ordsets:ordset(var_name()),
Blocks :: block_map(),
Def :: ordsets:ordset(var_name()),
- Used :: ordsets:ordset(var_name()).
+ Unused :: ordsets:ordset(var_name()).
-def_used(Ls, Blocks) ->
+def_unused(Ls, Unused, Blocks) ->
Top = rpo(Ls, Blocks),
Blks = [map_get(L, Blocks) || L <- Top],
Preds = cerl_sets:from_list(Top),
- def_used_1(Blks, Preds, [], []).
+ def_unused_1(Blks, Preds, [], Unused).
%% dominators(BlockMap) -> {Dominators,Numbering}.
%% Calculate the dominator tree, returning a map where each entry
@@ -651,34 +672,28 @@ is_commutative('=/=') -> true;
is_commutative('/=') -> true;
is_commutative(_) -> false.
-def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, UsedAcc) ->
- {Def,Used} = def_used_is(Is, Preds, Def0, used(Last)),
- case Used of
- [] ->
- def_used_1(Bs, Preds, Def, UsedAcc);
- [_|_] ->
- def_used_1(Bs, Preds, Def, [Used|UsedAcc])
- end;
-def_used_1([], _Preds, Def0, UsedAcc) ->
- Def = ordsets:from_list(Def0),
- Used = umerge(UsedAcc),
- {Def,Used}.
+def_unused_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, Unused0) ->
+ Unused1 = ordsets:subtract(Unused0, used(Last)),
+ {Def,Unused} = def_unused_is(Is, Preds, Def0, Unused1),
+ def_unused_1(Bs, Preds, Def, Unused);
+def_unused_1([], _Preds, Def, Unused) ->
+ {ordsets:from_list(Def), Unused}.
-def_used_is([#b_set{op=phi,dst=Dst,args=Args}|Is],
- Preds, Def0, Used0) ->
+def_unused_is([#b_set{op=phi,dst=Dst,args=Args}|Is],
+ Preds, Def0, Unused0) ->
Def = [Dst|Def0],
%% We must be careful to only include variables that will
%% be used when arriving from one of the predecessor blocks
%% in Preds.
- Used1 = [V || {#b_var{}=V,L} <- Args, cerl_sets:is_element(L, Preds)],
- Used = ordsets:union(ordsets:from_list(Used1), Used0),
- def_used_is(Is, Preds, Def, Used);
-def_used_is([#b_set{dst=Dst}=I|Is], Preds, Def0, Used0) ->
+ Unused1 = [V || {#b_var{}=V,L} <- Args, cerl_sets:is_element(L, Preds)],
+ Unused = ordsets:subtract(Unused0, ordsets:from_list(Unused1)),
+ def_unused_is(Is, Preds, Def, Unused);
+def_unused_is([#b_set{dst=Dst}=I|Is], Preds, Def0, Unused0) ->
Def = [Dst|Def0],
- Used = ordsets:union(used(I), Used0),
- def_used_is(Is, Preds, Def, Used);
-def_used_is([], _Preds, Def, Used) ->
- {Def,Used}.
+ Unused = ordsets:subtract(Unused0, used(I)),
+ def_unused_is(Is, Preds, Def, Unused);
+def_unused_is([], _Preds, Def, Unused) ->
+ {Def,Unused}.
def_1([#b_blk{is=Is}|Bs], Def0) ->
Def = def_is(Is, Def0),
diff --git a/lib/compiler/src/beam_ssa.hrl b/lib/compiler/src/beam_ssa.hrl
index fa76b08453..509a94135e 100644
--- a/lib/compiler/src/beam_ssa.hrl
+++ b/lib/compiler/src/beam_ssa.hrl
@@ -62,5 +62,13 @@
-record(b_local, {name :: beam_ssa:b_literal(),
arity :: non_neg_integer()}).
-%% If this block exists, it calls erlang:error(badarg).
--define(BADARG_BLOCK, 1).
+%% This is a psuedo-block used to express that certain instructions and BIFs
+%% throw exceptions on failure. The code generator rewrites all branches to
+%% this block to {f,0} which causes the instruction to throw an exception
+%% instead of branching.
+%%
+%% Since this is not an ordinary block, it's illegal to merge it with other
+%% blocks, and jumps are only valid when we know that an exception will be
+%% thrown by the operation that branches here; the *block itself* does not
+%% throw an exception.
+-define(EXCEPTION_BLOCK, 1).
diff --git a/lib/compiler/src/beam_ssa_bool.erl b/lib/compiler/src/beam_ssa_bool.erl
new file mode 100644
index 0000000000..0860029c59
--- /dev/null
+++ b/lib/compiler/src/beam_ssa_bool.erl
@@ -0,0 +1,1625 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% The purpose of this pass is to optimize boolean expressions in
+%% guards. Instead of evaluating a boolean expression and finally
+%% comparing it to 'true', evaluate the expression using control flow.
+%%
+%% This pass is run directly after conversion to SSA code because some
+%% optimizations in beam_ssa_opt (especially sinking of
+%% get_tuple_element instructions) would prevent these optimizations
+%% or at least make them much more difficult to perform.
+%%
+%% As an example, take the guard:
+%%
+%% when is_integer(V0), is_atom(V1) ->
+%%
+%% The unoptimized SSA code translated to pseudo BEAM code would look
+%% like:
+%%
+%% bif is_integer V0 => Bool0
+%% bif is_atom V1 => Bool1
+%% bif and Bool0 Bool1 => Bool
+%% test Bool =:= true else goto Fail
+%% ...
+%% Fail:
+%% ...
+%%
+%% The optimized code would look like:
+%%
+%% test is_integer V0 else goto Fail
+%% test is_atom V1 else goto Fail
+%% ...
+%% Fail:
+%% ...
+%%
+%% An 'or' operation is only slightly more complicated:
+%%
+%% test is_integer V0 else goto NotFailedYet
+%% goto Success
+%%
+%% NotFailedYet:
+%% test is_atom V1 else goto Fail
+%%
+%% Success:
+%% ...
+%% Fail:
+%% ...
+%%
+%% The unoptimized SSA code for the first example looks like:
+%%
+%% 0:
+%% _2 = bif:is_integer _0
+%% _3 = bif:is_atom _1
+%% _7 = bif:'and' _2, _3
+%% @ssa_bool = succeeded _7
+%% br @ssa_bool, label 4, label 3
+%%
+%% 4:
+%% @ssa_bool:5 = bif:'=:=' _7, literal true
+%% br @ssa_bool:5, label 6, label 3
+%%
+%% 6:
+%% ret literal ok
+%%
+%% 3: Error.
+%% ...
+%%
+%% The optimized SSA code looks like:
+%%
+%% 0:
+%% _2 = bif:is_integer _0
+%% br _2, label 11, label 3
+%%
+%% 11:
+%% _3 = bif:is_atom _1
+%% br _3, label 6, label 3
+%%
+%% 6:
+%% ret literal ok
+%%
+%% 3: Error.
+%% ...
+
+-module(beam_ssa_bool).
+-export([module/2]).
+
+-import(lists, [all/2,foldl/3,keyfind/3,last/1,partition/2,
+ reverse/1,reverse/2,sort/1]).
+
+-include("beam_ssa.hrl").
+
+-record(st, {defs=#{},
+ ldefs=#{},
+ count :: beam_ssa:label(),
+ dom,
+ uses}).
+
+-spec module(beam_ssa:b_module(), [compile:option()]) ->
+ {'ok',beam_ssa:b_module()}.
+
+module(#b_module{body=Fs0}=Module, _Opts) ->
+ Fs = [function(F) || F <- Fs0],
+ {ok,Module#b_module{body=Fs}}.
+
+function(#b_function{anno=Anno}=F) ->
+ try
+ opt_function(F)
+ catch
+ Class:Error:Stack ->
+ #{func_info:={_,Name,Arity}} = Anno,
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+opt_function(#b_function{bs=Blocks0,cnt=Count0}=F) ->
+ {Blocks1,Count1} = pre_opt(Blocks0, Count0),
+ DefVars = interesting_defs(Blocks1),
+ if
+ map_size(DefVars) > 1 ->
+ Dom = beam_ssa:dominators(Blocks1),
+ Uses = beam_ssa:uses(Blocks1),
+ St0 = #st{defs=DefVars,count=Count1,dom=Dom,uses=Uses},
+ {Blocks,St} = bool_opt(Blocks1, St0),
+ Count = St#st.count,
+ F#b_function{bs=Blocks,cnt=Count};
+ true ->
+ %% There are no boolean operators that can be optimized in
+ %% this function.
+ F#b_function{bs=Blocks1,cnt=Count1}
+ end.
+
+%%%
+%%% Do some optimizations to help the main boolean optimization pass:
+%%%
+%%% * Remove `succeeded` instructions that can't fail after `and`,
+%%% `or`, and `not`. The main optimization pass can only optimize
+%%% boolean operators that are known not to fail.
+%%%
+%%% * Rewrite a boolean #b_switch{} to a #b_br{} if the fail label
+%%% can't be reached or is not important. (The main optimization
+%%% can't handle #b_switch{}.)
+%%%
+%%% * Simplify phi nodes, eliminating them if they only have one
+%%% value. Also annotate phi nodes that are known to evaluate
+%%% to a boolean.
+%%%
+
+-type var() :: beam_ssa:b_var().
+
+%% Note: We use the substitution map for both substitutions and type
+%% information. If the associated value for a variable is a #b_set{},
+%% it means that the value is a boolean.
+-type pre_sub_val() ::
+ beam_ssa:value() | %Value to be substituted.
+ beam_ssa:b_set() | %This variable is a boolean.
+ {'true_or_any',beam_ssa:label()} |
+ '=:='.
+
+-type pre_sub_map() :: #{'uses' => {'uses',beam_ssa:block_map() | list()},
+ var() => pre_sub_val()}.
+
+pre_opt(Blocks, Count) ->
+ Top = beam_ssa:rpo(Blocks),
+
+ %% Collect information to help the pre_opt pass to optimize
+ %% `switch` instructions.
+ Sub0 = #{uses => {uses,Blocks}},
+ Sub1 = get_phi_info(Top, Blocks, Sub0),
+ Sub = maps:remove(uses, Sub1),
+
+ %% Now do the actual optimizations.
+ Reached = gb_sets:singleton(hd(Top)),
+ pre_opt(Top, Sub, Reached, Count, Blocks).
+
+-spec get_phi_info(Ls, Blocks, Sub0) -> Sub when
+ Ls :: [beam_ssa:label()],
+ Blocks :: beam_ssa:block_map(),
+ Sub0 :: pre_sub_map(),
+ Sub :: pre_sub_map().
+
+%% get_phi_info([Label], Blocks, Sub0) -> Sub.
+%% Collect information to help us optimize `switch` instructions
+%% such as:
+%%
+%% switch SomeVar, label _, [ {literal false, _ }, {literal true, _ } ]
+%% .
+%% .
+%% .
+%% PhiVar = phi { SomeVar, _ }, { literal fail, _ }, { literal false, _}
+%% EqBool = bif:'=:=' PhiVar, literal true
+%%
+%% Here it can be seen that `SomeVar` is compared to `true`. If
+%% `SomeVar` is not `true`, it does not matter whether its value is
+%% `false` or some other value. That means that the `switch` can be
+%% replaced with a two-way `br`:
+%%
+%% NewBoolVar = bif:'=:=' SomeVar, literal true
+%% br NewBoolVar, label _, label _
+%%
+%% For this example, the value {true_or_any,LabelOfPhiBlock} will be
+%% added for the key `SomeVar` in the substitution map.
+
+get_phi_info([L|Ls], Blocks, Sub0) ->
+ Sub = get_phi_info(Ls, Blocks, Sub0),
+ #b_blk{is=Is} = map_get(L, Blocks),
+ get_phi_info_is(Is, L, Sub);
+get_phi_info([], _, Sub) -> Sub.
+
+get_phi_info_is([I|Is], From, Sub0) ->
+ Sub = get_phi_info_is(Is, From, Sub0),
+ get_phi_info_instr(I, From, Sub);
+get_phi_info_is([], _, Sub) -> Sub.
+
+get_phi_info_instr(#b_set{op={bif,'=:='},
+ args=[#b_var{}=Bool,#b_literal{val=true}]},
+ _From, Sub) ->
+ Sub#{Bool=>'=:='};
+get_phi_info_instr(#b_set{op=phi,dst=Dst,args=Args}, From, Sub0) ->
+ {Safe,Sub} =
+ case Sub0 of
+ #{Dst:='=:='} ->
+ get_phi_info_single_use(Dst, Sub0);
+ #{Dst:={true_or_any,_}} ->
+ get_phi_info_single_use(Dst, Sub0);
+ #{} ->
+ {false,Sub0}
+ end,
+ case Safe of
+ true ->
+ foldl(fun({#b_var{}=V,_}, A) ->
+ A#{V => {true_or_any,From}};
+ (_, A) -> A
+ end, Sub, Args);
+ false -> Sub
+ end;
+get_phi_info_instr(_, _, Sub) -> Sub.
+
+get_phi_info_single_use(Var, Sub) ->
+ case map_get(uses, Sub) of
+ Uses when is_map(Uses) ->
+ {case Uses of
+ #{Var:=[_]} -> true;
+ #{Var:=[_|_]} -> false
+ end,Sub};
+ {uses,Blocks} ->
+ Uses = beam_ssa:uses(Blocks),
+ get_phi_info_single_use(Var, Sub#{uses => Uses})
+ end.
+
+-spec pre_opt(Ls, Sub, Reached, Count0, Blocks0) -> {Blocks,Count} when
+ Ls :: [beam_ssa:label()],
+ Reached :: gb_sets:set(beam_ssa:label()),
+ Count0 :: beam_ssa:label(),
+ Blocks0 :: beam_ssa:block_map(),
+ Sub :: pre_sub_map(),
+ Count :: beam_ssa:label(),
+ Blocks :: beam_ssa:block_map().
+
+pre_opt([L|Ls], Sub0, Reached0, Count0, Blocks) ->
+ case gb_sets:is_member(L, Reached0) of
+ false ->
+ %% This block will never be reached.
+ pre_opt(Ls, Sub0, Reached0, Count0, maps:remove(L, Blocks));
+ true ->
+ #b_blk{is=Is0,last=Last0} = Blk0 = map_get(L, Blocks),
+ {Is,Sub} = pre_opt_is(Is0, Reached0, Sub0, []),
+ case pre_opt_terminator(Last0, Sub, Blocks) of
+ {#b_set{}=Test0,#b_br{}=Br0} ->
+ %% Here is a #b_switch{} that has been reduced to
+ %% a '=:=' followed by a two-way `br`.
+ Bool = #b_var{name={'@ssa_bool',Count0}},
+ Count = Count0 + 1,
+ Test = Test0#b_set{dst=Bool},
+ Br = Br0#b_br{bool=Bool},
+ Blk = Blk0#b_blk{is=Is++[Test],last=Br},
+ Successors = beam_ssa:successors(Blk),
+ Reached = gb_sets:union(Reached0,
+ gb_sets:from_list(Successors)),
+ pre_opt(Ls, Sub, Reached, Count, Blocks#{L:=Blk});
+ Last ->
+ Blk = Blk0#b_blk{is=Is,last=Last},
+ Successors = beam_ssa:successors(Blk),
+ Reached = gb_sets:union(Reached0,
+ gb_sets:from_list(Successors)),
+ pre_opt(Ls, Sub, Reached, Count0, Blocks#{L:=Blk})
+ end
+ end;
+pre_opt([], _, _, Count, Blocks) ->
+ {Blocks,Count}.
+
+pre_opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is], Reached, Sub0, Acc) ->
+ Args1 = [{Val,From} || {Val,From} <- Args0,
+ gb_sets:is_member(From, Reached)],
+ Args = sub_args(Args1, Sub0),
+ case all_same(Args) of
+ true ->
+ %% Single value or all values are the same. We can remove
+ %% the phi node.
+ {Arg,_} = hd(Args),
+ Sub = Sub0#{Dst=>Arg},
+ pre_opt_is(Is, Reached, Sub, Acc);
+ false ->
+ case pre_is_phi_bool(Args, Sub0) of
+ true ->
+ %% The value of the phi node is always a
+ %% boolean. Update type information in the sub map
+ %% and add an annotation.
+ Anno = I0#b_set.anno,
+ I = I0#b_set{args=Args,anno=Anno#{boolean_phi=>true}},
+ Sub = Sub0#{Dst=>I},
+ pre_opt_is(Is, Reached, Sub, [I|Acc]);
+ false ->
+ I = I0#b_set{args=Args},
+ pre_opt_is(Is, Reached, Sub0, [I|Acc])
+ end
+ end;
+pre_opt_is([#b_set{op=succeeded,dst=Dst,args=Args0}=I0|Is], Reached, Sub0, Acc) ->
+ [Arg] = Args = sub_args(Args0, Sub0),
+ I = I0#b_set{args=Args},
+ case pre_is_safe_bool(Arg, Sub0) of
+ true ->
+ %% The preceding boolean operation can't fail. Get rid
+ %% of this `succeeded` instruction.
+ Sub = Sub0#{Dst=>#b_literal{val=true}},
+ pre_opt_is(Is, Reached, Sub, Acc);
+ false ->
+ pre_opt_is(Is, Reached, Sub0, [I|Acc])
+ end;
+pre_opt_is([#b_set{dst=Dst,args=Args0}=I0|Is], Reached, Sub0, Acc) ->
+ Args = sub_args(Args0, Sub0),
+ I = I0#b_set{args=Args},
+ case is_bool_expr(I) of
+ true ->
+ case pre_eval_op(I, Sub0) of
+ none ->
+ Sub = Sub0#{Dst=>I},
+ pre_opt_is(Is, Reached, Sub, [I|Acc]);
+ #b_var{}=Var ->
+ %% We must remove the 'succeeded' instruction that
+ %% follows since the variable it checks is gone.
+ [#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] = Is,
+ Sub = Sub0#{Dst=>Var,SuccDst=>#b_literal{val=true}},
+ pre_opt_is([], Reached, Sub, Acc);
+ #b_literal{}=Lit ->
+ Sub = Sub0#{Dst=>Lit},
+ pre_opt_is(Is, Reached, Sub, Acc)
+ end;
+ false ->
+ pre_opt_is(Is, Reached, Sub0, [I|Acc])
+ end;
+pre_opt_is([], _Reached, Sub, Acc) ->
+ {reverse(Acc),Sub}.
+
+pre_opt_terminator(#b_br{bool=#b_literal{}}=Br, _Sub, _Blocks) ->
+ Br;
+pre_opt_terminator(#b_br{bool=Bool}=Br0, Sub, Blocks) ->
+ case beam_ssa:normalize(Br0#b_br{bool=sub_arg(Bool, Sub)}) of
+ Br0 ->
+ Br0;
+ #b_br{bool=#b_literal{val=true},succ=Next}=Br ->
+ %% See if the terminator from the successor block
+ %% can be incorporated into this block to give
+ %% more opportunities for optimization.
+ #b_blk{is=Is,last=Last} = map_get(Next, Blocks),
+ case {Is,Last} of
+ {[],#b_switch{}} ->
+ pre_opt_terminator(Last, Sub, Blocks);
+ {_,_} ->
+ Br
+ end
+ end;
+pre_opt_terminator(#b_ret{arg=Arg}=Ret, Sub, _Blocks) ->
+ Ret#b_ret{arg=sub_arg(Arg, Sub)};
+pre_opt_terminator(#b_switch{arg=Arg0,list=List}=Sw0, Sub, Blocks) ->
+ Arg = sub_arg(Arg0, Sub),
+ Sw = Sw0#b_switch{arg=Arg},
+ case sort(List) of
+ [{#b_literal{val=false},Fail},
+ {#b_literal{val=true},Succ}] ->
+ case pre_is_arg_bool(Arg, Sub) of
+ false ->
+ pre_opt_sw(Sw, Fail, Succ, Sub, Blocks);
+ true ->
+ beam_ssa:normalize(#b_br{bool=Arg,succ=Succ,fail=Fail})
+ end;
+ _ ->
+ Sw
+ end.
+
+pre_opt_sw(#b_switch{arg=Arg,fail=Fail}=Sw, False, True, Sub, Blocks) ->
+ case Sub of
+ #{Arg:={true_or_any,PhiL}} ->
+ #{Fail:=FailBlk,False:=FalseBlk,PhiL:=PhiBlk} = Blocks,
+ case {FailBlk,FalseBlk,PhiBlk} of
+ {#b_blk{is=[],last=#b_br{succ=PhiL,fail=PhiL}},
+ #b_blk{is=[],last=#b_br{succ=PhiL,fail=PhiL}},
+ #b_blk{is=[#b_set{op=phi,args=PhiArgs}|_]}} ->
+ case keyfind(False, 2, PhiArgs) of
+ {#b_literal{val=Bool},False} when Bool =/= true ->
+ %% This is an `andalso` in a guard. The code
+ %% can be simplified to a two-way `br` because
+ %% the actual value of the variable does not
+ %% matter if it is not equal to `true`.
+ DummyDst = #b_var{name=0},
+ {#b_set{op={bif,'=:='},dst=DummyDst,
+ args=[Arg,#b_literal{val=true}]},
+ #b_br{bool=DummyDst,succ=True,fail=False}};
+ {_,_} ->
+ Sw
+ end;
+ {_,_,_} ->
+ Sw
+ end;
+ #{} ->
+ Sw
+ end.
+
+pre_eval_op(#b_set{op={bif,Op},args=Args}, Sub) ->
+ case pre_are_args_bool(Args, Sub) of
+ true ->
+ case {Op,Args} of
+ {'and',[#b_literal{val=true},#b_var{}=Res]} -> Res;
+ {'and',[#b_literal{val=false}=Res,#b_var{}]} -> Res;
+ {'and',[#b_var{}=Res,#b_literal{val=true}]} -> Res;
+ {'and',[#b_var{},#b_literal{val=false}=Res]} -> Res;
+ {'or',[#b_literal{val=true}=Res,#b_var{}]} -> Res;
+ {'or',[#b_literal{val=false},#b_var{}=Res]} -> Res;
+ {'or',[#b_var{},#b_literal{val=true}=Res]} -> Res;
+ {'or',[#b_var{}=Res,#b_literal{val=false}]} -> Res;
+ _ -> none
+ end;
+ false ->
+ none
+ end.
+
+all_same([{H,_}|T]) ->
+ all(fun({E,_}) -> E =:= H end, T).
+
+pre_is_phi_bool([{#b_literal{val=Lit},_}|As], Sub) ->
+ is_boolean(Lit) andalso pre_is_phi_bool(As, Sub);
+pre_is_phi_bool([{#b_var{}=A,_}|As], Sub) ->
+ case Sub of
+ #{A:=#b_set{}} ->
+ pre_is_phi_bool(As, Sub);
+ #{} ->
+ false
+ end;
+pre_is_phi_bool([], _Sub) -> true.
+
+pre_is_safe_bool(#b_literal{}, _Sub) ->
+ true;
+pre_is_safe_bool(Var, Sub) ->
+ case Sub of
+ #{Var:=#b_set{op={bif,is_function},
+ args=[_,Arity]}} ->
+ case Arity of
+ #b_literal{val=Lit} ->
+ is_integer(Lit) andalso Lit >= 0;
+ #b_var{} ->
+ false
+ end;
+ #{Var:=#b_set{op={bif,Op},args=Args}} ->
+ Arity = length(Args),
+ erl_internal:bool_op(Op, Arity) andalso
+ pre_are_args_bool(Args, Sub);
+ #{} ->
+ false
+ end.
+
+pre_are_args_bool([A|As], Sub) ->
+ pre_is_arg_bool(A, Sub) andalso pre_are_args_bool(As, Sub);
+pre_are_args_bool([], _Sub) -> true.
+
+pre_is_arg_bool(#b_literal{val=Lit}, _Sub) ->
+ is_boolean(Lit);
+pre_is_arg_bool(#b_var{}=A, Sub) ->
+ case Sub of
+ #{A:=#b_set{}} ->
+ true;
+ #{} ->
+ false
+ end.
+
+%%%
+%%% Build a map from variable to definitions for boolean expressions
+%%% phi nodes. This map will be used by collect_bool_vars() and by
+%%% shortcut_branches().
+%%%
+
+interesting_defs(Blocks) ->
+ interesting_defs(maps:to_list(Blocks), []).
+
+interesting_defs([{L,#b_blk{is=Is}}|Bs], Acc) ->
+ interesting_defs(Bs, interesting_defs_is(Is, L, Acc));
+interesting_defs([], Acc) ->
+ maps:from_list(Acc).
+
+interesting_defs_is([#b_set{op={bif,_},dst=V}=I|Is], L, Acc) ->
+ case is_bool_expr(I) of
+ true ->
+ interesting_defs_is(Is, L, [{V,{L,I}}|Acc]);
+ false ->
+ interesting_defs_is(Is, L, Acc)
+ end;
+interesting_defs_is([#b_set{op=phi,dst=V}=Set|Is], L, Acc) ->
+ interesting_defs_is(Is, L, [{V,{L,Set}}|Acc]);
+interesting_defs_is([#b_set{}|Is], L, Acc) ->
+ interesting_defs_is(Is, L, Acc);
+interesting_defs_is([], _L, Acc) -> Acc.
+
+%%%
+%%% Search for boolean expressions to optimize.
+%%%
+%%% The main purpose of this module is to optimize guards. A guard ends in the
+%%% following instructions:
+%%%
+%%% Bool = bif:'=:=' Var, literal true
+%%% br BoolVar, label Success, label Failure
+%%%
+%%% To make sure that we'll find the end of the guard instead of some
+%%% interior '=:=' instruction we will visit the blocks in postorder.
+%%%
+
+bool_opt(Blocks, St) ->
+ bool_opt(beam_ssa:rpo(Blocks), Blocks, St).
+
+bool_opt([L|Ls], Blocks0, St0) ->
+ {Blocks,St1} = bool_opt(Ls, Blocks0, St0),
+ case Blocks of
+ #{L:=#b_blk{is=[_|_]=Is,last=#b_br{bool=#b_var{}=Bool}=Br}} ->
+ case last(Is) of
+ #b_set{op={bif,'=:='},dst=Bool,
+ args=[#b_var{},#b_literal{val=true}]} ->
+ try
+ bool_opt_rewrite(Bool, L, Br, Blocks, St1)
+ catch
+ throw:not_possible ->
+ {Blocks,St1}
+ end;
+ #b_set{} ->
+ {Blocks,St1}
+ end;
+ #{} ->
+ %% Either this block was removed by a previous successful
+ %% optimization, it is empty, or its terminator is not a
+ %% two-way `br` instruction.
+ {Blocks,St1}
+ end;
+bool_opt([], Blocks, St) ->
+ {Blocks,St}.
+
+bool_opt_rewrite(Bool, From, Br, Blocks0, St0) ->
+ TreeVars = collect_bool_vars(Bool, St0),
+ case TreeVars of
+ [Bool] ->
+ %% Only one variable means that there is nothing to
+ %% optimize. (The variable is either a function argument,
+ %% or has been defined by an instruction such as `call` or
+ %% `get_tuple_element`.)
+ not_possible();
+ [_|_] ->
+ ok
+ end,
+
+ %% Find the common dominator block for all the blocks with boolean
+ %% variables.
+ Dom = bool_opt_dom(TreeVars, St0),
+
+ %% Split out non-boolean instruction from the block that dominates
+ %% all the boolean operators. Splitting will save some work, and
+ %% it could also make more optimizations possible since phi nodes
+ %% could be difficult to handle later when they have been included
+ %% in the graph.
+ {DomPreIs,Blocks1} = split_dom_block(Dom, Blocks0),
+
+ %% Collect all blocks from the Dom block up to and including
+ %% the From block.
+ Bs = collect_digraph_blocks(Dom, From, Br, Blocks1),
+
+ %% Build a digraph from the collected blocks.
+ {Root,G0,St1} = build_digraph(Bs, Br, St0),
+
+ %% Optimize the digraph.
+ LDefs = digraph_bool_def(G0),
+ St = St1#st{ldefs=LDefs},
+ G1 = opt_digraph_top(Bool, G0, St),
+ G = shortcut_branches(Root, G1, St),
+
+ %% Make sure that every variable that is used will be defined
+ %% on every path to its use.
+ ensure_init(Root, G, G0),
+
+ %% Delete the original blocks. This is important so that we will not
+ %% try optimize the already optimized code. That would not work
+ %% because the map of definitions in St#st.defs would not be updated
+ %% to include the newly optimized blocks.
+ DomBlk0 = map_get(Dom, Blocks1),
+ Blocks2 = maps:without([L || {L,#b_blk{}} <- Bs], Blocks1),
+
+ %% Convert the optimized digraph back to SSA code.
+ Blocks3 = digraph_to_ssa([Root], G, Blocks2),
+
+ %% Add a branch from the pre-sequence in the dominating block to
+ %% the first block of the optimized code.
+ DomBlk = DomBlk0#b_blk{is=DomPreIs,last=oneway_br(Root)},
+ Blocks = Blocks3#{Dom => DomBlk},
+ {Blocks,St#st{ldefs=#{}}}.
+
+%%%
+%%% Collect boolean variables recursively reachable from the root
+%%% boolean variable.
+%%%
+
+collect_bool_vars(RootBool, St) ->
+ #b_set{args=[#b_var{}=Var,#b_literal{}]} = get_def(RootBool, St),
+ collect_bool_vars([Var], St, [RootBool]).
+
+collect_bool_vars([V|Vs], St, Acc) ->
+ case get_def(V, St) of
+ #b_set{op=phi,anno=Anno,args=Args} ->
+ {Vars,Ls} = collect_phi_args(Args, Anno),
+ collect_bool_vars(Vars ++ Vs, St, Ls ++ Vars ++ Acc);
+ #b_set{args=Args}=I ->
+ %% This is a boolean expression.
+ Vars = [Arg || #b_var{}=Arg <- Args],
+ case is_rewritable_bool_op(I) of
+ true ->
+ %% This is a bool op ('and', 'or', or
+ %% 'not'). Recursively collect more boolean
+ %% variables from its arguments.
+ collect_bool_vars(Vars ++ Vs, St, [V|Acc]);
+ false ->
+ %% This is a comparison operator (such as `<`) or
+ %% type test. Don't visit its arguments
+ %% recursively.
+ collect_bool_vars(Vs, St, [V|Acc])
+ end;
+ none ->
+ collect_bool_vars(Vs, St, Acc)
+ end;
+collect_bool_vars([], _St, Acc) ->
+ ordsets:from_list(Acc).
+
+is_rewritable_bool_op(#b_set{op={bif,Bif}}) ->
+ %% `xor` is a bool op, but it is not practical to rewrite it.
+ case Bif of
+ 'and' -> true;
+ 'or' -> true;
+ 'not' -> true;
+ _ -> false
+ end.
+
+collect_phi_args(Args, Anno) ->
+ case is_map_key(boolean_phi, Anno) of
+ true ->
+ Vars = [V || {#b_var{}=V,_} <- Args],
+ case Vars of
+ [_|_] ->
+ {Vars,[]};
+ [] ->
+ %% This phi node only contains literal values.
+ %% Force the inclusion of referenced blocks.
+ Ls = [{block,L} || {_,L} <- Args],
+ {[],Ls}
+ end;
+ false ->
+ %% We can't rewrite phi nodes that don't return
+ %% a boolean value.
+ {[],[]}
+ end.
+
+%%%
+%%% Dominator utility functions.
+%%%
+
+bool_opt_dom(TreeVars, #st{defs=Defs,dom={DomBy,Num}}) ->
+ Ls0 = foldl(fun({block,L}, A) ->
+ [L|A];
+ (V, A) ->
+ {L,_} = map_get(V, Defs),
+ [L|A]
+ end, [], TreeVars),
+ Ls = ordsets:from_list(Ls0),
+ [Common|_] = beam_ssa:common_dominators(Ls, DomBy, Num),
+ Common.
+
+split_dom_block(L, Blocks0) ->
+ #b_blk{is=Is} = Blk0 = map_get(L, Blocks0),
+ {PreIs,TailIs} = split_dom_block_is(Is, []),
+ Blk = Blk0#b_blk{is=TailIs},
+ Blocks = Blocks0#{L:=Blk},
+ {PreIs,Blocks}.
+
+split_dom_block_is([#b_set{},#b_set{op=succeeded}]=Is, PreAcc) ->
+ {reverse(PreAcc),Is};
+split_dom_block_is([#b_set{}=I|Is]=Is0, PreAcc) ->
+ case is_bool_expr(I) of
+ true ->
+ {reverse(PreAcc),Is0};
+ false ->
+ split_dom_block_is(Is, [I|PreAcc])
+ end;
+split_dom_block_is([], PreAcc) ->
+ {reverse(PreAcc),[]}.
+
+%%%
+%%% Find and collect the blocks that should be converted to a digraph.
+%%%
+
+collect_digraph_blocks(FirstL, LastL, #b_br{succ=Succ,fail=Fail}, Blocks) ->
+ Ws = gb_sets:singleton(FirstL),
+ Seen = cerl_sets:from_list([Succ,Fail]),
+ collect_digraph_blocks(Ws, LastL, Blocks, Seen, []).
+
+collect_digraph_blocks(Ws0, LastL, Blocks, Seen0, Acc0) ->
+ case gb_sets:is_empty(Ws0) of
+ true ->
+ Acc0;
+ false ->
+ {L,Ws1} = gb_sets:take_smallest(Ws0),
+ Seen = cerl_sets:add_element(L, Seen0),
+ Blk = map_get(L, Blocks),
+ Acc = [{L,Blk}|Acc0],
+ Ws = cdb_update_workset(L, Blk, LastL, Seen, Ws1),
+ collect_digraph_blocks(Ws, LastL, Blocks, Seen, Acc)
+ end.
+
+cdb_update_workset(LastL, _Blk, LastL, _Seen, Ws) ->
+ Ws;
+cdb_update_workset(_L, Blk, _LastL, Seen, Ws) ->
+ Successors = beam_ssa:successors(Blk),
+ cdb_update_workset(Successors, Seen, Ws).
+
+cdb_update_workset([L|Ls], Seen, Ws) ->
+ case cerl_sets:is_element(L, Seen) of
+ true ->
+ cdb_update_workset(Ls, Seen, Ws);
+ false ->
+ cdb_update_workset(Ls, Seen, gb_sets:add_element(L, Ws))
+ end;
+cdb_update_workset([], _Seen, Ws) -> Ws.
+
+%%%
+%%% For the blocks from the dominating block up to the last block,
+%%% build a digraph where each vertex is an instruction. This is just
+%%% a more convenient way to represent the code, more suitable for
+%%% the optimizations we are about to do.
+%%%
+
+build_digraph(Bs, #b_br{succ=Succ,fail=Fail}, St0) ->
+ Ignore = ordsets:from_list([Succ,Fail]),
+ G0 = beam_digraph:new(),
+ {Map0,G1,St1} = build_mapping(Bs, #{}, G0, St0),
+ {Map,G2} = add_external_vertices(Ignore, Map0, G1),
+ {G,St} = build_digraph_1(Bs, G2, Map, St1),
+
+ %% Find the root node now. After we have done optimizations,
+ %% there may be more than one root node (that is, nodes without
+ %% any incident vertices).
+ [Root] = digraph_roots(G),
+ {Root,G,St}.
+
+build_mapping([{L,Blk}|Bs], Map0, G0, St0) ->
+ {Vtx,St} = new_label(St0),
+ Map = Map0#{L=>Vtx},
+ Label = case Blk of
+ #b_blk{is=[]} -> br;
+ #b_blk{} -> initial
+ end,
+ G = beam_digraph:add_vertex(G0, Vtx, Label),
+ build_mapping(Bs, Map, G, St);
+build_mapping([], Map, G, St) ->
+ {Map,G,St}.
+
+add_external_vertices([V|Vs], Map0, G0) ->
+ G = beam_digraph:add_vertex(G0, V, {external,#{}}),
+ Map = Map0#{V=>V},
+ add_external_vertices(Vs, Map, G);
+add_external_vertices([], Map, G) ->
+ {Map,G}.
+
+build_digraph_1([{L,Blk}|Bs], G0, Map, St0) ->
+ #b_blk{is=Is,last=Last} = Blk,
+ Vtx = map_get(L, Map),
+ {G,St} = build_digraph_is(Is, Last, Vtx, Map, G0, St0),
+ build_digraph_1(Bs, G, Map, St);
+build_digraph_1([], G, _Map, St) ->
+ {G,St}.
+
+build_digraph_is([#b_set{op=phi,args=Args0}=I0|Is], Last, Vtx, Map, G, St) ->
+ case Is of
+ [#b_set{op=phi}|_] -> not_possible();
+ _ -> ok
+ end,
+ Args = [{V,map_get(L, Map)} || {V,L} <- Args0],
+ I = I0#b_set{args=Args},
+ build_digraph_is_1(I, Is, Last, Vtx, Map, G, St);
+build_digraph_is([#b_set{}=I|Is], Last, Vtx, Map, G, St) ->
+ case beam_ssa:no_side_effect(I) of
+ true ->
+ build_digraph_is_1(I, Is, Last, Vtx, Map, G, St);
+ false ->
+ not_possible()
+ end;
+build_digraph_is([], Last, From, Map, G0, St) ->
+ case Last of
+ #b_br{bool=#b_literal{val=true},succ=To0,fail=To0} ->
+ To = map_get(To0, Map),
+ G = beam_digraph:add_edge(G0, From, To, next),
+ {G,St};
+ #b_br{bool=#b_var{}=Bool,succ=Succ0,fail=Fail0} ->
+ #{Succ0:=Succ,Fail0:=Fail} = Map,
+ case beam_digraph:vertex(G0, From) of
+ #b_set{dst=Bool} ->
+ G = add_succ_fail_edges(From, Succ, Fail, G0),
+ {G,St};
+ #b_set{} ->
+ %% Wrong variable being tested. This is rare.
+ not_possible();
+ br ->
+ G1 = add_succ_fail_edges(From, Succ, Fail, G0),
+ G = beam_digraph:add_vertex(G1, From, {br,Bool}),
+ {G,St}
+ end;
+ _ ->
+ not_possible()
+ end.
+
+build_digraph_is_1(I, Is, Last, Vtx, Map, G0, St0) ->
+ G1 = beam_digraph:add_vertex(G0, Vtx, I),
+ case Is of
+ [] ->
+ build_digraph_is(Is, Last, Vtx, Map, G1, St0);
+ [_|_] ->
+ {NextVtx,St} = new_label(St0),
+ G2 = beam_digraph:add_vertex(G1, NextVtx, initial),
+ G = beam_digraph:add_edge(G2, Vtx, NextVtx, next),
+ build_digraph_is(Is, Last, NextVtx, Map, G, St)
+ end.
+
+%%%
+%%% Optimize the graph, attempting to eliminating 'and', 'or', and 'not'
+%%% instructions.
+%%%
+
+opt_digraph_top(Arg, G0, St) ->
+ I = get_def(Arg, G0, St),
+ #b_set{op={bif,'=:='},dst=Dst,
+ args=[#b_var{}=Bool,#b_literal{val=true}]} = I,
+ {br,Succ,Fail} = get_targets(Dst, G0, St),
+ G1 = ensure_single_use(Dst, G0, St),
+ G = convert_to_br_node(I, Succ, G1, St),
+ redirect_test(Bool, {fail,Fail}, G, St).
+
+do_opt_digraph([A|As], G0, St) ->
+ I = get_def(A, G0, St),
+ try opt_digraph_instr(I, G0, St) of
+ G ->
+ do_opt_digraph(As, G, St)
+ catch
+ throw:not_possible ->
+ do_opt_digraph(As, G0, St)
+ end;
+do_opt_digraph([], G, _St) -> G.
+
+opt_digraph_instr(#b_set{dst=Dst}=I, G0, St) ->
+ %% We KNOW that this node has two outgoing edges (one labeled
+ %% `succ` and one `fail`).
+ {br,Succ,Fail} = get_targets(Dst, G0, St),
+ G1 = ensure_single_use(Dst, G0, St),
+ case I of
+ #b_set{op={bif,'and'},args=Args} ->
+ G2 = convert_to_br_node(I, Succ, G1, St),
+ {First,Second} = order_args(Args, G2, St),
+ G = redirect_test(First, {fail,Fail}, G2, St),
+ redirect_test(Second, {fail,Fail}, G, St);
+ #b_set{op={bif,'or'},args=Args} ->
+ {First,Second} = order_args(Args, G1, St),
+
+ %% Here we give up the optimization if the optimization
+ %% would skip instructions that may fail. A possible
+ %% future improvement would be to hoist the failing
+ %% instructions so that they would always be executed.
+ ensure_no_failing_instructions(First, Second, G1, St),
+
+ G2 = convert_to_br_node(I, Succ, G1, St),
+ G = redirect_test(First, {succ,Succ}, G2, St),
+ redirect_test(Second, {fail,Fail}, G, St);
+ #b_set{op={bif,'xor'}} ->
+ %% Rewriting 'xor' is not practical. Fortunately,
+ %% 'xor' is almost never used in practice.
+ not_possible();
+ #b_set{op={bif,'not'},args=[#b_var{}=Bool]} ->
+ G = convert_to_br_node(I, Fail, G1, St),
+ redirect_test(Bool, {fail,Succ}, G, St);
+ #b_set{op=phi,dst=Bool} ->
+ Vtx = get_vertex(Bool, St),
+ G2 = del_out_edges(Vtx, G1),
+ G = beam_digraph:add_edge(G2, Vtx, Succ, next),
+ redirect_test(Bool, {fail,Fail}, G, St);
+ #b_set{} ->
+ G1
+ end.
+
+ensure_single_use(Bool, G, #st{uses=U}=St) ->
+ case map_get(Bool, U) of
+ [_] ->
+ G;
+ Uses ->
+ Vtx = get_vertex(Bool, St),
+ ensure_single_use_1(Bool, Vtx, Uses, G)
+ end.
+
+ensure_single_use_1(Bool, Vtx, Uses, G) ->
+ Fail = case get_targets(Vtx, G) of
+ {br,_,Fail0} -> Fail0;
+ _ -> not_possible()
+ end,
+ case partition(fun({L,#b_set{}}) when L =:= Fail -> true;
+ (_) -> false
+ end, Uses) of
+ {[_],[_]} ->
+ case beam_digraph:vertex(G, Fail) of
+ {external,Bs0} ->
+ %% The only other use of the variable Bool
+ %% is in the failure block. It can be
+ %% replaced with the literal `false`
+ %% in that block.
+ Bs = Bs0#{Bool => #b_literal{val=false}},
+ beam_digraph:add_vertex(G, Fail, {external,Bs});
+ _ ->
+ not_possible()
+ end;
+ {_,_} ->
+ not_possible()
+ end.
+
+convert_to_br_node(I, Target, G0, St) ->
+ Vtx = get_vertex(I, St),
+ G1 = del_out_edges(Vtx, G0),
+ G = beam_digraph:add_vertex(G1, Vtx, br),
+ beam_digraph:add_edge(G, Vtx, Target, next).
+
+
+%% ensure_no_failing_instructions(First, Second, G, St) -> ok.
+%% Ensure that there are no instructions that can fail that would not
+%% be executed if right-hand side of the `or` would be skipped. That
+%% means that the `or` could succeed when it was supposed to
+%% fail. Example:
+%%
+%% (element(1, T) =:= tag) or
+%% (element(10, T) =:= y)
+
+ensure_no_failing_instructions(First, Second, G, St) ->
+ Vs0 = covered(get_vertex(First, St), get_vertex(Second, St), G),
+ Vs = [{V,beam_digraph:vertex(G, V)} || V <- Vs0],
+ Failing = [P || {V,#b_set{op=succeeded}}=P <- Vs,
+ not eaten_by_phi(V, G)],
+ case Failing of
+ [] -> ok;
+ [_|_] -> not_possible()
+ end.
+
+eaten_by_phi(V, G) ->
+ {br,_,Fail} = get_targets(V, G),
+ case beam_digraph:vertex(G, Fail) of
+ br ->
+ [To] = beam_digraph:out_neighbours(G, Fail),
+ case beam_digraph:vertex(G, To) of
+ #b_set{op=phi} ->
+ true;
+ _ ->
+ false
+ end;
+ _ ->
+ false
+ end.
+
+%% order_args([Arg1,Arg2], G, St) -> {First,Second}.
+%% Order arguments for a boolean operator so that there is path in the
+%% digraph from the instruction referered to by the first operand to
+%% the instruction refered to by the second operand.
+
+order_args([#b_var{}=VarA,#b_var{}=VarB], G, St) ->
+ {VA,VB} = {get_vertex(VarA, St),get_vertex(VarB, St)},
+ case beam_digraph:is_path(G, VA, VB) of
+ true ->
+ %% Core Erlang code generated by v3_core always
+ %% has operands already in correct order.
+ {VarA,VarB};
+ false ->
+ %% Core Erlang code generated by other frontends
+ %% such as LFE may have the operands swapped.
+ true = beam_digraph:is_path(G, VB, VA), %Assertion.
+ {VarB,VarB}
+ end;
+order_args(_Args, _G, _St) ->
+ %% Literal operands. Can only happen if the Core Erlang optimization
+ %% passes have been turned off.
+ not_possible().
+
+redirect_test(Bool, SuccFail, G0, St) ->
+ V = get_vertex(Bool, St),
+ I = get_def(Bool, G0, St),
+ case I of
+ #b_set{op=phi,args=Args} ->
+ G = ensure_single_use(Bool, G0, St),
+ redirect_phi(Bool, Args, SuccFail, G, St);
+ #b_set{} ->
+ G1 = redirect_test_1(V, SuccFail, G0),
+ G = ensure_single_use(Bool, G1, St),
+ do_opt_digraph([Bool], G, St)
+ end.
+
+redirect_test_1(V, SuccFail, G) ->
+ case get_targets(V, G) of
+ {br,_Succ,Fail} ->
+ %% I have only seen this happen in code generated by LFE
+ %% (in lfe_andor_SUITE.core and lfe_guard_SUITE.core)
+ case SuccFail of
+ {fail,Fail} -> G;
+ {succ,_} -> not_possible()
+ end;
+ {br,Next} ->
+ case SuccFail of
+ {succ,Succ} ->
+ add_succ_fail_edges(V, Succ, Next, G);
+ {fail,Fail} ->
+ add_succ_fail_edges(V, Next, Fail, G)
+ end
+ end.
+
+redirect_phi(Phi, Args, SuccFail, G0, St) ->
+ PhiVtx = get_vertex(Phi, St),
+ G = beam_digraph:add_vertex(G0, PhiVtx, br),
+ redirect_phi_1(PhiVtx, sort(Args), SuccFail, G, St).
+
+redirect_phi_1(PhiVtx, [{#b_literal{val=false},FalseExit},
+ {#b_var{}=SuccBool,_BoolExit}],
+ SuccFail, G0, St) ->
+ BoolVtx = get_vertex(SuccBool, St),
+ [FalseOut] = beam_digraph:out_edges(G0, FalseExit),
+ G1 = beam_digraph:del_edge(G0, FalseOut),
+ case SuccFail of
+ {fail,Fail} ->
+ G2 = beam_digraph:add_edge(G1, FalseExit, Fail, next),
+ G = add_succ_fail_edges(BoolVtx, PhiVtx, FalseExit, G2),
+ do_opt_digraph([SuccBool], G, St);
+ {succ,Succ} ->
+ G2 = beam_digraph:add_edge(G1, FalseExit, PhiVtx, next),
+ G = add_succ_fail_edges(BoolVtx, Succ, PhiVtx, G2),
+ do_opt_digraph([SuccBool], G, St)
+ end;
+redirect_phi_1(PhiVtx, [{#b_literal{val=true},TrueExit},
+ {#b_var{}=SuccBool,_BoolExit}],
+ {fail,Fail}, G0, St) ->
+ %% This was probably an `orelse` in the source code.
+ BoolVtx = get_vertex(SuccBool, St),
+ [TrueOut] = beam_digraph:out_edges(G0, TrueExit),
+ G1 = beam_digraph:del_edge(G0, TrueOut),
+ G2 = beam_digraph:add_edge(G1, TrueExit, PhiVtx, next),
+ G = add_succ_fail_edges(BoolVtx, PhiVtx, Fail, G2),
+ %% As as future improvement, we could follow TrueExit
+ %% back to its originating boolean expression and
+ %% optimize that too.
+ do_opt_digraph([SuccBool], G, St);
+redirect_phi_1(_PhiVtx, [{#b_literal{val=false},FalseExit},
+ {#b_literal{val=true},TrueExit}],
+ SuccFail, G0, _St) ->
+ case SuccFail of
+ {fail,Fail} ->
+ [FalseOut] = beam_digraph:out_edges(G0, FalseExit),
+ G = beam_digraph:del_edge(G0, FalseOut),
+ beam_digraph:add_edge(G, FalseExit, Fail, next);
+ {succ,Succ} ->
+ [TrueOut] = beam_digraph:out_edges(G0, TrueExit),
+ G = beam_digraph:del_edge(G0, TrueOut),
+ beam_digraph:add_edge(G, TrueExit, Succ, next)
+ end;
+redirect_phi_1(_PhiVtx, _Args, _SuccFail, _G, _St) ->
+ not_possible().
+
+digraph_bool_def(G) ->
+ Vs = beam_digraph:vertices(G),
+ Ds = [{Dst,Vtx} || {Vtx,#b_set{dst=Dst}} <- Vs],
+ maps:from_list(Ds).
+
+%%%
+%%% Shortcut branches that branch to other branches.
+%%%
+%%% Shortcutting may eliminate problems with variables that
+%%% are not defined on all paths to their use. For example,
+%%% code such as the following can be made safe again:
+%%%
+%%% ensure_written(Head, false) when not Head#head.ram_file -> ...
+%%%
+%%% Shortcutting also simplifies the conversion from the digraph
+%%% back to the standard SSA format.
+%%%
+
+shortcut_branches(Vtx, G, St) ->
+ Vs = reverse(beam_digraph:reverse_postorder(G, [Vtx])),
+ do_shortcut_branches(Vs, G, St).
+
+do_shortcut_branches([V|Vs], G0, St) ->
+ case get_targets(V, G0) of
+ {br,Succ0,Fail0} ->
+ {SuccBs,FailBs} = eval_bs(V, G0, St),
+ Succ = eval_instr(Succ0, G0, SuccBs),
+ G1 = redirect_edge(V, Succ0, {succ,Succ}, G0),
+ Fail = eval_instr(Fail0, G1, FailBs),
+ G = redirect_edge(V, Fail0, {fail,Fail}, G1),
+ do_shortcut_branches(Vs, G, St);
+ {br,Next0} ->
+ Next = eval_instr(Next0, G0, #{}),
+ G = redirect_edge(V, Next0, {next,Next}, G0),
+ do_shortcut_branches(Vs, G, St);
+ none ->
+ %% This is an external vertex.
+ do_shortcut_branches(Vs, G0, St)
+ end;
+do_shortcut_branches([], G, _St) -> G.
+
+redirect_edge(_From, To, {_Label,To}, G) ->
+ G;
+redirect_edge(From, To0, {Label,To}, G0) ->
+ G = beam_digraph:del_edge(G0, {From,To0,Label}),
+ beam_digraph:add_edge(G, From, To, Label).
+
+eval_bs(Vtx, G, St) ->
+ case beam_digraph:vertex(G, Vtx) of
+ #b_set{op={bif,'=:='},args=[#b_var{}=Bool,#b_literal{val=true}]} ->
+ case get_def(Bool, G, St) of
+ #b_set{op=phi}=Phi ->
+ phi_bs(Phi);
+ _ ->
+ {#{},#{}}
+ end;
+ _ ->
+ {#{},#{}}
+ end.
+
+phi_bs(#b_set{op=phi,dst=PhiDst,args=PhiArgs}) ->
+ Literals0 = [Lit || {#b_literal{}=Lit,_} <- PhiArgs],
+ case length(Literals0) =:= length(PhiArgs) of
+ true ->
+ %% The values in the phi node are all literals.
+ Literals = ordsets:from_list(Literals0),
+ case partition(fun(#b_literal{val=Val}) ->
+ Val =:= true
+ end, Literals) of
+ {[True],[FailVal]} ->
+ %% As there is only two possible values, we can
+ %% predict the value of the phi node on both
+ %% branches.
+ SuccBs = #{PhiDst => True},
+ FailBs = #{PhiDst => FailVal},
+ {SuccBs,FailBs};
+ {_,_} ->
+ {#{},#{}}
+ end;
+ false ->
+ {#{},#{}}
+ end.
+
+eval_instr(Vtx, G, Bs) ->
+ case beam_digraph:vertex(G, Vtx) of
+ #b_set{} when map_size(Bs) =:= 0 ->
+ %% With no bindings, eval_safe_bool_expr() is
+ %% unlikely to do anything useful. If we would
+ %% call it anyway, the time complexity would be
+ %% quadratic, which would be slow for large
+ %% graphs.
+ Vtx;
+ #b_set{}=I ->
+ case is_safe_bool_expr(I) of
+ true -> eval_safe_bool_expr(I, Vtx, G, Bs);
+ false -> Vtx
+ end;
+ br ->
+ %% We can shortcut this branch unless its
+ %% target is a phi node.
+ [Next] = beam_digraph:out_neighbours(G, Vtx),
+ case beam_digraph:vertex(G, Next) of
+ #b_set{op=phi} -> Vtx;
+ _ -> eval_instr(Next, G, Bs)
+ end;
+ {br,#b_var{}} ->
+ Vtx;
+ {external,_} ->
+ Vtx
+ end.
+
+eval_safe_bool_expr(#b_set{op={bif,Bif},dst=Dst,args=Args0}, Vtx, G, Bs) ->
+ case get_targets(Vtx, G) of
+ {br,Succ,Fail} ->
+ True = #b_literal{val=true},
+ False = #b_literal{val=false},
+ Args = sub_args(Args0, Bs),
+ case eval_bif(Bif, Args) of
+ none ->
+ case {eval_instr(Succ, G, Bs#{Dst=>True}),
+ eval_instr(Fail, G, Bs#{Dst=>False})} of
+ {Same,Same} -> Same;
+ {_,_} -> Vtx
+ end;
+ true ->
+ eval_instr(Succ, G, Bs#{Dst=>True});
+ false ->
+ eval_instr(Fail, G, Bs#{Dst=>False})
+ end;
+ {br,_} ->
+ Vtx
+ end.
+
+eval_bif(Bif, Args0) ->
+ case eval_literal_args(Args0, []) of
+ none ->
+ none;
+ Args ->
+ %% We have already made sure that this expression can't
+ %% fail; thus there is no need for a `try`.
+ apply(erlang, Bif, Args)
+ end.
+
+eval_literal_args([#b_literal{val=Val}|As], Acc) ->
+ eval_literal_args(As, [Val|Acc]);
+eval_literal_args([_|_], _) ->
+ none;
+eval_literal_args([], Acc) ->
+ reverse(Acc).
+
+%%%
+%%% Check that variables are initialized on all paths and abort
+%%% the optimization if not.
+%%%
+%%% Expressions that use `or` and `not` may have added
+%%% `bif:is_boolean` instructions at the end of the boolean
+%%% expression. It can happen that the variables tested by
+%%% `bif:is_boolean` are not initialized on all paths.
+%%%
+
+ensure_init(Root, G, G0) ->
+ Vs = beam_digraph:vertices(G),
+
+ %% Build an ordset of a all variables used by the code
+ %% before the optimization.
+ Used = ensure_init_used(G0),
+
+ %% Build a map of all variables that are set by instructions in
+ %% the digraph. Variables not included in this map have been
+ %% defined by code before the code in the digraph.
+ Vars = maps:from_list([{Dst,unset} ||
+ {_,#b_set{dst=Dst}} <- Vs]),
+ RPO = beam_digraph:reverse_postorder(G, [Root]),
+ ensure_init_1(RPO, Used, G, #{Root=>Vars}).
+
+ensure_init_1([V|Vs], Used, G, InitMaps0) ->
+ InitMaps = ensure_init_instr(V, Used, G, InitMaps0),
+ ensure_init_1(Vs, Used, G, InitMaps);
+ensure_init_1([], _, _, _) -> ok.
+
+ensure_init_instr(Vtx, Used, G, InitMaps0) ->
+ VarMap0 = map_get(Vtx, InitMaps0),
+ case beam_digraph:vertex(G, Vtx) of
+ #b_set{dst=Dst}=I ->
+ do_ensure_init_instr(I, VarMap0, InitMaps0),
+ OutVs = beam_digraph:out_neighbours(G, Vtx),
+ VarMap = VarMap0#{Dst=>set},
+ InitMaps = InitMaps0#{Vtx:=VarMap},
+ ensure_init_successors(OutVs, G, VarMap, InitMaps);
+ {external,_} ->
+ %% We have reached the success or failure node.
+ %% If the code we have been optimizing does not
+ %% originate from a guard, it is possible that a
+ %% variable set in the optimized code will be used
+ %% here.
+ case [V || {V,unset} <- maps:to_list(VarMap0)] of
+ [] ->
+ InitMaps0;
+ [_|_]=Unset0 ->
+ %% There are some variables that are not always
+ %% set when this node is reached. We must make
+ %% sure that they are not used at this node or
+ %% one of its successors.
+ Unset = ordsets:from_list(Unset0),
+ case ordsets:is_subset(Unset, Used) of
+ true ->
+ %% Note that all of the potentially unset
+ %% variables are only used once (otherwise
+ %% the optimization would have been
+ %% aborted earlier). Therefore, since all
+ %% variables are used in the optimized code,
+ %% they cannot be used in this node or in one
+ %% of its successors.
+ InitMaps0;
+ false ->
+ %% The original code probably did not
+ %% originate from a guard. One of the
+ %% potentially unset variables are not
+ %% used in the optimized code. That means
+ %% that it must be used at this node or in
+ %% one of its successors. (Or that it was
+ %% not used at all in the original code,
+ %% but that basically only happens in test
+ %% cases.)
+ not_possible()
+ end
+ end;
+ _ ->
+ OutVs = beam_digraph:out_neighbours(G, Vtx),
+ ensure_init_successors(OutVs, G, VarMap0, InitMaps0)
+ end.
+
+ensure_init_used(G) ->
+ Vs = beam_digraph:vertices(G),
+ ensure_init_used_1(Vs, G, []).
+
+ensure_init_used_1([{Vtx,#b_set{dst=Dst}=I}|Vs], G, Acc0) ->
+ Acc1 = [beam_ssa:used(I)|Acc0],
+ case beam_digraph:out_degree(G, Vtx) of
+ 2 ->
+ Acc = [[Dst]|Acc1],
+ ensure_init_used_1(Vs, G, Acc);
+ _ ->
+ ensure_init_used_1(Vs, G, Acc1)
+ end;
+ensure_init_used_1([{_Vtx,{br,Bool}}|Vs], G, Acc) ->
+ ensure_init_used_1(Vs, G, [[Bool]|Acc]);
+ensure_init_used_1([_|Vs], G, Acc) ->
+ ensure_init_used_1(Vs, G, Acc);
+ensure_init_used_1([], _G, Acc) ->
+ ordsets:union(Acc).
+
+do_ensure_init_instr(#b_set{op=phi,args=Args},
+ _VarMap, InitMaps) ->
+ _ = [ensure_init_used(Var, map_get(From, InitMaps)) ||
+ {#b_var{}=Var,From} <- Args],
+ ok;
+do_ensure_init_instr(#b_set{}=I, VarMap, _InitMaps) ->
+ Used = beam_ssa:used(I),
+ _ = [ensure_init_used(Var, VarMap) || Var <- Used],
+ ok.
+
+ensure_init_used(Var, VarMap) ->
+ case VarMap of
+ #{Var:=unset} -> not_possible();
+ #{Var:=set} -> ok;
+ #{} -> ok
+ end.
+
+ensure_init_successors([To|Vs], G, Vars0, InitMaps0) ->
+ case InitMaps0 of
+ #{To:=Vars1} ->
+ Vars = join_inits(Vars0, Vars1),
+ InitMaps = InitMaps0#{To:=Vars},
+ ensure_init_successors(Vs, G, Vars0, InitMaps);
+ #{} ->
+ InitMaps = InitMaps0#{To=>Vars0},
+ ensure_init_successors(Vs, G, Vars0, InitMaps)
+ end;
+ensure_init_successors([], _, _, InitMaps) ->
+ InitMaps.
+
+join_inits(VarMap0, VarMap1) ->
+ join_inits_1(maps:to_list(VarMap0), VarMap1).
+
+join_inits_1([{V,State0}|Vs], VarMap) ->
+ State1 = map_get(V, VarMap),
+ State = case {State0,State1} of
+ {set,set} -> set;
+ {_,_} -> unset
+ end,
+ case State =:= State1 of
+ true ->
+ join_inits_1(Vs, VarMap);
+ false ->
+ join_inits_1(Vs, VarMap#{V:=State})
+ end;
+join_inits_1([], VarMap) ->
+ VarMap.
+
+%%%
+%%% Transform the digraph back to standard SSA code.
+%%%
+
+digraph_to_ssa(Ls, G, Blocks0) ->
+ Seen = cerl_sets:new(),
+ {Blocks,_} = digraph_to_ssa(Ls, G, Blocks0, Seen),
+ Blocks.
+
+digraph_to_ssa([L|Ls], G, Blocks0, Seen0) ->
+ Seen1 = cerl_sets:add_element(L, Seen0),
+ {Blk,Successors0} = digraph_to_ssa_blk(L, G, Blocks0, []),
+ Blocks1 = Blocks0#{L=>Blk},
+ Successors = [S || S <- Successors0,
+ not cerl_sets:is_element(S, Seen1)],
+ {Blocks,Seen} = digraph_to_ssa(Successors, G, Blocks1, Seen1),
+ digraph_to_ssa(Ls, G, Blocks, Seen);
+digraph_to_ssa([], _G, Blocks, Seen) ->
+ {Blocks,Seen}.
+
+digraph_to_ssa_blk(From, G, Blocks, Acc) ->
+ case beam_digraph:vertex(G, From) of
+ #b_set{dst=Dst}=I ->
+ case get_targets(From, G) of
+ {br,Succ,Fail} ->
+ %% This is a two-way branch that ends the current block.
+ Br = #b_br{bool=Dst,succ=Succ,fail=Fail},
+ Is = reverse(Acc, [I]),
+ Blk = #b_blk{is=Is,last=Br},
+ {Blk,beam_ssa:successors(Blk)};
+ {br,Next} ->
+ case beam_digraph:in_degree(G, Next) of
+ 1 ->
+ digraph_to_ssa_blk(Next, G, Blocks, [I|Acc]);
+ _ ->
+ %% The Next node has multiple incident edge. That
+ %% means that it can't be part of the current block,
+ %% but must start a new block.
+ Br = oneway_br(Next),
+ Is = reverse(Acc, [I]),
+ Blk = #b_blk{is=Is,last=Br},
+ {Blk,beam_ssa:successors(Blk)}
+ end
+ end;
+ br ->
+ case Acc of
+ [] ->
+ %% Create an empty block.
+ {br,Next} = get_targets(From, G),
+ Blk = #b_blk{is=[],last=oneway_br(Next)},
+ {Blk,beam_ssa:successors(Blk)};
+ [_|_] ->
+ %% Finish up the block, and let the block
+ %% transfer control to the `br` node at From.
+ Br = oneway_br(From),
+ Is = reverse(Acc),
+ Blk = #b_blk{is=Is,last=Br},
+ {Blk,beam_ssa:successors(Blk)}
+ end;
+ {br,Bool} ->
+ %% This is a two-way `br` instruction. The most common
+ %% reason for its existence in the graph is that the root
+ %% node only contained a phi instruction (which was taken
+ %% out of the block before building the graph).
+ [] = Acc, %Assertion.
+ {br,Succ,Fail} = get_targets(From, G),
+ Br = #b_br{bool=Bool,succ=Succ,fail=Fail},
+ Blk = #b_blk{is=[],last=Br},
+ {Blk,beam_ssa:successors(Blk)};
+ {external,Sub} ->
+ #b_blk{is=Is0} = Blk = map_get(From, Blocks),
+ Is = [I#b_set{args=sub_args(Args0, Sub)} ||
+ #b_set{args=Args0}=I <- Is0],
+ {Blk#b_blk{is=Is},[]}
+ end.
+
+%%%
+%%% Helper functions follow.
+%%%
+
+%% get_def(Var, #st{}) -> #b_set{} | none.
+%% Find the definition for a variable. Only boolean
+%% expressions and phi nodes can be found.
+
+get_def(#b_var{}=Bool, #st{defs=Defs}) ->
+ case Defs of
+ #{Bool:={_,Def}} ->
+ Def;
+ #{} ->
+ none
+ end.
+
+%% get_def(Var, Graph, #st{}) -> #b_set{} | none.
+%% Find the definition for a variable, looking first in the digraph
+%% Graph. If it is not found there, look in the global map of
+%% interesting definitions from the entire functions.
+
+get_def(Var, G, #st{ldefs=LDefs,defs=Defs}) ->
+ case LDefs of
+ #{Var:=Vtx} ->
+ beam_digraph:vertex(G, Vtx);
+ #{} ->
+ %% Not in the graph. Returning definitions for phi nodes
+ %% outside the graph is useful for shortcut_branches().
+ case Defs of
+ #{Var:={_,Def}} -> Def;
+ #{} -> none
+ end
+ end.
+
+add_succ_fail_edges(From, Succ, Fail, G0) ->
+ G1 = beam_digraph:add_edge(G0, From, Succ, succ),
+ G = beam_digraph:add_edge(G1, From, Fail, fail),
+ case beam_digraph:out_edges(G0, From) of
+ [{From,_,next}=E] -> beam_digraph:del_edge(G, E);
+ [] -> G
+ end.
+
+get_vertex(#b_set{dst=Dst}, St) ->
+ get_vertex(Dst, St);
+get_vertex(#b_var{}=Var, #st{ldefs=LDefs}) ->
+ map_get(Var, LDefs).
+
+get_targets(Vtx, G) when is_integer(Vtx) ->
+ case beam_digraph:out_edges(G, Vtx) of
+ [{_,To,next}] ->
+ {br,To};
+ [{_,Succ,succ},{_,Fail,fail}] ->
+ {br,Succ,Fail};
+ [{_,Fail,fail},{_,Succ,succ}] ->
+ {br,Succ,Fail};
+ [] ->
+ none
+ end.
+
+get_targets(#b_var{}=Var, G, #st{ldefs=LDefs}) ->
+ get_targets(map_get(Var, LDefs), G).
+
+del_out_edges(V, G) ->
+ beam_digraph:del_edges(G, beam_digraph:out_edges(G, V)).
+
+covered(From, To, G) ->
+ Seen0 = gb_sets:empty(),
+ {yes,Seen} = covered_1(From, To, G, Seen0),
+ gb_sets:to_list(Seen).
+
+covered_1(To, To, _G, Seen) ->
+ {yes,Seen};
+covered_1(From, To, G, Seen0) ->
+ Vs0 = beam_digraph:out_neighbours(G, From),
+ Vs = [V || V <- Vs0, not gb_sets:is_member(V, Seen0)],
+ Seen = gb_sets:union(gb_sets:from_list(Vs), Seen0),
+ case Vs of
+ [] ->
+ no;
+ [_|_] ->
+ covered_list(Vs, To, G, Seen, false)
+ end.
+
+covered_list([V|Vs], To, G, Seen0, AnyFound) ->
+ case covered_1(V, To, G, Seen0) of
+ {yes,Seen} ->
+ covered_list(Vs, To, G, Seen, true);
+ no ->
+ covered_list(Vs, To, G, Seen0, AnyFound)
+ end;
+covered_list([], _, _, Seen, AnyFound) ->
+ case AnyFound of
+ true -> {yes,Seen};
+ false -> no
+ end.
+
+digraph_roots(G) ->
+ digraph_roots_1(beam_digraph:vertices(G), G).
+
+digraph_roots_1([{V,_}|Vs], G) ->
+ case beam_digraph:in_degree(G, V) of
+ 0 ->
+ [V|digraph_roots_1(Vs, G)];
+ _ ->
+ digraph_roots_1(Vs, G)
+ end;
+digraph_roots_1([], _G) -> [].
+
+not_possible() ->
+ throw(not_possible).
+
+new_label(#st{count=Count}=St) ->
+ {Count,St#st{count=Count+1}}.
+
+sub_args(Args, Sub) ->
+ [sub_arg(Arg, Sub) || Arg <- Args].
+
+sub_arg({#b_var{}=Arg,From}, Sub) when is_integer(From) ->
+ {do_sub_arg(Arg, Sub),From};
+sub_arg(#b_var{}=Arg, Sub) ->
+ do_sub_arg(Arg, Sub);
+sub_arg(#b_remote{mod=Mod,name=Name}=Rem, Sub) ->
+ Rem#b_remote{mod=do_sub_arg(Mod, Sub),
+ name=do_sub_arg(Name, Sub)};
+sub_arg(Arg, _Sub) -> Arg.
+
+do_sub_arg(#b_var{}=Old, Sub) ->
+ case Sub of
+ #{Old:=#b_literal{}=New} -> New;
+ #{Old:=#b_var{}=New} -> New;
+ #{} -> Old
+ end;
+do_sub_arg(#b_literal{}=Old, _Sub) -> Old.
+
+is_bool_expr(#b_set{op={bif,Op},args=Args}) ->
+ Arity = length(Args),
+ erl_internal:comp_op(Op, Arity) orelse
+ erl_internal:new_type_test(Op, Arity) orelse
+ erl_internal:bool_op(Op, Arity);
+is_bool_expr(_) -> false.
+
+%% Test whether the expression always succeeds and
+%% always returns a boolean.
+is_safe_bool_expr(#b_set{op={bif,Op},args=Args}) ->
+ Arity = length(Args),
+ erl_internal:comp_op(Op, Arity) orelse
+ erl_internal:new_type_test(Op, Arity);
+is_safe_bool_expr(#b_set{}) -> false.
+
+oneway_br(To) ->
+ #b_br{bool=#b_literal{val=true},succ=To,fail=To}.
diff --git a/lib/compiler/src/beam_ssa_bsm.erl b/lib/compiler/src/beam_ssa_bsm.erl
index cb36f1c242..1d5a99a7a1 100644
--- a/lib/compiler/src/beam_ssa_bsm.erl
+++ b/lib/compiler/src/beam_ssa_bsm.erl
@@ -57,6 +57,7 @@
-export([module/2, format_error/1]).
-include("beam_ssa.hrl").
+-include("beam_types.hrl").
-import(lists, [member/2, reverse/1, reverse/2, splitwith/2, map/2, foldl/3,
mapfoldl/3, nth/2, max/1, unzip/1]).
@@ -469,7 +470,7 @@ combine_matches(#b_function{bs=Blocks0,cnt=Counter0}=F, ModInfo) ->
cm_1([#b_set{ op=bs_start_match,
dst=Ctx,
- args=[Src] },
+ args=[_,Src] },
#b_set{ op=succeeded,
dst=Bool,
args=[Ctx] }]=MatchSeq, Acc0, Lbl, State0) ->
@@ -574,7 +575,7 @@ aca_1(Blocks, State) ->
EntryBlock = maps:get(0, Blocks),
aca_enable_reuse(EntryBlock#b_blk.is, EntryBlock, Blocks, [], State).
-aca_enable_reuse([#b_set{op=bs_start_match,args=[Src]}=I0 | Rest],
+aca_enable_reuse([#b_set{op=bs_start_match,args=[_,Src]}=I0 | Rest],
EntryBlock, Blocks0, Acc, State0) ->
case aca_is_reuse_safe(Src, State0) of
true ->
@@ -618,7 +619,8 @@ aca_is_reuse_safe(Src, State) ->
%% they're unused so far.
ordsets:is_element(Src, State#aca.unused_parameters).
-aca_reuse_context(#b_set{dst=Dst, args=[Src]}=I0, Block, Blocks0, State0) ->
+aca_reuse_context(#b_set{op=bs_start_match,dst=Dst,args=[_,Src]}=I0,
+ Block, Blocks0, State0) ->
%% When matching fails on a reused context it needs to be converted back
%% to a binary. We only need to do this on the success path since it can't
%% be a context on the type failure path, but it's very common for these
@@ -687,9 +689,9 @@ aca_copy_successors(Lbl0, Blocks0, Counter0) ->
Lbl = maps:get(Lbl0, BRs),
{Lbl, Blocks, Counter}.
-aca_cs_build_brs([?BADARG_BLOCK=Lbl | Path], Counter, Acc) ->
- %% ?BADARG_BLOCK is a marker and not an actual block, so renaming it will
- %% break exception handling.
+aca_cs_build_brs([?EXCEPTION_BLOCK=Lbl | Path], Counter, Acc) ->
+ %% ?EXCEPTION_BLOCK is a marker and not an actual block, so renaming it
+ %% will break exception handling.
aca_cs_build_brs(Path, Counter, Acc#{ Lbl => Lbl });
aca_cs_build_brs([Lbl | Path], Counter0, Acc) ->
aca_cs_build_brs(Path, Counter0 + 1, Acc#{ Lbl => Counter0 });
@@ -874,25 +876,25 @@ sote_rewrite_call(Call0, [Arg | ArgsIn], ArgsOut, State0) ->
sote_rewrite_call(Call0, ArgsIn, [Arg | ArgsOut], State0)
end.
-%% Adds parameter_type_info annotations to help the validator determine whether
-%% our optimizations were safe.
+%% Adds parameter annotations to help the validator determine whether our
+%% optimizations were safe.
annotate_context_parameters({Fs, ModInfo}) ->
mapfoldl(fun annotate_context_parameters/2, ModInfo, Fs).
annotate_context_parameters(F, ModInfo) ->
ParamInfo = funcinfo_get(F, parameter_info, ModInfo),
- TypeAnno0 = beam_ssa:get_anno(parameter_type_info, F, #{}),
- TypeAnno = maps:fold(fun(K, _V, Acc) when is_map_key(K, Acc) ->
- %% Assertion.
- error(conflicting_parameter_types);
- (K, suitable_for_reuse, Acc) ->
- T = beam_validator:type_anno(match_context),
- Acc#{ K => T };
- (_K, _V, Acc) ->
- Acc
- end, TypeAnno0, ParamInfo),
- {beam_ssa:add_anno(parameter_type_info, TypeAnno, F), ModInfo}.
+ ParamAnno0 = beam_ssa:get_anno(parameter_info, F, #{}),
+ ParamAnno = maps:fold(fun(K, _V, Acc) when is_map_key(K, Acc) ->
+ %% Assertion.
+ error(conflicting_parameter_types);
+ (K, suitable_for_reuse, Acc) ->
+ Info = maps:get(K, Acc, []),
+ Acc#{ K => [accepts_match_context | Info] };
+ (_K, _V, Acc) ->
+ Acc
+ end, ParamAnno0, ParamInfo),
+ {beam_ssa:add_anno(parameter_info, ParamAnno, F), ModInfo}.
%%%
%%% +bin_opt_info
diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl
index 08641e2abc..5799f0e00f 100644
--- a/lib/compiler/src/beam_ssa_codegen.erl
+++ b/lib/compiler/src/beam_ssa_codegen.erl
@@ -28,7 +28,7 @@
-include("beam_ssa.hrl").
--import(lists, [foldl/3,keymember/3,keysort/2,last/1,map/2,mapfoldl/3,
+-import(lists, [foldl/3,keymember/3,keysort/2,map/2,mapfoldl/3,
reverse/1,reverse/2,sort/1,splitwith/2,takewhile/2]).
-record(cg, {lcount=1 :: beam_label(), %Label counter
@@ -37,7 +37,8 @@
used_labels=gb_sets:empty() :: gb_sets:set(ssa_label()),
regs=#{} :: #{beam_ssa:var_name()=>ssa_register()},
ultimate_fail=1 :: beam_label(),
- catches=gb_sets:empty() :: gb_sets:set(ssa_label())
+ catches=gb_sets:empty() :: gb_sets:set(ssa_label()),
+ fc_label=1 :: beam_label()
}).
-spec module(beam_ssa:b_module(), [compile:option()]) ->
@@ -114,17 +115,17 @@ functions(Forms, AtomMod) ->
function(#b_function{anno=Anno,bs=Blocks}, AtomMod, St0) ->
#{func_info:={_,Name,Arity}} = Anno,
try
- assert_badarg_block(Blocks), %Assertion.
+ assert_exception_block(Blocks), %Assertion.
Regs = maps:get(registers, Anno),
St1 = St0#cg{labels=#{},used_labels=gb_sets:empty(),
regs=Regs},
{Fi,St2} = new_label(St1), %FuncInfo label
{Entry,St3} = local_func_label(Name, Arity, St2),
{Ult,St4} = new_label(St3), %Ultimate failure
- Labels = (St4#cg.labels)#{0=>Entry,?BADARG_BLOCK=>0},
+ Labels = (St4#cg.labels)#{0=>Entry,?EXCEPTION_BLOCK=>0},
St5 = St4#cg{labels=Labels,used_labels=gb_sets:singleton(Entry),
ultimate_fail=Ult},
- {Body,St} = cg_fun(Blocks, St5),
+ {Body,St} = cg_fun(Blocks, St5#cg{fc_label=Fi}),
Asm = [{label,Fi},line(Anno),
{func_info,AtomMod,{atom,Name},Arity}] ++
add_parameter_annos(Body, Anno) ++
@@ -137,10 +138,10 @@ function(#b_function{anno=Anno,bs=Blocks}, AtomMod, St0) ->
erlang:raise(Class, Error, Stack)
end.
-assert_badarg_block(Blocks) ->
- %% Assertion: ?BADARG_BLOCK must be the call erlang:error(badarg).
+assert_exception_block(Blocks) ->
+ %% Assertion: ?EXCEPTION_BLOCK must be a call erlang:error(badarg).
case Blocks of
- #{?BADARG_BLOCK:=Blk} ->
+ #{?EXCEPTION_BLOCK:=Blk} ->
#b_blk{is=[#b_set{op=call,dst=Ret,
args=[#b_remote{mod=#b_literal{val=erlang},
name=#b_literal{val=error}},
@@ -148,19 +149,21 @@ assert_badarg_block(Blocks) ->
last=#b_ret{arg=Ret}} = Blk,
ok;
#{} ->
- %% ?BADARG_BLOCK has been removed because it was never used.
+ %% ?EXCEPTION_BLOCK has been removed because it was never used.
ok
end.
add_parameter_annos([{label, _}=Entry | Body], Anno) ->
- ParamInfo = maps:get(parameter_type_info, Anno, #{}),
+ ParamTypes = maps:get(parameter_info, Anno, #{}),
+
Annos = maps:fold(
- fun(K, V, Acc) when is_map_key(K, ParamInfo) ->
- TypeInfo = maps:get(K, ParamInfo),
- [{'%', {type_info, V, TypeInfo}} | Acc];
+ fun(K, V, Acc) when is_map_key(K, ParamTypes) ->
+ Info = map_get(K, ParamTypes),
+ [{'%', {var_info, V, Info}} | Acc];
(_K, _V, Acc) ->
Acc
end, [], maps:get(registers, Anno)),
+
[Entry | sort(Annos)] ++ Body.
cg_fun(Blocks, St0) ->
@@ -365,7 +368,7 @@ classify_heap_need(bs_save) -> neutral;
classify_heap_need(bs_get_position) -> gc;
classify_heap_need(bs_set_position) -> neutral;
classify_heap_need(bs_skip) -> gc;
-classify_heap_need(bs_start_match) -> neutral;
+classify_heap_need(bs_start_match) -> gc;
classify_heap_need(bs_test_tail) -> neutral;
classify_heap_need(bs_utf16_size) -> neutral;
classify_heap_need(bs_utf8_size) -> neutral;
@@ -384,6 +387,7 @@ classify_heap_need(is_tagged_tuple) -> neutral;
classify_heap_need(kill_try_tag) -> gc;
classify_heap_need(landingpad) -> gc;
classify_heap_need(make_fun) -> gc;
+classify_heap_need(match_fail) -> gc;
classify_heap_need(new_try_tag) -> gc;
classify_heap_need(peek_message) -> gc;
classify_heap_need(put_map) -> gc;
@@ -629,7 +633,7 @@ liveness_get(S, LiveMap) ->
end.
liveness_successors(Terminator) ->
- successors(Terminator) -- [?BADARG_BLOCK].
+ successors(Terminator) -- [?EXCEPTION_BLOCK].
liveness_is([#cg_alloc{}=I0|Is], Regs, Live, Acc) ->
I = I0#cg_alloc{live=num_live(Live, Regs)},
@@ -973,6 +977,12 @@ cg_block(Is0, Last, Next, St0) ->
case Last of
#cg_br{succ=Next,fail=Next} ->
cg_block(Is0, none, St0);
+ #cg_br{succ=Same,fail=Same} when Same =:= ?EXCEPTION_BLOCK ->
+ %% An expression in this block *always* throws an exception, so we
+ %% terminate it with an 'if_end' to make sure the validator knows
+ %% that the following instructions won't actually be reached.
+ {Is,St} = cg_block(Is0, none, St0),
+ {Is++[if_end],St};
#cg_br{succ=Same,fail=Same} ->
{Fail,St1} = use_block_label(Same, St0),
{Is,St} = cg_block(Is0, none, St1),
@@ -1136,7 +1146,10 @@ cg_block([#cg_set{op=bs_init,dst=Dst0,args=Args0,anno=Anno}=I,
Is = [Line,{bs_append,Fail,Bits,Alloc,Live,Unit,Src,Flags,Dst}],
{Is,St}
end;
-cg_block([#cg_set{anno=Anno,op=bs_start_match,dst=Ctx0,args=[Bin0]}=I,
+cg_block([#cg_set{anno=Anno,
+ op=bs_start_match,
+ dst=Ctx0,
+ args=[#b_literal{val=new},Bin0]}=I,
#cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) ->
[Dst,Bin1] = beam_args([Ctx0,Bin0], St),
{Bin,Pre} = force_reg(Bin1, Dst),
@@ -1178,6 +1191,10 @@ cg_block([#cg_set{op=call}=I,
#cg_set{op=succeeded,dst=Bool}], {Bool,_Fail}, St) ->
%% A call in try/catch block.
cg_block([I], none, St);
+cg_block([#cg_set{op=match_fail}=I,
+ #cg_set{op=succeeded,dst=Bool}], {Bool,_Fail}, St) ->
+ %% A match_fail instruction in a try/catch block.
+ cg_block([I], none, St);
cg_block([#cg_set{op=get_map_element,dst=Dst0,args=Args0},
#cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) ->
[Dst,Map,Key] = beam_args([Dst0|Args0], St),
@@ -1201,8 +1218,9 @@ cg_block([#cg_set{op=is_tagged_tuple,dst=Bool,args=Args0}], {Bool,Fail}, St) ->
cg_block([#cg_set{op=is_nonempty_list,dst=Bool,args=Args0}], {Bool,Fail}, St) ->
Args = beam_args(Args0, St),
{[{test,is_nonempty_list,ensure_label(Fail, St),Args}],St};
-cg_block([#cg_set{op=has_map_field,dst=Bool,args=Args0}], {Bool,Fail}, St) ->
+cg_block([#cg_set{op=has_map_field,dst=Bool,args=Args0}], {Bool,Fail0}, St) ->
[Src,Key] = beam_args(Args0, St),
+ Fail = ensure_label(Fail0, St),
{[{test,has_map_fields,Fail,Src,{list,[Key]}}],St};
cg_block([#cg_set{op=call}=Call], {_Bool,_Fail}=Context, St0) ->
{Is0,St1} = cg_call(Call, body, none, St0),
@@ -1220,15 +1238,24 @@ cg_block([#cg_set{op=call}=Call|T], Context, St0) ->
{Is0,St1} = cg_call(Call, body, none, St0),
{Is1,St} = cg_block(T, Context, St1),
{Is0++Is1,St};
-cg_block([#cg_set{op=make_fun,dst=Dst0,args=[Local|Args0]}|T],
+cg_block([#cg_set{anno=Anno,op=make_fun,dst=Dst0,args=[Local|Args0]}|T],
Context, St0) ->
#b_local{name=#b_literal{val=Func},arity=Arity} = Local,
[Dst|Args] = beam_args([Dst0|Args0], St0),
{FuncLbl,St1} = local_func_label(Func, Arity, St0),
Is0 = setup_args(Args) ++
[{make_fun2,{f,FuncLbl},0,0,length(Args)}|copy({x,0}, Dst)],
- {Is1,St} = cg_block(T, Context, St1),
- {Is0++Is1,St};
+
+ Is1 = case Anno of
+ #{ result_type := Type } ->
+ Info = {var_info, Dst, [{fun_type, Type}]},
+ Is0 ++ [{'%', Info}];
+ #{} ->
+ Is0
+ end,
+
+ {Is2,St} = cg_block(T, Context, St1),
+ {Is1++Is2,St};
cg_block([#cg_set{op=copy}|_]=T0, Context, St0) ->
{Is0,T} = cg_copy(T0, St0),
{Is1,St} = cg_block(T, Context, St0),
@@ -1239,6 +1266,28 @@ cg_block([#cg_set{op=copy}|_]=T0, Context, St0) ->
no ->
{Is,St}
end;
+cg_block([#cg_set{op=match_fail,args=Args0,anno=Anno}], none, St) ->
+ Args = beam_args(Args0, St),
+ Is = cg_match_fail(Args, line(Anno), none),
+ {Is,St};
+cg_block([#cg_set{op=match_fail,args=Args0,anno=Anno}|T], Context, St0) ->
+ FcLabel = case Context of
+ {return,_,none} ->
+ %% There is no stack frame. If this is a function_clause
+ %% exception, it is safe to jump to the label of the
+ %% func_info instruction.
+ St0#cg.fc_label;
+ _ ->
+ %% This is most probably not a function_clause.
+ %% If this is a function_clause exception
+ %% (rare), it is not safe to jump to the
+ %% func_info label.
+ none
+ end,
+ Args = beam_args(Args0, St0),
+ Is0 = cg_match_fail(Args, line(Anno), FcLabel),
+ {Is1,St} = cg_block(T, Context, St0),
+ {Is0++Is1,St};
cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=Set], none, St) ->
[Dst|Args] = beam_args([Dst0|Args0], St),
Is = cg_instr(Op, Args, Dst, Set),
@@ -1270,8 +1319,7 @@ cg_copy(T0, St) ->
end, T0),
Moves0 = cg_copy_1(Copies, St),
Moves1 = [Move || {move,Src,Dst}=Move <- Moves0, Src =/= Dst],
- Scratch = {x,1022},
- Moves = order_moves(Moves1, Scratch),
+ Moves = order_moves(Moves1),
{Moves,T}.
cg_copy_1([#cg_set{dst=Dst0,args=Args}|T], St) ->
@@ -1473,8 +1521,9 @@ cg_call(#cg_set{anno=Anno,op=call,dst=Dst0,args=[#b_local{}=Func0|Args0]},
Call = build_call(call, Arity, {f,FuncLbl}, Context, Dst),
Is = setup_args(Args, Anno, Context, St) ++ Line ++ Call,
case Anno of
- #{ result_type := Info } ->
- {Is ++ [{'%', {type_info, Dst, Info}}], St};
+ #{ result_type := Type } ->
+ Info = {var_info, Dst, [{type,Type}]},
+ {Is ++ [{'%', Info}], St};
#{} ->
{Is, St}
end;
@@ -1510,7 +1559,49 @@ cg_call(#cg_set{anno=Anno,op=call,dst=Dst0,args=Args0},
Arity = length(Args),
Call = build_call(call_fun, Arity, Func, Context, Dst),
Is = setup_args(Args++[Func], Anno, Context, St) ++ Line ++ Call,
- {Is,St}.
+ case Anno of
+ #{ result_type := Type } ->
+ Info = {var_info, Dst, [{type,Type}]},
+ {Is ++ [{'%', Info}], St};
+ #{} ->
+ {Is, St}
+ end.
+
+cg_match_fail([{atom,function_clause}|Args], Line, Fc) ->
+ case Fc of
+ none ->
+ %% There is a stack frame (probably because of inlining).
+ %% Jumping to the func_info label is not allowed by
+ %% beam_validator. Rewrite the instruction as a call to
+ %% erlang:error/2.
+ make_fc(Args, Line);
+ _ ->
+ setup_args(Args) ++ [{jump,{f,Fc}}]
+ end;
+cg_match_fail([{atom,Op}], Line, _Fc) ->
+ [Line,Op];
+cg_match_fail([{atom,Op},Val], Line, _Fc) ->
+ [Line,{Op,Val}].
+
+make_fc(Args, Line) ->
+ %% Recreate the original call to erlang:error/2.
+ Live = foldl(fun({x,X}, A) -> max(X+1, A);
+ (_, A) -> A
+ end, 0, Args),
+ TmpReg = {x,Live},
+ StkMoves = build_stk(reverse(Args), TmpReg, nil),
+ [{test_heap,2*length(Args),Live}|StkMoves] ++
+ [{move,{atom,function_clause},{x,0}},
+ Line,
+ {call_ext,2,{extfunc,erlang,error,2}}].
+
+build_stk([V], _TmpReg, Tail) ->
+ [{put_list,V,Tail,{x,1}}];
+build_stk([V|Vs], TmpReg, Tail) ->
+ I = {put_list,V,Tail,TmpReg},
+ [I|build_stk(Vs, TmpReg, TmpReg)];
+build_stk([], _TmpReg, nil) ->
+ [{move,nil,{x,1}}].
build_call(call_fun, Arity, _Func, none, Dst) ->
[{call_fun,Arity}|copy({x,0}, Dst)];
@@ -1550,15 +1641,22 @@ build_apply(Arity, {return,Val,N}, _Dst) when is_integer(N) ->
build_apply(Arity, none, Dst) ->
[{apply,Arity}|copy({x,0}, Dst)].
-cg_instr(put_map, [{atom,assoc},SrcMap|Ss], Dst, Set) ->
+cg_instr(bs_start_match, [{atom,resume}, Src], Dst, Set) ->
Live = get_live(Set),
- [{put_map_assoc,{f,0},SrcMap,Dst,Live,{list,Ss}}];
+ [{bs_start_match4,{atom,resume},Live,Src,Dst}];
+cg_instr(bs_start_match, [{atom,new}, Src0], Dst, Set) ->
+ {Src, Pre} = force_reg(Src0, Dst),
+ Live = get_live(Set),
+ Pre ++ [{bs_start_match4,{atom,no_fail},Live,Src,Dst}];
cg_instr(bs_get_tail, [Src], Dst, Set) ->
Live = get_live(Set),
[{bs_get_tail,Src,Dst,Live}];
cg_instr(bs_get_position, [Ctx], Dst, Set) ->
Live = get_live(Set),
[{bs_get_position,Ctx,Dst,Live}];
+cg_instr(put_map, [{atom,assoc},SrcMap|Ss], Dst, Set) ->
+ Live = get_live(Set),
+ [{put_map_assoc,{f,0},SrcMap,Dst,Live,{list,Ss}}];
cg_instr(Op, Args, Dst, _Set) ->
cg_instr(Op, Args, Dst).
@@ -1592,6 +1690,8 @@ cg_instr(get_tl=Op, [Src], Dst) ->
[{Op,Src,Dst}];
cg_instr(get_tuple_element=Op, [Src,{integer,N}], Dst) ->
[{Op,Src,N,Dst}];
+cg_instr(has_map_field, [Map,Key], Dst) ->
+ [{bif,is_map_key,{f,0},[Key,Map],Dst}];
cg_instr(put_list=Op, [Hd,Tl], Dst) ->
[{Op,Hd,Tl,Dst}];
cg_instr(put_tuple, Elements, Dst) ->
@@ -1728,7 +1828,7 @@ cg_catch(Agg, T0, Context, St0) ->
cg_try(Agg, Tag, T0, Context, St0) ->
{Moves0,T1} = cg_extract(T0, Agg, St0),
- Moves = order_moves(Moves0, {x,3}),
+ Moves = order_moves(Moves0),
[#cg_set{op=kill_try_tag}|T2] = T1,
{T,St} = cg_block(T2, Context, St0),
{[{try_case,Tag}|Moves++T],St}.
@@ -1780,7 +1880,7 @@ linearize(Blocks) ->
Linear = beam_ssa:linearize(Blocks),
linearize_1(Linear, Blocks).
-linearize_1([{?BADARG_BLOCK,_}|Ls], Blocks) ->
+linearize_1([{?EXCEPTION_BLOCK,_}|Ls], Blocks) ->
linearize_1(Ls, Blocks);
linearize_1([{L,Block0}|Ls], Blocks) ->
Block = translate_block(L, Block0, Blocks),
@@ -1884,8 +1984,7 @@ setup_args([]) ->
[];
setup_args([_|_]=Args) ->
Moves = gen_moves(Args, 0, []),
- Scratch = {x,1+last(sort([length(Args)-1|[X || {x,X} <- Args]]))},
- order_moves(Moves, Scratch).
+ order_moves(Moves).
%% kill_yregs(Anno, #cg{}) -> [{kill,{y,Y}}].
%% Kill Y registers that will not be used again.
@@ -1905,47 +2004,48 @@ gen_moves([A|As], I, Acc) ->
gen_moves([], _, Acc) ->
keysort(3, Acc).
-%% order_moves([Move], ScratchReg) -> [Move]
+%% order_moves([Move]) -> [Move]
%% Orders move instruction so that source registers are not
%% destroyed before they are used. If there are cycles
%% (such as {move,{x,0},{x,1}}, {move,{x,1},{x,1}}),
-%% the scratch register is used to break up the cycle.
-%% If possible, the first move of the input list is placed
+%% swap instructions will be used to break up the cycle.
+%%
+%% If possible, the first move of the input list is placed
%% last in the result list (to make the move to {x,0} occur
%% just before the call to allow the Beam loader to coalesce
%% the instructions).
-order_moves(Ms, Scr) -> order_moves(Ms, Scr, []).
+order_moves(Ms) -> order_moves(Ms, []).
-order_moves([{move,_,_}=M|Ms0], ScrReg, Acc0) ->
- {Chain,Ms} = collect_chain(Ms0, [M], ScrReg),
+order_moves([{move,_,_}=M|Ms0], Acc0) ->
+ {Chain,Ms} = collect_chain(Ms0, [M]),
Acc = reverse(Chain, Acc0),
- order_moves(Ms, ScrReg, Acc);
-order_moves([], _, Acc) -> Acc.
+ order_moves(Ms, Acc);
+order_moves([], Acc) -> Acc.
-collect_chain(Ms, Path, ScrReg) ->
- collect_chain(Ms, Path, [], ScrReg).
+collect_chain(Ms, Path) ->
+ collect_chain(Ms, Path, []).
-collect_chain([{move,Src,Same}=M|Ms0], [{move,Same,_}|_]=Path, Others, ScrReg) ->
+collect_chain([{move,Src,Same}=M|Ms0], [{move,Same,_}|_]=Path, Others) ->
case keymember(Src, 3, Path) of
false ->
- collect_chain(reverse(Others, Ms0), [M|Path], [], ScrReg);
+ collect_chain(reverse(Others, Ms0), [M|Path], []);
true ->
- %% There is a cycle, which we must break up.
- {break_up_cycle(M, Path, ScrReg),reverse(Others, Ms0)}
+ %% There is a cycle.
+ {break_up_cycle(M, Path),reverse(Others, Ms0)}
end;
-collect_chain([M|Ms], Path, Others, ScrReg) ->
- collect_chain(Ms, Path, [M|Others], ScrReg);
-collect_chain([], Path, Others, _) ->
+collect_chain([M|Ms], Path, Others) ->
+ collect_chain(Ms, Path, [M|Others]);
+collect_chain([], Path, Others) ->
{Path,Others}.
-break_up_cycle({move,Src,_}=M, Path, ScrReg) ->
- [{move,ScrReg,Src},M|break_up_cycle1(Src, Path, ScrReg)].
+break_up_cycle({move,Src,_Dst}=M, Path) ->
+ break_up_cycle_1(Src, [M|Path], []).
-break_up_cycle1(Dst, [{move,Src,Dst}|Path], ScrReg) ->
- [{move,Src,ScrReg}|Path];
-break_up_cycle1(Dst, [M|Path], LastMove) ->
- [M|break_up_cycle1(Dst, Path, LastMove)].
+break_up_cycle_1(Dst, [{move,_Src,Dst}|Path], Acc) ->
+ reverse(Acc, Path);
+break_up_cycle_1(Dst, [{move,S,D}|Path], Acc) ->
+ break_up_cycle_1(Dst, Path, [{swap,S,D}|Acc]).
%%%
%%% General utility functions.
diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl
index e78e4647a8..021b773419 100644
--- a/lib/compiler/src/beam_ssa_dead.erl
+++ b/lib/compiler/src/beam_ssa_dead.erl
@@ -28,7 +28,7 @@
-include("beam_ssa.hrl").
-import(lists, [append/1,keymember/3,last/1,member/2,
- takewhile/2,reverse/1]).
+ reverse/1,sort/1,takewhile/2]).
-type used_vars() :: #{beam_ssa:label():=cerl_sets:set(beam_ssa:var_name())}.
@@ -97,38 +97,38 @@ shortcut_opt(#st{bs=Blocks}=St) ->
%% in the first clause of shortcut_2/5).
Ls = beam_ssa:rpo(Blocks),
- shortcut_opt(Ls, #{}, St).
+ shortcut_opt(Ls, St).
-shortcut_opt([L|Ls], Bs, #st{bs=Blocks0}=St) ->
+shortcut_opt([L|Ls], #st{bs=Blocks0}=St) ->
#b_blk{is=Is,last=Last0} = Blk0 = get_block(L, St),
- case shortcut_terminator(Last0, Is, L, Bs, St) of
+ case shortcut_terminator(Last0, Is, L, St) of
Last0 ->
%% No change. No need to update the block.
- shortcut_opt(Ls, Bs, St);
+ shortcut_opt(Ls, St);
Last ->
%% The terminator was simplified in some way.
%% Update the block.
Blk = Blk0#b_blk{last=Last},
Blocks = Blocks0#{L=>Blk},
- shortcut_opt(Ls, Bs, St#st{bs=Blocks})
+ shortcut_opt(Ls, St#st{bs=Blocks})
end;
-shortcut_opt([], _, St) -> St.
+shortcut_opt([], St) -> St.
shortcut_terminator(#b_br{bool=#b_literal{val=true},succ=Succ0},
- _Is, From, Bs, St0) ->
+ _Is, From, St0) ->
St = St0#st{rel_op=none},
- shortcut(Succ0, From, Bs, St);
+ shortcut(Succ0, From, #{}, St);
shortcut_terminator(#b_br{bool=#b_var{}=Bool,succ=Succ0,fail=Fail0}=Br,
- Is, From, Bs, St0) ->
+ Is, From, St0) ->
St = St0#st{target=one_way},
RelOp = get_rel_op(Bool, Is),
%% The boolean in a `br` is seldom used by the successors. By
%% not binding its value unless it is actually used we might be able
%% to skip some work in shortcut/4 and sub/2.
- SuccBs = bind_var_if_used(Succ0, Bool, #b_literal{val=true}, Bs, St),
+ SuccBs = bind_var_if_used(Succ0, Bool, #b_literal{val=true}, St),
BrSucc = shortcut(Succ0, From, SuccBs, St#st{rel_op=RelOp}),
- FailBs = bind_var_if_used(Fail0, Bool, #b_literal{val=false}, Bs, St),
+ FailBs = bind_var_if_used(Fail0, Bool, #b_literal{val=false}, St),
BrFail = shortcut(Fail0, From, FailBs, St#st{rel_op=invert_op(RelOp)}),
case {BrSucc,BrFail} of
@@ -141,19 +141,34 @@ shortcut_terminator(#b_br{bool=#b_var{}=Bool,succ=Succ0,fail=Fail0}=Br,
%% No change.
Br
end;
-shortcut_terminator(#b_switch{arg=Bool,list=List0}=Sw, _Is, From, Bs, St) ->
- List = shortcut_switch(List0, Bool, From, Bs, St),
- beam_ssa:normalize(Sw#b_switch{list=List});
-shortcut_terminator(Last, _Is, _Bs, _From, _St) ->
+shortcut_terminator(#b_switch{arg=Bool,fail=Fail0,list=List0}=Sw,
+ _Is, From, St) ->
+ Fail = shortcut_sw_fail(Fail0, List0, Bool, From, St),
+ List = shortcut_sw_list(List0, Bool, From, St),
+ beam_ssa:normalize(Sw#b_switch{fail=Fail,list=List});
+shortcut_terminator(Last, _Is, _From, _St) ->
Last.
-shortcut_switch([{Lit,L0}|T], Bool, From, Bs, St0) ->
+shortcut_sw_fail(Fail0, List, Bool, From, St0) ->
+ case sort(List) of
+ [{#b_literal{val=false},_},
+ {#b_literal{val=true},_}] ->
+ RelOp = {{'not',is_boolean},Bool},
+ St = St0#st{rel_op=RelOp,target=one_way},
+ #b_br{bool=#b_literal{val=true},succ=Fail} =
+ shortcut(Fail0, From, #{}, St),
+ Fail;
+ _ ->
+ Fail0
+ end.
+
+shortcut_sw_list([{Lit,L0}|T], Bool, From, St0) ->
RelOp = {'=:=',Bool,Lit},
St = St0#st{rel_op=RelOp},
#b_br{bool=#b_literal{val=true},succ=L} =
- shortcut(L0, From, bind_var(Bool, Lit, Bs), St#st{target=one_way}),
- [{Lit,L}|shortcut_switch(T, Bool, From, Bs, St0)];
-shortcut_switch([], _, _, _, _) -> [].
+ shortcut(L0, From, bind_var(Bool, Lit, #{}), St#st{target=one_way}),
+ [{Lit,L}|shortcut_sw_list(T, Bool, From, St0)];
+shortcut_sw_list([], _, _, _) -> [].
shortcut(L, _From, Bs, #st{rel_op=none,target=one_way}) when map_size(Bs) =:= 0 ->
%% There is no way that we can find a suitable branch, because there is no
@@ -409,8 +424,10 @@ is_br_safe(UnsetVars, Br, #st{us=Us}=St) ->
is_forbidden(L, St) ->
case get_block(L, St) of
- #b_blk{is=[#b_set{op=phi}|_]} -> true;
- #b_blk{is=[#b_set{op=peek_message}|_]} -> true;
+ #b_blk{is=[#b_set{op=phi}|_]} ->
+ true;
+ #b_blk{is=[#b_set{}=I|_]} ->
+ beam_ssa:is_loop_header(I);
#b_blk{} -> false
end.
@@ -423,11 +440,22 @@ eval_is([#b_set{op=phi,dst=Dst,args=Args}|Is], From, Bs0, St) ->
Val = get_phi_arg(Args, From),
Bs = bind_var(Dst, Val, Bs0),
eval_is(Is, From, Bs, St);
+eval_is([#b_set{op=succeeded,dst=Dst,args=[Var]}], _From, Bs, _St) ->
+ case Bs of
+ #{Var:=failed} ->
+ bind_var(Dst, #b_literal{val=false}, Bs);
+ #{Var:=#b_literal{}} ->
+ bind_var(Dst, #b_literal{val=true}, Bs);
+ #{} ->
+ Bs
+ end;
eval_is([#b_set{op={bif,_},dst=Dst}=I0|Is], From, Bs, St) ->
I = sub(I0, Bs),
case eval_bif(I, St) of
#b_literal{}=Val ->
eval_is(Is, From, bind_var(Dst, Val, Bs), St);
+ failed ->
+ eval_is(Is, From, bind_var(Dst, failed, Bs), St);
none ->
eval_is(Is, From, Bs, St)
end;
@@ -521,15 +549,14 @@ eval_switch_1([], _Arg, _PrevOp, Fail) ->
%% Fail is now either the failure label or 'none'.
Fail.
-bind_var_if_used(L, Var, Val0, Bs, #st{us=Us}) ->
+bind_var_if_used(L, Var, Val, #st{us=Us}) ->
case cerl_sets:is_element(Var, map_get(L, Us)) of
- true ->
- Val = get_value(Val0, Bs),
- Bs#{Var=>Val};
- false ->
- Bs
+ true -> #{Var=>Val};
+ false -> #{}
end.
+bind_var(Var, failed, Bs) ->
+ Bs#{Var=>failed};
bind_var(Var, Val0, Bs) ->
Val = get_value(Val0, Bs),
Bs#{Var=>Val}.
@@ -675,7 +702,7 @@ eval_rel_op(_Bif, _Args, #st{rel_op=none}) ->
eval_rel_op(Bif, Args, #st{rel_op=Prev}) ->
case normalize_op(Bif, Args) of
none ->
- none;
+ eval_boolean(Prev, Bif, Args);
RelOp ->
case will_succeed(Prev, RelOp) of
yes -> #b_literal{val=true};
@@ -684,11 +711,22 @@ eval_rel_op(Bif, Args, #st{rel_op=Prev}) ->
end
end.
+eval_boolean({{'not',is_boolean},Var}, {bif,'not'}, [Var]) ->
+ failed;
+eval_boolean({{'not',is_boolean},Var}, {bif,Op}, Args)
+ when Op =:= 'and'; Op =:= 'or' ->
+ case member(Var, Args) of
+ true -> failed;
+ false -> none
+ end;
+eval_boolean(_, _, _) ->
+ none.
+
%% will_succeed(PrevCondition, Condition) -> yes | no | maybe
%% PrevCondition is a condition known to be true. This function
%% will tell whether Condition will succeed.
-will_succeed({_Op,_Var,_Value}=Same, {_Op,_Var,_Value}=Same) ->
+will_succeed({_,_,_}=Same, {_,_,_}=Same) ->
%% Repeated test.
yes;
will_succeed({Op1,Var,#b_literal{val=A}}, {Op2,Var,#b_literal{val=B}}) ->
@@ -702,6 +740,9 @@ will_succeed({_,_}=Same, {_,_}=Same) ->
yes;
will_succeed({Test1,Var}, {Test2,Var}) ->
will_succeed_test(Test1, Test2);
+will_succeed({{'not',is_boolean},Var}, {'=:=',Var,#b_literal{val=Lit}})
+ when is_boolean(Lit) ->
+ no;
will_succeed({_,_}, {_,_}) ->
maybe;
will_succeed({_,_}, {_,_,_}) ->
@@ -760,8 +801,8 @@ will_succeed_1('=/=', A, '=:=', B) when A =:= B -> no;
will_succeed_1('<', A, '=:=', B) when B >= A -> no;
will_succeed_1('<', A, '=/=', B) when B >= A -> yes;
will_succeed_1('<', A, '<', B) when B >= A -> yes;
-will_succeed_1('<', A, '=<', B) when B > A -> yes;
-will_succeed_1('<', A, '>=', B) when B > A -> no;
+will_succeed_1('<', A, '=<', B) when B >= A -> yes;
+will_succeed_1('<', A, '>=', B) when B >= A -> no;
will_succeed_1('<', A, '>', B) when B >= A -> no;
will_succeed_1('=<', A, '=:=', B) when B > A -> no;
@@ -781,9 +822,9 @@ will_succeed_1('>=', A, '>', B) when B < A -> yes;
will_succeed_1('>', A, '=:=', B) when B =< A -> no;
will_succeed_1('>', A, '=/=', B) when B =< A -> yes;
will_succeed_1('>', A, '<', B) when B =< A -> no;
-will_succeed_1('>', A, '=<', B) when B < A -> no;
+will_succeed_1('>', A, '=<', B) when B =< A -> no;
will_succeed_1('>', A, '>=', B) when B =< A -> yes;
-will_succeed_1('>', A, '>', B) when B < A -> yes;
+will_succeed_1('>', A, '>', B) when B =< A -> yes;
will_succeed_1('==', A, '==', B) ->
if
@@ -920,17 +961,14 @@ combine_eqs_1([L|Ls], #st{bs=Blocks0}=St0) ->
end;
combine_eqs_1([], St) -> St.
-comb_get_sw(L, Blocks) ->
- comb_get_sw(L, true, Blocks).
-
-comb_get_sw(L, Safe0, #st{bs=Blocks,skippable=Skippable}) ->
+comb_get_sw(L, #st{bs=Blocks,skippable=Skippable}) ->
#b_blk{is=Is,last=Last} = map_get(L, Blocks),
- Safe1 = Safe0 andalso is_map_key(L, Skippable),
+ Safe0 = is_map_key(L, Skippable),
case Last of
#b_ret{} ->
none;
#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail} ->
- case comb_is(Is, Bool, Safe1) of
+ case comb_is(Is, Bool, Safe0) of
{none,_} ->
none;
{#b_set{op={bif,'=:='},args=[#b_var{}=Arg,#b_literal{}=Lit]},Safe} ->
@@ -941,7 +979,7 @@ comb_get_sw(L, Safe0, #st{bs=Blocks,skippable=Skippable}) ->
#b_br{} ->
none;
#b_switch{arg=#b_var{}=Arg,fail=Fail,list=List} ->
- {none,Safe} = comb_is(Is, none, Safe1),
+ {none,Safe} = comb_is(Is, none, Safe0),
{Safe,Arg,L,Fail,List}
end.
diff --git a/lib/compiler/src/beam_ssa_lint.erl b/lib/compiler/src/beam_ssa_lint.erl
index a003607dab..224095d4c4 100644
--- a/lib/compiler/src/beam_ssa_lint.erl
+++ b/lib/compiler/src/beam_ssa_lint.erl
@@ -65,13 +65,19 @@ format_error({{_M,F,A},{phi_inside_block, Name, Id}}) ->
[F, A, format_var(Name), Id]);
format_error({{_M,F,A},{undefined_label_in_phi, Label, I}}) ->
io_lib:format("~p/~p: Unknown block label ~p in phi node ~ts",
- [F, A, Label, format_instr(I)]).
+ [F, A, Label, format_instr(I)]);
+format_error({{_M,F,A},{succeeded_not_preceded, I}}) ->
+ io_lib:format("~p/~p: ~ts does not reference the preceding instruction",
+ [F, A, format_instr(I)]);
+format_error({{_M,F,A},{succeeded_not_last, I}}) ->
+ io_lib:format("~p/~p: ~ts is not the last instruction in its block",
+ [F, A, format_instr(I)]).
format_instr(I) ->
[$',beam_ssa_pp:format_instr(I),$'].
format_var(V) ->
- beam_ssa_pp:format_var(#b_var{name=V}).
+ beam_ssa_pp:format_var(V).
validate_function(F) ->
try
@@ -86,34 +92,36 @@ validate_function(F) ->
erlang:raise(Class, Error, Stack)
end.
--type defined_vars() :: gb_sets:set(beam_ssa:var_name()).
+-type defined_vars() :: gb_sets:set(beam_ssa:argument()).
-record(vvars,
{blocks :: #{ beam_ssa:label() => beam_ssa:b_blk() },
branch_def_vars :: #{
- %% Describes the variable state at the time of this exact branch (phi
- %% node validation).
- {From :: beam_ssa:label(), To :: beam_ssa:label()} => defined_vars(),
- %% Describes the variable state common to all branches leading to this
- %% label (un/redefined variable validation).
- beam_ssa:label() => defined_vars() },
+ %% Describes the variable state at the time of
+ %% this exact branch (phi node validation).
+ {From :: beam_ssa:label(),
+ To :: beam_ssa:label()} => defined_vars(),
+ %% Describes the variable state common to all
+ %% branches leading to this label (un/redefined
+ %% variable validation).
+ beam_ssa:label() => defined_vars() },
defined_vars :: defined_vars()}).
-spec validate_variables(beam_ssa:b_function()) -> ok.
validate_variables(#b_function{ args = Args, bs = Blocks }) ->
%% Prefill the mapping with function arguments.
- ArgNames = vvars_get_varnames(Args),
- DefVars = gb_sets:from_list(ArgNames),
+ Args = vvars_get_variables(Args),
+ DefVars = gb_sets:from_list(Args),
Entry = 0,
State = #vvars{blocks = Blocks,
branch_def_vars = #{ Entry => DefVars },
defined_vars = DefVars},
- ok = vvars_assert_unique(Blocks, ArgNames),
+ ok = vvars_assert_unique(Blocks, Args),
vvars_phi_nodes(vvars_block(Entry, State)).
%% Checks the uniqueness of all variables across all blocks.
--spec vvars_assert_unique(Blocks, [beam_ssa:var_name()]) -> ok when
+-spec vvars_assert_unique(Blocks, [beam_ssa:b_var()]) -> ok when
Blocks :: #{ beam_ssa:label() => beam_ssa:b_blk() }.
vvars_assert_unique(Blocks, Args) ->
BlockIs = [Is || #b_blk{is=Is} <- maps:values(Blocks)],
@@ -124,12 +132,12 @@ vvars_assert_unique(Blocks, Args) ->
ok.
-spec vvars_assert_unique_1(Is, Defined) -> ok when
- Is :: list(beam_ssa:b_set()),
- Defined :: #{ beam_ssa:var_name() => beam_ssa:b_set() }.
-vvars_assert_unique_1([#b_set{dst=#b_var{name=DstName}}=I|Is], Defined) ->
+ Is :: list(beam_ssa:b_set()),
+ Defined :: #{ beam_ssa:b_var() => beam_ssa:b_set() }.
+vvars_assert_unique_1([#b_set{dst=Dst}=I|Is], Defined) ->
case Defined of
- #{DstName:=Old} -> throw({redefined_variable, DstName, Old, I});
- _ -> vvars_assert_unique_1(Is, Defined#{DstName=>I})
+ #{Dst:=Old} -> throw({redefined_variable, Dst, Old, I});
+ _ -> vvars_assert_unique_1(Is, Defined#{Dst=>I})
end;
vvars_assert_unique_1([], Defined) ->
Defined.
@@ -141,17 +149,17 @@ vvars_phi_nodes(#vvars{ blocks = Blocks }=State) ->
ok.
-spec vvars_phi_nodes_1(Is, Id, State) -> ok when
- Is :: list(beam_ssa:b_set()),
- Id :: beam_ssa:label(),
- State :: #vvars{}.
+ Is :: list(beam_ssa:b_set()),
+ Id :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_phi_nodes_1([#b_set{ op = phi, args = Phis }=I | Is], Id, State) ->
ok = vvars_assert_phi_paths(Phis, I, Id, State),
ok = vvars_assert_phi_vars(Phis, I, Id, State),
vvars_phi_nodes_1(Is, Id, State);
vvars_phi_nodes_1([_ | Is], Id, _State) ->
- case [Dst || #b_set{op=phi,dst=#b_var{name=Dst}} <- Is] of
- [Name|_] ->
- throw({phi_inside_block, Name, Id});
+ case [Dst || #b_set{op=phi,dst=Dst} <- Is] of
+ [Var|_] ->
+ throw({phi_inside_block, Var, Id});
[] ->
ok
end;
@@ -161,10 +169,10 @@ vvars_phi_nodes_1([], _Id, _State) ->
%% Checks whether all paths leading to this phi node are represented, and that
%% it doesn't reference any non-existent paths.
-spec vvars_assert_phi_paths(Phis, I, Id, State) -> ok when
- Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
- Id :: beam_ssa:label(),
- I :: beam_ssa:b_set(),
- State :: #vvars{}.
+ Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
+ Id :: beam_ssa:label(),
+ I :: beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_phi_paths(Phis, I, Id, State) ->
BranchKeys = maps:keys(State#vvars.branch_def_vars),
RequiredPaths = ordsets:from_list([From || {From, To} <- BranchKeys, To =:= Id]),
@@ -173,34 +181,34 @@ vvars_assert_phi_paths(Phis, I, Id, State) ->
[_|_]=MissingPaths -> throw({missing_phi_paths, MissingPaths, I});
[] -> ok
end.
- %% %% The following test is sometimes useful to find missing optimizations.
- %% %% It is commented out, though, because it can be triggered by
- %% %% by weird but legal code.
- %% case ordsets:subtract(ProvidedPaths, RequiredPaths) of
- %% [_|_]=GarbagePaths -> throw({garbage_phi_paths, GarbagePaths, I});
- %% [] -> ok
- %% end.
+%% %% The following test is sometimes useful to find missing optimizations.
+%% %% It is commented out, though, because it can be triggered by
+%% %% by weird but legal code.
+%% case ordsets:subtract(ProvidedPaths, RequiredPaths) of
+%% [_|_]=GarbagePaths -> throw({garbage_phi_paths, GarbagePaths, I});
+%% [] -> ok
+%% end.
%% Checks whether all variables used in this phi node are defined in the branch
%% they arrived on.
-spec vvars_assert_phi_vars(Phis, I, Id, State) -> ok when
- Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
- Id :: beam_ssa:label(),
- I :: beam_ssa:b_set(),
- State :: #vvars{}.
+ Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
+ Id :: beam_ssa:label(),
+ I :: beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_phi_vars(Phis, I, Id, #vvars{blocks=Blocks,
branch_def_vars=BranchDefVars}) ->
Vars = [{Var, From} || {#b_var{}=Var, From} <- Phis],
- foreach(fun({#b_var{name=VarName}, From}) ->
+ foreach(fun({Var, From}) ->
BranchKey = {From, Id},
case BranchDefVars of
#{BranchKey:=DefVars} ->
- case gb_sets:is_member(VarName, DefVars) of
+ case gb_sets:is_member(Var, DefVars) of
true -> ok;
- false -> throw({unknown_variable, VarName, I})
+ false -> throw({unknown_variable, Var, I})
end;
#{} ->
- throw({unknown_phi_variable, VarName, BranchKey, I})
+ throw({unknown_phi_variable, Var, BranchKey, I})
end
end, Vars),
Labels = [From || {#b_literal{},From} <- Phis],
@@ -214,32 +222,44 @@ vvars_assert_phi_vars(Phis, I, Id, #vvars{blocks=Blocks,
end, Labels).
-spec vvars_block(Id, State) -> #vvars{} when
- Id :: beam_ssa:label(),
- State :: #vvars{}.
+ Id :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_block(Id, State0) ->
#{ Id := #b_blk{ is = Is, last = Terminator} } = State0#vvars.blocks,
#{ Id := DefVars } = State0#vvars.branch_def_vars,
State = State0#vvars{ defined_vars = DefVars },
vvars_terminator(Terminator, Id, vvars_block_1(Is, State)).
--spec vvars_block_1(Blocks, State) -> #vvars{} when
- Blocks :: list(beam_ssa:b_blk()),
- State :: #vvars{}.
+-spec vvars_block_1(Is, State) -> #vvars{} when
+ Is :: list(#b_set{}),
+ State :: #vvars{}.
vvars_block_1([], State) ->
State;
-vvars_block_1([#b_set{ dst = #b_var{ name = DstName }, op = phi } | Is], State0) ->
+vvars_block_1([#b_set{dst=OpVar,args=OpArgs}=I,
+ #b_set{op=succeeded,args=[OpVar],dst=SuccVar}], State) ->
+ ok = vvars_assert_args(OpArgs, I, State),
+ vvars_save_var(SuccVar, vvars_save_var(OpVar, State));
+vvars_block_1([#b_set{op=succeeded,args=Args}=I | [_|_]], State) ->
+ ok = vvars_assert_args(Args, I, State),
+ %% 'succeeded' must be the last instruction in its block.
+ throw({succeeded_not_last, I});
+vvars_block_1([#b_set{op=succeeded,args=Args}=I], State)->
+ ok = vvars_assert_args(Args, I, State),
+ %% 'succeeded' must be be directly preceded by the operation it checks.
+ throw({succeeded_not_preceded, I});
+vvars_block_1([#b_set{ dst = Dst, op = phi } | Is], State) ->
%% We don't check phi node arguments at this point since we may not have
%% visited their definition yet. They'll be handled later on in
%% vvars_phi_nodes/1 after all blocks are processed.
- vvars_block_1(Is, vvars_save_var(DstName, State0));
-vvars_block_1([#b_set{ dst = #b_var{ name = DstName }, args = Args }=I | Is], State0) ->
- ok = vvars_assert_args(Args, I, State0),
- vvars_block_1(Is, vvars_save_var(DstName, State0)).
+ vvars_block_1(Is, vvars_save_var(Dst, State));
+vvars_block_1([#b_set{ dst = Dst, args = Args }=I | Is], State) ->
+ ok = vvars_assert_args(Args, I, State),
+ vvars_block_1(Is, vvars_save_var(Dst, State)).
-spec vvars_terminator(Terminator, From, State) -> #vvars{} when
- Terminator :: beam_ssa:terminator(),
- From :: beam_ssa:label(),
- State :: #vvars{}.
+ Terminator :: beam_ssa:terminator(),
+ From :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_terminator(#b_ret{ arg = Arg }=I, _From, State) ->
ok = vvars_assert_args([Arg], I, State),
State;
@@ -264,62 +284,62 @@ vvars_terminator(#b_br{ bool = Arg, succ = Succ, fail = Fail }=I, From, State) -
vvars_terminator_1(Labels, From, State).
-spec vvars_terminator_1(Labels, From, State) -> #vvars{} when
- Labels :: list(beam_ssa:label()),
- From :: beam_ssa:label(),
- State :: #vvars{}.
+ Labels :: list(beam_ssa:label()),
+ From :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_terminator_1(Labels0, From, State0) ->
%% Filter out all branches that have already been taken. This should result
%% in either all of Labels0 or an empty list.
Labels = [To || To <- Labels0,
- not maps:is_key({From, To}, State0#vvars.branch_def_vars)],
+ not maps:is_key({From, To}, State0#vvars.branch_def_vars)],
true = Labels =:= Labels0 orelse Labels =:= [], %Assertion
State1 = foldl(fun(To, State) ->
- vvars_save_branch(From, To, State)
+ vvars_save_branch(From, To, State)
end, State0, Labels),
foldl(fun(To, State) ->
- vvars_block(To, State)
+ vvars_block(To, State)
end, State1, Labels).
%% Gets all variable names in args, ignoring literals etc
--spec vvars_get_varnames(Args) -> list(beam_ssa:var_name()) when
- Args :: list(beam_ssa:argument()).
-vvars_get_varnames(Args) ->
- [Name || #b_var{ name = Name } <- Args].
+-spec vvars_get_variables(Args) -> list(beam_ssa:b_var()) when
+ Args :: list(beam_ssa:argument()).
+vvars_get_variables(Args) ->
+ [Var || #b_var{}=Var <- Args].
%% Checks that all variables in Args are defined in all paths leading to the
%% current State.
-spec vvars_assert_args(Args, I, State) -> ok when
- Args :: list(beam_ssa:argument()),
- I :: beam_ssa:terminator() | beam_ssa:b_set(),
- State :: #vvars{}.
+ Args :: list(beam_ssa:argument()),
+ I :: beam_ssa:terminator() | beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_args(Args, I, #vvars{defined_vars=DefVars}=State) ->
foreach(fun(#b_remote{mod=Mod,name=Name}) ->
vvars_assert_args([Mod,Name], I, State);
- (#b_var{name=Name}) ->
- case gb_sets:is_member(Name, DefVars) of
+ (#b_var{}=Var) ->
+ case gb_sets:is_member(Var, DefVars) of
true -> ok;
- false -> throw({unknown_variable,Name,I})
+ false -> throw({unknown_variable,Var,I})
end;
(_) -> ok
end, Args).
%% Checks that all given labels are defined in State.
-spec vvars_assert_labels(Labels, I, State) -> ok when
- Labels :: list(beam_ssa:label()),
- I :: beam_ssa:terminator(),
- State :: #vvars{}.
+ Labels :: list(beam_ssa:label()),
+ I :: beam_ssa:terminator(),
+ State :: #vvars{}.
vvars_assert_labels(Labels, I, #vvars{blocks=Blocks}) ->
foreach(fun(Label) ->
- case maps:is_key(Label, Blocks) of
- false -> throw({unknown_block, Label, I});
- true -> ok
- end
+ case maps:is_key(Label, Blocks) of
+ false -> throw({unknown_block, Label, I});
+ true -> ok
+ end
end, Labels).
-spec vvars_save_branch(From, To, State) -> #vvars{} when
- From :: beam_ssa:label(),
- To :: beam_ssa:label(),
- State :: #vvars{}.
+ From :: beam_ssa:label(),
+ To :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_save_branch(From, To, State) ->
DefVars = State#vvars.defined_vars,
Branches0 = State#vvars.branch_def_vars,
@@ -335,15 +355,15 @@ vvars_save_branch(From, To, State) ->
end.
-spec vvars_merge_branches(New, Existing) -> defined_vars() when
- New :: defined_vars(),
- Existing :: defined_vars().
+ New :: defined_vars(),
+ Existing :: defined_vars().
vvars_merge_branches(New, Existing) ->
gb_sets:intersection(New, Existing).
--spec vvars_save_var(VarName, State) -> #vvars{} when
- VarName :: beam_ssa:var_name(),
- State :: #vvars{}.
-vvars_save_var(VarName, State0) ->
+-spec vvars_save_var(Var, State) -> #vvars{} when
+ Var :: #b_var{},
+ State :: #vvars{}.
+vvars_save_var(Var, State0) ->
%% vvars_assert_unique guarantees that variables are never set twice.
- DefVars = gb_sets:insert(VarName, State0#vvars.defined_vars),
+ DefVars = gb_sets:insert(Var, State0#vvars.defined_vars),
State0#vvars{ defined_vars = DefVars }.
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index d87c66c272..00d57ca7a9 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -28,9 +28,9 @@
%%% in one phase and then apply it in the next without having to risk working
%%% with incomplete information.
%%%
-%%% Each sub-pass operates on a #st{} record and a func_info_db(), where the
-%%% former is just a #b_function{} whose blocks can be represented either in
-%%% linear or map form, and the latter is a map with information about all
+%%% Each sub-pass operates on a #opt_st{} record and a func_info_db(), where
+%%% the former is just a #b_function{} whose blocks can be represented either
+%%% in linear or map form, and the latter is a map with information about all
%%% functions in the module (see beam_ssa_opt.hrl for more details).
%%%
@@ -39,45 +39,76 @@
-include("beam_ssa_opt.hrl").
--import(lists, [all/2,append/1,duplicate/2,foldl/3,keyfind/3,member/2,
- reverse/1,reverse/2,
- splitwith/2,sort/1,takewhile/2,unzip/1]).
+-import(lists, [all/2,append/1,duplicate/2,flatten/1,foldl/3,keyfind/3,
+ member/2,reverse/1,reverse/2,splitwith/2,sort/1,
+ takewhile/2,unzip/1]).
--define(DEFAULT_REPETITIONS, 2).
+-define(MAX_REPETITIONS, 16).
-spec module(beam_ssa:b_module(), [compile:option()]) ->
{'ok',beam_ssa:b_module()}.
--record(st, {ssa :: [{beam_ssa:label(),beam_ssa:b_blk()}] |
- beam_ssa:block_map(),
- args :: [beam_ssa:b_var()],
- cnt :: beam_ssa:label(),
- anno :: beam_ssa:anno()}).
--type st_map() :: #{ func_id() => #st{} }.
-
module(Module, Opts) ->
- FuncDb0 = case proplists:get_value(no_module_opt, Opts, false) of
- false -> build_func_db(Module);
- true -> #{}
- end,
+ FuncDb = case proplists:get_value(no_module_opt, Opts, false) of
+ false -> build_func_db(Module);
+ true -> #{}
+ end,
%% Passes that perform module-level optimizations are often aided by
%% optimizing callers before callees and vice versa, so we optimize all
- %% functions in call order, flipping it as required.
+ %% functions in call order, alternating the order every time.
StMap0 = build_st_map(Module),
- Order = get_call_order_po(StMap0, FuncDb0),
-
- Phases =
- [{Order, prologue_passes(Opts)}] ++
- repeat(Opts, repeated_passes(Opts), Order) ++
- [{Order, epilogue_passes(Opts)}],
+ Order = get_call_order_po(StMap0, FuncDb),
- {StMap, _FuncDb} = foldl(fun({FuncIds, Ps}, {StMap, FuncDb}) ->
- phase(FuncIds, Ps, StMap, FuncDb)
- end, {StMap0, FuncDb0}, Phases),
+ Phases = [{once, Order, prologue_passes(Opts)},
+ {module, module_passes(Opts)},
+ {fixpoint, Order, repeated_passes(Opts)},
+ {once, Order, epilogue_passes(Opts)}],
+ StMap = run_phases(Phases, StMap0, FuncDb),
{ok, finish(Module, StMap)}.
+run_phases([{module, Passes} | Phases], StMap0, FuncDb0) ->
+ {StMap, FuncDb} = compile:run_sub_passes(Passes, {StMap0, FuncDb0}),
+ run_phases(Phases, StMap, FuncDb);
+run_phases([{once, FuncIds0, Passes} | Phases], StMap0, FuncDb0) ->
+ FuncIds = skip_removed(FuncIds0, StMap0),
+ {StMap, FuncDb} = phase(FuncIds, Passes, StMap0, FuncDb0),
+ run_phases(Phases, StMap, FuncDb);
+run_phases([{fixpoint, FuncIds0, Passes} | Phases], StMap0, FuncDb0) ->
+ FuncIds = skip_removed(FuncIds0, StMap0),
+ RevFuncIds = reverse(FuncIds),
+ Order = {FuncIds, RevFuncIds},
+ {StMap, FuncDb} = fixpoint(RevFuncIds, Order, Passes,
+ StMap0, FuncDb0, ?MAX_REPETITIONS),
+ run_phases(Phases, StMap, FuncDb);
+run_phases([], StMap, _FuncDb) ->
+ StMap.
+
+skip_removed(FuncIds, StMap) ->
+ [F || F <- FuncIds, is_map_key(F, StMap)].
+
+%% Run the given passes until a fixpoint is reached.
+fixpoint(_FuncIds, _Order, _Passes, StMap, FuncDb, 0) ->
+ %% Too many repetitions. Give up and return what we have.
+ {StMap, FuncDb};
+fixpoint(FuncIds0, Order0, Passes, StMap0, FuncDb0, N) ->
+ {StMap, FuncDb} = phase(FuncIds0, Passes, StMap0, FuncDb0),
+ Repeat = changed(FuncIds0, FuncDb0, FuncDb, StMap0, StMap),
+ case cerl_sets:size(Repeat) of
+ 0 ->
+ %% No change. Fixpoint reached.
+ {StMap, FuncDb};
+ _ ->
+ %% Repeat the optimizations for functions whose code has
+ %% changed or for which there is potentially updated type
+ %% information.
+ {OrderA, OrderB} = Order0,
+ Order = {OrderB, OrderA},
+ FuncIds = [Id || Id <- OrderA, cerl_sets:is_element(Id, Repeat)],
+ fixpoint(FuncIds, Order, Passes, StMap, FuncDb, N - 1)
+ end.
+
phase([FuncId | Ids], Ps, StMap, FuncDb0) ->
try compile:run_sub_passes(Ps, {map_get(FuncId, StMap), FuncDb0}) of
{St, FuncDb} ->
@@ -91,19 +122,94 @@ phase([FuncId | Ids], Ps, StMap, FuncDb0) ->
phase([], _Ps, StMap, FuncDb) ->
{StMap, FuncDb}.
-%% Repeats the given passes, alternating the order between runs to make the
-%% type pass more efficient.
-repeat(Opts, Ps, OrderA) ->
- Repeat = proplists:get_value(ssa_opt_repeat, Opts, ?DEFAULT_REPETITIONS),
- OrderB = reverse(OrderA),
- repeat_1(Repeat, Ps, OrderA, OrderB).
-
-repeat_1(0, _Opts, _OrderA, _OrderB) ->
- [];
-repeat_1(N, Ps, OrderA, OrderB) when N > 0, N rem 2 =:= 0 ->
- [{OrderA, Ps} | repeat_1(N - 1, Ps, OrderA, OrderB)];
-repeat_1(N, Ps, OrderA, OrderB) when N > 0, N rem 2 =:= 1 ->
- [{OrderB, Ps} | repeat_1(N - 1, Ps, OrderA, OrderB)].
+changed(PrevIds, FuncDb0, FuncDb, StMap0, StMap) ->
+ %% Find all functions in FuncDb that can be reached by changes
+ %% of argument and/or return types. Those are the functions that
+ %% may gain from running the optimization passes again.
+ %%
+ %% Note that we examine all functions in FuncDb, not only functions
+ %% optimized in the previous run, because the argument types can
+ %% have been updated for functions not included in the previous run.
+
+ F = fun(Id, A) ->
+ case cerl_sets:is_element(Id, A) of
+ true ->
+ A;
+ false ->
+ {#func_info{arg_types=ATs0,succ_types=ST0},
+ #func_info{arg_types=ATs1,succ_types=ST1}} =
+ {map_get(Id, FuncDb0),map_get(Id, FuncDb)},
+
+ %% If the argument types have changed for this
+ %% function, re-optimize this function and all
+ %% functions it calls directly or indirectly.
+ %%
+ %% If the return type has changed, re-optimize
+ %% this function and all functions that call
+ %% this function directly or indirectly.
+ Opts = case ATs0 =:= ATs1 of
+ true -> [];
+ false -> [called]
+ end ++
+ case ST0 =:= ST1 of
+ true -> [];
+ false -> [callers]
+ end,
+ case Opts of
+ [] -> A;
+ [_|_] -> add_changed([Id], Opts, FuncDb, A)
+ end
+ end
+ end,
+ Ids = foldl(F, cerl_sets:new(), maps:keys(FuncDb)),
+
+ %% From all functions that were optimized in the previous run,
+ %% find the functions that had any change in the SSA code. Those
+ %% functions might gain from being optimized again. (For example,
+ %% when beam_ssa_dead has shortcut branches, the types for some
+ %% variables could become narrower, giving beam_ssa_type new
+ %% opportunities for optimization.)
+ %%
+ %% Note that the functions examined could be functions with module-level
+ %% optimization turned off (and thus not included in FuncDb).
+
+ foldl(fun(Id, A) ->
+ case cerl_sets:is_element(Id, A) of
+ true ->
+ %% Already scheduled for another optimization.
+ %% No need to compare the SSA code.
+ A;
+ false ->
+ %% Compare the SSA code before and after optimization.
+ case {map_get(Id, StMap0),map_get(Id, StMap)} of
+ {Same,Same} -> A;
+ {_,_} -> cerl_sets:add_element(Id, A)
+ end
+ end
+ end, Ids, PrevIds).
+
+add_changed([Id|Ids], Opts, FuncDb, S0) when is_map_key(Id, FuncDb) ->
+ case cerl_sets:is_element(Id, S0) of
+ true ->
+ add_changed(Ids, Opts, FuncDb, S0);
+ false ->
+ S1 = cerl_sets:add_element(Id, S0),
+ #func_info{in=In,out=Out} = map_get(Id, FuncDb),
+ S2 = case member(callers, Opts) of
+ true -> add_changed(In, Opts, FuncDb, S1);
+ false -> S1
+ end,
+ S = case member(called, Opts) of
+ true -> add_changed(Out, Opts, FuncDb, S2);
+ false -> S2
+ end,
+ add_changed(Ids, Opts, FuncDb, S)
+ end;
+add_changed([_|Ids], Opts, FuncDb, S) ->
+ %% This function is exempt from module-level optimization and will not
+ %% provide any more information.
+ add_changed(Ids, Opts, FuncDb, S);
+add_changed([], _, _, S) -> S.
%%
@@ -117,7 +223,7 @@ build_st_map(#b_module{body=Fs}) ->
build_st_map_1([F | Fs], Map) ->
#b_function{anno=Anno,args=Args,cnt=Counter,bs=Bs} = F,
- St = #st{anno=Anno,args=Args,cnt=Counter,ssa=Bs},
+ St = #opt_st{anno=Anno,args=Args,cnt=Counter,ssa=Bs},
build_st_map_1(Fs, Map#{ get_func_id(F) => St });
build_st_map_1([], Map) ->
Map.
@@ -127,9 +233,14 @@ finish(#b_module{body=Fs0}=Module, StMap) ->
Module#b_module{body=finish_1(Fs0, StMap)}.
finish_1([F0 | Fs], StMap) ->
- #st{anno=Anno,cnt=Counter,ssa=Blocks} = map_get(get_func_id(F0), StMap),
- F = F0#b_function{anno=Anno,bs=Blocks,cnt=Counter},
- [F | finish_1(Fs, StMap)];
+ FuncId = get_func_id(F0),
+ case StMap of
+ #{ FuncId := #opt_st{anno=Anno,cnt=Counter,ssa=Blocks} } ->
+ F = F0#b_function{anno=Anno,bs=Blocks,cnt=Counter},
+ [F | finish_1(Fs, StMap)];
+ #{} ->
+ finish_1(Fs, StMap)
+ end;
finish_1([], _StMap) ->
[].
@@ -145,10 +256,17 @@ prologue_passes(Opts) ->
?PASS(ssa_opt_linearize),
?PASS(ssa_opt_tuple_size),
?PASS(ssa_opt_record),
- ?PASS(ssa_opt_cse), %Helps the first type pass.
- ?PASS(ssa_opt_type_start)],
+ ?PASS(ssa_opt_cse), % Helps the first type pass.
+ ?PASS(ssa_opt_live)], % ...
passes_1(Ps, Opts).
+module_passes(Opts) ->
+ Ps0 = [{ssa_opt_type_start,
+ fun({StMap, FuncDb}) ->
+ beam_ssa_type:opt_start(StMap, FuncDb)
+ end}],
+ passes_1(Ps0, Opts).
+
%% These passes all benefit from each other (in roughly this order), so they
%% are repeated as required.
repeated_passes(Opts) ->
@@ -157,6 +275,9 @@ repeated_passes(Opts) ->
?PASS(ssa_opt_dead),
?PASS(ssa_opt_cse),
?PASS(ssa_opt_tail_phis),
+ ?PASS(ssa_opt_sink),
+ ?PASS(ssa_opt_tuple_size),
+ ?PASS(ssa_opt_record),
?PASS(ssa_opt_type_continue)], %Must run after ssa_opt_dead to
%clean up phi nodes.
passes_1(Ps, Opts).
@@ -169,14 +290,15 @@ epilogue_passes(Opts) ->
%% Run live one more time to clean up after the float and sw
%% passes.
?PASS(ssa_opt_live),
+ ?PASS(ssa_opt_try),
?PASS(ssa_opt_bsm),
- ?PASS(ssa_opt_bsm_units),
?PASS(ssa_opt_bsm_shortcut),
- ?PASS(ssa_opt_blockify),
?PASS(ssa_opt_sink),
+ ?PASS(ssa_opt_blockify),
?PASS(ssa_opt_merge_blocks),
?PASS(ssa_opt_get_tuple_element),
- ?PASS(ssa_opt_trim_unreachable)],
+ ?PASS(ssa_opt_trim_unreachable),
+ ?PASS(ssa_opt_unfold_literals)],
passes_1(Ps, Opts).
passes_1(Ps, Opts0) ->
@@ -194,16 +316,26 @@ passes_1(Ps, Opts0) ->
%% Builds a function information map with basic information about incoming and
%% outgoing local calls, as well as whether the function is exported.
-spec build_func_db(#b_module{}) -> func_info_db().
-build_func_db(#b_module{body=Fs,exports=Exports}) ->
+build_func_db(#b_module{body=Fs,attributes=Attr,exports=Exports0}) ->
+ Exports = fdb_exports(Attr, Exports0),
try
- fdb_1(Fs, gb_sets:from_list(Exports), #{})
+ fdb_fs(Fs, Exports, #{})
catch
%% All module-level optimizations are invalid when a NIF can override a
%% function, so we have to bail out.
throw:load_nif -> #{}
end.
-fdb_1([#b_function{ args=Args,bs=Bs }=F | Fs], Exports, FuncDb0) ->
+fdb_exports([{on_load, L} | Attrs], Exports) ->
+ %% Functions marked with on_load must be treated as exported to prevent
+ %% them from being optimized away when unused.
+ fdb_exports(Attrs, flatten(L) ++ Exports);
+fdb_exports([_Attr | Attrs], Exports) ->
+ fdb_exports(Attrs, Exports);
+fdb_exports([], Exports) ->
+ gb_sets:from_list(Exports).
+
+fdb_fs([#b_function{ args=Args,bs=Bs }=F | Fs], Exports, FuncDb0) ->
Id = get_func_id(F),
#b_local{name=#b_literal{val=Name}, arity=Arity} = Id,
@@ -224,8 +356,8 @@ fdb_1([#b_function{ args=Args,bs=Bs }=F | Fs], Exports, FuncDb0) ->
fdb_is(Is, Id, FuncDb)
end, FuncDb1, Bs),
- fdb_1(Fs, Exports, FuncDb);
-fdb_1([], _Exports, FuncDb) ->
+ fdb_fs(Fs, Exports, FuncDb);
+fdb_fs([], _Exports, FuncDb) ->
FuncDb.
fdb_is([#b_set{op=call,
@@ -237,6 +369,12 @@ fdb_is([#b_set{op=call,
name=#b_literal{val=load_nif}},
_Path, _LoadInfo]} | _Is], _Caller, _FuncDb) ->
throw(load_nif);
+fdb_is([#b_set{op=make_fun,
+ args=[#b_local{}=Callee | _]} | Is],
+ Caller, FuncDb) ->
+ %% The make_fun instruction's type depends on the return type of the
+ %% function in question, so we treat this as a function call.
+ fdb_is(Is, Caller, fdb_update(Caller, Callee, FuncDb));
fdb_is([_ | Is], Caller, FuncDb) ->
fdb_is(Is, Caller, FuncDb);
fdb_is([], _Caller, FuncDb) ->
@@ -289,29 +427,25 @@ gco_rpo([], _, Seen, Acc) ->
%%% Trivial sub passes.
%%%
-ssa_opt_dead({#st{ssa=Linear}=St, FuncDb}) ->
- {St#st{ssa=beam_ssa_dead:opt(Linear)}, FuncDb}.
-
-ssa_opt_linearize({#st{ssa=Blocks}=St, FuncDb}) ->
- {St#st{ssa=beam_ssa:linearize(Blocks)}, FuncDb}.
+ssa_opt_dead({#opt_st{ssa=Linear}=St, FuncDb}) ->
+ {St#opt_st{ssa=beam_ssa_dead:opt(Linear)}, FuncDb}.
-ssa_opt_type_start({#st{ssa=Linear0,args=Args,anno=Anno}=St0, FuncDb0}) ->
- {Linear, FuncDb} = beam_ssa_type:opt_start(Linear0, Args, Anno, FuncDb0),
- {St0#st{ssa=Linear}, FuncDb}.
+ssa_opt_linearize({#opt_st{ssa=Blocks}=St, FuncDb}) ->
+ {St#opt_st{ssa=beam_ssa:linearize(Blocks)}, FuncDb}.
-ssa_opt_type_continue({#st{ssa=Linear0,args=Args,anno=Anno}=St0, FuncDb0}) ->
+ssa_opt_type_continue({#opt_st{ssa=Linear0,args=Args,anno=Anno}=St0, FuncDb0}) ->
{Linear, FuncDb} = beam_ssa_type:opt_continue(Linear0, Args, Anno, FuncDb0),
- {St0#st{ssa=Linear}, FuncDb}.
+ {St0#opt_st{ssa=Linear}, FuncDb}.
-ssa_opt_type_finish({#st{args=Args,anno=Anno0}=St0, FuncDb0}) ->
+ssa_opt_type_finish({#opt_st{args=Args,anno=Anno0}=St0, FuncDb0}) ->
{Anno, FuncDb} = beam_ssa_type:opt_finish(Args, Anno0, FuncDb0),
- {St0#st{anno=Anno}, FuncDb}.
+ {St0#opt_st{anno=Anno}, FuncDb}.
-ssa_opt_blockify({#st{ssa=Linear}=St, FuncDb}) ->
- {St#st{ssa=maps:from_list(Linear)}, FuncDb}.
+ssa_opt_blockify({#opt_st{ssa=Linear}=St, FuncDb}) ->
+ {St#opt_st{ssa=maps:from_list(Linear)}, FuncDb}.
-ssa_opt_trim_unreachable({#st{ssa=Blocks}=St, FuncDb}) ->
- {St#st{ssa=beam_ssa:trim_unreachable(Blocks)}, FuncDb}.
+ssa_opt_trim_unreachable({#opt_st{ssa=Blocks}=St, FuncDb}) ->
+ {St#opt_st{ssa=beam_ssa:trim_unreachable(Blocks)}, FuncDb}.
%%%
%%% Split blocks before certain instructions to enable more optimizations.
@@ -323,14 +457,14 @@ ssa_opt_trim_unreachable({#st{ssa=Blocks}=St, FuncDb}) ->
%%% for sinking get_tuple_element instructions.
%%%
-ssa_opt_split_blocks({#st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_split_blocks({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
P = fun(#b_set{op={bif,element}}) -> true;
(#b_set{op=call}) -> true;
(#b_set{op=make_fun}) -> true;
(_) -> false
end,
{Blocks,Count} = beam_ssa:split_blocks(P, Blocks0, Count0),
- {St#st{ssa=Blocks,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=Blocks,cnt=Count}, FuncDb}.
%%%
%%% Coalesce phi nodes.
@@ -354,10 +488,10 @@ ssa_opt_split_blocks({#st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
%%% different registers).
%%%
-ssa_opt_coalesce_phis({#st{ssa=Blocks0}=St, FuncDb}) ->
+ssa_opt_coalesce_phis({#opt_st{ssa=Blocks0}=St, FuncDb}) ->
Ls = beam_ssa:rpo(Blocks0),
Blocks = c_phis_1(Ls, Blocks0),
- {St#st{ssa=Blocks}, FuncDb}.
+ {St#opt_st{ssa=Blocks}, FuncDb}.
c_phis_1([L|Ls], Blocks0) ->
case map_get(L, Blocks0) of
@@ -460,9 +594,9 @@ c_fix_branches([], _, Blocks) -> Blocks.
%%% - Smaller stack frames
%%%
-ssa_opt_tail_phis({#st{ssa=SSA0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_tail_phis({#opt_st{ssa=SSA0,cnt=Count0}=St, FuncDb}) ->
{SSA,Count} = opt_tail_phis(SSA0, Count0),
- {St#st{ssa=SSA,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=SSA,cnt=Count}, FuncDb}.
opt_tail_phis(Blocks, Count) when is_map(Blocks) ->
opt_tail_phis(maps:values(Blocks), Blocks, Count);
@@ -591,7 +725,7 @@ are_all_literals(Args) ->
%%% be replaced with get_tuple_element/3 instructions.
%%%
-ssa_opt_element({#st{ssa=Blocks}=St, FuncDb}) ->
+ssa_opt_element({#opt_st{ssa=Blocks}=St, FuncDb}) ->
%% Collect the information about element instructions in this
%% function.
GetEls = collect_element_calls(beam_ssa:linearize(Blocks)),
@@ -603,7 +737,7 @@ ssa_opt_element({#st{ssa=Blocks}=St, FuncDb}) ->
%% For each chain, swap the first element call with the
%% element call with the highest index.
- {St#st{ssa=swap_element_calls(Chains, Blocks)}, FuncDb}.
+ {St#opt_st{ssa=swap_element_calls(Chains, Blocks)}, FuncDb}.
collect_element_calls([{L,#b_blk{is=Is0,last=Last}}|Bs]) ->
case {Is0,Last} of
@@ -664,9 +798,9 @@ swap_element_calls_1([], _, Blocks) ->
%%% when applicable.
%%%
-ssa_opt_record({#st{ssa=Linear}=St, FuncDb}) ->
+ssa_opt_record({#opt_st{ssa=Linear}=St, FuncDb}) ->
Blocks = maps:from_list(Linear),
- {St#st{ssa=record_opt(Linear, Blocks)}, FuncDb}.
+ {St#opt_st{ssa=record_opt(Linear, Blocks)}, FuncDb}.
record_opt([{L,#b_blk{is=Is0,last=Last}=Blk0}|Bs], Blocks) ->
Is = record_opt_is(Is0, Last, Blocks),
@@ -759,9 +893,9 @@ is_tagged_tuple_4([], _, _) -> no.
%%% subexpressions across instructions that clobber the X registers.
%%%
-ssa_opt_cse({#st{ssa=Linear}=St, FuncDb}) ->
+ssa_opt_cse({#opt_st{ssa=Linear}=St, FuncDb}) ->
M = #{0=>#{}},
- {St#st{ssa=cse(Linear, #{}, M)}, FuncDb}.
+ {St#opt_st{ssa=cse(Linear, #{}, M)}, FuncDb}.
cse([{L,#b_blk{is=Is0,last=Last0}=Blk}|Bs], Sub0, M0) ->
Es0 = map_get(L, M0),
@@ -898,34 +1032,52 @@ cse_suitable(#b_set{}) -> false.
-record(fs,
{s=undefined :: 'undefined' | 'cleared',
regs=#{} :: #{beam_ssa:b_var():=beam_ssa:b_var()},
+ vars=cerl_sets:new() :: cerl_sets:set(),
fail=none :: 'none' | beam_ssa:label(),
non_guards :: gb_sets:set(beam_ssa:label()),
bs :: beam_ssa:block_map()
}).
-ssa_opt_float({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_float({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
NonGuards = non_guards(Linear0),
Blocks = maps:from_list(Linear0),
Fs = #fs{non_guards=NonGuards,bs=Blocks},
{Linear,Count} = float_opt(Linear0, Count0, Fs),
- {St#st{ssa=Linear,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}.
-float_blk_is_in_guard(#b_blk{last=#b_br{fail=F}}, #fs{non_guards=NonGuards}) ->
- not gb_sets:is_member(F, NonGuards);
-float_blk_is_in_guard(#b_blk{}, #fs{}) ->
+%% The fconv instruction doesn't support jumping to a fail label, so we have to
+%% skip this optimization if the fail block is a guard.
+%%
+%% We also skip the optimization in blocks that always fail, as it's both
+%% difficult and pointless to rewrite them to use float ops.
+float_can_optimize_blk(#b_blk{last=#b_br{bool=#b_var{},fail=F}},
+ #fs{non_guards=NonGuards}) ->
+ gb_sets:is_member(F, NonGuards);
+float_can_optimize_blk(#b_blk{}, #fs{}) ->
false.
+float_opt([{L,#b_blk{is=[#b_set{op=exception_trampoline,args=[Var]}]}=Blk0} |
+ Bs0], Count0, Fs) ->
+ %% If we've replaced a BIF with float operations, we'll have a lot of extra
+ %% blocks that jump to the same failure block, which may have a trampoline
+ %% that refers to the original operation.
+ %%
+ %% Since the point of the trampoline is to keep the BIF from being removed
+ %% by liveness optimization, we can discard it as the liveness pass leaves
+ %% floats alone.
+ Blk = case cerl_sets:is_element(Var, Fs#fs.vars) of
+ true -> Blk0#b_blk{is=[]};
+ false -> Blk0
+ end,
+ {Bs, Count} = float_opt(Bs0, Count0, Fs),
+ {[{L,Blk}|Bs],Count};
float_opt([{L,Blk}|Bs0], Count0, Fs) ->
- case float_blk_is_in_guard(Blk, Fs) of
+ case float_can_optimize_blk(Blk, Fs) of
true ->
- %% This block is inside a guard. Don't do
- %% any floating point optimizations.
- {Bs,Count} = float_opt(Bs0, Count0, Fs),
- {[{L,Blk}|Bs],Count};
+ float_opt_1(L, Blk, Bs0, Count0, Fs);
false ->
- %% This block is not inside a guard.
- %% We can do the optimization.
- float_opt_1(L, Blk, Bs0, Count0, Fs)
+ {Bs,Count} = float_opt(Bs0, Count0, Fs),
+ {[{L,Blk}|Bs],Count}
end;
float_opt([], Count, _Fs) ->
{[],Count}.
@@ -1004,12 +1156,12 @@ float_maybe_flush(Blk0, #fs{s=cleared,fail=Fail,bs=Blocks}=Fs0, Count0) ->
#b_blk{last=#b_br{bool=#b_var{},succ=Succ}=Br} = Blk0,
%% If the success block starts with a floating point operation, we can
- %% defer flushing to that block as long as it isn't a guard.
+ %% defer flushing to that block as long as it's suitable for optimization.
#b_blk{is=Is} = SuccBlk = map_get(Succ, Blocks),
- SuccIsGuard = float_blk_is_in_guard(SuccBlk, Fs0),
+ CanOptimizeSucc = float_can_optimize_blk(SuccBlk, Fs0),
case Is of
- [#b_set{anno=#{float_op:=_}}|_] when not SuccIsGuard ->
+ [#b_set{anno=#{float_op:=_}}|_] when CanOptimizeSucc ->
%% No flush needed.
{[],Blk0,Fs0,Count0};
_ ->
@@ -1065,21 +1217,22 @@ float_opt_is([], Fs, _Count, _Acc) ->
none.
float_make_op(#b_set{op={bif,Op},dst=Dst,args=As0}=I0,
- Ts, #fs{s=S,regs=Rs0}=Fs, Count0) ->
+ Ts, #fs{s=S,regs=Rs0,vars=Vs0}=Fs, Count0) ->
{As1,Rs1,Count1} = float_load(As0, Ts, Rs0, Count0, []),
{As,Is0} = unzip(As1),
{Fr,Count2} = new_reg('@fr', Count1),
FrDst = #b_var{name=Fr},
I = I0#b_set{op={float,Op},dst=FrDst,args=As},
+ Vs = cerl_sets:add_element(Dst, Vs0),
Rs = Rs1#{Dst=>FrDst},
Is = append(Is0) ++ [I],
case S of
undefined ->
{Ignore,Count} = new_reg('@ssa_ignore', Count2),
C = #b_set{op={float,clearerror},dst=#b_var{name=Ignore}},
- {[C|Is],Fs#fs{s=cleared,regs=Rs},Count};
+ {[C|Is],Fs#fs{s=cleared,regs=Rs,vars=Vs},Count};
cleared ->
- {Is,Fs#fs{regs=Rs},Count2}
+ {Is,Fs#fs{regs=Rs,vars=Vs},Count2}
end.
float_load([A|As], [T|Ts], Rs0, Count0, Acc) ->
@@ -1143,12 +1296,12 @@ float_flush_regs(#fs{regs=Rs}) ->
%%% with a cheaper instructions
%%%
-ssa_opt_live({#st{ssa=Linear0}=St, FuncDb}) ->
+ssa_opt_live({#opt_st{ssa=Linear0}=St, FuncDb}) ->
RevLinear = reverse(Linear0),
Blocks0 = maps:from_list(RevLinear),
Blocks = live_opt(RevLinear, #{}, Blocks0),
Linear = beam_ssa:linearize(Blocks),
- {St#st{ssa=Linear}, FuncDb}.
+ {St#opt_st{ssa=Linear}, FuncDb}.
live_opt([{L,Blk0}|Bs], LiveMap0, Blocks) ->
Blk1 = beam_ssa_share:block(Blk0, Blocks),
@@ -1208,34 +1361,31 @@ live_opt_is([#b_set{op=phi,dst=Dst}=I|Is], Live, Acc) ->
false ->
live_opt_is(Is, Live, Acc)
end;
-live_opt_is([#b_set{op=succeeded,dst=SuccDst=SuccDstVar,
- args=[Dst]}=SuccI,
- #b_set{dst=Dst}=I|Is], Live0, Acc) ->
- case gb_sets:is_member(Dst, Live0) of
- true ->
- Live1 = gb_sets:add(Dst, Live0),
- Live = gb_sets:delete_any(SuccDst, Live1),
- live_opt_is([I|Is], Live, [SuccI|Acc]);
- false ->
- case live_opt_unused(I) of
- {replace,NewI0} ->
- NewI = NewI0#b_set{dst=SuccDstVar},
- live_opt_is([NewI|Is], Live0, Acc);
- keep ->
- case gb_sets:is_member(SuccDst, Live0) of
- true ->
- Live1 = gb_sets:add(Dst, Live0),
- Live = gb_sets:delete(SuccDst, Live1),
- live_opt_is([I|Is], Live, [SuccI|Acc]);
- false ->
- live_opt_is([I|Is], Live0, Acc)
- end
- end
+live_opt_is([#b_set{op=succeeded,dst=SuccDst,args=[MapDst]}=SuccI,
+ #b_set{op=get_map_element,dst=MapDst}=MapI | Is],
+ Live0, Acc) ->
+ case {gb_sets:is_member(SuccDst, Live0),
+ gb_sets:is_member(MapDst, Live0)} of
+ {true, true} ->
+ Live = gb_sets:delete(SuccDst, Live0),
+ live_opt_is([MapI | Is], Live, [SuccI | Acc]);
+ {true, false} ->
+ %% 'get_map_element' is unused; replace 'succeeded' with
+ %% 'has_map_field'
+ NewI = MapI#b_set{op=has_map_field,dst=SuccDst},
+ live_opt_is([NewI | Is], Live0, Acc);
+ {false, true} ->
+ %% 'succeeded' is unused (we know it will succeed); discard it and
+ %% keep 'get_map_element'
+ live_opt_is([MapI | Is], Live0, Acc);
+ {false, false} ->
+ live_opt_is(Is, Live0, Acc)
end;
live_opt_is([#b_set{dst=Dst}=I|Is], Live0, Acc) ->
case gb_sets:is_member(Dst, Live0) of
true ->
- Live1 = gb_sets:union(Live0, gb_sets:from_ordset(beam_ssa:used(I))),
+ LiveUsed = gb_sets:from_ordset(beam_ssa:used(I)),
+ Live1 = gb_sets:union(Live0, LiveUsed),
Live = gb_sets:delete(Dst, Live1),
live_opt_is(Is, Live, [I|Acc]);
false ->
@@ -1243,16 +1393,112 @@ live_opt_is([#b_set{dst=Dst}=I|Is], Live0, Acc) ->
true ->
live_opt_is(Is, Live0, Acc);
false ->
- Live = gb_sets:union(Live0, gb_sets:from_ordset(beam_ssa:used(I))),
+ LiveUsed = gb_sets:from_ordset(beam_ssa:used(I)),
+ Live = gb_sets:union(Live0, LiveUsed),
live_opt_is(Is, Live, [I|Acc])
end
end;
live_opt_is([], Live, Acc) ->
{Acc,Live}.
-live_opt_unused(#b_set{op=get_map_element}=Set) ->
- {replace,Set#b_set{op=has_map_field}};
-live_opt_unused(_) -> keep.
+%%%
+%%% Do a strength reduction of try/catch and catch.
+%%%
+%%% In try/catch constructs where the expression is restricted
+%%% (essentially a guard expression) and the error reason is ignored
+%%% in the catch part, such as:
+%%%
+%%% try
+%%% <RestrictedExpression>
+%%% catch
+%%% _:_ ->
+%%% ...
+%%% end
+%%%
+%%% the try/catch can be eliminated by simply removing the `new_try_tag`,
+%%% `landingpad`, and `kill_try_tag` instructions.
+
+ssa_opt_try({#opt_st{ssa=Linear0}=St, FuncDb}) ->
+ Linear = opt_try(Linear0),
+ {St#opt_st{ssa=Linear}, FuncDb}.
+
+opt_try([{L,#b_blk{is=[#b_set{op=new_try_tag}],
+ last=Last}=Blk0}|Bs0]) ->
+ #b_br{succ=Succ,fail=Fail} = Last,
+ Ws = cerl_sets:from_list([Succ,Fail]),
+ try do_opt_try(Bs0, Ws) of
+ Bs ->
+ Blk = Blk0#b_blk{is=[],
+ last=#b_br{bool=#b_literal{val=true},
+ succ=Succ,fail=Succ}},
+ [{L,Blk}|opt_try(Bs)]
+ catch
+ throw:not_possible ->
+ [{L,Blk0}|opt_try(Bs0)]
+ end;
+opt_try([{L,Blk}|Bs]) ->
+ [{L,Blk}|opt_try(Bs)];
+opt_try([]) -> [].
+
+do_opt_try([{L,Blk}|Bs]=Bs0, Ws0) ->
+ case cerl_sets:is_element(L, Ws0) of
+ false ->
+ %% This block is not reachable from the block with the
+ %% `new_try_tag` instruction. Retain it. There is no
+ %% need to check it for safety.
+ case cerl_sets:size(Ws0) of
+ 0 -> Bs0;
+ _ -> [{L,Blk}|do_opt_try(Bs, Ws0)]
+ end;
+ true ->
+ Ws1 = cerl_sets:del_element(L, Ws0),
+ #b_blk{is=Is0} = Blk,
+ case is_safe_without_try(Is0, []) of
+ safe ->
+ %% This block does not execute any instructions
+ %% that would require a try. Analyze successors.
+ Successors = beam_ssa:successors(Blk),
+ Ws = cerl_sets:union(cerl_sets:from_list(Successors),
+ Ws1),
+ [{L,Blk}|do_opt_try(Bs, Ws)];
+ unsafe ->
+ %% There is something unsafe in the block, for
+ %% example a `call` instruction or an `extract`
+ %% instruction.
+ throw(not_possible);
+ {done,Is} ->
+ %% This block kills the try tag (either after successful
+ %% execution or at the landing pad). Don't analyze
+ %% successors.
+ [{L,Blk#b_blk{is=Is}}|do_opt_try(Bs, Ws1)]
+ end
+ end;
+do_opt_try([], Ws) ->
+ 0 = cerl_sets:size(Ws), %Assertion.
+ [].
+
+is_safe_without_try([#b_set{op=kill_try_tag}|Is], Acc) ->
+ %% Remove this kill_try_tag instruction. If there was a landingpad
+ %% instruction in this block, it has already been removed. Preserve
+ %% all other instructions in the block.
+ {done,reverse(Is, Acc)};
+is_safe_without_try([#b_set{op=extract}|_], _Acc) ->
+ %% The error reason is accessed.
+ unsafe;
+is_safe_without_try([#b_set{op=exception_trampoline}|Is], Acc) ->
+ is_safe_without_try(Is, Acc);
+is_safe_without_try([#b_set{op=landingpad}|Is], Acc) ->
+ is_safe_without_try(Is, Acc);
+is_safe_without_try([#b_set{op=Op}=I|Is], Acc) ->
+ IsSafe = case Op of
+ phi -> true;
+ _ -> beam_ssa:no_side_effect(I)
+ end,
+ case IsSafe of
+ true -> is_safe_without_try(Is, [I|Acc]);
+ false -> unsafe
+ end;
+is_safe_without_try([], _Acc) -> safe.
%%%
%%% Optimize binary matching.
@@ -1264,10 +1510,10 @@ live_opt_unused(_) -> keep.
%%% with bs_test_tail.
%%%
-ssa_opt_bsm({#st{ssa=Linear}=St, FuncDb}) ->
+ssa_opt_bsm({#opt_st{ssa=Linear}=St, FuncDb}) ->
Extracted0 = bsm_extracted(Linear),
Extracted = cerl_sets:from_list(Extracted0),
- {St#st{ssa=bsm_skip(Linear, Extracted)}, FuncDb}.
+ {St#opt_st{ssa=bsm_skip(Linear, Extracted)}, FuncDb}.
bsm_skip([{L,#b_blk{is=Is0}=Blk}|Bs0], Extracted) ->
Bs = bsm_skip(Bs0, Extracted),
@@ -1365,14 +1611,14 @@ coalesce_skips_is(_, _, _) ->
%%% Short-cutting binary matching instructions.
%%%
-ssa_opt_bsm_shortcut({#st{ssa=Linear}=St, FuncDb}) ->
+ssa_opt_bsm_shortcut({#opt_st{ssa=Linear}=St, FuncDb}) ->
Positions = bsm_positions(Linear, #{}),
case map_size(Positions) of
0 ->
%% No binary matching instructions.
{St, FuncDb};
_ ->
- {St#st{ssa=bsm_shortcut(Linear, Positions)}, FuncDb}
+ {St#opt_st{ssa=bsm_shortcut(Linear, Positions)}, FuncDb}
end.
bsm_positions([{L,#b_blk{is=Is,last=Last}}|Bs], PosMap0) ->
@@ -1431,110 +1677,6 @@ bsm_shortcut([{L,#b_blk{is=Is,last=Last0}=Blk}|Bs], PosMap) ->
bsm_shortcut([], _PosMap) -> [].
%%%
-%%% Eliminate redundant bs_test_unit2 instructions.
-%%%
-
-ssa_opt_bsm_units({#st{ssa=Linear}=St, FuncDb}) ->
- {St#st{ssa=bsm_units(Linear, #{})}, FuncDb}.
-
-bsm_units([{L,#b_blk{last=#b_br{succ=Succ,fail=Fail}}=Block0} | Bs], UnitMaps0) ->
- UnitsIn = maps:get(L, UnitMaps0, #{}),
- {Block, UnitsOut} = bsm_units_skip(Block0, UnitsIn),
- UnitMaps1 = bsm_units_join(Succ, UnitsOut, UnitMaps0),
- UnitMaps = bsm_units_join(Fail, UnitsIn, UnitMaps1),
- [{L, Block} | bsm_units(Bs, UnitMaps)];
-bsm_units([{L,#b_blk{last=#b_switch{fail=Fail,list=Switch}}=Block} | Bs], UnitMaps0) ->
- UnitsIn = maps:get(L, UnitMaps0, #{}),
- Labels = [Fail | [Lbl || {_Arg, Lbl} <- Switch]],
- UnitMaps = foldl(fun(Lbl, UnitMaps) ->
- bsm_units_join(Lbl, UnitsIn, UnitMaps)
- end, UnitMaps0, Labels),
- [{L, Block} | bsm_units(Bs, UnitMaps)];
-bsm_units([{L, Block} | Bs], UnitMaps) ->
- [{L, Block} | bsm_units(Bs, UnitMaps)];
-bsm_units([], _UnitMaps) ->
- [].
-
-bsm_units_skip(Block, Units) ->
- bsm_units_skip_1(Block#b_blk.is, Block, Units).
-
-bsm_units_skip_1([#b_set{op=bs_start_match,dst=New}|_], Block, Units) ->
- %% We bail early since there can't be more than one match per block.
- {Block, Units#{ New => 1 }};
-bsm_units_skip_1([#b_set{op=bs_match,
- dst=New,
- args=[#b_literal{val=skip},
- Ctx,
- #b_literal{val=binary},
- _Flags,
- #b_literal{val=all},
- #b_literal{val=OpUnit}]}=Skip | Test],
- Block0, Units) ->
- [#b_set{op=succeeded,dst=Bool,args=[New]}] = Test, %Assertion.
- #b_br{bool=Bool} = Last0 = Block0#b_blk.last, %Assertion.
- CtxUnit = map_get(Ctx, Units),
- if
- CtxUnit rem OpUnit =:= 0 ->
- Is = takewhile(fun(I) -> I =/= Skip end, Block0#b_blk.is),
- Last = Last0#b_br{bool=#b_literal{val=true}},
- Block = Block0#b_blk{is=Is,last=Last},
- {Block, Units#{ New => CtxUnit }};
- CtxUnit rem OpUnit =/= 0 ->
- {Block0, Units#{ New => OpUnit, Ctx => OpUnit }}
- end;
-bsm_units_skip_1([#b_set{op=bs_match,dst=New,args=Args}|_], Block, Units) ->
- [_,Ctx|_] = Args,
- CtxUnit = map_get(Ctx, Units),
- OpUnit = bsm_op_unit(Args),
- {Block, Units#{ New => gcd(OpUnit, CtxUnit) }};
-bsm_units_skip_1([_I | Is], Block, Units) ->
- bsm_units_skip_1(Is, Block, Units);
-bsm_units_skip_1([], Block, Units) ->
- {Block, Units}.
-
-bsm_op_unit([_,_,_,Size,#b_literal{val=U}]) ->
- case Size of
- #b_literal{val=Sz} when is_integer(Sz) -> Sz*U;
- _ -> U
- end;
-bsm_op_unit([#b_literal{val=string},_,#b_literal{val=String}]) ->
- bit_size(String);
-bsm_op_unit([#b_literal{val=utf8}|_]) ->
- 8;
-bsm_op_unit([#b_literal{val=utf16}|_]) ->
- 16;
-bsm_op_unit([#b_literal{val=utf32}|_]) ->
- 32;
-bsm_op_unit(_) ->
- 1.
-
-%% Several paths can lead to the same match instruction and the inferred units
-%% may differ between them, so we can only keep the information that is common
-%% to all paths.
-bsm_units_join(Lbl, MapA, UnitMaps0) when is_map_key(Lbl, UnitMaps0) ->
- MapB = map_get(Lbl, UnitMaps0),
- Merged = if
- map_size(MapB) =< map_size(MapA) ->
- bsm_units_join_1(maps:keys(MapB), MapA, MapB);
- map_size(MapB) > map_size(MapA) ->
- bsm_units_join_1(maps:keys(MapA), MapB, MapA)
- end,
- UnitMaps0#{Lbl := Merged};
-bsm_units_join(Lbl, MapA, UnitMaps0) when MapA =/= #{} ->
- UnitMaps0#{Lbl => MapA};
-bsm_units_join(_Lbl, _MapA, UnitMaps0) ->
- UnitMaps0.
-
-bsm_units_join_1([Key | Keys], Left, Right) when is_map_key(Key, Left) ->
- UnitA = map_get(Key, Left),
- UnitB = map_get(Key, Right),
- bsm_units_join_1(Keys, Left, Right#{Key := gcd(UnitA, UnitB)});
-bsm_units_join_1([Key | Keys], Left, Right) ->
- bsm_units_join_1(Keys, Left, maps:remove(Key, Right));
-bsm_units_join_1([], _MapA, Right) ->
- Right.
-
-%%%
%%% Optimize binary construction.
%%%
%%% If an integer segment or a float segment has a literal size and
@@ -1543,9 +1685,9 @@ bsm_units_join_1([], _MapA, Right) ->
%%% to bs_put_string instructions in later pass.
%%%
-ssa_opt_bs_puts({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_bs_puts({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
{Linear,Count} = opt_bs_puts(Linear0, Count0, []),
- {St#st{ssa=Linear,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}.
opt_bs_puts([{L,#b_blk{is=Is}=Blk0}|Bs], Count0, Acc0) ->
case Is of
@@ -1641,8 +1783,13 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags},
I = I0#b_set{args=Args},
opt_bs_put(I);
{binary,_} when is_bitstring(Val) ->
- <<Bitstring:EffectiveSize/bits,_/bits>> = Val,
- [Bitstring];
+ case Val of
+ <<Bitstring:EffectiveSize/bits,_/bits>> ->
+ [Bitstring];
+ _ ->
+ %% Specified size exceeds size of bitstring.
+ not_possible
+ end;
{float,Endian} ->
try
[opt_bs_put_float(Val, EffectiveSize, Endian)]
@@ -1763,12 +1910,12 @@ opt_bs_put_split_int_1(Int, L, R) ->
%%% is_tuple_of_arity instruction by the loader.
%%%
-ssa_opt_tuple_size({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_tuple_size({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
%% This optimization is only safe in guards, as prefixing tuple_size with
%% an is_tuple check prevents it from throwing an exception.
NonGuards = non_guards(Linear0),
{Linear,Count} = opt_tup_size(Linear0, NonGuards, Count0, []),
- {St#st{ssa=Linear,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}.
opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], NonGuards, Count0, Acc0) ->
case {Is,Last} of
@@ -1835,33 +1982,20 @@ opt_tup_size_is([], _, _, _Acc) -> none.
%%%
%%% Optimize #b_switch{} instructions.
%%%
-%%% If the argument for a #b_switch{} comes from a phi node with all
-%%% literals, any values in the switch list which are not in the phi
-%%% node can be removed.
-%%%
-%%% If the values in the phi node and switch list are the same,
-%%% the failure label can't be reached and be eliminated.
-%%%
%%% A #b_switch{} with only one value can be rewritten to
%%% a #b_br{}. A switch that only verifies that the argument
-%%% is 'true' or 'false' can be rewritten to a is_boolean test.
-%%%
+%%% is 'true' or 'false' can be rewritten to an is_boolean test.
+%%%b
-ssa_opt_sw({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
+ssa_opt_sw({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
{Linear,Count} = opt_sw(Linear0, Count0, []),
- {St#st{ssa=Linear,cnt=Count}, FuncDb}.
+ {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}.
opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Sw0}=Blk0}|Bs], Count0, Acc) ->
- %% Ensure that no label in the switch list is the same
- %% as the failure label.
- #b_switch{fail=Fail,list=List0} = Sw0,
- List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
- Sw1 = beam_ssa:normalize(Sw0#b_switch{list=List}),
- case Sw1 of
+ case Sw0 of
#b_switch{arg=Arg,fail=Fail,list=[{Lit,Lbl}]} ->
%% Rewrite a single value switch to a br.
- Bool = #b_var{name={'@ssa_bool',Count0}},
- Count = Count0 + 1,
+ {Bool,Count} = new_var('@ssa_bool', Count0),
IsEq = #b_set{op={bif,'=:='},dst=Bool,args=[Arg,Lit]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsEq],last=Br},
@@ -1870,17 +2004,13 @@ opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Sw0}=Blk0}|Bs], Count0, Acc) ->
list=[{#b_literal{val=B1},Lbl},{#b_literal{val=B2},Lbl}]}
when B1 =:= not B2 ->
%% Replace with is_boolean test.
- Bool = #b_var{name={'@ssa_bool',Count0}},
- Count = Count0 + 1,
+ {Bool,Count} = new_var('@ssa_bool', Count0),
IsBool = #b_set{op={bif,is_boolean},dst=Bool,args=[Arg]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsBool],last=Br},
opt_sw(Bs, Count, [{L,Blk}|Acc]);
- Sw0 ->
- opt_sw(Bs, Count0, [{L,Blk0}|Acc]);
- Sw ->
- Blk = Blk0#b_blk{last=Sw},
- opt_sw(Bs, Count0, [{L,Blk}|Acc])
+ _ ->
+ opt_sw(Bs, Count0, [{L,Blk0}|Acc])
end;
opt_sw([{L,#b_blk{}=Blk}|Bs], Count, Acc) ->
opt_sw(Bs, Count, [{L,Blk}|Acc]);
@@ -1891,10 +2021,10 @@ opt_sw([], Count, Acc) ->
%%% Merge blocks.
%%%
-ssa_opt_merge_blocks({#st{ssa=Blocks}=St, FuncDb}) ->
+ssa_opt_merge_blocks({#opt_st{ssa=Blocks}=St, FuncDb}) ->
Preds = beam_ssa:predecessors(Blocks),
Merged = merge_blocks_1(beam_ssa:rpo(Blocks), Preds, Blocks),
- {St#st{ssa=Merged}, FuncDb}.
+ {St#opt_st{ssa=Merged}, FuncDb}.
merge_blocks_1([L|Ls], Preds0, Blocks0) ->
case Preds0 of
@@ -1937,9 +2067,17 @@ verify_merge_is([#b_set{op=Op}|_]) ->
verify_merge_is(_) ->
ok.
-is_merge_allowed(_, #b_blk{}, #b_blk{is=[#b_set{op=peek_message}|_]}) ->
+is_merge_allowed(_, #b_blk{}, #b_blk{is=[#b_set{op=exception_trampoline}|_]}) ->
false;
-is_merge_allowed(L, #b_blk{last=#b_br{}}=Blk, #b_blk{is=Is}) ->
+is_merge_allowed(_, #b_blk{is=[#b_set{op=exception_trampoline}|_]}, #b_blk{}) ->
+ false;
+is_merge_allowed(L, #b_blk{}=Blk1, #b_blk{is=[#b_set{}=I|_]}=Blk2) ->
+ not beam_ssa:is_loop_header(I) andalso
+ is_merge_allowed_1(L, Blk1, Blk2);
+is_merge_allowed(L, Blk1, Blk2) ->
+ is_merge_allowed_1(L, Blk1, Blk2).
+
+is_merge_allowed_1(L, #b_blk{last=#b_br{}}=Blk, #b_blk{is=Is}) ->
%% The predecessor block must have exactly one successor (L) for
%% the merge to be safe.
case beam_ssa:successors(Blk) of
@@ -1958,7 +2096,7 @@ is_merge_allowed(L, #b_blk{last=#b_br{}}=Blk, #b_blk{is=Is}) ->
[_|_] ->
false
end;
-is_merge_allowed(_, #b_blk{last=#b_switch{}}, #b_blk{}) ->
+is_merge_allowed_1(_, #b_blk{last=#b_switch{}}, #b_blk{}) ->
false.
%%%
@@ -1977,9 +2115,7 @@ is_merge_allowed(_, #b_blk{last=#b_switch{}}, #b_blk{}) ->
%%% extracted values.
%%%
-ssa_opt_sink({#st{ssa=Blocks0}=St, FuncDb}) ->
- Linear = beam_ssa:linearize(Blocks0),
-
+ssa_opt_sink({#opt_st{ssa=Linear}=St, FuncDb}) ->
%% Create a map with all variables that define get_tuple_element
%% instructions. The variable name map to the block it is defined in.
case def_blocks(Linear) of
@@ -1988,10 +2124,12 @@ ssa_opt_sink({#st{ssa=Blocks0}=St, FuncDb}) ->
{St, FuncDb};
[_|_]=Defs0 ->
Defs = maps:from_list(Defs0),
- {do_ssa_opt_sink(Linear, Defs, St), FuncDb}
+ {do_ssa_opt_sink(Defs, St), FuncDb}
end.
-do_ssa_opt_sink(Linear, Defs, #st{ssa=Blocks0}=St) ->
+do_ssa_opt_sink(Defs, #opt_st{ssa=Linear}=St) ->
+ Blocks0 = maps:from_list(Linear),
+
%% Now find all the blocks that use variables defined by get_tuple_element
%% instructions.
Used = used_blocks(Linear, Defs, []),
@@ -2016,7 +2154,8 @@ do_ssa_opt_sink(Linear, Defs, #st{ssa=Blocks0}=St) ->
From = map_get(V, Defs),
move_defs(V, From, To, A)
end, Blocks0, DefLoc),
- St#st{ssa=Blocks}.
+
+ St#opt_st{ssa=beam_ssa:linearize(Blocks)}.
def_blocks([{L,#b_blk{is=Is}}|Bs]) ->
def_blocks_is(Is, L, def_blocks(Bs));
@@ -2045,15 +2184,14 @@ unsuitable(Linear, Blocks) ->
Unsuitable1 = unsuitable_recv(Linear, Blocks, Predecessors),
gb_sets:from_list(Unsuitable0 ++ Unsuitable1).
-unsuitable_1([{L,#b_blk{is=[#b_set{op=Op}|_]}}|Bs]) ->
+unsuitable_1([{L,#b_blk{is=[#b_set{op=Op}=I|_]}}|Bs]) ->
Unsuitable = case Op of
bs_extract -> true;
bs_put -> true;
+ exception_trampoline -> true;
{float,_} -> true;
landingpad -> true;
- peek_message -> true;
- wait_timeout -> true;
- _ -> false
+ _ -> beam_ssa:is_loop_header(I)
end,
case Unsuitable of
true ->
@@ -2087,10 +2225,10 @@ unsuitable_loop(L, Blocks, Predecessors, Acc) ->
unsuitable_loop_1(Ps, Blocks, Predecessors, Acc).
unsuitable_loop_1([P|Ps], Blocks, Predecessors, Acc0) ->
- case map_get(P, Blocks) of
- #b_blk{is=[#b_set{op=peek_message}|_]} ->
+ case is_loop_header(P, Blocks) of
+ true ->
unsuitable_loop_1(Ps, Blocks, Predecessors, Acc0);
- #b_blk{} ->
+ false ->
case ordsets:is_element(P, Acc0) of
false ->
Acc1 = ordsets:add_element(P, Acc0),
@@ -2102,6 +2240,14 @@ unsuitable_loop_1([P|Ps], Blocks, Predecessors, Acc0) ->
end;
unsuitable_loop_1([], _, _, Acc) -> Acc.
+is_loop_header(L, Blocks) ->
+ case map_get(L, Blocks) of
+ #b_blk{is=[I|_]} ->
+ beam_ssa:is_loop_header(I);
+ #b_blk{} ->
+ false
+ end.
+
%% new_def_locations([{Variable,[UsedInBlock]}|Vs], Defs,
%% Dominators, Numbering, Unsuitable) ->
%% [{Variable,NewDefinitionBlock}]
@@ -2207,9 +2353,9 @@ insert_def_is([], _V, Def) ->
%%% for combining get_tuple_element instructions.
%%%
-ssa_opt_get_tuple_element({#st{ssa=Blocks0}=St, FuncDb}) ->
+ssa_opt_get_tuple_element({#opt_st{ssa=Blocks0}=St, FuncDb}) ->
Blocks = opt_get_tuple_element(maps:to_list(Blocks0), Blocks0),
- {St#st{ssa=Blocks}, FuncDb}.
+ {St#opt_st{ssa=Blocks}, FuncDb}.
opt_get_tuple_element([{L,#b_blk{is=Is0}=Blk0}|Bs], Blocks) ->
case opt_get_tuple_element_is(Is0, false, []) of
@@ -2243,27 +2389,247 @@ collect_get_tuple_element(Is, _Src, Acc) ->
{Acc,Is}.
%%%
-%%% Common utilities.
+%%% Unfold literals to avoid unnecessary move instructions in call
+%%% instructions.
+%%%
+%%% Consider the following example:
+%%%
+%%% -module(whatever).
+%%% -export([foo/0]).
+%%% foo() ->
+%%% foobar(1, 2, 3).
+%%% foobar(A, B, C) ->
+%%% foobar(A, B, C, []).
+%%% foobar(A, B, C, D) -> ...
+%%%
+%%% The type optimization pass will find out that A, B, and C have constant
+%%% values and do constant folding, rewriting foobar/3 to:
+%%%
+%%% foobar(A, B, C) ->
+%%% foobar(1, 2, 3, []).
+%%%
+%%% That will result in three extra `move` instructions.
+%%%
+%%% This optimization sub pass will undo the constant folding
+%%% optimization, rewriting code to use the original variable instead
+%%% of the constant if the original variable is known to be in an x
+%%% register.
+%%%
+%%% This optimization sub pass will also undo constant folding of the
+%%% list of arguments in the call to error/2 in the last clause of a
+%%% function. For example:
+%%%
+%%% bar(X, Y) ->
+%%% error(function_clause, [X,42]).
+%%%
+%%% will be rewritten to:
+%%%
+%%% bar(X, Y) ->
+%%% error(function_clause, [X,Y]).
%%%
-gcd(A, B) ->
- case A rem B of
- 0 -> B;
- X -> gcd(B, X)
+ssa_opt_unfold_literals({St,FuncDb}) ->
+ #opt_st{ssa=Blocks0,args=Args,anno=Anno,cnt=Count0} = St,
+ ParamInfo = maps:get(parameter_info, Anno, #{}),
+ LitMap = collect_arg_literals(Args, ParamInfo, 0, #{}),
+ case map_size(LitMap) of
+ 0 ->
+ %% None of the arguments for this function are known
+ %% literals. Nothing to do.
+ {St,FuncDb};
+ _ ->
+ SafeMap = #{0 => true},
+ {Blocks,Count} = unfold_literals(beam_ssa:rpo(Blocks0),
+ LitMap, SafeMap, Count0, Blocks0),
+ {St#opt_st{ssa=Blocks,cnt=Count},FuncDb}
end.
+collect_arg_literals([V|Vs], Info, X, Acc0) ->
+ case Info of
+ #{V:=VarInfo} ->
+ Type = proplists:get_value(type, VarInfo, any),
+ case beam_types:get_singleton_value(Type) of
+ {ok,Val} ->
+ F = fun(Vars) -> [{X,V}|Vars] end,
+ Acc = maps:update_with(Val, F, [{X,V}], Acc0),
+ collect_arg_literals(Vs, Info, X + 1, Acc);
+ error ->
+ collect_arg_literals(Vs, Info, X + 1, Acc0)
+ end;
+ #{} ->
+ collect_arg_literals(Vs, Info, X + 1, Acc0)
+ end;
+collect_arg_literals([], _Info, _X, Acc) -> Acc.
+
+unfold_literals([L|Ls], LitMap, SafeMap0, Count0, Blocks0) ->
+ {Blocks,Safe,Count} =
+ case map_get(L, SafeMap0) of
+ false ->
+ %% Before reaching this block, an instruction that
+ %% clobbers x registers has been executed. *If* we
+ %% would use an argument variable instead of literal,
+ %% it would force the value to be saved to a y
+ %% register. This is not what we want.
+ {Blocks0,false,Count0};
+ true ->
+ %% All x registers live when entering the function
+ %% are still live. Using the variable instead of
+ %% the substituted value will eliminate a `move`
+ %% instruction.
+ #b_blk{is=Is0} = Blk = map_get(L, Blocks0),
+ {Is,Safe0,Count1} = unfold_lit_is(Is0, LitMap, Count0, []),
+ {Blocks0#{L:=Blk#b_blk{is=Is}},Safe0,Count1}
+ end,
+ %% Propagate safeness to successors.
+ Successors = beam_ssa:successors(L, Blocks),
+ SafeMap = unfold_update_succ(Successors, Safe, SafeMap0),
+ unfold_literals(Ls, LitMap, SafeMap, Count,Blocks);
+unfold_literals([], _, _, Count, Blocks) ->
+ {Blocks,Count}.
+
+unfold_update_succ([S|Ss], Safe, SafeMap0) ->
+ F = fun(Prev) -> Prev and Safe end,
+ SafeMap = maps:update_with(S, F, Safe, SafeMap0),
+ unfold_update_succ(Ss, Safe, SafeMap);
+unfold_update_succ([], _, SafeMap) -> SafeMap.
+
+unfold_lit_is([#b_set{op=call,
+ args=[#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error},
+ arity=2},
+ #b_literal{val=function_clause},
+ ArgumentList]}=I0|Is], LitMap, Count0, Acc0) ->
+ %% This is a call to error/2 that raises a function_clause
+ %% exception in the final clause of a function. Try to undo
+ %% constant folding in the list of arguments (the second argument
+ %% for error/2).
+ case unfold_arg_list(Acc0, ArgumentList, LitMap, Count0, 0, []) of
+ {[FinalPutList|_]=Acc,Count} ->
+ %% Acc now contains the possibly rewritten code that
+ %% creates the argument list. All that remains is to
+ %% rewrite the call to error/2 itself so that is will
+ %% refer to rewritten argument list. This is essential
+ %% when all arguments have known literal values as in this
+ %% example:
+ %%
+ %% foo(X, Y) -> error(function_clause, [0,1]).
+ %%
+ #b_set{op=put_list,dst=ListVar} = FinalPutList,
+ #b_set{args=[ErlangError,Fc,_]} = I0,
+ I = I0#b_set{args=[ErlangError,Fc,ListVar]},
+ {reverse(Acc, [I|Is]),false,Count};
+ {[],_} ->
+ %% Handle code such as:
+ %%
+ %% bar(KnownValue, Stk) -> error(function_clause, Stk).
+ {reverse(Acc0, [I0|Is]),false,Count0}
+ end;
+unfold_lit_is([#b_set{op=Op,args=Args0}=I0|Is], LitMap, Count, Acc) ->
+ %% Using a register instead of a literal is a clear win only for
+ %% `call` and `make_fun` instructions. Substituting into other
+ %% instructions is unlikely to be an improvement.
+ Unfold = case Op of
+ call -> true;
+ make_fun -> true;
+ _ -> false
+ end,
+ I = case Unfold of
+ true ->
+ Args = unfold_call_args(Args0, LitMap, -1),
+ I0#b_set{args=Args};
+ false ->
+ I0
+ end,
+ case beam_ssa:clobbers_xregs(I) of
+ true ->
+ %% This instruction clobbers x register. Don't do
+ %% any substitutions in rest of this block or in any
+ %% of its successors.
+ {reverse(Acc, [I|Is]),false,Count};
+ false ->
+ unfold_lit_is(Is, LitMap, Count, [I|Acc])
+ end;
+unfold_lit_is([], _LitMap, Count, Acc) ->
+ {reverse(Acc),true,Count}.
+
+%% unfold_arg_list(Is, ArgumentList, LitMap, Count0, X, Acc) ->
+%% {UpdatedAcc, Count}.
+%%
+%% Unfold the arguments in the argument list (second argument for error/2).
+%%
+%% Note that Is is the reversed list of instructions before the
+%% call to error/2. Because of the way the list is built in reverse,
+%% it means that the first put_list instruction found will add the first
+%% argument (x0) to the list, the second the second argument (x1), and
+%% so on.
+
+unfold_arg_list(Is, #b_literal{val=[Hd|Tl]}, LitMap, Count0, X, Acc) ->
+ %% Handle the case that the entire argument list (the second argument
+ %% for error/2) is a literal.
+ {PutListDst,Count} = new_var('@put_list', Count0),
+ PutList = #b_set{op=put_list,dst=PutListDst,
+ args=[#b_literal{val=Hd},#b_literal{val=Tl}]},
+ unfold_arg_list([PutList|Is], PutListDst, LitMap, Count, X, Acc);
+unfold_arg_list([#b_set{op=put_list,dst=List,
+ args=[Hd0,#b_literal{val=[Hd|Tl]}]}=I0|Is0],
+ List, LitMap, Count0, X, Acc) ->
+ %% The rest of the argument list is a literal list.
+ {PutListDst,Count} = new_var('@put_list', Count0),
+ PutList = #b_set{op=put_list,dst=PutListDst,
+ args=[#b_literal{val=Hd},#b_literal{val=Tl}]},
+ I = I0#b_set{args=[Hd0,PutListDst]},
+ unfold_arg_list([I,PutList|Is0], List, LitMap, Count, X, Acc);
+unfold_arg_list([#b_set{op=put_list,dst=List,args=[Hd0,Tl]}=I0|Is],
+ List, LitMap, Count, X, Acc) ->
+ %% Unfold the head of the list.
+ Hd = unfold_arg(Hd0, LitMap, X),
+ I = I0#b_set{args=[Hd,Tl]},
+ unfold_arg_list(Is, Tl, LitMap, Count, X + 1, [I|Acc]);
+unfold_arg_list([I|Is], List, LitMap, Count, X, Acc) ->
+ %% Some other instruction, such as bs_get_tail.
+ unfold_arg_list(Is, List, LitMap, Count, X, [I|Acc]);
+unfold_arg_list([], _, _, Count, _, Acc) ->
+ {reverse(Acc),Count}.
+
+unfold_call_args([A0|As], LitMap, X) ->
+ A = unfold_arg(A0, LitMap, X),
+ [A|unfold_call_args(As, LitMap, X + 1)];
+unfold_call_args([], _, _) -> [].
+
+unfold_arg(#b_literal{val=Val}=Lit, LitMap, X) ->
+ case LitMap of
+ #{Val:=Vars} ->
+ %% This literal is available in an x register.
+ %% If it is in the correct x register, use
+ %% the register. Don't bother if it is in the
+ %% wrong register, because that would still result
+ %% in a `move` instruction.
+ case keyfind(X, 1, Vars) of
+ false -> Lit;
+ {X,Var} -> Var
+ end;
+ #{} -> Lit
+ end;
+unfold_arg(Expr, _LitMap, _X) -> Expr.
+
+%%%
+%%% Common utilities.
+%%%
+
non_guards(Linear) ->
gb_sets:from_list(non_guards_1(Linear)).
non_guards_1([{L,#b_blk{is=Is}}|Bs]) ->
case Is of
+ [#b_set{op=exception_trampoline}|_] ->
+ [L | non_guards_1(Bs)];
[#b_set{op=landingpad}|_] ->
[L | non_guards_1(Bs)];
_ ->
non_guards_1(Bs)
end;
non_guards_1([]) ->
- [?BADARG_BLOCK].
+ [?EXCEPTION_BLOCK].
rel2fam(S0) ->
S1 = sofs:relation(S0),
@@ -2300,4 +2666,6 @@ new_var(#b_var{name={Base,N}}, Count) ->
true = is_integer(N), %Assertion.
{#b_var{name={Base,Count}},Count+1};
new_var(#b_var{name=Base}, Count) ->
+ {#b_var{name={Base,Count}},Count+1};
+new_var(Base, Count) when is_atom(Base) ->
{#b_var{name={Base,Count}},Count+1}.
diff --git a/lib/compiler/src/beam_ssa_opt.hrl b/lib/compiler/src/beam_ssa_opt.hrl
index 37711a6f48..800096dce2 100644
--- a/lib/compiler/src/beam_ssa_opt.hrl
+++ b/lib/compiler/src/beam_ssa_opt.hrl
@@ -38,16 +38,34 @@
%% when dealing with co-recursive functions.
arg_types = [] :: list(arg_type_map()),
- %% The inferred return type of this function, this is either [type()]
- %% or [] to note absence.
- ret_type = [] :: list()}).
+ %% The success types of this function, grouping return values by their
+ %% argument types at the time of return.
+ %%
+ %% This gives us more precise types than a naive join of all returned
+ %% values, as we can rule out the cases where the arguments are
+ %% incompatible with the ones we're passing.
+ %%
+ %% Note that the argument types are those seen on successful return,
+ %% they do not cover all types that are provided to the function.
+ succ_types = [] :: success_type_set()}).
-type arg_key() :: {CallerId :: func_id(),
CallDst :: beam_ssa:b_var()}.
-type arg_type_map() :: #{ arg_key() => term() }.
+-type call_self() :: {call_self, ArgTypes :: [term()]}.
+-type success_type_set() :: [{ArgTypes :: [term()],
+ RetType :: call_self() | term()}].
+
%% Per-function metadata used by various optimization passes to perform
%% module-level optimization. If a function is absent it means that
%% module-level optimization has been turned off for said function.
-type func_id() :: beam_ssa:b_local().
-type func_info_db() :: #{ func_id() => #func_info{} }.
+
+-record(opt_st, {ssa :: [{beam_ssa:label(),beam_ssa:b_blk()}] |
+ beam_ssa:block_map(),
+ args :: [beam_ssa:b_var()],
+ cnt :: beam_ssa:label(),
+ anno :: beam_ssa:anno()}).
+-type st_map() :: #{ func_id() => #opt_st{} }.
diff --git a/lib/compiler/src/beam_ssa_pp.erl b/lib/compiler/src/beam_ssa_pp.erl
index 34ac08b32e..fdfc91b425 100644
--- a/lib/compiler/src/beam_ssa_pp.erl
+++ b/lib/compiler/src/beam_ssa_pp.erl
@@ -35,10 +35,10 @@ format_function(#b_function{anno=Anno0,args=Args,
#{} ->
Anno0
end,
- ReachableBlocks = beam_ssa:rpo(Blocks),
- All = maps:keys(Blocks),
- Unreachable = ordsets:subtract(ordsets:from_list(All),
- ordsets:from_list(ReachableBlocks)),
+ ReachableBlocks = beam_ssa:rpo(Blocks),
+ All = maps:keys(Blocks),
+ Unreachable = ordsets:subtract(ordsets:from_list(All),
+ ordsets:from_list(ReachableBlocks)),
[case Anno0 of
#{location:={Filename,Line}} ->
io_lib:format("%% ~ts:~p\n", [Filename,Line]);
@@ -48,7 +48,8 @@ format_function(#b_function{anno=Anno0,args=Args,
io_lib:format("%% Counter = ~p\n", [Counter]),
[format_anno(Key, Value) ||
{Key,Value} <- lists:sort(maps:to_list(Anno))],
- io_lib:format("function ~p:~p(~ts) {\n", [M,F,format_args(Args, FuncAnno)]),
+ io_lib:format("function `~p`:`~p`(~ts) {\n",
+ [M, F, format_args(Args, FuncAnno)]),
[format_live_interval(Var, FuncAnno) || Var <- Args],
format_blocks(ReachableBlocks, Blocks, FuncAnno),
case Unreachable of
@@ -82,6 +83,20 @@ format_var(V) ->
%%% Local functions.
%%%
+format_anno(parameter_info, Map) when is_map(Map) ->
+ case map_size(Map) of
+ 0 ->
+ [];
+ _ ->
+ Params = lists:sort(maps:to_list(Map)),
+ Break = "\n%% ",
+ [io_lib:format("%% Parameters\n", []),
+ [io_lib:format("%% ~s =>~s~s\n",
+ [format_var(V),
+ Break,
+ format_param_info(I, Break)]) ||
+ {V,I} <- Params]]
+ end;
format_anno(Key, Map) when is_map(Map) ->
Sorted = lists:sort(maps:to_list(Map)),
[io_lib:format("%% ~s:\n", [Key]),
@@ -89,6 +104,20 @@ format_anno(Key, Map) when is_map(Map) ->
format_anno(Key, Value) ->
io_lib:format("%% ~s: ~p\n", [Key,Value]).
+format_param_info([{type, T} | Infos], Break) ->
+ [format_type(T, Break) |
+ format_param_info(Infos, Break)];
+format_param_info([Info | Infos], Break) ->
+ [io_lib:format("~s~p", [Break, Info]) |
+ format_param_info(Infos, Break)];
+format_param_info([], _Break) ->
+ [].
+
+format_type(T, Break) ->
+ %% Gross hack, but it's short and simple.
+ Indented = lists:flatten(io_lib:format("~p", [T])),
+ string:replace(Indented, [$\n], Break, all).
+
format_blocks(Ls, Blocks, Anno) ->
PP = [format_block(L, Blocks, Anno) || L <- Ls],
lists:join($\n, PP).
@@ -140,16 +169,20 @@ format_i_number(#{n:=N}) ->
format_i_number(#{}) -> [].
format_terminator(#b_br{anno=A,bool=#b_literal{val=true},succ=Lbl}, _) ->
- io_lib:format(" ~sbr label ~p\n", [format_i_number(A),Lbl]);
+ io_lib:format(" ~sbr ~ts\n", [format_i_number(A),format_label(Lbl)]);
format_terminator(#b_br{anno=A,bool=#b_literal{val=false},fail=Lbl}, _) ->
- io_lib:format(" ~sbr label ~p\n", [format_i_number(A),Lbl]);
+ io_lib:format(" ~sbr ~ts\n", [format_i_number(A),format_label(Lbl)]);
format_terminator(#b_br{anno=A,bool=Bool,succ=Succ,fail=Fail}, FuncAnno) ->
- io_lib:format(" ~sbr ~ts, label ~p, label ~p\n",
- [format_i_number(A),format_arg(Bool, FuncAnno),Succ,Fail]);
+ io_lib:format(" ~sbr ~ts, ~ts, ~ts\n",
+ [format_i_number(A),
+ format_arg(Bool, FuncAnno),
+ format_label(Succ),
+ format_label(Fail)]);
format_terminator(#b_switch{anno=A,arg=Arg,fail=Fail,list=List}, FuncAnno) ->
- io_lib:format(" ~sswitch ~ts, label ~p, ~ts\n",
- [format_i_number(A),format_arg(Arg, FuncAnno),Fail,
- format_list(List,FuncAnno)]);
+ io_lib:format(" ~sswitch ~ts, ~ts, ~ts\n",
+ [format_i_number(A),format_arg(Arg, FuncAnno),
+ format_label(Fail),
+ format_switch_list(List, FuncAnno)]);
format_terminator(#b_ret{anno=A,arg=Arg}, FuncAnno) ->
io_lib:format(" ~sret ~ts\n", [format_i_number(A),format_arg(Arg, FuncAnno)]).
@@ -189,30 +222,36 @@ format_args(Args, FuncAnno) ->
format_arg(#b_var{}=Arg, FuncAnno) ->
format_var(Arg, FuncAnno);
format_arg(#b_literal{val=Val}, _FuncAnno) ->
- io_lib:format("literal ~p", [Val]);
+ io_lib:format("`~p`", [Val]);
format_arg(#b_remote{mod=Mod,name=Name,arity=Arity}, FuncAnno) ->
- io_lib:format("remote (~ts):(~ts)/~p",
+ io_lib:format("(~ts:~ts/~p)",
[format_arg(Mod, FuncAnno),format_arg(Name, FuncAnno),Arity]);
format_arg(#b_local{name=Name,arity=Arity}, FuncAnno) ->
- io_lib:format("local ~ts/~p", [format_arg(Name, FuncAnno),Arity]);
+ io_lib:format("(~ts/~p)", [format_arg(Name, FuncAnno),Arity]);
format_arg({Value,Label}, FuncAnno) when is_integer(Label) ->
- io_lib:format("{ ~ts, ~p }", [format_arg(Value, FuncAnno),Label]);
+ io_lib:format("{ ~ts, ~ts }", [format_arg(Value, FuncAnno),
+ format_label(Label)]);
format_arg(Other, _) ->
io_lib:format("*** ~p ***", [Other]).
-format_list(List, FuncAnno) ->
- Ss = [io_lib:format("{ ~ts, ~ts }", [format_arg(Val, FuncAnno),format_label(L)]) ||
- {Val,L} <- List],
- io_lib:format("[ ~ts ]", [lists:join(", ", Ss)]).
+format_switch_list(List, FuncAnno) ->
+ Ss = [io_lib:format("{ ~ts, ~ts }", [format_arg(Val, FuncAnno),
+ format_label(L)]) || {Val,L} <- List],
+ io_lib:format("[\n ~ts\n ]", [lists:join(",\n ", Ss)]).
format_label(L) ->
- ["label ",integer_to_list(L)].
+ io_lib:format("^~w", [L]).
format_anno(#{n:=_}=Anno) ->
format_anno(maps:remove(n, Anno));
format_anno(#{location:={File,Line}}=Anno0) ->
Anno = maps:remove(location, Anno0),
- [io_lib:format(" %% ~ts:~p\n", [File,Line])|format_anno_1(Anno)];
+ [io_lib:format(" %% ~ts:~p\n", [File,Line])|format_anno(Anno)];
+format_anno(#{result_type:=T}=Anno0) ->
+ Anno = maps:remove(result_type, Anno0),
+ Break = "\n %% ",
+ [io_lib:format(" %% Result type:~s~s\n",
+ [Break, format_type(T, Break)]) | format_anno(Anno)];
format_anno(Anno) ->
format_anno_1(Anno).
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index 6b4b4890a1..b94223c3cf 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -108,7 +108,8 @@ functions([], _Ps, _UseBSM3) -> [].
intervals=[] :: [{b_var(),[range()]}],
res=[] :: [{b_var(),reservation()}] | #{b_var():=reservation()},
regs=#{} :: #{b_var():=ssa_register()},
- extra_annos=[] :: [{atom(),term()}]
+ extra_annos=[] :: [{atom(),term()}],
+ location :: term()
}).
-define(PASS(N), {N,fun N/1}).
@@ -118,8 +119,10 @@ passes(Opts) ->
Ps = [?PASS(assert_no_critical_edges),
%% Preliminaries.
+ ?PASS(exception_trampolines),
?PASS(fix_bs),
?PASS(sanitize),
+ ?PASS(match_fail_instructions),
case FixTuples of
false -> ignore;
true -> ?PASS(fix_tuples)
@@ -164,7 +167,9 @@ passes(Opts) ->
function(#b_function{anno=Anno,args=Args,bs=Blocks0,cnt=Count0}=F0,
Ps, UseBSM3) ->
try
- St0 = #st{ssa=Blocks0,args=Args,use_bsm3=UseBSM3,cnt=Count0},
+ Location = maps:get(location, Anno, none),
+ St0 = #st{ssa=Blocks0,args=Args,use_bsm3=UseBSM3,
+ cnt=Count0,location=Location},
St = compile:run_sub_passes(Ps, St0),
#st{ssa=Blocks,cnt=Count,regs=Regs,extra_annos=ExtraAnnos} = St,
F1 = add_extra_annos(F0, ExtraAnnos),
@@ -253,25 +258,26 @@ bs_pos_bsm3(Linear0, CtxChain, Count0) ->
S = sofs:to_external(S1),
{SavePoints,Count1} = make_bs_pos_dict(S, Count0, []),
- {Gets,Count2} = make_bs_setpos_map(Rs, SavePoints, Count1, []),
- {Sets,Count} = make_bs_getpos_map(maps:to_list(Rs0), SavePoints, Count2, []),
+
+ {Gets,Count2} = make_bs_getpos_map(Rs, SavePoints, Count1, []),
+ {Sets,Count} = make_bs_setpos_map(maps:to_list(Rs0), SavePoints, Count2, []),
%% Now insert all saves and restores.
- {bs_insert_bsm3(Linear0, Gets, Sets, SavePoints),Count}.
+ {bs_insert_bsm3(Linear0, Gets, Sets), Count}.
-make_bs_setpos_map([{Ctx,Save}=Ps|T], SavePoints, Count, Acc) ->
+make_bs_getpos_map([{Ctx,Save}=Ps|T], SavePoints, Count, Acc) ->
SavePoint = get_savepoint(Ps, SavePoints),
I = #b_set{op=bs_get_position,dst=SavePoint,args=[Ctx]},
- make_bs_setpos_map(T, SavePoints, Count+1, [{Save,I}|Acc]);
-make_bs_setpos_map([], _, Count, Acc) ->
+ make_bs_getpos_map(T, SavePoints, Count+1, [{Save,I}|Acc]);
+make_bs_getpos_map([], _, Count, Acc) ->
{maps:from_list(Acc),Count}.
-make_bs_getpos_map([{Bef,{Ctx,_}=Ps}|T], SavePoints, Count, Acc) ->
+make_bs_setpos_map([{Bef,{Ctx,_}=Ps}|T], SavePoints, Count, Acc) ->
Ignored = #b_var{name={'@ssa_ignored',Count}},
Args = [Ctx, get_savepoint(Ps, SavePoints)],
I = #b_set{op=bs_set_position,dst=Ignored,args=Args},
- make_bs_getpos_map(T, SavePoints, Count+1, [{Bef,I}|Acc]);
-make_bs_getpos_map([], _, Count, Acc) ->
+ make_bs_setpos_map(T, SavePoints, Count+1, [{Bef,I}|Acc]);
+make_bs_setpos_map([], _, Count, Acc) ->
{maps:from_list(Acc),Count}.
get_savepoint({_,_}=Ps, SavePoints) ->
@@ -394,30 +400,37 @@ join_positions_1(MapPos0, MapPos1) ->
%%
bs_restores_is([#b_set{op=bs_start_match,dst=Start}|Is],
- CtxChain, SPos0, FPos, Rs) ->
- %% We only allow one match per block.
- SPos0 = FPos, %Assertion.
+ CtxChain, SPos0, _FPos, Rs) ->
+ %% Match instructions leave the position unchanged on failure, so
+ %% FPos must be the SPos we entered the *instruction* with, and not the
+ %% *block*.
+ %%
+ %% This is important when we have multiple matches in a single block where
+ %% all but the last are guaranteed to succeed; the upcoming fail block must
+ %% restore to the position of the next-to-last match, not the position we
+ %% entered the current block with.
+ FPos = SPos0,
SPos = SPos0#{Start=>Start},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=bs_match,dst=NewPos,args=Args}=I|Is],
- CtxChain, SPos0, FPos0, Rs0) ->
- SPos0 = FPos0, %Assertion.
+ CtxChain, SPos0, _FPos, Rs0) ->
Start = bs_subst_ctx(NewPos, CtxChain),
[_,FromPos|_] = Args,
case SPos0 of
#{Start:=FromPos} ->
%% Same position, no restore needed.
SPos = case bs_match_type(I) of
- plain ->
- %% Update position to new position.
- SPos0#{Start:=NewPos};
- _ ->
- %% Position will not change (test_unit
- %% instruction or no instruction at
- %% all).
- SPos0#{Start:=FromPos}
- end,
- bs_restores_is(Is, CtxChain, SPos, FPos0, Rs0);
+ plain ->
+ %% Update position to new position.
+ SPos0#{Start:=NewPos};
+ _ ->
+ %% Position will not change (test_unit
+ %% instruction or no instruction at
+ %% all).
+ SPos0
+ end,
+ FPos = SPos0,
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs0);
#{Start:=_} ->
%% Different positions, might need a restore instruction.
case bs_match_type(I) of
@@ -425,50 +438,71 @@ bs_restores_is([#b_set{op=bs_match,dst=NewPos,args=Args}=I|Is],
%% This is a tail test that will be optimized away.
%% There's no need to do a restore, and all
%% positions are unchanged.
- bs_restores_is(Is, CtxChain, SPos0, FPos0, Rs0);
+ FPos = SPos0,
+ bs_restores_is(Is, CtxChain, SPos0, FPos, Rs0);
test_unit ->
%% This match instruction will be replaced by
%% a test_unit instruction. We will need a
%% restore. The new position will be the position
%% restored to (NOT NewPos).
SPos = SPos0#{Start:=FromPos},
- FPos = FPos0#{Start:=FromPos},
+ FPos = SPos,
Rs = Rs0#{NewPos=>{Start,FromPos}},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
plain ->
%% Match or skip. Position will be changed.
SPos = SPos0#{Start:=NewPos},
- FPos = FPos0#{Start:=FromPos},
+ FPos = SPos0#{Start:=FromPos},
Rs = Rs0#{NewPos=>{Start,FromPos}},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs)
end
end;
bs_restores_is([#b_set{op=bs_extract,args=[FromPos|_]}|Is],
- CtxChain, SPos, FPos, Rs) ->
+ CtxChain, SPos, _FPos, Rs) ->
Start = bs_subst_ctx(FromPos, CtxChain),
+
#{Start:=FromPos} = SPos, %Assertion.
- #{Start:=FromPos} = FPos, %Assertion.
+ FPos = SPos,
+
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=call,dst=Dst,args=Args}|Is],
- CtxChain, SPos0, FPos0, Rs0) ->
- {Rs, SPos1, FPos1} = bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0),
- {SPos, FPos} = bs_invalidate_pos(Args, SPos1, FPos1, CtxChain),
+ CtxChain, SPos0, _FPos, Rs0) ->
+ {SPos1, Rs} = bs_restore_args(Args, SPos0, CtxChain, Dst, Rs0),
+
+ SPos = bs_invalidate_pos(Args, SPos1, CtxChain),
+ FPos = SPos,
+
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
-bs_restores_is([#b_set{op=landingpad}|Is], CtxChain, SPos0, FPos0, Rs) ->
+bs_restores_is([#b_set{op=landingpad}|Is], CtxChain, SPos0, _FPos, Rs) ->
%% We can land here from any point, so all positions are invalid.
Invalidate = fun(_Start,_Pos) -> unknown end,
+
SPos = maps:map(Invalidate, SPos0),
- FPos = maps:map(Invalidate, FPos0),
+ FPos = SPos,
+
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=Op,dst=Dst,args=Args}|Is],
- CtxChain, SPos0, FPos0, Rs0)
+ CtxChain, SPos0, _FPos, Rs0)
when Op =:= bs_test_tail;
Op =:= bs_get_tail ->
- {Rs, SPos, FPos} = bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0),
+ {SPos, Rs} = bs_restore_args(Args, SPos0, CtxChain, Dst, Rs0),
+ FPos = SPos,
+
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
-bs_restores_is([_|Is], CtxChain, SPos, FPos, Rs) ->
+bs_restores_is([#b_set{op=succeeded,args=[Arg]}], CtxChain, SPos, FPos0, Rs) ->
+ %% If we're branching on a match operation, the positions will be different
+ %% depending on whether it succeeds.
+ Ctx = bs_subst_ctx(Arg, CtxChain),
+ FPos = case SPos of
+ #{ Ctx := _ } -> FPos0;
+ #{} -> SPos
+ end,
+ {SPos, FPos, Rs};
+bs_restores_is([_ | Is], CtxChain, SPos, _FPos, Rs) ->
+ FPos = SPos,
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
-bs_restores_is([], _CtxChain, SPos, FPos, Rs) ->
+bs_restores_is([], _CtxChain, SPos, _FPos, Rs) ->
+ FPos = SPos,
{SPos, FPos, Rs}.
bs_match_type(#b_set{args=[#b_literal{val=skip},_Ctx,
@@ -483,54 +517,52 @@ bs_match_type(_) ->
%% Call instructions leave the match position in an undefined state,
%% requiring us to invalidate each affected argument.
-bs_invalidate_pos([#b_var{}=Arg|Args], SPos0, FPos0, CtxChain) ->
+bs_invalidate_pos([#b_var{}=Arg|Args], Pos0, CtxChain) ->
Start = bs_subst_ctx(Arg, CtxChain),
- case SPos0 of
+ case Pos0 of
#{Start:=_} ->
- SPos = SPos0#{Start:=unknown},
- FPos = FPos0#{Start:=unknown},
- bs_invalidate_pos(Args, SPos, FPos, CtxChain);
+ Pos = Pos0#{Start:=unknown},
+ bs_invalidate_pos(Args, Pos, CtxChain);
#{} ->
%% Not a match context.
- bs_invalidate_pos(Args, SPos0, FPos0, CtxChain)
+ bs_invalidate_pos(Args, Pos0, CtxChain)
end;
-bs_invalidate_pos([_|Args], SPos, FPos, CtxChain) ->
- bs_invalidate_pos(Args, SPos, FPos, CtxChain);
-bs_invalidate_pos([], SPos, FPos, _CtxChain) ->
- {SPos, FPos}.
+bs_invalidate_pos([_|Args], Pos, CtxChain) ->
+ bs_invalidate_pos(Args, Pos, CtxChain);
+bs_invalidate_pos([], Pos, _CtxChain) ->
+ Pos.
-bs_restore_args([#b_var{}=Arg|Args], SPos0, FPos0, CtxChain, Dst, Rs0) ->
+bs_restore_args([#b_var{}=Arg|Args], Pos0, CtxChain, Dst, Rs0) ->
Start = bs_subst_ctx(Arg, CtxChain),
- case SPos0 of
+ case Pos0 of
#{Start:=Arg} ->
%% Same position, no restore needed.
- bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0);
+ bs_restore_args(Args, Pos0, CtxChain, Dst, Rs0);
#{Start:=_} ->
%% Different positions, need a restore instruction.
- SPos = SPos0#{Start:=Arg},
- FPos = FPos0#{Start:=Arg},
+ Pos = Pos0#{Start:=Arg},
Rs = Rs0#{Dst=>{Start,Arg}},
- bs_restore_args(Args, SPos, FPos, CtxChain, Dst, Rs);
+ bs_restore_args(Args, Pos, CtxChain, Dst, Rs);
#{} ->
%% Not a match context.
- bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0)
+ bs_restore_args(Args, Pos0, CtxChain, Dst, Rs0)
end;
-bs_restore_args([_|Args], SPos, FPos, CtxChain, Dst, Rs) ->
- bs_restore_args(Args, SPos, FPos, CtxChain, Dst, Rs);
-bs_restore_args([], SPos, FPos, _CtxChain, _Dst, Rs) ->
- {Rs,SPos,FPos}.
+bs_restore_args([_|Args], Pos, CtxChain, Dst, Rs) ->
+ bs_restore_args(Args, Pos, CtxChain, Dst, Rs);
+bs_restore_args([], Pos, _CtxChain, _Dst, Rs) ->
+ {Pos, Rs}.
%% Insert all bs_save and bs_restore instructions.
-bs_insert_bsm3(Blocks, Saves, Restores, SavePoints) ->
- bs_insert_1(Blocks, Saves, Restores, SavePoints, fun(I) -> I end).
+bs_insert_bsm3(Blocks, Saves, Restores) ->
+ bs_insert_1(Blocks, [], Saves, Restores, fun(I) -> I end).
-bs_insert_bsm2(Blocks, Saves, Restores, SavePoints) ->
+bs_insert_bsm2(Blocks, Saves, Restores, Slots) ->
%% The old instructions require bs_start_match to be annotated with the
%% number of position slots it needs.
- bs_insert_1(Blocks, Saves, Restores, SavePoints,
+ bs_insert_1(Blocks, [], Saves, Restores,
fun(#b_set{op=bs_start_match,dst=Dst}=I0) ->
- NumSlots = case SavePoints of
+ NumSlots = case Slots of
#{Dst:=NumSlots0} -> NumSlots0;
#{} -> 0
end,
@@ -539,46 +571,38 @@ bs_insert_bsm2(Blocks, Saves, Restores, SavePoints) ->
I
end).
-bs_insert_1([{L,#b_blk{is=Is0}=Blk}|Bs0], Saves, Restores, Slots, XFrm) ->
- Is = bs_insert_is_1(Is0, Restores, Slots, XFrm),
- Bs = bs_insert_saves(Is, Bs0, Saves),
- [{L,Blk#b_blk{is=Is}}|bs_insert_1(Bs, Saves, Restores, Slots, XFrm)];
-bs_insert_1([], _, _, _, _) -> [].
+bs_insert_1([{L,#b_blk{is=Is0}=Blk} | Bs], Deferred0, Saves, Restores, XFrm) ->
+ Is1 = bs_insert_deferred(Is0, Deferred0),
+ {Is, Deferred} = bs_insert_is(Is1, Saves, Restores, XFrm, []),
+ [{L,Blk#b_blk{is=Is}} | bs_insert_1(Bs, Deferred, Saves, Restores, XFrm)];
+bs_insert_1([], [], _, _, _) ->
+ [].
-bs_insert_is_1([#b_set{op=Op,dst=Dst}=I0|Is], Restores, SavePoints, XFrm) ->
- I = XFrm(I0),
- if
- Op =:= bs_test_tail;
- Op =:= bs_get_tail;
- Op =:= bs_match;
- Op =:= call ->
- Rs = case Restores of
- #{Dst:=R} -> [R];
- #{} -> []
- end,
- Rs ++ [I|bs_insert_is_1(Is, Restores, SavePoints, XFrm)];
- true ->
- [I|bs_insert_is_1(Is, Restores, SavePoints, XFrm)]
- end;
-bs_insert_is_1([], _, _, _) -> [].
+bs_insert_deferred([#b_set{op=bs_extract}=I | Is], Deferred) ->
+ [I | bs_insert_deferred(Is, Deferred)];
+bs_insert_deferred(Is, Deferred) ->
+ Deferred ++ Is.
-bs_insert_saves([#b_set{dst=Dst}|Is], Bs, Saves) ->
- case Saves of
- #{Dst:=S} ->
- bs_insert_save(S, Bs);
- #{} ->
- bs_insert_saves(Is, Bs, Saves)
+bs_insert_is([#b_set{dst=Dst}=I0|Is], Saves, Restores, XFrm, Acc0) ->
+ I = XFrm(I0),
+ Pre = case Restores of
+ #{Dst:=R} -> [R];
+ #{} -> []
+ end,
+ Post = case Saves of
+ #{Dst:=S} -> [S];
+ #{} -> []
+ end,
+ Acc = [I | Pre] ++ Acc0,
+ case Is of
+ [#b_set{op=succeeded,args=[Dst]}] ->
+ %% Defer the save sequence to the success block.
+ {reverse(Acc, Is), Post};
+ _ ->
+ bs_insert_is(Is, Saves, Restores, XFrm, Post ++ Acc)
end;
-bs_insert_saves([], Bs, _) -> Bs.
-
-bs_insert_save(Save, [{L,#b_blk{is=Is0}=Blk}|Bs]) ->
- Is = case Is0 of
- [#b_set{op=bs_extract}=Ex|Is1] ->
- [Ex,Save|Is1];
- _ ->
- [Save|Is0]
- end,
- [{L,Blk#b_blk{is=Is}}|Bs].
+bs_insert_is([], _, _, _, Acc) ->
+ {reverse(Acc), []}.
%% Translate bs_match instructions to bs_get, bs_match_string,
%% or bs_skip. Also rename match context variables to use the
@@ -598,6 +622,10 @@ bs_instrs([{L,#b_blk{is=Is0}=Blk}|Bs], CtxChain, Acc0) ->
bs_instrs([], _, Acc) ->
reverse(Acc).
+bs_instrs_is([#b_set{op=succeeded}=I|Is], CtxChain, Acc) ->
+ %% This instruction refers to a specific operation, so we must not
+ %% substitute the context argument.
+ bs_instrs_is(Is, CtxChain, [I | Acc]);
bs_instrs_is([#b_set{op=Op,args=Args0}=I0|Is], CtxChain, Acc) ->
Args = [bs_subst_ctx(A, CtxChain) || A <- Args0],
I1 = I0#b_set{args=Args},
@@ -606,8 +634,6 @@ bs_instrs_is([#b_set{op=Op,args=Args0}=I0|Is], CtxChain, Acc) ->
I1#b_set{op=bs_skip,args=[Type,Ctx|As]};
{bs_match,[#b_literal{val=string},Ctx|As]} ->
I1#b_set{op=bs_match_string,args=[Ctx|As]};
- {bs_get_tail,[Ctx|As]} ->
- I1#b_set{op=bs_get_tail,args=[Ctx|As]};
{_,_} ->
I1
end,
@@ -691,6 +717,54 @@ legacy_bs_is([I|Is], Last, IsYreg, Count, Copies, Acc) ->
legacy_bs_is([], _Last, _IsYreg, Count, Copies, Acc) ->
{reverse(Acc),Count,Copies}.
+%% exception_trampolines(St0) -> St.
+%%
+%% Removes the "exception trampolines" that were added to prevent exceptions
+%% from being optimized away.
+
+exception_trampolines(#st{ssa=Blocks0}=St) ->
+ RPO = reverse(beam_ssa:rpo(Blocks0)),
+ Blocks = et_1(RPO, #{}, #{}, Blocks0),
+ St#st{ssa=Blocks}.
+
+et_1([L | Ls], Trampolines, Exceptions, Blocks) ->
+ #{ L := #b_blk{is=Is,last=Last0}=Block0 } = Blocks,
+ case {Is, Last0} of
+ {[#b_set{op=exception_trampoline,args=[Arg]}], #b_br{succ=Succ}} ->
+ et_1(Ls,
+ Trampolines#{ L => Succ },
+ Exceptions#{ L => Arg },
+ maps:remove(L, Blocks));
+ {_, #b_br{succ=Same,fail=Same}} when Same =:= ?EXCEPTION_BLOCK ->
+ %% The exception block is just a marker saying that we should raise
+ %% an exception (= {f,0}) instead of jumping to a particular fail
+ %% block. Since it's not a reachable block we can't allow
+ %% unconditional jumps to it except through a trampoline.
+ error({illegal_jump_to_exception_block, L});
+ {_, #b_br{succ=Same,fail=Same}}
+ when map_get(Same, Trampolines) =:= ?EXCEPTION_BLOCK ->
+ %% This block always fails at runtime (and we are not in a
+ %% try/catch); rewrite the terminator to a return.
+ Last = #b_ret{arg=map_get(Same, Exceptions)},
+ Block = Block0#b_blk{last=Last},
+ et_1(Ls, Trampolines, Exceptions, Blocks#{ L := Block });
+ {_, #b_br{succ=Succ0,fail=Fail0}} ->
+ Succ = maps:get(Succ0, Trampolines, Succ0),
+ Fail = maps:get(Fail0, Trampolines, Fail0),
+ if
+ Succ =/= Succ0; Fail =/= Fail0 ->
+ Last = Last0#b_br{succ=Succ,fail=Fail},
+ Block = Block0#b_blk{last=Last},
+ et_1(Ls, Trampolines, Exceptions, Blocks#{ L := Block });
+ Succ =:= Succ0, Fail =:= Fail0 ->
+ et_1(Ls, Trampolines, Exceptions, Blocks)
+ end;
+ {_, _} ->
+ et_1(Ls, Trampolines, Exceptions, Blocks)
+ end;
+et_1([], _Trampolines, _Exceptions, Blocks) ->
+ Blocks.
+
%% sanitize(St0) -> St.
%% Remove constructs that can cause problems later:
%%
@@ -819,12 +893,17 @@ sanitize_instr(is_tagged_tuple, [#b_literal{val=Tuple},
true ->
{value,false}
end;
+sanitize_instr(bs_add, [_,#b_literal{val=Sz},_|_], I0) ->
+ if
+ is_integer(Sz), Sz >= 0 -> ok;
+ true -> {ok,sanitize_badarg(I0)}
+ end;
sanitize_instr(bs_init, [#b_literal{val=new},#b_literal{val=Sz}|_], I0) ->
if
is_integer(Sz), Sz >= 0 -> ok;
true -> {ok,sanitize_badarg(I0)}
end;
-sanitize_instr(bs_init, [#b_literal{val=append},_,#b_literal{val=Sz}|_], I0) ->
+sanitize_instr(bs_init, [#b_literal{},_,#b_literal{val=Sz}|_], I0) ->
if
is_integer(Sz), Sz >= 0 -> ok;
true -> {ok,sanitize_badarg(I0)}
@@ -856,6 +935,114 @@ prune_phi(#b_set{args=Args0}=Phi, Reachable) ->
gb_sets:is_element(Pred, Reachable)],
Phi#b_set{args=Args}.
+%%% Rewrite certain calls to erlang:error/{1,2} to specialized
+%%% instructions:
+%%%
+%%% erlang:error({badmatch,Value}) => badmatch Value
+%%% erlang:error({case_clause,Value}) => case_end Value
+%%% erlang:error({try_clause,Value}) => try_case_end Value
+%%% erlang:error(if_clause) => if_end
+%%% erlang:error(function_clause, Args) => jump FuncInfoLabel
+%%%
+%%% In SSA code, we represent those instructions as a 'match_fail'
+%%% instruction with the name of the BEAM instruction as the first
+%%% argument.
+
+match_fail_instructions(#st{ssa=Blocks0,args=Args,location=Location}=St) ->
+ Ls = maps:to_list(Blocks0),
+ Info = {length(Args),Location},
+ Blocks = match_fail_instrs_1(Ls, Info, Blocks0),
+ St#st{ssa=Blocks}.
+
+match_fail_instrs_1([{L,#b_blk{is=Is0}=Blk}|Bs], Arity, Blocks0) ->
+ case match_fail_instrs_blk(Is0, Arity, []) of
+ none ->
+ match_fail_instrs_1(Bs, Arity, Blocks0);
+ Is ->
+ Blocks = Blocks0#{L:=Blk#b_blk{is=Is}},
+ match_fail_instrs_1(Bs, Arity, Blocks)
+ end;
+match_fail_instrs_1([], _Arity, Blocks) -> Blocks.
+
+match_fail_instrs_blk([#b_set{op=put_tuple,dst=Dst,
+ args=[#b_literal{val=Tag},Val]},
+ #b_set{op=call,
+ args=[#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error}},
+ Dst]}=Call|Is],
+ _Arity, Acc) ->
+ match_fail_instr(Call, Tag, Val, Is, Acc);
+match_fail_instrs_blk([#b_set{op=call,
+ args=[#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error}},
+ #b_literal{val={Tag,Val}}]}=Call|Is],
+ _Arity, Acc) ->
+ match_fail_instr(Call, Tag, #b_literal{val=Val}, Is, Acc);
+match_fail_instrs_blk([#b_set{op=call,
+ args=[#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error}},
+ #b_literal{val=if_clause}]}=Call|Is],
+ _Arity, Acc) ->
+ I = Call#b_set{op=match_fail,args=[#b_literal{val=if_end}]},
+ reverse(Acc, [I|Is]);
+match_fail_instrs_blk([#b_set{op=call,anno=Anno,
+ args=[#b_remote{mod=#b_literal{val=erlang},
+ name=#b_literal{val=error}},
+ #b_literal{val=function_clause},
+ Stk]}=Call],
+ {Arity,Location}, Acc) ->
+ case match_fail_stk(Stk, Acc, [], []) of
+ {[_|_]=Vars,Is} when length(Vars) =:= Arity ->
+ case maps:get(location, Anno, none) of
+ Location ->
+ I = Call#b_set{op=match_fail,
+ args=[#b_literal{val=function_clause}|Vars]},
+ Is ++ [I];
+ _ ->
+ %% erlang:error/2 has a different location than the
+ %% func_info instruction at the beginning of the function
+ %% (probably because of inlining). Keep the original call.
+ reverse(Acc, [Call])
+ end;
+ _ ->
+ %% Either the stacktrace could not be picked apart (for example,
+ %% if the call to erlang:error/2 was handwritten) or the number
+ %% of arguments in the stacktrace was different from the arity
+ %% of the host function (because it is the implementation of a
+ %% fun). Keep the original call.
+ reverse(Acc, [Call])
+ end;
+match_fail_instrs_blk([I|Is], Arity, Acc) ->
+ match_fail_instrs_blk(Is, Arity, [I|Acc]);
+match_fail_instrs_blk(_, _, _) ->
+ none.
+
+match_fail_instr(Call, Tag, Val, Is, Acc) ->
+ Op = case Tag of
+ badmatch -> Tag;
+ case_clause -> case_end;
+ try_clause -> try_case_end;
+ _ -> none
+ end,
+ case Op of
+ none ->
+ none;
+ _ ->
+ I = Call#b_set{op=match_fail,args=[#b_literal{val=Op},Val]},
+ reverse(Acc, [I|Is])
+ end.
+
+match_fail_stk(#b_var{}=V, [#b_set{op=put_list,dst=V,args=[H,T]}|Is], IAcc, VAcc) ->
+ match_fail_stk(T, Is, IAcc, [H|VAcc]);
+match_fail_stk(#b_literal{val=[H|T]}, Is, IAcc, VAcc) ->
+ match_fail_stk(#b_literal{val=T}, Is, IAcc, [#b_literal{val=H}|VAcc]);
+match_fail_stk(#b_literal{val=[]}, [], IAcc, VAcc) ->
+ {reverse(VAcc),IAcc};
+match_fail_stk(T, [#b_set{op=Op}=I|Is], IAcc, VAcc)
+ when Op =:= bs_get_tail; Op =:= bs_set_position ->
+ match_fail_stk(T, Is, [I|IAcc], VAcc);
+match_fail_stk(_, _, _, _) -> none.
+
%%%
%%% Fix tuples.
%%%
@@ -911,9 +1098,8 @@ use_set_tuple_element(#st{ssa=Blocks0}=St) ->
Blocks = use_ste_1(RPO, Uses, Blocks0),
St#st{ssa=Blocks}.
-use_ste_1([L|Ls], Uses, Blocks0) ->
- {Blk0,Blocks} = use_ste_across(L, Uses, Blocks0),
- #b_blk{is=Is0} = Blk0,
+use_ste_1([L|Ls], Uses, Blocks) ->
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks),
case use_ste_is(Is0, Uses) of
Is0 ->
use_ste_1(Ls, Uses, Blocks);
@@ -976,69 +1162,6 @@ extract_ste(#b_set{op=call,dst=Dst,
end;
extract_ste(#b_set{}) -> none.
-%%% Optimize accross blocks within a try/catch block.
-
-use_ste_across(L, Uses, Blocks) ->
- case map_get(L, Blocks) of
- #b_blk{last=#b_br{bool=#b_var{}}}=Blk ->
- try
- use_ste_across_1(L, Blk, Uses, Blocks)
- catch
- throw:not_possible ->
- {Blk,Blocks}
- end;
- #b_blk{}=Blk ->
- {Blk,Blocks}
- end.
-
-use_ste_across_1(L, Blk0, Uses, Blocks0) ->
- #b_blk{is=IsThis,last=#b_br{bool=Bool,succ=Next}} = Blk0,
- case reverse(IsThis) of
- [#b_set{op=succeeded,dst=Bool,args=[Result]}=Succ0,
- #b_set{op=call,args=[#b_remote{}|_],dst=Result}=Call1|Prefix] ->
- case is_single_use(Bool, Uses) andalso
- is_n_uses(2, Result, Uses) of
- true -> ok;
- false -> throw(not_possible)
- end,
- Call2 = use_ste_across_next(Next, Uses, Blocks0),
- Is = [Call1,Call2],
- case use_ste_is(Is, decrement_uses(Result, Uses)) of
- [#b_set{}=Call,#b_set{op=set_tuple_element}=Ste] ->
- Blocks1 = use_ste_fix_next(Ste, Next, Blocks0),
- Succ = Succ0#b_set{args=[Call#b_set.dst]},
- Blk = Blk0#b_blk{is=reverse(Prefix, [Call,Succ])},
- Blocks = Blocks1#{L:=Blk},
- {Blk,Blocks};
- _ ->
- throw(not_possible)
- end;
- _ ->
- throw(not_possible)
- end.
-
-use_ste_across_next(Next, Uses, Blocks) ->
- case map_get(Next, Blocks) of
- #b_blk{is=[#b_set{op=call,dst=Result,args=[#b_remote{}|_]}=Call,
- #b_set{op=succeeded,dst=Bool,args=[Result]}],
- last=#b_br{bool=Bool}} ->
- case is_single_use(Bool, Uses) andalso
- is_n_uses(2, Result, Uses) of
- true -> ok;
- false -> throw(not_possible)
- end,
- Call;
- #b_blk{} ->
- throw(not_possible)
- end.
-
-use_ste_fix_next(Ste, Next, Blocks) ->
- Blk0 = map_get(Next, Blocks),
- #b_blk{is=[#b_set{op=call},#b_set{op=succeeded}],last=Br0} = Blk0,
- Br = beam_ssa:normalize(Br0#b_br{bool=#b_literal{val=true}}),
- Blk = Blk0#b_blk{is=[Ste],last=Br},
- Blocks#{Next:=Blk}.
-
%% Count how many times each variable is used.
count_uses(Blocks) ->
@@ -1048,7 +1171,7 @@ count_uses_blk([#b_blk{is=Is,last=Last}|Bs], CountMap0) ->
F = fun(I, CountMap) ->
foldl(fun(Var, Acc) ->
case Acc of
- #{Var:=3} -> Acc;
+ #{Var:=2} -> Acc;
#{Var:=C} -> Acc#{Var:=C+1};
#{} -> Acc#{Var=>1}
end
@@ -1058,16 +1181,6 @@ count_uses_blk([#b_blk{is=Is,last=Last}|Bs], CountMap0) ->
count_uses_blk(Bs, CountMap);
count_uses_blk([], CountMap) -> CountMap.
-decrement_uses(V, Uses) ->
- #{V:=C} = Uses,
- Uses#{V:=C-1}.
-
-is_n_uses(N, V, Uses) ->
- case Uses of
- #{V:=N} -> true;
- #{} -> false
- end.
-
is_single_use(V, Uses) ->
case Uses of
#{V:=1} -> true;
@@ -1189,10 +1302,10 @@ place_frame_here(L, Blocks, Doms, Frames) ->
Descendants = beam_ssa:rpo([L], Blocks),
PhiPredecessors = phi_predecessors(L, Blocks),
MustDominate = ordsets:from_list(PhiPredecessors ++ Descendants),
- Dominates = all(fun(?BADARG_BLOCK) ->
+ Dominates = all(fun(?EXCEPTION_BLOCK) ->
%% This block defines no variables and calls
%% erlang:error(badarg). It does not matter
- %% whether L dominates ?BADARG_BLOCK or not;
+ %% whether L dominates ?EXCEPTION_BLOCK or not;
%% it is still safe to put the frame in L.
true;
(Bl) ->
@@ -1250,14 +1363,9 @@ need_frame_1([#b_set{op=call,args=[Func|_]}|Is], Context) ->
#b_remote{mod=#b_literal{val=Mod},
name=#b_literal{val=Name},
arity=Arity} when is_atom(Mod), is_atom(Name) ->
- case erl_bifs:is_exit_bif(Mod, Name, Arity) of
- true ->
- false;
- false ->
- Context =:= body orelse
- Is =/= [] orelse
- is_trap_bif(Mod, Name, Arity)
- end;
+ Context =:= body orelse
+ Is =/= [] orelse
+ is_trap_bif(Mod, Name, Arity);
#b_remote{} ->
%% This is an apply(), which always needs a frame.
true;
@@ -1340,9 +1448,9 @@ recv_common(_Defs, none, _Blocks) ->
%% in the tail position of a function.
[];
recv_common(Defs, Exit, Blocks) ->
- {ExitDefs,ExitUsed} = beam_ssa:def_used([Exit], Blocks),
+ {ExitDefs,ExitUnused} = beam_ssa:def_unused([Exit], Defs, Blocks),
Def = ordsets:subtract(Defs, ExitDefs),
- ordsets:intersection(Def, ExitUsed).
+ ordsets:subtract(Def, ExitUnused).
%% recv_crit_edges([RemoveMessageLabel], LoopExit,
%% Blocks0, Count0) -> {Blocks,Count}.
@@ -1447,9 +1555,9 @@ exit_predecessors([], _Exit, _Blocks) -> [].
%% later used within a clause of the receive.
fix_receive([L|Ls], Defs, Blocks0, Count0) ->
- {RmDefs,Used0} = beam_ssa:def_used([L], Blocks0),
+ {RmDefs,Unused} = beam_ssa:def_unused([L], Defs, Blocks0),
Def = ordsets:subtract(Defs, RmDefs),
- Used = ordsets:intersection(Def, Used0),
+ Used = ordsets:subtract(Def, Unused),
{NewVars,Count} = new_vars([Base || #b_var{name=Base} <- Used], Count0),
Ren = zip(Used, NewVars),
Blocks1 = beam_ssa:rename_vars(Ren, [L], Blocks0),
@@ -1483,8 +1591,8 @@ find_loop_exit(_, _) ->
%% loop exit block.
none.
-find_loop_exit_1([?BADARG_BLOCK|Ls], RmSet, Dominators, Blocks) ->
- %% ?BADARG_BLOCK is a marker and not an actual block, so it is not
+find_loop_exit_1([?EXCEPTION_BLOCK|Ls], RmSet, Dominators, Blocks) ->
+ %% ?EXCEPTION_BLOCK is a marker and not an actual block, so it is not
%% the block we are looking for.
find_loop_exit_1(Ls, RmSet, Dominators, Blocks);
find_loop_exit_1([L|Ls0], RmSet, Dominators, Blocks) ->
@@ -1767,7 +1875,7 @@ collect_yregs([], Yregs) -> Yregs.
copy_retval_2([L|Ls], Yregs, Copy0, Blocks0, Count0) ->
#b_blk{is=Is0,last=Last} = Blk = map_get(L, Blocks0),
RC = case {Last,Ls} of
- {#b_br{succ=Succ,fail=?BADARG_BLOCK},[Succ|_]} ->
+ {#b_br{succ=Succ,fail=?EXCEPTION_BLOCK},[Succ|_]} ->
true;
{_,_} ->
false
@@ -1984,11 +2092,9 @@ number_is_2([], N, Acc) ->
live_intervals(#st{args=Args,ssa=Blocks}=St) ->
Vars0 = [{V,{0,1}} || #b_var{}=V <- Args],
- F = fun(L, _, A) -> live_interval_blk(L, Blocks, A) end,
- LiveMap0 = #{},
- Acc0 = {[],LiveMap0},
- {Vars,_} = beam_ssa:fold_po(F, Acc0, Blocks),
- Intervals = merge_ranges(rel2fam(Vars0++Vars)),
+ PO = reverse(beam_ssa:rpo(Blocks)),
+ Vars = live_interval_blk(PO, Blocks, Vars0, #{}),
+ Intervals = merge_ranges(rel2fam(Vars)),
St#st{intervals=Intervals}.
merge_ranges([{V,Rs}|T]) ->
@@ -2001,32 +2107,51 @@ merge_ranges_1([R|Rs]) ->
[R|merge_ranges_1(Rs)];
merge_ranges_1([]) -> [].
-live_interval_blk(L, Blocks, {Vars0,LiveMap0}) ->
+live_interval_blk([L|Ls], Blocks, Vars0, LiveMap0) ->
Live0 = [],
- Successors = beam_ssa:successors(L, Blocks),
+ Blk = map_get(L, Blocks),
+ Successors = beam_ssa:successors(Blk),
Live1 = update_successors(Successors, L, Blocks, LiveMap0, Live0),
%% Add ranges for all variables that are live in the successors.
- #b_blk{is=Is,last=Last} = map_get(L, Blocks),
+ #b_blk{is=Is,last=Last} = Blk,
End = beam_ssa:get_anno(n, Last),
- Use = [{V,{use,End+1}} || V <- Live1],
+ EndUse = {use,End+1},
+ Use = [{V,EndUse} || V <- Live1],
%% Determine used and defined variables in this block.
FirstNumber = first_number(Is, Last),
- UseDef0 = live_interval_blk_1([Last|reverse(Is)], FirstNumber, Use),
- UseDef = rel2fam(UseDef0),
+ UseDef0 = live_interval_last(Last, Use),
+ UseDef1 = live_interval_blk_is(Is, FirstNumber, UseDef0),
+ UseDef = rel2fam(UseDef1),
%% Update what is live at the beginning of this block and
%% store it.
- Used = [V || {V,[{use,_}|_]} <- UseDef],
- Live2 = ordsets:union(Live1, Used),
- Killed = [V || {V,[{def,_}|_]} <- UseDef],
- Live = ordsets:subtract(Live2, Killed),
+ Live = [V || {V,[{use,_}|_]} <- UseDef],
LiveMap = LiveMap0#{L=>Live},
%% Construct the ranges for this block.
Vars = make_block_ranges(UseDef, FirstNumber, Vars0),
- {Vars,LiveMap}.
+ live_interval_blk(Ls, Blocks, Vars, LiveMap);
+live_interval_blk([], _Blocks, Vars, _LiveMap) ->
+ Vars.
+
+live_interval_last(I, Acc) ->
+ N = beam_ssa:get_anno(n, I),
+ Used = beam_ssa:used(I),
+ [{V,{use,N}} || V <- Used] ++ Acc.
+
+live_interval_blk_is([#b_set{op=phi,dst=Dst}|Is], FirstNumber, Acc0) ->
+ Acc = [{Dst,{def,FirstNumber}}|Acc0],
+ live_interval_blk_is(Is, FirstNumber, Acc);
+live_interval_blk_is([#b_set{dst=Dst}=I|Is], FirstNumber, Acc0) ->
+ N = beam_ssa:get_anno(n, I),
+ Acc1 = [{Dst,{def,N}}|Acc0],
+ Used = beam_ssa:used(I),
+ Acc = [{V,{use,N}} || V <- Used] ++ Acc1,
+ live_interval_blk_is(Is, FirstNumber, Acc);
+live_interval_blk_is([], _FirstNumber, Acc) ->
+ Acc.
make_block_ranges([{V,[{def,Def}]}|Vs], First, Acc) ->
make_block_ranges(Vs, First, [{V,{Def,Def}}|Acc]);
@@ -2038,30 +2163,6 @@ make_block_ranges([{V,[{use,_}|_]=Uses}|Vs], First, Acc) ->
make_block_ranges(Vs, First, [{V,{First,Last}}|Acc]);
make_block_ranges([], _, Acc) -> Acc.
-live_interval_blk_1([#b_set{op=phi,dst=Dst}|Is], FirstNumber, Acc0) ->
- Acc = [{Dst,{def,FirstNumber}}|Acc0],
- live_interval_blk_1(Is, FirstNumber, Acc);
-live_interval_blk_1([#b_set{op=bs_start_match}=I|Is],
- FirstNumber, Acc0) ->
- N = beam_ssa:get_anno(n, I),
- #b_set{dst=Dst} = I,
- Acc1 = [{Dst,{def,N}}|Acc0],
- Acc = [{V,{use,N}} || V <- beam_ssa:used(I)] ++ Acc1,
- live_interval_blk_1(Is, FirstNumber, Acc);
-live_interval_blk_1([I|Is], FirstNumber, Acc0) ->
- N = beam_ssa:get_anno(n, I),
- Acc1 = case I of
- #b_set{dst=Dst} ->
- [{Dst,{def,N}}|Acc0];
- _ ->
- Acc0
- end,
- Used = beam_ssa:used(I),
- Acc = [{V,{use,N}} || V <- Used] ++ Acc1,
- live_interval_blk_1(Is, FirstNumber, Acc);
-live_interval_blk_1([], _FirstNumber, Acc) ->
- Acc.
-
%% first_number([#b_set{}]) -> InstructionNumber.
%% Return the number for the first instruction for the block.
%% Note that this number is one less than the first
@@ -2116,8 +2217,8 @@ reserve_yregs(#st{frames=Frames}=St0) ->
reserve_yregs_1(L, #st{ssa=Blocks0,cnt=Count0,res=Res0}=St) ->
Blk = map_get(L, Blocks0),
Yregs = beam_ssa:get_anno(yregs, Blk),
- {Def,Used} = beam_ssa:def_used([L], Blocks0),
- UsedYregs = ordsets:intersection(Yregs, Used),
+ {Def,Unused} = beam_ssa:def_unused([L], Yregs, Blocks0),
+ UsedYregs = ordsets:subtract(Yregs, Unused),
DefBefore = ordsets:subtract(UsedYregs, Def),
{BeforeVars,Blocks,Count} = rename_vars(DefBefore, L, Blocks0, Count0),
InsideVars = ordsets:subtract(UsedYregs, DefBefore),
@@ -2307,68 +2408,69 @@ reserve_zregs(Blocks, Intervals, Res) ->
end,
beam_ssa:fold_rpo(F, [0], Res, Blocks).
-reserve_zreg([#b_set{op=Op,dst=Dst}],
- #b_br{bool=Dst}, _ShortLived, A) when Op =:= call;
- Op =:= get_tuple_element ->
- %% If type optimization has determined that the result of these
- %% instructions can be used directly in a branch, we must avoid reserving a
- %% z register or code generation will fail.
- A;
reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst},
- #b_set{op={bif,'=:='},args=[Dst,Val]}], Last, ShortLived, A0) ->
+ #b_set{op={bif,'=:='},args=[Dst,Val],dst=Bool}],
+ Last, ShortLived, A) ->
case {Val,Last} of
- {#b_literal{val=Arity},#b_br{bool=#b_var{}}} when Arity bsr 32 =:= 0 ->
+ {#b_literal{val=Arity},#b_br{bool=Bool}} when Arity bsr 32 =:= 0 ->
%% These two instructions can be combined to a test_arity
%% instruction provided that the arity variable is short-lived.
- reserve_zreg_1(Dst, ShortLived, A0);
+ reserve_test_zreg(Dst, ShortLived, A);
{_,_} ->
%% Either the arity is too big, or the boolean value is not
%% used in a conditional branch.
- A0
+ A
end;
reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst}],
- #b_switch{}, ShortLived, A) ->
- reserve_zreg_1(Dst, ShortLived, A);
-reserve_zreg([#b_set{op={bif,'xor'}}], _Last, _ShortLived, A) ->
- %% There is no short, easy way to rewrite 'xor' to a series of
- %% test instructions.
- A;
-reserve_zreg([#b_set{op={bif,is_record}}], _Last, _ShortLived, A) ->
- %% There is no short, easy way to rewrite is_record/2 to a series of
- %% test instructions.
- A;
-reserve_zreg([#b_set{op=Op,dst=Dst}|Is], Last, ShortLived, A0) ->
- IsZReg = case Op of
- bs_match_string -> true;
- bs_save -> true;
- bs_restore -> true;
- bs_set_position -> true;
- {float,clearerror} -> true;
- kill_try_tag -> true;
- landingpad -> true;
- put_tuple_elements -> true;
- remove_message -> true;
- set_tuple_element -> true;
- succeeded -> true;
- timeout -> true;
- wait_timeout -> true;
- _ -> false
- end,
- A = case IsZReg of
- true -> [{Dst,z}|A0];
- false -> A0
- end,
- reserve_zreg(Is, Last, ShortLived, A);
-reserve_zreg([], #b_br{bool=Bool}, ShortLived, A) ->
- reserve_zreg_1(Bool, ShortLived, A);
+ #b_switch{arg=Dst}, ShortLived, A) ->
+ reserve_test_zreg(Dst, ShortLived, A);
+reserve_zreg([#b_set{op=Op,dst=Dst}], #b_br{bool=Dst}, ShortLived, A) ->
+ case use_zreg(Op) of
+ yes -> [{Dst,z} | A];
+ no -> A;
+ maybe -> reserve_test_zreg(Dst, ShortLived, A)
+ end;
+reserve_zreg([#b_set{op=Op,dst=Dst} | Is], Last, ShortLived, A) ->
+ case use_zreg(Op) of
+ yes -> reserve_zreg(Is, Last, ShortLived, [{Dst,z} | A]);
+ _Other -> reserve_zreg(Is, Last, ShortLived, A)
+ end;
reserve_zreg([], _, _, A) -> A.
-reserve_zreg_1(#b_var{}=V, ShortLived, A) ->
+use_zreg(bs_match_string) -> yes;
+use_zreg(bs_save) -> yes;
+use_zreg(bs_restore) -> yes;
+use_zreg(bs_set_position) -> yes;
+use_zreg({float,clearerror}) -> yes;
+use_zreg(kill_try_tag) -> yes;
+use_zreg(landingpad) -> yes;
+use_zreg(put_tuple_elements) -> yes;
+use_zreg(remove_message) -> yes;
+use_zreg(set_tuple_element) -> yes;
+use_zreg(succeeded) -> yes;
+use_zreg(timeout) -> yes;
+use_zreg(wait_timeout) -> yes;
+%% There's no way we can combine these into a test instruction, so we must
+%% avoid using a z register if their result is used directly in a branch.
+use_zreg(call) -> no;
+use_zreg({bif,is_map_key}) -> no;
+use_zreg({bif,is_record}) -> no;
+use_zreg({bif,map_get}) -> no;
+use_zreg({bif,'xor'}) -> no;
+use_zreg(get_hd) -> no;
+use_zreg(get_tl) -> no;
+use_zreg(get_tuple_element) -> no;
+%% Assume the instruction can use a z register, provided it's the last in its
+%% block and that the result is only used in the terminator.
+use_zreg(_) -> maybe.
+
+%% If V is defined just before a branch, we may be able to combine it into a
+%% test instruction.
+reserve_test_zreg(#b_var{}=V, ShortLived, A) ->
case cerl_sets:is_element(V, ShortLived) of
true -> [{V,z}|A];
false -> A
- end;
-reserve_zreg_1(#b_literal{}, _, A) -> A.
+ end.
reserve_fregs(Blocks, Res) ->
F = fun(_, #b_blk{is=Is}, A) ->
@@ -2506,9 +2608,9 @@ reserve_xregs_is([], Res, Xs, _Used) ->
{Res,Xs}.
%% Pick up register hints from the successors of this blocks.
-reserve_terminator(_L, _Is, #b_br{bool=#b_var{},succ=Succ,fail=?BADARG_BLOCK},
+reserve_terminator(_L, _Is, #b_br{bool=#b_var{},succ=Succ,fail=?EXCEPTION_BLOCK},
_Blocks, XsMap, _Res) ->
- %% We know that no variables are used at ?BADARG_BLOCK, so
+ %% We know that no variables are used at ?EXCEPTION_BLOCK, so
%% any register hints from the success blocks are safe to use.
map_get(Succ, XsMap);
reserve_terminator(L, Is, #b_br{bool=#b_var{},succ=Succ,fail=Fail},
@@ -2915,11 +3017,9 @@ are_overlapping_1({_,_}, []) -> false.
%% Check whether the block is a loop header.
is_loop_header(L, Blocks) ->
- %% We KNOW that a loop header must start with a peek_message
- %% instruction.
case map_get(L, Blocks) of
- #b_blk{is=[#b_set{op=peek_message}|_]} -> true;
- _ -> false
+ #b_blk{is=[I|_]} -> beam_ssa:is_loop_header(I);
+ #b_blk{} -> false
end.
rel2fam(S0) ->
diff --git a/lib/compiler/src/beam_ssa_recv.erl b/lib/compiler/src/beam_ssa_recv.erl
index 767242d1e5..20fcd3e9a6 100644
--- a/lib/compiler/src/beam_ssa_recv.erl
+++ b/lib/compiler/src/beam_ssa_recv.erl
@@ -165,19 +165,21 @@ recv_opt_makes_ref([I|Is], RecvLbl, Blocks, Acc) ->
recv_opt_makes_ref([], _, _, _) -> no.
makes_ref(#b_set{dst=Dst,args=[Func0|_]}, Blocks) ->
- Func = case Func0 of
- #b_remote{mod=#b_literal{val=erlang},
- name=#b_literal{val=Name},arity=A0} ->
- {Name,A0};
+ MFA = case Func0 of
+ #b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Func},arity=A0} ->
+ {Mod,Func,A0};
_ ->
none
end,
- case Func of
- {make_ref,0} ->
+ case MFA of
+ {erlang,make_ref,0} ->
{yes,Dst};
- {monitor,2} ->
+ {erlang,monitor,2} ->
{yes,Dst};
- {spawn_monitor,A} when A =:= 1; A =:= 3 ->
+ {erlang,spawn_request,A} when 1 =< A, A =< 5 ->
+ {yes,Dst};
+ {erlang,spawn_monitor,A} when 1 =< A, A =< 4 ->
ref_in_tuple(Dst, Blocks);
_ ->
no
diff --git a/lib/compiler/src/beam_ssa_share.erl b/lib/compiler/src/beam_ssa_share.erl
index 73983bd34a..98cf0d9247 100644
--- a/lib/compiler/src/beam_ssa_share.erl
+++ b/lib/compiler/src/beam_ssa_share.erl
@@ -117,8 +117,8 @@ share_terminator(_Last, _Blocks) -> none.
%% possible if the blocks are not equivalent, as that is the common
%% case.
-are_equivalent(_Succ, _, ?BADARG_BLOCK, _, _Blocks) ->
- %% ?BADARG_BLOCK is special. Sharing could be incorrect.
+are_equivalent(_Succ, _, ?EXCEPTION_BLOCK, _, _Blocks) ->
+ %% ?EXCEPTION_BLOCK is special. Sharing could be incorrect.
false;
are_equivalent(_Succ, #b_blk{is=Is1,last=#b_ret{arg=RetVal1}=Ret1},
_Fail, #b_blk{is=Is2,last=#b_ret{arg=RetVal2}=Ret2}, _Blocks) ->
@@ -331,11 +331,16 @@ canonical_terminator(_, _, _) -> none.
canonical_terminator_phis([#b_set{op=phi,args=PhiArgs}=Phi|Is], L) ->
{Value,L} = keyfind(L, 2, PhiArgs),
[Phi#b_set{op=copy,args=[Value]}|canonical_terminator_phis(Is, L)];
-canonical_terminator_phis([#b_set{op=peek_message}=I|_], L) ->
- %% We could get stuck into an infinite loop if we allowed the
- %% comparisons to continue into this block. Force an unequal
- %% compare with all other predecessors of this block.
- [I#b_set{op=copy,args=[#b_literal{val=L}]}];
+canonical_terminator_phis([#b_set{}=I|_], L) ->
+ case beam_ssa:is_loop_header(I) of
+ true ->
+ %% We could get stuck into an infinite loop if we allowed the
+ %% comparisons to continue into this loop. Force an unequal
+ %% compare with all other predecessors of this block.
+ [I#b_set{op=copy,args=[#b_literal{val=L}]}];
+ false ->
+ []
+ end;
canonical_terminator_phis(_, _) -> [].
canonical_arg(#b_var{}=Var, VarMap) ->
@@ -368,7 +373,9 @@ shortcut_nonempty_block(L, Blocks) ->
is_forbidden(L, Blocks) ->
case map_get(L, Blocks) of
- #b_blk{is=[#b_set{op=phi}|_]} -> true;
- #b_blk{is=[#b_set{op=peek_message}|_]} -> true;
+ #b_blk{is=[#b_set{op=phi}|_]} ->
+ true;
+ #b_blk{is=[#b_set{}=I|_]} ->
+ beam_ssa:is_loop_header(I);
#b_blk{} -> false
end.
diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl
index 3c06c83e2e..e0baadccb2 100644
--- a/lib/compiler/src/beam_ssa_type.erl
+++ b/lib/compiler/src/beam_ssa_type.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -17,126 +17,553 @@
%%
%% %CopyrightEnd%
%%
+%% This pass infers types from expressions and attempts to simplify or remove
+%% subsequent instructions based on that information.
+%%
+%% This is divided into two subpasses; the first figures out function type
+%% signatures for the whole module without optimizing anything, and the second
+%% optimizes based on that information, further refining the type signatures as
+%% it goes.
+%%
-module(beam_ssa_type).
--export([opt_start/4, opt_continue/4, opt_finish/3]).
+-export([opt_start/2, opt_continue/4, opt_finish/3]).
-include("beam_ssa_opt.hrl").
--import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2,
- keyfind/3,reverse/1,reverse/2,
- sort/1,split/2]).
-
--define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}).
-
--record(d,
- {ds :: #{beam_ssa:b_var():=beam_ssa:b_set()},
- ls :: #{beam_ssa:label():=type_db()},
- once :: cerl_sets:set(beam_ssa:b_var()),
- func_id :: func_id(),
- func_db :: func_info_db(),
- sub = #{} :: #{beam_ssa:b_var():=beam_ssa:value()},
- ret_type = [] :: [type()]}).
-
--define(ATOM_SET_SIZE, 5).
-
-%% Records that represent type information.
--record(t_atom, {elements=any :: 'any' | [atom()]}).
--record(t_integer, {elements=any :: 'any' | {integer(),integer()}}).
--record(t_bs_match, {type :: type()}).
--record(t_tuple, {size=0 :: integer(),
- exact=false :: boolean(),
- %% Known element types (1-based index), unknown elements are
- %% are assumed to be 'any'.
- elements=#{} :: #{ non_neg_integer() => type() }}).
-
--type type() :: 'any' | 'none' |
- #t_atom{} | #t_integer{} | #t_bs_match{} | #t_tuple{} |
- {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' | 'number'.
--type type_db() :: #{beam_ssa:var_name():=type()}.
-
--spec opt_start(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when
- Linear :: [{non_neg_integer(), beam_ssa:b_blk()}],
- Args :: [beam_ssa:b_var()],
- Anno :: beam_ssa:anno(),
- FuncDb :: func_info_db().
-opt_start(Linear, Args, Anno, FuncDb) ->
- %% This is the first run through the module, so our arg_types can be
- %% incomplete as we may not have visited all call sites at least once.
- Ts = maps:from_list([{V,any} || #b_var{}=V <- Args]),
- opt_continue_1(Linear, Args, get_func_id(Anno), Ts, FuncDb).
+-include("beam_types.hrl").
+
+-import(lists, [all/2,any/2,duplicate/2,foldl/3,member/2,
+ keyfind/3,reverse/1,split/2,zip/2]).
+
+%% The maximum number of #b_ret{} terminators a function can have before
+%% collapsing success types into a single entry. Consider the following code:
+%%
+%% f(0) -> 1;
+%% f(...) -> ...;
+%% f(500000) -> 500000.
+%%
+%% Since success types are grouped by return type and each clause returns a
+%% distinct type (singleton #t_integer{}s), we'll add 500000 entries which
+%% makes progress glacial since every call needs to examine them all to
+%% determine the return type.
+%%
+%% The entries are collapsed unconditionally if the number of returns in a
+%% function exceeds this threshold. This is necessary because collapsing as we
+%% go might widen a type; if we're at (?RETURN_LIMIT - 1) entries and suddenly
+%% narrow a type down, it could push us over the edge and collapse all entries,
+%% possibly widening the return type and breaking optimizations that were based
+%% on the earlier (narrower) types.
+-define(RETURN_LIMIT, 100).
+
+%% Constants common to all subpasses.
+-record(metadata,
+ { func_id :: func_id(),
+ limit_return :: boolean(),
+ params :: [beam_ssa:b_var()],
+ used_once :: cerl_sets:set(beam_ssa:b_var()) }).
+
+-type type_db() :: #{ beam_ssa:var_name() := type() }.
+
+%%
+
+-spec opt_start(term(), term()) -> term().
+opt_start(StMap, FuncDb0) when FuncDb0 =/= #{} ->
+ {ArgDb, FuncDb} = signatures(StMap, FuncDb0),
+
+ opt_start_1(maps:keys(StMap), ArgDb, StMap, FuncDb);
+opt_start(StMap, FuncDb) ->
+ %% Module-level analysis is disabled, likely because of a call to
+ %% load_nif/2 or similar. opt_continue/4 will assume that all arguments and
+ %% return types are 'any'.
+ {StMap, FuncDb}.
+
+opt_start_1([Id | Ids], ArgDb, StMap0, FuncDb0) ->
+ case ArgDb of
+ #{ Id := ArgTypes } ->
+ #opt_st{ssa=Linear0,args=Args} = St0 = map_get(Id, StMap0),
+
+ Ts = maps:from_list(zip(Args, ArgTypes)),
+ {Linear, FuncDb} = opt_function(Linear0, Args, Id, Ts, FuncDb0),
+
+ St = St0#opt_st{ssa=Linear},
+ StMap = StMap0#{ Id := St },
+
+ opt_start_1(Ids, ArgDb, StMap, FuncDb);
+ #{} ->
+ %% Unreachable functions must be removed so that opt_continue/4
+ %% won't process them and potentially taint the argument types of
+ %% other functions.
+ StMap = maps:remove(Id, StMap0),
+ FuncDb = maps:remove(Id, FuncDb0),
+
+ opt_start_1(Ids, ArgDb, StMap, FuncDb)
+ end;
+opt_start_1([], _CommittedArgs, StMap, FuncDb) ->
+ {StMap, FuncDb}.
+
+%%
+%% The initial signature analysis is based on the paper "Practical Type
+%% Inference Based on Success Typings" [1] by `Tobias Lindahl` and
+%% `Konstantinos Sagonas`, mainly section 6.1 and onwards.
+%%
+%% The general idea is to start out at the module's entry points and propagate
+%% types to the functions we call. The argument types of all exported functions
+%% start out a 'any', whereas local functions start at 'none'. Every time a
+%% function call widens the argument types, we analyze the callee again and
+%% propagate its return types to the callers, analyzing them again, and
+%% continuing this process until all arguments and return types have been
+%% widened as far as they can be.
+%%
+%% Note that we do not "jump-start the analysis" by first determining success
+%% types as in the paper because we need to know all possible inputs including
+%% those that will not return.
+%%
+%% [1] http://www.it.uu.se/research/group/hipe/papers/succ_types.pdf
+%%
+
+-record(sig_st,
+ { wl = wl_new() :: worklist(),
+ committed = #{} :: #{ func_id() => [type()] },
+ updates = #{} :: #{ func_id() => [type()] }}).
+
+signatures(StMap, FuncDb0) ->
+ State0 = init_sig_st(StMap, FuncDb0),
+ {State, FuncDb} = signatures_1(StMap, FuncDb0, State0),
+ {State#sig_st.committed, FuncDb}.
+
+signatures_1(StMap, FuncDb0, State0) ->
+ case wl_next(State0#sig_st.wl) of
+ {ok, FuncId} ->
+ {State, FuncDb} = sig_function(FuncId, StMap, State0, FuncDb0),
+ signatures_1(StMap, FuncDb, State);
+ empty ->
+ %% No more work to do, assert that we don't have any outstanding
+ %% updates.
+ #sig_st{updates=Same,committed=Same} = State0, %Assertion.
+
+ {State0, FuncDb0}
+ end.
+
+sig_function(Id, StMap, State0, FuncDb0) ->
+ case sig_function_1(Id, StMap, State0, FuncDb0) of
+ {false, false, State, FuncDb} ->
+ %% No added work and the types are identical. Pop ourselves from
+ %% the work list and move on to the next function.
+ Wl = wl_pop(Id, State#sig_st.wl),
+ {State#sig_st{wl=Wl}, FuncDb};
+ {false, true, State, FuncDb} ->
+ %% We've added some work and our return type is unchanged. Keep
+ %% following the work list without popping ourselves; we're very
+ %% likely to need to return here later and can avoid a lot of
+ %% redundant work by keeping our place in line.
+ {State, FuncDb};
+ {true, WlChanged, State, FuncDb} ->
+ %% Our return type has changed so all of our (previously analyzed)
+ %% callers need to be analyzed again.
+ %%
+ %% If our worklist is unchanged we'll pop ourselves since our
+ %% callers will add us back if we need to analyzed again, and
+ %% it's wasteful to stay in the worklist when we don't.
+ Wl0 = case WlChanged of
+ true -> State#sig_st.wl;
+ false -> wl_pop(Id, State#sig_st.wl)
+ end,
+
+ #func_info{in=Cs0} = map_get(Id, FuncDb0),
+ Callers = [C || C <- Cs0, is_map_key(C, State#sig_st.updates)],
+ Wl = wl_defer_list(Callers, Wl0),
+
+ {State#sig_st{wl=Wl}, FuncDb}
+ end.
+
+sig_function_1(Id, StMap, State0, FuncDb) ->
+ #opt_st{ssa=Linear,args=Args} = map_get(Id, StMap),
+
+ {ArgTypes, State1} = sig_commit_args(Id, State0),
+ Ts = maps:from_list(zip(Args, ArgTypes)),
+
+ FakeCall = #b_set{op=call,args=[#b_remote{mod=#b_literal{val=unknown},
+ name=#b_literal{val=unknown},
+ arity=0}]},
+
+ Ds = maps:from_list([{Var, FakeCall#b_set{dst=Var}} ||
+ #b_var{}=Var <- Args]),
+
+ Ls = #{ ?EXCEPTION_BLOCK => Ts,
+ 0 => Ts },
+
+ Meta = init_metadata(Id, Linear, Args),
+
+ Wl0 = State1#sig_st.wl,
+
+ {State, SuccTypes} = sig_bs(Linear, Ds, Ls, FuncDb, #{}, [], Meta, State1),
+
+ WlChanged = wl_changed(Wl0, State#sig_st.wl),
+ #{ Id := #func_info{succ_types=SuccTypes0}=Entry0 } = FuncDb,
+
+ if
+ SuccTypes0 =:= SuccTypes ->
+ {false, WlChanged, State, FuncDb};
+ SuccTypes0 =/= SuccTypes ->
+ Entry = Entry0#func_info{succ_types=SuccTypes},
+ {true, WlChanged, State, FuncDb#{ Id := Entry }}
+ end.
+
+sig_bs([{L, #b_blk{is=Is,last=Last0}} | Bs],
+ Ds0, Ls0, Fdb, Sub0, SuccTypes0, Meta, State0) when is_map_key(L, Ls0) ->
+
+ #{ L := Ts0 } = Ls0,
+
+ {Ts, Ds, Sub, State} = sig_is(Is, Ts0, Ds0, Ls0, Fdb, Sub0, State0),
+
+ Last = simplify_terminator(Last0, Ts, Ds, Sub),
+ SuccTypes = update_success_types(Last, Ts, Ds, Meta, SuccTypes0),
+ {_, Ls} = update_successors(Last, Ts, Ds, Ls0, Meta#metadata.used_once),
+
+ sig_bs(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, State);
+sig_bs([_Blk | Bs], Ds, Ls, Fdb, Sub, SuccTypes, Meta, State) ->
+ %% This block is never reached. Ignore it.
+ sig_bs(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, State);
+sig_bs([], _Ds, _Ls, _Fdb, _Sub, SuccTypes, _Meta, State) ->
+ {State, SuccTypes}.
+
+sig_is([#b_set{op=call,
+ args=[#b_local{}=Callee | _]=Args0,
+ dst=Dst}=I0 | Is],
+ Ts0, Ds0, Ls, Fdb, Sub, State0) ->
+ Args = simplify_args(Args0, Ts0, Sub),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ [_ | CallArgs] = Args,
+ {I, State} = sig_local_call(I1, Callee, CallArgs, Ts0, Fdb, State0),
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ sig_is(Is, Ts, Ds, Ls, Fdb, Sub, State);
+sig_is([#b_set{op=call,
+ args=[#b_var{} | _]=Args0,
+ dst=Dst}=I0 | Is],
+ Ts0, Ds0, Ls, Fdb, Sub, State) ->
+ Args = simplify_args(Args0, Ts0, Sub),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ [Fun | _] = Args,
+ I = case normalized_type(Fun, Ts0) of
+ #t_fun{type=Type} -> beam_ssa:add_anno(result_type, Type, I1);
+ _ -> I1
+ end,
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ sig_is(Is, Ts, Ds, Ls, Fdb, Sub, State);
+sig_is([#b_set{op=make_fun,args=Args0,dst=Dst}=I0|Is],
+ Ts0, Ds0, Ls, Fdb, Sub0, State0) ->
+ Args = simplify_args(Args0, Ts0, Sub0),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ {I, State} = sig_make_fun(I1, Ts0, Fdb, State0),
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ sig_is(Is, Ts, Ds, Ls, Fdb, Sub0, State);
+sig_is([I0 | Is], Ts0, Ds0, Ls, Fdb, Sub0, State) ->
+ case simplify(I0, Ts0, Ds0, Ls, Sub0) of
+ {#b_set{}, Ts, Ds} ->
+ sig_is(Is, Ts, Ds, Ls, Fdb, Sub0, State);
+ Sub when is_map(Sub) ->
+ sig_is(Is, Ts0, Ds0, Ls, Fdb, Sub, State)
+ end;
+sig_is([], Ts, Ds, _Ls, _Fdb, Sub, State) ->
+ {Ts, Ds, Sub, State}.
+
+sig_local_call(I0, Callee, Args, Ts, Fdb, State) ->
+ ArgTypes = argument_types(Args, Ts),
+ I = sig_local_return(I0, Callee, ArgTypes, Fdb),
+ {I, sig_update_args(Callee, ArgTypes, State)}.
+
+%% While it's impossible to tell which arguments a fun will be called with
+%% (someone could steal it through tracing and call it), we do know its free
+%% variables and can update their types as if this were a local call.
+sig_make_fun(#b_set{op=make_fun,
+ args=[#b_local{}=Callee | FreeVars]}=I0,
+ Ts, Fdb, State) ->
+ ArgCount = Callee#b_local.arity - length(FreeVars),
+
+ FVTypes = [raw_type(FreeVar, Ts) || FreeVar <- FreeVars],
+ ArgTypes = duplicate(ArgCount, any) ++ FVTypes,
+
+ I = sig_local_return(I0, Callee, ArgTypes, Fdb),
+ {I, sig_update_args(Callee, ArgTypes, State)}.
+
+sig_local_return(I, Callee, ArgTypes, Fdb) ->
+ #func_info{succ_types=SuccTypes} = map_get(Callee, Fdb),
+ case return_type(SuccTypes, ArgTypes) of
+ any -> I;
+ Type -> beam_ssa:add_anno(result_type, Type, I)
+ end.
+
+init_sig_st(StMap, FuncDb) ->
+ %% Start out as if all the roots have been called with 'any' for all
+ %% arguments.
+ Roots = init_sig_roots(FuncDb),
+ #sig_st{ committed=#{},
+ updates=init_sig_args(Roots, StMap, #{}),
+ wl=wl_defer_list(Roots, wl_new()) }.
+
+init_sig_roots(FuncDb) ->
+ maps:fold(fun(Id, #func_info{exported=true}, Acc) ->
+ [Id | Acc];
+ (_, _, Acc) ->
+ Acc
+ end, [], FuncDb).
+
+init_sig_args([Root | Roots], StMap, Acc) ->
+ #opt_st{args=Args0} = map_get(Root, StMap),
+ ArgTypes = lists:duplicate(length(Args0), any),
+ init_sig_args(Roots, StMap, Acc#{ Root => ArgTypes });
+init_sig_args([], _StMap, Acc) ->
+ Acc.
+
+sig_commit_args(Id, #sig_st{updates=Us,committed=Committed0}=State0) ->
+ Types = map_get(Id, Us),
+ Committed = Committed0#{ Id => Types },
+ State = State0#sig_st{committed=Committed},
+ {Types, State}.
+
+sig_update_args(Callee, Types, #sig_st{committed=Committed}=State) ->
+ case Committed of
+ #{ Callee := Current } ->
+ case parallel_join(Current, Types) of
+ Current ->
+ %% We've already processed this function with these
+ %% arguments, so there's no need to visit it again.
+ State;
+ Widened ->
+ sig_update_args_1(Callee, Widened, State)
+ end;
+ #{} ->
+ sig_update_args_1(Callee, Types, State)
+ end.
+
+sig_update_args_1(Callee, Types, #sig_st{updates=Us0,wl=Wl0}=State) ->
+ Us = case Us0 of
+ #{ Callee := Current } ->
+ Us0#{ Callee => parallel_join(Current, Types) };
+ #{} ->
+ Us0#{ Callee => Types }
+ end,
+ State#sig_st{updates=Us,wl=wl_add(Callee, Wl0)}.
-spec opt_continue(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when
Linear :: [{non_neg_integer(), beam_ssa:b_blk()}],
Args :: [beam_ssa:b_var()],
Anno :: beam_ssa:anno(),
FuncDb :: func_info_db().
-opt_continue(Linear, Args, Anno, FuncDb) ->
+opt_continue(Linear0, Args, Anno, FuncDb) when FuncDb =/= #{} ->
Id = get_func_id(Anno),
case FuncDb of
#{ Id := #func_info{exported=false,arg_types=ArgTypes} } ->
%% This is a local function and we're guaranteed to have visited
%% every call site at least once, so we know that the parameter
%% types are at least as narrow as the join of all argument types.
- Ts = join_arg_types(Args, ArgTypes, Anno),
- opt_continue_1(Linear, Args, Id, Ts, FuncDb);
- #{} ->
- %% We can't infer the parameter types of exported functions, nor
- %% the ones where module-level optimization is disabled, but
+ Ts = join_arg_types(Args, ArgTypes, #{}),
+ opt_function(Linear0, Args, Id, Ts, FuncDb);
+ #{ Id := #func_info{exported=true} } ->
+ %% We can't infer the parameter types of exported functions, but
%% running the pass again could still help other functions.
Ts = maps:from_list([{V,any} || #b_var{}=V <- Args]),
- opt_continue_1(Linear, Args, Id, Ts, FuncDb)
- end.
+ opt_function(Linear0, Args, Id, Ts, FuncDb)
+ end;
+opt_continue(Linear0, Args, Anno, _FuncDb) ->
+ %% Module-level optimization is disabled, pass an empty function database
+ %% so we only perform local optimizations.
+ Id = get_func_id(Anno),
+ Ts = maps:from_list([{V,any} || #b_var{}=V <- Args]),
+ {Linear, _} = opt_function(Linear0, Args, Id, Ts, #{}),
+ {Linear, #{}}.
-join_arg_types(Args, ArgTypes, Anno) ->
- %% We suppress type optimization for parameters that have already been
- %% optimized by another pass, as they may have done things we have no idea
- %% how to interpret and running them over could generate incorrect code.
- ParamTypes = maps:get(parameter_type_info, Anno, #{}),
- Ts0 = join_arg_types_1(Args, ArgTypes, #{}),
- maps:fold(fun(Arg, _V, Ts) ->
- maps:put(Arg, any, Ts)
- end, Ts0, ParamTypes).
-
-join_arg_types_1([Arg | Args], [TM | TMs], Ts) when map_size(TM) =/= 0 ->
- join_arg_types_1(Args, TMs, Ts#{ Arg => join(maps:values(TM))});
-join_arg_types_1([Arg | Args], [_TM | TMs], Ts) ->
- join_arg_types_1(Args, TMs, Ts#{ Arg => any });
-join_arg_types_1([], [], Ts) ->
+join_arg_types([Arg | Args], [TypeMap | TMs], Ts) ->
+ Type = beam_types:join(maps:values(TypeMap)),
+ join_arg_types(Args, TMs, Ts#{ Arg => Type });
+join_arg_types([], [], Ts) ->
Ts.
--spec opt_continue_1(Linear, Args, Id, Ts, FuncDb) -> Result when
+%%
+%% Optimizes a function based on the type information inferred by signatures/2
+%% and earlier runs of opt_function/5.
+%%
+%% This is pretty straightforward as it only walks through each function once,
+%% and because it only makes types narrower it's safe to optimize the functions
+%% in any order or not at all.
+%%
+
+-spec opt_function(Linear, Args, Id, Ts, FuncDb) -> Result when
Linear :: [{non_neg_integer(), beam_ssa:b_blk()}],
Args :: [beam_ssa:b_var()],
Id :: func_id(),
Ts :: type_db(),
FuncDb :: func_info_db(),
Result :: {Linear, FuncDb}.
-opt_continue_1(Linear0, Args, Id, Ts, FuncDb0) ->
- UsedOnce = used_once(Linear0, Args),
+opt_function(Linear0, Args, Id, Ts, FuncDb0) ->
FakeCall = #b_set{op=call,args=[#b_remote{mod=#b_literal{val=unknown},
name=#b_literal{val=unknown},
arity=0}]},
- Defs = maps:from_list([{Var,FakeCall#b_set{dst=Var}} ||
- #b_var{}=Var <- Args]),
- D = #d{ func_db=FuncDb0,
- func_id=Id,
- ds=Defs,
- ls=#{0=>Ts,?BADARG_BLOCK=>#{}},
- once=UsedOnce },
+ Ds = maps:from_list([{Var, FakeCall#b_set{dst=Var}} ||
+ #b_var{}=Var <- Args]),
+
+ Ls = #{ ?EXCEPTION_BLOCK => Ts,
+ 0 => Ts },
+
+ Meta = init_metadata(Id, Linear0, Args),
- {Linear, FuncDb, NewRet} = opt(Linear0, D, []),
+ {Linear, FuncDb, SuccTypes} =
+ opt_bs(Linear0, Ds, Ls, FuncDb0, #{}, [], Meta, []),
case FuncDb of
#{ Id := Entry0 } ->
- Entry = Entry0#func_info{ret_type=NewRet},
+ Entry = Entry0#func_info{succ_types=SuccTypes},
{Linear, FuncDb#{ Id := Entry }};
#{} ->
- %% Module-level optimizations have been turned off for this
- %% function.
+ %% Module-level optimizations have been turned off.
{Linear, FuncDb}
end.
+get_func_id(Anno) ->
+ #{func_info:={_Mod, Name, Arity}} = Anno,
+ #b_local{name=#b_literal{val=Name}, arity=Arity}.
+
+opt_bs([{L, #b_blk{is=Is0,last=Last0}=Blk0} | Bs],
+ Ds0, Ls0, Fdb0, Sub0, SuccTypes0, Meta, Acc) when is_map_key(L, Ls0) ->
+
+ #{ L := Ts0 } = Ls0,
+ {Is, Ts, Ds, Fdb, Sub} = opt_is(Is0, Ts0, Ds0, Ls0, Fdb0, Sub0, Meta, []),
+
+ Last1 = simplify_terminator(Last0, Ts, Ds, Sub),
+
+ SuccTypes = update_success_types(Last1, Ts, Ds, Meta, SuccTypes0),
+ {Last, Ls} = update_successors(Last1, Ts, Ds, Ls0, Meta#metadata.used_once),
+
+ Blk = Blk0#b_blk{is=Is,last=Last},
+ opt_bs(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, [{L,Blk} | Acc]);
+opt_bs([_Blk | Bs], Ds, Ls, Fdb, Sub, SuccTypes, Meta, Acc) ->
+ %% This block is never reached. Discard it.
+ opt_bs(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, Acc);
+opt_bs([], _Ds, _Ls, Fdb, _Sub, SuccTypes, _Meta, Acc) ->
+ {reverse(Acc), Fdb, SuccTypes}.
+
+opt_is([#b_set{op=call,
+ args=[#b_local{}=Callee | _]=Args0,
+ dst=Dst}=I0 | Is],
+ Ts0, Ds0, Ls, Fdb0, Sub, Meta, Acc) ->
+ Args = simplify_args(Args0, Ts0, Sub),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ [_ | CallArgs] = Args,
+ {I, Fdb} = opt_local_call(I1, Callee, CallArgs, Dst, Ts0, Fdb0, Meta),
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ opt_is(Is, Ts, Ds, Ls, Fdb, Sub, Meta, [I | Acc]);
+opt_is([#b_set{op=call,
+ args=[#b_var{} | _]=Args0,
+ dst=Dst}=I0 | Is],
+ Ts0, Ds0, Ls, Fdb, Sub, Meta, Acc) ->
+ Args = simplify_args(Args0, Ts0, Sub),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ [Fun | _] = Args,
+ I = case normalized_type(Fun, Ts0) of
+ #t_fun{type=Type} -> beam_ssa:add_anno(result_type, Type, I1);
+ _ -> I1
+ end,
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ opt_is(Is, Ts, Ds, Ls, Fdb, Sub, Meta, [I | Acc]);
+opt_is([#b_set{op=make_fun,args=Args0,dst=Dst}=I0|Is],
+ Ts0, Ds0, Ls, Fdb0, Sub0, Meta, Acc) ->
+ Args = simplify_args(Args0, Ts0, Sub0),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ {I, Fdb} = opt_make_fun(I1, Ts0, Fdb0, Meta),
+
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ opt_is(Is, Ts, Ds, Ls, Fdb, Sub0, Meta, [I|Acc]);
+opt_is([I0 | Is], Ts0, Ds0, Ls, Fdb, Sub0, Meta, Acc) ->
+ case simplify(I0, Ts0, Ds0, Ls, Sub0) of
+ {#b_set{}=I, Ts, Ds} ->
+ opt_is(Is, Ts, Ds, Ls, Fdb, Sub0, Meta, [I | Acc]);
+ Sub when is_map(Sub) ->
+ opt_is(Is, Ts0, Ds0, Ls, Fdb, Sub, Meta, Acc)
+ end;
+opt_is([], Ts, Ds, _Ls, Fdb, Sub, _Meta, Acc) ->
+ {reverse(Acc), Ts, Ds, Fdb, Sub}.
+
+opt_local_call(I0, Callee, Args, Dst, Ts, Fdb, Meta) ->
+ ArgTypes = argument_types(Args, Ts),
+ I = opt_local_return(I0, Callee, ArgTypes, Fdb),
+ case Fdb of
+ #{ Callee := #func_info{exported=false,arg_types=AT0}=Info0 } ->
+ %% Update the argument types of *this exact call*, the types
+ %% will be joined later when the callee is optimized.
+ CallId = {Meta#metadata.func_id, Dst},
+
+ AT = update_arg_types(ArgTypes, AT0, CallId),
+ Info = Info0#func_info{arg_types=AT},
+
+ {I, Fdb#{ Callee := Info }};
+ #{} ->
+ %% We can't narrow the argument types of exported functions as they
+ %% can receive anything as part of an external call. We can still
+ %% rely on their return types however.
+ {I, Fdb}
+ end.
+
+%% See sig_make_fun/4
+opt_make_fun(#b_set{op=make_fun,
+ dst=Dst,
+ args=[#b_local{}=Callee | FreeVars]}=I0,
+ Ts, Fdb, Meta) ->
+ ArgCount = Callee#b_local.arity - length(FreeVars),
+ FVTypes = [raw_type(FreeVar, Ts) || FreeVar <- FreeVars],
+ ArgTypes = duplicate(ArgCount, any) ++ FVTypes,
+
+ I = opt_local_return(I0, Callee, ArgTypes, Fdb),
+
+ case Fdb of
+ #{ Callee := #func_info{exported=false,arg_types=AT0}=Info0 } ->
+ CallId = {Meta#metadata.func_id, Dst},
+
+ AT = update_arg_types(ArgTypes, AT0, CallId),
+ Info = Info0#func_info{arg_types=AT},
+
+ {I, Fdb#{ Callee := Info }};
+ #{} ->
+ %% We can't narrow the argument types of exported functions as they
+ %% can receive anything as part of an external call.
+ {I, Fdb}
+ end.
+
+opt_local_return(I, Callee, ArgTypes, Fdb) when Fdb =/= #{} ->
+ #func_info{succ_types=SuccTypes} = map_get(Callee, Fdb),
+ case return_type(SuccTypes, ArgTypes) of
+ any -> I;
+ Type -> beam_ssa:add_anno(result_type, Type, I)
+ end;
+opt_local_return(I, _Callee, _ArgTyps, _Fdb) ->
+ %% Module-level optimization is disabled, assume it returns anything.
+ I.
+
+update_arg_types([ArgType | ArgTypes], [TypeMap0 | TypeMaps], CallId) ->
+ TypeMap = TypeMap0#{ CallId => ArgType },
+ [TypeMap | update_arg_types(ArgTypes, TypeMaps, CallId)];
+update_arg_types([], [], _CallId) ->
+ [].
+
+%%
+
-spec opt_finish(Args, Anno, FuncDb) -> {Anno, FuncDb} when
Args :: [beam_ssa:b_var()],
Anno :: beam_ssa:anno(),
@@ -145,325 +572,129 @@ opt_finish(Args, Anno, FuncDb) ->
Id = get_func_id(Anno),
case FuncDb of
#{ Id := #func_info{exported=false,arg_types=ArgTypes} } ->
- ParamInfo0 = maps:get(parameter_type_info, Anno, #{}),
+ ParamInfo0 = maps:get(parameter_info, Anno, #{}),
ParamInfo = opt_finish_1(Args, ArgTypes, ParamInfo0),
- {Anno#{ parameter_type_info => ParamInfo }, FuncDb};
+ {Anno#{ parameter_info => ParamInfo }, FuncDb};
#{} ->
{Anno, FuncDb}
end.
-opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo)
- when is_map_key(Arg, ParamInfo); %% See join_arg_types/3
- map_size(TypeMap) =:= 0 ->
- opt_finish_1(Args, TypeMaps, ParamInfo);
-opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo0) ->
- case join(maps:values(TypeMap)) of
+opt_finish_1([Arg | Args], [TypeMap | TypeMaps], Acc0) ->
+ case beam_types:join(maps:values(TypeMap)) of
any ->
- opt_finish_1(Args, TypeMaps, ParamInfo0);
- none ->
- %% This function will never be called. Pretend that we don't
- %% know the type for this argument.
- opt_finish_1(Args, TypeMaps, ParamInfo0);
+ opt_finish_1(Args, TypeMaps, Acc0);
JoinedType ->
- JoinedType = verified_type(JoinedType),
- ParamInfo = ParamInfo0#{ Arg => validator_anno(JoinedType) },
- opt_finish_1(Args, TypeMaps, ParamInfo)
+ Info = maps:get(Arg, Acc0, []),
+ Acc = Acc0#{ Arg => [{type, JoinedType} | Info] },
+ opt_finish_1(Args, TypeMaps, Acc)
end;
-opt_finish_1([], [], ParamInfo) ->
- ParamInfo.
-
-validator_anno(#t_tuple{size=Size,exact=Exact,elements=Elements0}) ->
- Elements = maps:fold(fun(Index, Type, Acc) ->
- Key = beam_validator:type_anno(integer, Index),
- Acc#{ Key => validator_anno(Type) }
- end, #{}, Elements0),
- beam_validator:type_anno(tuple, Size, Exact, Elements);
-validator_anno(#t_integer{elements={Same,Same}}) ->
- beam_validator:type_anno(integer, Same);
-validator_anno(#t_integer{}) ->
- beam_validator:type_anno(integer);
-validator_anno(float) ->
- beam_validator:type_anno(float);
-validator_anno(#t_atom{elements=[Val]}) ->
- beam_validator:type_anno(atom, Val);
-validator_anno(#t_atom{}=A) ->
- case t_is_boolean(A) of
- true -> beam_validator:type_anno(bool);
- false -> beam_validator:type_anno(atom)
- end;
-validator_anno(T) ->
- beam_validator:type_anno(T).
+opt_finish_1([], [], Acc) ->
+ Acc.
-get_func_id(Anno) ->
- #{func_info:={_Mod, Name, Arity}} = Anno,
- #b_local{name=#b_literal{val=Name}, arity=Arity}.
+%%%
+%%% Optimization helpers
+%%%
-opt([{L,Blk}|Bs], #d{ls=Ls}=D, Acc) ->
- case Ls of
- #{L:=Ts} ->
- opt_1(L, Blk, Bs, Ts, D, Acc);
- #{} ->
- %% This block is never reached. Discard it.
- opt(Bs, D, Acc)
+simplify_terminator(#b_br{bool=Bool}=Br0, Ts, Ds, Sub) ->
+ Br = beam_ssa:normalize(Br0#b_br{bool=simplify_arg(Bool, Ts, Sub)}),
+ simplify_not(Br, Ts, Ds, Sub);
+simplify_terminator(#b_switch{arg=Arg0,fail=Fail,list=List0}=Sw0,
+ Ts, Ds, Sub) ->
+ Arg = simplify_arg(Arg0, Ts, Sub),
+ %% Ensure that no label in the switch list is the same as the
+ %% failure label.
+ List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
+ case beam_ssa:normalize(Sw0#b_switch{arg=Arg,list=List}) of
+ #b_switch{}=Sw ->
+ case beam_types:is_boolean_type(raw_type(Arg, Ts)) of
+ true -> simplify_switch_bool(Sw, Ts, Ds, Sub);
+ false -> Sw
+ end;
+ #b_br{}=Br ->
+ simplify_terminator(Br, Ts, Ds, Sub)
end;
-opt([], D, Acc) ->
- #d{func_db=FuncDb,ret_type=NewRet} = D,
- {reverse(Acc), FuncDb, NewRet}.
-
-opt_1(L, #b_blk{is=Is0,last=Last0}=Blk0, Bs, Ts0,
- #d{ds=Ds0,sub=Sub0,func_db=Fdb0}=D0, Acc) ->
- case opt_is(Is0, Ts0, Ds0, Fdb0, D0, Sub0, []) of
- {Is,Ts,Ds,Fdb,Sub} ->
- D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
- Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
- Last = opt_terminator(Last1, Ts, Ds),
- D = update_successors(Last, Ts, D1),
- Blk = Blk0#b_blk{is=Is,last=Last},
- opt(Bs, D, [{L,Blk}|Acc]);
- {no_return,Ret,Is,Ds,Fdb,Sub} ->
- %% This call will never reach the successor block.
- %% Rewrite the terminator to a 'ret', and remove
- %% all type information for this label. That can
- %% potentially narrow the type of the phi node
- %% in the former successor.
- Ls = maps:remove(L, D0#d.ls),
- RetType = join([none|D0#d.ret_type]),
- D = D0#d{ds=Ds,ls=Ls,sub=Sub,
- func_db=Fdb,ret_type=[RetType]},
- Blk = Blk0#b_blk{is=Is,last=Ret},
- opt(Bs, D, [{L,Blk}|Acc])
- end.
-
-simplify_terminator(#b_br{bool=Bool}=Br, Sub, Ts, _Ds) ->
- Br#b_br{bool=simplify_arg(Bool, Sub, Ts)};
-simplify_terminator(#b_switch{arg=Arg}=Sw, Sub, Ts, _Ds) ->
- Sw#b_switch{arg=simplify_arg(Arg, Sub, Ts)};
-simplify_terminator(#b_ret{arg=Arg}=Ret, Sub, Ts, Ds) ->
+simplify_terminator(#b_ret{arg=Arg}=Ret, Ts, Ds, Sub) ->
%% Reducing the result of a call to a literal (fairly common for 'ok')
%% breaks tail call optimization.
case Ds of
#{ Arg := #b_set{op=call}} -> Ret;
- #{} -> Ret#b_ret{arg=simplify_arg(Arg, Sub, Ts)}
+ #{} -> Ret#b_ret{arg=simplify_arg(Arg, Ts, Sub)}
end.
-opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
- Ts0, Ds0, Fdb, #d{ls=Ls}=D, Sub0, Acc) ->
+%%
+%% Simplifies an instruction, returning either a new instruction (with updated
+%% type and definition maps), or an updated substitution map if the instruction
+%% was redundant.
+%%
+
+simplify(#b_set{op=phi,dst=Dst,args=Args0}=I0, Ts0, Ds0, Ls, Sub) ->
%% Simplify the phi node by removing all predecessor blocks that no
%% longer exists or no longer branches to this block.
- Args = [{simplify_arg(Arg, Sub0, Ts0),From} ||
+ Args = [{simplify_arg(Arg, Ts0, Sub), From} ||
{Arg,From} <- Args0, maps:is_key(From, Ls)],
case all_same(Args) of
true ->
%% Eliminate the phi node if there is just one source
%% value or if the values are identical.
[{Val,_}|_] = Args,
- Sub = Sub0#{Dst=>Val},
- opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
+ Sub#{ Dst => Val };
false ->
I = I0#b_set{args=Args},
Ts = update_types(I, Ts0, Ds0),
Ds = Ds0#{Dst=>I},
- opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc])
+ {I, Ts, Ds}
end;
-opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
- Args = simplify_args(Args0, Sub0, Ts0),
- I1 = beam_ssa:normalize(I0#b_set{args=Args}),
- {Ts1,Ds,Fdb,I2} = opt_call(I1, D, Ts0, Ds0, Fdb0),
- case {map_get(Dst, Ts1),Is} of
- {Type,[#b_set{op=succeeded}]} when Type =/= none ->
- %% This call instruction is inside a try/catch
- %% block. Don't attempt to simplify it.
- opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I2|Acc]);
- {none,[#b_set{op=succeeded}]} ->
- %% This call instruction is inside a try/catch
- %% block, but we know it will never return and
- %% later optimizations may try to exploit that.
- %%
- %% For example, if we have an expression that
- %% either returns this call or a tuple, we know
- %% that the expression always returns a tuple
- %% and can turn a later element/3 into
- %% get_tuple_element.
- %%
- %% This is sound but difficult to validate in a
- %% meaningful way as try/catch currently forces
- %% us to maintain the illusion that the success
- %% block is reachable even when its not, so we
- %% disable the optimization to keep things
- %% simple.
- Ts = Ts1#{ Dst := any },
- opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I2|Acc]);
- {none,_} ->
- %% This call never returns. The rest of the
- %% instructions will not be executed.
- Ret = #b_ret{arg=Dst},
- {no_return,Ret,reverse(Acc, [I2]),Ds,Fdb,Sub0};
- {_,_} ->
- case simplify_call(I2) of
- #b_set{}=I ->
- opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I|Acc]);
- #b_literal{}=Lit ->
- Sub = Sub0#{Dst=>Lit},
- Ts = maps:remove(Dst, Ts1),
- opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc);
- #b_var{}=Var ->
- Ts = maps:remove(Dst, Ts1),
- Sub = Sub0#{Dst=>Var},
- opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc)
- end
+simplify(#b_set{op=succeeded,dst=Dst}=I0, Ts0, Ds0, _Ls, Sub) ->
+ case will_succeed(I0, Ts0, Ds0, Sub) of
+ yes ->
+ Lit = #b_literal{val=true},
+ Sub#{ Dst => Lit };
+ no ->
+ Lit = #b_literal{val=false},
+ Sub#{ Dst => Lit };
+ maybe ->
+ %% Note that we never simplify args; this instruction is specific
+ %% to the operation being checked, and simplifying could break that
+ %% connection.
+ I = beam_ssa:normalize(I0),
+ Ts = Ts0#{ Dst => beam_types:make_boolean() },
+ Ds = Ds0#{ Dst => I },
+ {I, Ts, Ds}
end;
-opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
- Ts0, Ds0, Fdb, D, Sub0, Acc) ->
- case Ds0 of
- #{ Arg := #b_set{op=call} } ->
- %% The success check of a call is part of exception handling and
- %% must not be optimized away. We still have to update its type
- %% though.
- Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{Dst=>I},
-
- opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc]);
- #{} ->
- Args = simplify_args([Arg], Sub0, Ts0),
- Type = type(succeeded, Args, Ts0, Ds0),
- case get_literal_from_type(Type) of
- #b_literal{}=Lit ->
- Sub = Sub0#{Dst=>Lit},
- opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
- none ->
- Ts = Ts0#{Dst=>Type},
- Ds = Ds0#{Dst=>I},
- opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc])
- end
- end;
-opt_is([#b_set{args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb, D, Sub0, Acc) ->
- Args = simplify_args(Args0, Sub0, Ts0),
+simplify(#b_set{op=bs_match,dst=Dst,args=Args0}=I0, Ts0, Ds0, _Ls, Sub) ->
+ Args = simplify_args(Args0, Ts0, Sub),
+ I1 = beam_ssa:normalize(I0#b_set{args=Args}),
+ I2 = case {Args0,Args} of
+ {[_,_,_,#b_var{},_],[Type,Val,Flags,#b_literal{val=all},Unit]} ->
+ %% The size `all` is used for the size of the final binary
+ %% segment in a pattern. Using `all` explicitly is not allowed,
+ %% so we convert it to an obvious invalid size.
+ I1#b_set{args=[Type,Val,Flags,#b_literal{val=bad_size},Unit]};
+ {_,_} ->
+ I1
+ end,
+ %% We KNOW that simplify/2 will return a #b_set{} record when called with
+ %% a bs_match instruction.
+ #b_set{} = I3 = simplify(I2, Ts0),
+ I = beam_ssa:normalize(I3),
+ Ts = update_types(I, Ts0, Ds0),
+ Ds = Ds0#{ Dst => I },
+ {I, Ts, Ds};
+simplify(#b_set{dst=Dst,args=Args0}=I0, Ts0, Ds0, _Ls, Sub) ->
+ Args = simplify_args(Args0, Ts0, Sub),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
case simplify(I1, Ts0) of
#b_set{}=I2 ->
I = beam_ssa:normalize(I2),
Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{Dst=>I},
- opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
+ Ds = Ds0#{ Dst => I },
+ {I, Ts, Ds};
#b_literal{}=Lit ->
- Sub = Sub0#{Dst=>Lit},
- opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
+ Sub#{ Dst => Lit };
#b_var{}=Var ->
- case Is of
- [#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] ->
- %% We must remove this 'succeeded' instruction.
- Sub = Sub0#{Dst=>Var,SuccDst=>#b_literal{val=true}},
- opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
- _ ->
- Sub = Sub0#{Dst=>Var},
- opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc)
- end
- end;
-opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
- {reverse(Acc), Ts, Ds, Fdb, Sub}.
-
-simplify_call(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I) ->
- case Rem of
- #b_remote{mod=#b_literal{val=Mod},
- name=#b_literal{val=Name}} ->
- case erl_bifs:is_pure(Mod, Name, length(Args)) of
- true ->
- simplify_remote_call(Mod, Name, Args, I);
- false ->
- I
- end;
- #b_remote{} ->
- I
- end;
-simplify_call(I) -> I.
-
-%% Simplify a remote call to a pure BIF.
-simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
- Tl;
-simplify_remote_call(erlang, setelement,
- [#b_literal{val=Pos},
- #b_literal{val=Tuple},
- #b_var{}=Value], I)
- when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) ->
- %% Position is a literal integer and the shape of the
- %% tuple is known.
- Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)],
- {Bef,[_|Aft]} = split(Pos - 1, Els0),
- Els = Bef ++ [Value|Aft],
- I#b_set{op=put_tuple,args=Els};
-simplify_remote_call(Mod, Name, Args0, I) ->
- case make_literal_list(Args0) of
- none ->
- I;
- Args ->
- %% The arguments are literals. Try to evaluate the BIF.
- try apply(Mod, Name, Args) of
- Val ->
- case cerl:is_literal_term(Val) of
- true ->
- #b_literal{val=Val};
- false ->
- %% The value can't be expressed as a literal
- %% (e.g. a pid).
- I
- end
- catch
- _:_ ->
- %% Failed. Don't bother trying to optimize
- %% the call.
- I
- end
+ Sub#{ Dst => Var }
end.
-opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) ->
- {Ts, Ds, I} = opt_local_call(I0, Ts0, Ds0, Fdb0),
- case Fdb0 of
- #{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
- %% Update the argument types of *this exact call*, the types
- %% will be joined later when the callee is optimized.
- CallId = {D#d.func_id, Dst},
- ArgTypes = update_arg_types(Args, ArgTypes0, CallId, Ts0),
-
- Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
- {Ts, Ds, Fdb, I};
- #{} ->
- %% We can't narrow the argument types of exported functions as they
- %% can receive anything as part of an external call.
- {Ts, Ds, Fdb0, I}
- end;
-opt_call(#b_set{dst=Dst}=I, _D, Ts0, Ds0, Fdb) ->
- Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{ Dst => I },
- {Ts, Ds, Fdb, I}.
-
-opt_local_call(#b_set{dst=Dst,args=[Id|_]}=I0, Ts0, Ds0, Fdb) ->
- Type = case Fdb of
- #{ Id := #func_info{ret_type=[T]} } -> T;
- #{} -> any
- end,
- I = case Type of
- any -> I0;
- none -> I0;
- _ -> beam_ssa:add_anno(result_type, validator_anno(Type), I0)
- end,
- Ts = Ts0#{ Dst => Type },
- Ds = Ds0#{ Dst => I },
- {Ts, Ds, I}.
-
-update_arg_types([Arg | Args], [TypeMap0 | TypeMaps], CallId, Ts) ->
- %% Match contexts are treated as bitstrings when optimizing arguments, as
- %% we don't yet support removing the "bs_start_match3" instruction.
- NewType = case get_type(Arg, Ts) of
- #t_bs_match{} -> {binary, 1};
- Type -> Type
- end,
- TypeMap = TypeMap0#{ CallId => NewType },
- [TypeMap | update_arg_types(Args, TypeMaps, CallId, Ts)];
-update_arg_types([], [], _CallId, _Ts) ->
- [].
-
simplify(#b_set{op={bif,'and'},args=Args}=I, Ts) ->
case is_safe_bool_op(Args, Ts) of
true ->
@@ -487,8 +718,10 @@ simplify(#b_set{op={bif,'or'},args=Args}=I, Ts) ->
I
end;
simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I0, Ts) ->
- case t_tuple_size(get_type(Tuple, Ts)) of
- {_,Size} when is_integer(Index), 1 =< Index, Index =< Size ->
+ case normalized_type(Tuple, Ts) of
+ #t_tuple{size=Size} when is_integer(Index),
+ 1 =< Index,
+ Index =< Size ->
I = I0#b_set{op=get_tuple_element,
args=[Tuple,#b_literal{val=Index-1}]},
simplify(I, Ts);
@@ -496,67 +729,110 @@ simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I0, Ts) ->
eval_bif(I0, Ts)
end;
simplify(#b_set{op={bif,hd},args=[List]}=I, Ts) ->
- case get_type(List, Ts) of
- cons ->
+ case normalized_type(List, Ts) of
+ #t_cons{} ->
I#b_set{op=get_hd};
_ ->
eval_bif(I, Ts)
end;
simplify(#b_set{op={bif,tl},args=[List]}=I, Ts) ->
- case get_type(List, Ts) of
- cons ->
+ case normalized_type(List, Ts) of
+ #t_cons{} ->
I#b_set{op=get_tl};
_ ->
eval_bif(I, Ts)
end;
simplify(#b_set{op={bif,size},args=[Term]}=I, Ts) ->
- case get_type(Term, Ts) of
+ case normalized_type(Term, Ts) of
#t_tuple{} ->
simplify(I#b_set{op={bif,tuple_size}}, Ts);
+ #t_bitstring{size_unit=U} when U rem 8 =:= 0 ->
+ %% If the bitstring is a binary (the size in bits is
+ %% evenly divisibly by 8), byte_size/1 gives
+ %% the same result as size/1.
+ simplify(I#b_set{op={bif,byte_size}}, Ts);
_ ->
eval_bif(I, Ts)
end;
simplify(#b_set{op={bif,tuple_size},args=[Term]}=I, Ts) ->
- case get_type(Term, Ts) of
+ case normalized_type(Term, Ts) of
#t_tuple{size=Size,exact=true} ->
#b_literal{val=Size};
_ ->
I
end;
-simplify(#b_set{op={bif,'=='},args=Args}=I, Ts) ->
- Types = get_types(Args, Ts),
- EqEq = case {meet(Types),join(Types)} of
- {none,any} -> true;
- {#t_integer{},#t_integer{}} -> true;
- {float,float} -> true;
- {{binary,_},_} -> true;
- {#t_atom{},_} -> true;
- {_,_} -> false
- end,
+simplify(#b_set{op={bif,is_function},args=[Fun,#b_literal{val=Arity}]}=I, Ts)
+ when is_integer(Arity), Arity >= 0 ->
+ case normalized_type(Fun, Ts) of
+ #t_fun{arity=any} ->
+ I;
+ #t_fun{arity=Arity} ->
+ #b_literal{val=true};
+ any ->
+ I;
+ _ ->
+ #b_literal{val=false}
+ end;
+simplify(#b_set{op={bif,is_map_key},args=[Key,Map]}=I, Ts) ->
+ case normalized_type(Map, Ts) of
+ #t_map{} ->
+ I#b_set{op=has_map_field,args=[Map,Key]};
+ _ ->
+ I
+ end;
+simplify(#b_set{op={bif,Op0},args=Args}=I, Ts) when Op0 =:= '==';
+ Op0 =:= '/=' ->
+ Types = normalized_types(Args, Ts),
+ EqEq0 = case {beam_types:meet(Types),beam_types:join(Types)} of
+ {none,any} -> true;
+ {#t_integer{},#t_integer{}} -> true;
+ {#t_float{},#t_float{}} -> true;
+ {#t_bitstring{},_} -> true;
+ {#t_atom{},_} -> true;
+ {_,_} -> false
+ end,
+ EqEq = EqEq0 orelse any_non_numeric_argument(Args, Ts),
case EqEq of
true ->
- simplify(I#b_set{op={bif,'=:='}}, Ts);
+ Op = case Op0 of
+ '==' -> '=:=';
+ '/=' -> '=/='
+ end,
+ simplify(I#b_set{op={bif,Op}}, Ts);
false ->
eval_bif(I, Ts)
end;
simplify(#b_set{op={bif,'=:='},args=[Same,Same]}, _Ts) ->
#b_literal{val=true};
-simplify(#b_set{op={bif,'=:='},args=[A1,_A2]=Args}=I, Ts) ->
- [T1,T2] = get_types(Args, Ts),
- case meet(T1, T2) of
+simplify(#b_set{op={bif,'=:='},args=[LHS,RHS]}=I, Ts) ->
+ LType = raw_type(LHS, Ts),
+ RType = raw_type(RHS, Ts),
+ case beam_types:meet(LType, RType) of
none ->
#b_literal{val=false};
_ ->
- case {t_is_boolean(T1),T2} of
+ case {beam_types:is_boolean_type(LType),
+ beam_types:normalize(RType)} of
{true,#t_atom{elements=[true]}} ->
%% Bool =:= true ==> Bool
- A1;
+ LHS;
+ {true,#t_atom{elements=[false]}} ->
+ %% Bool =:= false ==> not Bool
+ %%
+ %% This will be further optimized to eliminate the
+ %% 'not', swapping the success and failure
+ %% branches in the br instruction. If LHS comes
+ %% from a type test (such as is_atom/1) or a
+ %% comparison operator (such as >=) that can be
+ %% translated to test instruction, this
+ %% optimization will eliminate one instruction.
+ simplify(I#b_set{op={bif,'not'},args=[LHS]}, Ts);
{_,_} ->
eval_bif(I, Ts)
end
end;
simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
- Types = get_types(Args, Ts),
+ Types = normalized_types(Args, Ts),
case is_float_op(Op, Types) of
false ->
eval_bif(I, Ts);
@@ -564,29 +840,57 @@ simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
AnnoArgs = [anno_float_arg(A) || A <- Types],
eval_bif(beam_ssa:add_anno(float_op, AnnoArgs, I), Ts)
end;
-simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=N}]}=I, Ts) ->
- case get_type(Tuple, Ts) of
- #t_tuple{size=Size,elements=Es} when Size > N ->
- ElemType = get_element_type(N + 1, Es),
- case get_literal_from_type(ElemType) of
- #b_literal{}=Lit -> Lit;
- none -> I
- end;
- none ->
- %% Will never be executed because of type conflict.
- %% #b_literal{val=ignored};
+simplify(#b_set{op=bs_extract,args=[Ctx]}=I, Ts) ->
+ case raw_type(Ctx, Ts) of
+ #t_bitstring{} ->
+ %% This is a bs_match that has been rewritten as a bs_get_tail;
+ %% just return the input as-is.
+ Ctx;
+ #t_bs_context{} ->
I
end;
+simplify(#b_set{op=bs_match,
+ args=[#b_literal{val=binary}, Ctx, _Flags,
+ #b_literal{val=all},
+ #b_literal{val=OpUnit}]}=I, Ts) ->
+ %% <<..., Foo/binary>> can be rewritten as <<..., Foo/bits>> if we know the
+ %% unit is correct.
+ #t_bs_context{tail_unit=CtxUnit} = raw_type(Ctx, Ts),
+ if
+ CtxUnit rem OpUnit =:= 0 ->
+ I#b_set{op=bs_get_tail,args=[Ctx]};
+ CtxUnit rem OpUnit =/= 0 ->
+ I
+ end;
+simplify(#b_set{op=bs_start_match,args=[#b_literal{val=new}, Src]}=I, Ts) ->
+ case raw_type(Src, Ts) of
+ #t_bs_context{} ->
+ I#b_set{op=bs_start_match,args=[#b_literal{val=resume}, Src]};
+ _ ->
+ I
+ end;
+simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=N}]}=I, Ts) ->
+ #t_tuple{size=Size,elements=Es} = normalized_type(Tuple, Ts),
+ true = Size > N, %Assertion.
+ ElemType = beam_types:get_tuple_element(N + 1, Es),
+ case beam_types:get_singleton_value(ElemType) of
+ {ok, Val} -> #b_literal{val=Val};
+ error -> I
+ end;
simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
- case get_type(Src, Ts) of
- any -> I;
- list -> I;
- cons -> #b_literal{val=true};
- _ -> #b_literal{val=false}
+ case normalized_type(Src, Ts) of
+ any ->
+ I;
+ #t_list{} ->
+ I;
+ #t_cons{} ->
+ #b_literal{val=true};
+ _ ->
+ #b_literal{val=false}
end;
simplify(#b_set{op=is_tagged_tuple,
args=[Src,#b_literal{val=Size},#b_literal{}=Tag]}=I, Ts) ->
- simplify_is_record(I, get_type(Src, Ts), Size, Tag, Ts);
+ simplify_is_record(I, normalized_type(Src, Ts), Size, Tag, Ts);
simplify(#b_set{op=put_list,args=[#b_literal{val=H},
#b_literal{val=T}]}, _Ts) ->
#b_literal{val=[H|T]};
@@ -599,8 +903,274 @@ simplify(#b_set{op=wait_timeout,args=[#b_literal{val=0}]}, _Ts) ->
#b_literal{val=true};
simplify(#b_set{op=wait_timeout,args=[#b_literal{val=infinity}]}=I, _Ts) ->
I#b_set{op=wait,args=[]};
+simplify(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I, _Ts) ->
+ case Rem of
+ #b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Name}} ->
+ case erl_bifs:is_pure(Mod, Name, length(Args)) of
+ true ->
+ simplify_remote_call(Mod, Name, Args, I);
+ false ->
+ I
+ end;
+ #b_remote{} ->
+ I
+ end;
simplify(I, _Ts) -> I.
+will_succeed(#b_set{args=[Src]}, Ts, Ds, Sub) ->
+ case {Ds, Ts} of
+ {#{}, #{ Src := none }} ->
+ %% Checked operation never returns.
+ no;
+ {#{ Src := I }, #{}} ->
+ will_succeed_1(I, Src, Ts, Sub);
+ {#{}, #{}} ->
+ %% The checked instruction has been removed and substituted, so we
+ %% can assume it always succeeds.
+ true = is_map_key(Src, Sub), %Assertion.
+ yes
+ end.
+
+will_succeed_1(#b_set{op=bs_get_tail}, _Src, _Ts, _Sub) ->
+ yes;
+will_succeed_1(#b_set{op=bs_start_match,args=[_, Arg]}, _Src, Ts, _Sub) ->
+ ArgType = raw_type(Arg, Ts),
+ case beam_types:is_bs_matchable_type(ArgType) of
+ true ->
+ %% In the future we may be able to remove this instruction
+ %% altogether when we have a #t_bs_context{}, but for now we need
+ %% to keep it for compatibility with older releases of OTP.
+ yes;
+ false ->
+ %% Is it at all possible to match?
+ case beam_types:meet(ArgType, #t_bs_matchable{}) of
+ none -> no;
+ _ -> maybe
+ end
+ end;
+
+will_succeed_1(#b_set{op={bif,Bif},args=BifArgs}, _Src, Ts, _Sub) ->
+ ArgTypes = normalized_types(BifArgs, Ts),
+ beam_call_types:will_succeed(erlang, Bif, ArgTypes);
+will_succeed_1(#b_set{op=call,
+ args=[#b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Func}} |
+ CallArgs]},
+ _Src, Ts, _Sub) ->
+ ArgTypes = normalized_types(CallArgs, Ts),
+ beam_call_types:will_succeed(Mod, Func, ArgTypes);
+
+will_succeed_1(#b_set{op=get_hd}, _Src, _Ts, _Sub) ->
+ yes;
+will_succeed_1(#b_set{op=get_tl}, _Src, _Ts, _Sub) ->
+ yes;
+will_succeed_1(#b_set{op=has_map_field}, _Src, _Ts, _Sub) ->
+ yes;
+will_succeed_1(#b_set{op=get_tuple_element}, _Src, _Ts, _Sub) ->
+ yes;
+will_succeed_1(#b_set{op=put_tuple}, _Src, _Ts, _Sub) ->
+ yes;
+
+%% Remove the success branch from binary operations with invalid
+%% sizes. That will remove subsequent bs_put and bs_match instructions,
+%% which are probably not loadable.
+will_succeed_1(#b_set{op=bs_add,args=[_,#b_literal{val=Size},_]},
+ _Src, _Ts, _Sub) ->
+ if
+ is_integer(Size), Size >= 0 ->
+ maybe;
+ true ->
+ no
+ end;
+will_succeed_1(#b_set{op=bs_init,
+ args=[#b_literal{val=new},#b_literal{val=Size},_Unit]},
+ _Src, _Ts, _Sub) ->
+ if
+ is_integer(Size), Size >= 0 ->
+ maybe;
+ true ->
+ no
+ end;
+will_succeed_1(#b_set{op=bs_init,
+ args=[#b_literal{},_,#b_literal{val=Size},_Unit]},
+ _Src, _Ts, _Sub) ->
+ if
+ is_integer(Size), Size >= 0 ->
+ maybe;
+ true ->
+ no
+ end;
+will_succeed_1(#b_set{op=bs_match,
+ args=[#b_literal{val=Type},_,_,#b_literal{val=Size},_]},
+ _Src, _Ts, _Sub) ->
+ if
+ is_integer(Size), Size >= 0 ->
+ maybe;
+ Type =:= binary, Size =:= all ->
+ %% `all` is a legal size for binary segments at the end of
+ %% a binary pattern.
+ maybe;
+ true ->
+ %% Invalid size. Matching will fail.
+ no
+ end;
+
+%% These operations may fail even though we know their return value on success.
+will_succeed_1(#b_set{op=call}, _Src, _Ts, _Sub) ->
+ maybe;
+will_succeed_1(#b_set{op=get_map_element}, _Src, _Ts, _Sub) ->
+ maybe;
+
+will_succeed_1(#b_set{op=wait}, _Src, _Ts, _Sub) ->
+ no;
+
+will_succeed_1(#b_set{}, Src, Ts, Sub) ->
+ case simplify_arg(Src, Ts, Sub) of
+ #b_var{}=Src ->
+ %% No substitution; might fail at runtime.
+ maybe;
+ _ ->
+ %% Substituted with literal or other variable; always succeeds.
+ yes
+ end.
+
+simplify_is_record(I, #t_tuple{exact=Exact,
+ size=Size,
+ elements=Es},
+ RecSize, #b_literal{val=TagVal}=RecTag, Ts) ->
+ TagType = maps:get(1, Es, any),
+ TagMatch = case beam_types:get_singleton_value(TagType) of
+ {ok, TagVal} -> yes;
+ {ok, _} -> no;
+ error ->
+ %% Is it at all possible for the tag to match?
+ case beam_types:meet(raw_type(RecTag, Ts), TagType) of
+ none -> no;
+ _ -> maybe
+ end
+ end,
+ if
+ Size =/= RecSize, Exact; Size > RecSize; TagMatch =:= no ->
+ #b_literal{val=false};
+ Size =:= RecSize, Exact, TagMatch =:= yes ->
+ #b_literal{val=true};
+ true ->
+ I
+ end;
+simplify_is_record(I, any, _Size, _Tag, _Ts) ->
+ I;
+simplify_is_record(_I, _Type, _Size, _Tag, _Ts) ->
+ #b_literal{val=false}.
+
+simplify_switch_bool(#b_switch{arg=B,fail=Fail,list=List0}, Ts, Ds, Sub) ->
+ FalseVal = #b_literal{val=false},
+ TrueVal = #b_literal{val=true},
+ List1 = List0 ++ [{FalseVal,Fail},{TrueVal,Fail}],
+ {_,FalseLbl} = keyfind(FalseVal, 1, List1),
+ {_,TrueLbl} = keyfind(TrueVal, 1, List1),
+ Br = #b_br{bool=B,succ=TrueLbl,fail=FalseLbl},
+ simplify_terminator(Br, Ts, Ds, Sub).
+
+simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds, Sub) ->
+ case Ds of
+ #{V:=#b_set{op={bif,'not'},args=[Bool]}} ->
+ case beam_types:is_boolean_type(raw_type(Bool, Ts)) of
+ true ->
+ Br = Br0#b_br{bool=Bool,succ=Fail,fail=Succ},
+ simplify_terminator(Br, Ts, Ds, Sub);
+ false ->
+ Br0
+ end;
+ #{} ->
+ Br0
+ end;
+simplify_not(#b_br{bool=#b_literal{}}=Br, _Sub, _Ts, _Ds) ->
+ Br.
+
+%% Simplify a remote call to a pure BIF.
+simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
+ Tl;
+simplify_remote_call(erlang, setelement,
+ [#b_literal{val=Pos},
+ #b_literal{val=Tuple},
+ #b_var{}=Value], I)
+ when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) ->
+ %% Position is a literal integer and the shape of the
+ %% tuple is known.
+ Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)],
+ {Bef,[_|Aft]} = split(Pos - 1, Els0),
+ Els = Bef ++ [Value|Aft],
+ I#b_set{op=put_tuple,args=Els};
+simplify_remote_call(Mod, Name, Args0, I) ->
+ case make_literal_list(Args0) of
+ none ->
+ I;
+ Args ->
+ %% The arguments are literals. Try to evaluate the BIF.
+ try apply(Mod, Name, Args) of
+ Val ->
+ case cerl:is_literal_term(Val) of
+ true ->
+ #b_literal{val=Val};
+ false ->
+ %% The value can't be expressed as a literal
+ %% (e.g. a pid).
+ I
+ end
+ catch
+ _:_ ->
+ %% Failed. Don't bother trying to optimize
+ %% the call.
+ I
+ end
+ end.
+
+any_non_numeric_argument([#b_literal{val=Lit}|_], _Ts) ->
+ is_non_numeric(Lit);
+any_non_numeric_argument([#b_var{}=V|T], Ts) ->
+ is_non_numeric_type(raw_type(V, Ts)) orelse any_non_numeric_argument(T, Ts);
+any_non_numeric_argument([], _Ts) -> false.
+
+is_non_numeric([H|T]) ->
+ is_non_numeric(H) andalso is_non_numeric(T);
+is_non_numeric(Tuple) when is_tuple(Tuple) ->
+ is_non_numeric_tuple(Tuple, tuple_size(Tuple));
+is_non_numeric(Map) when is_map(Map) ->
+ %% Starting from OTP 18, map keys are compared using `=:=`.
+ %% Therefore, we only need to check that the values in the map are
+ %% non-numeric. (Support for compiling BEAM files for OTP releases
+ %% older than OTP 18 has been dropped.)
+ is_non_numeric(maps:values(Map));
+is_non_numeric(Num) when is_number(Num) ->
+ false;
+is_non_numeric(_) -> true.
+
+is_non_numeric_tuple(Tuple, El) when El >= 1 ->
+ is_non_numeric(element(El, Tuple)) andalso
+ is_non_numeric_tuple(Tuple, El-1);
+is_non_numeric_tuple(_Tuple, 0) -> true.
+
+is_non_numeric_type(#t_atom{}) -> true;
+is_non_numeric_type(#t_bitstring{}) -> true;
+is_non_numeric_type(#t_cons{type=Type,terminator=Terminator}) ->
+ is_non_numeric_type(Type) andalso is_non_numeric_type(Terminator);
+is_non_numeric_type(#t_list{type=Type,terminator=Terminator}) ->
+ is_non_numeric_type(Type) andalso is_non_numeric_type(Terminator);
+is_non_numeric_type(#t_map{super_value=Value}) ->
+ is_non_numeric_type(Value);
+is_non_numeric_type(nil) -> true;
+is_non_numeric_type(#t_tuple{size=Size,exact=true,elements=Types})
+ when map_size(Types) =:= Size ->
+ is_non_numeric_tuple_type(Size, Types);
+is_non_numeric_type(_) -> false.
+
+is_non_numeric_tuple_type(0, _Types) ->
+ true;
+is_non_numeric_tuple_type(Pos, Types) ->
+ is_non_numeric_type(map_get(Pos, Types)) andalso
+ is_non_numeric_tuple_type(Pos - 1, Types).
+
make_literal_list(Args) ->
make_literal_list(Args, []).
@@ -611,9 +1181,11 @@ make_literal_list([_|_], _) ->
make_literal_list([], Acc) ->
reverse(Acc).
-is_safe_bool_op(Args, Ts) ->
- [T1,T2] = get_types(Args, Ts),
- t_is_boolean(T1) andalso t_is_boolean(T2).
+is_safe_bool_op([LHS, RHS], Ts) ->
+ LType = raw_type(LHS, Ts),
+ RType = raw_type(RHS, Ts),
+ beam_types:is_boolean_type(LType) andalso
+ beam_types:is_boolean_type(RType).
all_same([{H,_}|T]) ->
all(fun({E,_}) -> E =:= H end, T).
@@ -626,21 +1198,7 @@ eval_bif(#b_set{op={bif,Bif},args=Args}=I, Ts) ->
true ->
case make_literal_list(Args) of
none ->
- case get_types(Args, Ts) of
- [any] ->
- I;
- [Type] ->
- case will_succeed(Bif, Type) of
- yes ->
- #b_literal{val=true};
- no ->
- #b_literal{val=false};
- maybe ->
- I
- end;
- _ ->
- I
- end;
+ eval_type_test_bif(I, Bif, raw_types(Args, Ts));
LitArgs ->
try apply(erlang, Bif, LitArgs) of
Val -> #b_literal{val=Val}
@@ -651,24 +1209,101 @@ eval_bif(#b_set{op={bif,Bif},args=Args}=I, Ts) ->
end
end.
-simplify_args(Args, Sub, Ts) ->
- [simplify_arg(Arg, Sub, Ts) || Arg <- Args].
+eval_type_test_bif(I, is_atom, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_atom{});
+eval_type_test_bif(I, is_binary, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_bs_matchable{tail_unit=8});
+eval_type_test_bif(I, is_bitstring, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_bs_matchable{});
+eval_type_test_bif(I, is_boolean, [Type]) ->
+ case beam_types:is_boolean_type(Type) of
+ true ->
+ #b_literal{val=true};
+ false ->
+ case beam_types:meet(Type, #t_atom{}) of
+ #t_atom{elements=[_|_]=Es} ->
+ case any(fun is_boolean/1, Es) of
+ true -> I;
+ false -> #b_literal{val=false}
+ end;
+ #t_atom{} ->
+ I;
+ none ->
+ #b_literal{val=false}
+ end
+ end;
+eval_type_test_bif(I, is_float, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_float{});
+eval_type_test_bif(I, is_function, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_fun{});
+eval_type_test_bif(I, is_integer, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_integer{});
+eval_type_test_bif(I, is_list, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_list{});
+eval_type_test_bif(I, is_map, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_map{});
+eval_type_test_bif(I, is_number, [Type]) ->
+ eval_type_test_bif_1(I, Type, number);
+eval_type_test_bif(I, is_tuple, [Type]) ->
+ eval_type_test_bif_1(I, Type, #t_tuple{});
+eval_type_test_bif(I, Op, Types) ->
+ case Types of
+ [#t_integer{},#t_integer{elements={0,0}}]
+ when Op =:= '+'; Op =:= '-'; Op =:= 'bor'; Op =:= 'bxor' ->
+ #b_set{args=[Result,_]} = I,
+ Result;
+ [#t_integer{},#t_integer{elements={0,0}}] when Op =:= '*'; Op =:= 'band' ->
+ #b_literal{val=0};
+ [#t_integer{},#t_integer{elements={1,1}}] when Op =:= '*'; Op =:= 'div' ->
+ #b_set{args=[Result,_]} = I,
+ Result;
+ [#t_integer{elements={LMin,LMax}},#t_integer{elements={RMin,RMax}}] ->
+ case is_inequality_op(Op) of
+ true ->
+ case {erlang:Op(LMin, RMin),erlang:Op(LMax, RMin),
+ erlang:Op(LMin, RMax),erlang:Op(LMax, RMax)} of
+ {Bool,Bool,Bool,Bool} ->
+ #b_literal{val=Bool};
+ _ ->
+ I
+ end;
+ false ->
+ I
+ end;
+ _ ->
+ I
+ end.
+
+is_inequality_op('<') -> true;
+is_inequality_op('=<') -> true;
+is_inequality_op('>') -> true;
+is_inequality_op('>=') -> true;
+is_inequality_op(_) -> false.
+
+eval_type_test_bif_1(I, ArgType, Required) ->
+ case beam_types:meet(ArgType, Required) of
+ ArgType -> #b_literal{val=true};
+ none -> #b_literal{val=false};
+ _ -> I
+ end.
+
+simplify_args(Args, Ts, Sub) ->
+ [simplify_arg(Arg, Ts, Sub) || Arg <- Args].
-simplify_arg(#b_var{}=Arg0, Sub, Ts) ->
+simplify_arg(#b_var{}=Arg0, Ts, Sub) ->
case sub_arg(Arg0, Sub) of
#b_literal{}=LitArg ->
LitArg;
#b_var{}=Arg ->
- Type = get_type(Arg, Ts),
- case get_literal_from_type(Type) of
- none -> Arg;
- #b_literal{}=Lit -> Lit
+ case beam_types:get_singleton_value(raw_type(Arg, Ts)) of
+ {ok, Val} -> #b_literal{val=Val};
+ error -> Arg
end
end;
-simplify_arg(#b_remote{mod=Mod,name=Name}=Rem, Sub, Ts) ->
- Rem#b_remote{mod=simplify_arg(Mod, Sub, Ts),
- name=simplify_arg(Name, Sub, Ts)};
-simplify_arg(Arg, _Sub, _Ts) -> Arg.
+simplify_arg(#b_remote{mod=Mod,name=Name}=Rem, Ts, Sub) ->
+ Rem#b_remote{mod=simplify_arg(Mod, Ts, Sub),
+ name=simplify_arg(Name, Ts, Sub)};
+simplify_arg(Arg, _Ts, _Sub) -> Arg.
sub_arg(#b_var{}=Old, Sub) ->
case Sub of
@@ -676,13 +1311,13 @@ sub_arg(#b_var{}=Old, Sub) ->
#{} -> Old
end.
-is_float_op('-', [float]) ->
+is_float_op('-', [#t_float{}]) ->
true;
is_float_op('/', [_,_]) ->
true;
-is_float_op(Op, [float,_Other]) ->
+is_float_op(Op, [#t_float{},_Other]) ->
is_float_op_1(Op);
-is_float_op(Op, [_Other,float]) ->
+is_float_op(Op, [_Other,#t_float{}]) ->
is_float_op_1(Op);
is_float_op(_, _) -> false.
@@ -691,486 +1326,365 @@ is_float_op_1('-') -> true;
is_float_op_1('*') -> true;
is_float_op_1(_) -> false.
-anno_float_arg(float) -> float;
+anno_float_arg(#t_float{}) -> float;
anno_float_arg(_) -> convert.
-opt_terminator(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) ->
- beam_ssa:normalize(Br);
-opt_terminator(#b_br{bool=#b_var{}}=Br, Ts, Ds) ->
- simplify_not(Br, Ts, Ds);
-opt_terminator(#b_switch{arg=#b_literal{}}=Sw, _Ts, _Ds) ->
- beam_ssa:normalize(Sw);
-opt_terminator(#b_switch{arg=#b_var{}=V}=Sw, Ts, Ds) ->
- case get_type(V, Ts) of
- any ->
- beam_ssa:normalize(Sw);
- Type ->
- beam_ssa:normalize(opt_switch(Sw, Type, Ts, Ds))
- end;
-opt_terminator(#b_ret{}=Ret, _Ts, _Ds) -> Ret.
-
-
-opt_switch(#b_switch{fail=Fail,list=List0}=Sw0, Type, Ts, Ds) ->
- List = prune_switch_list(List0, Fail, Type, Ts),
- Sw1 = Sw0#b_switch{list=List},
- case Type of
- #t_integer{elements={_,_}=Range} ->
- simplify_switch_int(Sw1, Range);
- #t_atom{elements=[_|_]} ->
- case t_is_boolean(Type) of
- true ->
- #b_br{} = Br = simplify_switch_bool(Sw1, Ts, Ds),
- opt_terminator(Br, Ts, Ds);
- false ->
- simplify_switch_atom(Type, Sw1)
- end;
- _ ->
- Sw1
- end.
+%%%
+%%% Type helpers
+%%%
-prune_switch_list([{_,Fail}|T], Fail, Type, Ts) ->
- prune_switch_list(T, Fail, Type, Ts);
-prune_switch_list([{Arg,_}=Pair|T], Fail, Type, Ts) ->
- case meet(get_type(Arg, Ts), Type) of
- none ->
- %% Different types. This value can never match.
- prune_switch_list(T, Fail, Type, Ts);
- _ ->
- [Pair|prune_switch_list(T, Fail, Type, Ts)]
- end;
-prune_switch_list([], _, _, _) -> [].
+%% Returns the narrowest possible return type for the given success types and
+%% arguments.
+return_type(SuccTypes0, CallArgs0) ->
+ SuccTypes = st_filter_reachable(SuccTypes0, CallArgs0, [], []),
+ st_join_return_types(SuccTypes, none).
-update_successors(#b_br{bool=#b_literal{val=true},succ=S}, Ts, D) ->
- update_successor(S, Ts, D);
-update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}, Ts0, D0) ->
- case cerl_sets:is_element(Bool, D0#d.once) of
+st_filter_reachable([{SuccArgs, {call_self, SelfArgs}}=SuccType | Rest],
+ CallArgs0, Deferred, Acc) ->
+ case st_is_reachable(SuccArgs, CallArgs0) of
true ->
- %% This variable is defined in this block and is only
- %% referenced by this br terminator. Therefore, there is
- %% no need to include it in the type database passed on to
- %% the successors of this block.
- Ts = maps:remove(Bool, Ts0),
- {SuccTs,FailTs} = infer_types_br(Bool, Ts, D0),
- D = update_successor(Fail, FailTs, D0),
- update_successor(Succ, SuccTs, D);
+ %% If we return a call to ourselves, we need to join our current
+ %% argument types with that of the call to ensure all possible
+ %% return paths are covered.
+ CallArgs = parallel_join(SelfArgs, CallArgs0),
+ st_filter_reachable(Rest, CallArgs, Deferred, Acc);
false ->
- {SuccTs,FailTs} = infer_types_br(Bool, Ts0, D0),
- D = update_successor_bool(Bool, false, Fail, FailTs, D0),
- update_successor_bool(Bool, true, Succ, SuccTs, D)
+ %% This may be reachable after we've joined another self-call, so
+ %% we defer it until we've gone through all other self-calls.
+ st_filter_reachable(Rest, CallArgs0, [SuccType | Deferred], Acc)
end;
-update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts, D0) ->
- case cerl_sets:is_element(V, D0#d.once) of
+st_filter_reachable([SuccType | Rest], CallArgs, Deferred, Acc) ->
+ st_filter_reachable(Rest, CallArgs, Deferred, [SuccType | Acc]);
+st_filter_reachable([], CallArgs, Deferred, Acc) ->
+ case st_any_reachable(Deferred, CallArgs) of
true ->
- %% This variable is defined in this block and is only
- %% referenced by this switch terminator. Therefore, there is
- %% no need to include it in the type database passed on to
- %% the successors of this block.
- D = update_successor(Fail, Ts, D0),
- F = fun({Val,S}, A) ->
- SuccTs0 = infer_types_switch(V, Val, Ts, D),
- SuccTs = maps:remove(V, SuccTs0),
- update_successor(S, SuccTs, A)
- end,
- foldl(F, D, List);
+ %% Handle all deferred self calls that may be reachable now that
+ %% we've expanded our argument types.
+ st_filter_reachable(Deferred, CallArgs, [], Acc);
false ->
- %% V can not be equal to any of the values in List at the fail
- %% block.
- FailTs = subtract_sw_list(V, List, Ts),
- D = update_successor(Fail, FailTs, D0),
- F = fun({Val,S}, A) ->
- SuccTs = infer_types_switch(V, Val, Ts, D),
- update_successor(S, SuccTs, A)
- end,
- foldl(F, D, List)
- end;
-update_successors(#b_ret{arg=Arg}, Ts, D) ->
- FuncId = D#d.func_id,
- case D#d.ds of
- #{ Arg := #b_set{op=call,args=[FuncId | _]} } ->
- %% Returning a call to ourselves doesn't affect our own return
- %% type.
- D;
- #{} ->
- RetType = join([get_type(Arg, Ts) | D#d.ret_type]),
- D#d{ret_type=[RetType]}
+ %% We have no reachable self calls, so we know our argument types
+ %% can't expand any further. Filter out our reachable sites and
+ %% return.
+ [ST || {SuccArgs, _}=ST <- Acc, st_is_reachable(SuccArgs, CallArgs)]
end.
-subtract_sw_list(V, List, Ts) ->
- Ts#{ V := sub_sw_list_1(get_type(V, Ts), List, Ts) }.
+st_join_return_types([{_SuccArgs, SuccRet} | Rest], Acc0) ->
+ st_join_return_types(Rest, beam_types:join(SuccRet, Acc0));
+st_join_return_types([], Acc) ->
+ Acc.
-sub_sw_list_1(Type, [{Val,_}|T], Ts) ->
- ValType = get_type(Val, Ts),
- sub_sw_list_1(subtract(Type, ValType), T, Ts);
-sub_sw_list_1(Type, [], _Ts) ->
- Type.
+st_any_reachable([{SuccArgs, _} | SuccType], CallArgs) ->
+ case st_is_reachable(SuccArgs, CallArgs) of
+ true -> true;
+ false -> st_any_reachable(SuccType, CallArgs)
+ end;
+st_any_reachable([], _CallArgs) ->
+ false.
-update_successor_bool(#b_var{}=Var, BoolValue, S, Ts, D) ->
- case t_is_boolean(get_type(Var, Ts)) of
- true ->
- update_successor(S, Ts#{Var:=t_atom(BoolValue)}, D);
- false ->
- %% The `br` terminator is preceeded by an instruction that
- %% does not produce a boolean value, such a `new_try_tag`.
- update_successor(S, Ts, D)
- end.
+st_is_reachable([A | SuccArgs], [B | CallArgs]) ->
+ case beam_types:meet(A, B) of
+ none -> false;
+ _Other -> st_is_reachable(SuccArgs, CallArgs)
+ end;
+st_is_reachable([], []) ->
+ true.
+
+update_success_types(#b_ret{arg=Arg}, Ts, Ds, Meta, SuccTypes) ->
+ #metadata{ func_id=FuncId,
+ limit_return=Limited,
+ params=Params } = Meta,
+
+ RetType = case Ds of
+ #{ Arg := #b_set{op=call,args=[FuncId | Args]} } ->
+ {call_self, argument_types(Args, Ts)};
+ #{} ->
+ argument_type(Arg, Ts)
+ end,
+ ArgTypes = argument_types(Params, Ts),
+
+ case Limited of
+ true -> ust_limited(SuccTypes, ArgTypes, RetType);
+ false -> ust_unlimited(SuccTypes, ArgTypes, RetType)
+ end;
+update_success_types(_Last, _Ts, _Ds, _Meta, SuccTypes) ->
+ SuccTypes.
+
+%% See ?RETURN_LIMIT for details.
+ust_limited(SuccTypes, CallArgs, {call_self, SelfArgs}) ->
+ NewArgs = parallel_join(CallArgs, SelfArgs),
+ ust_limited_1(SuccTypes, NewArgs, none);
+ust_limited(SuccTypes, CallArgs, CallRet) ->
+ ust_limited_1(SuccTypes, CallArgs, CallRet).
+
+ust_limited_1([], ArgTypes, RetType) ->
+ [{ArgTypes, RetType}];
+ust_limited_1([{SuccArgs, SuccRet}], CallArgs, CallRet) ->
+ NewTypes = parallel_join(SuccArgs, CallArgs),
+ NewType = beam_types:join(SuccRet, CallRet),
+ [{NewTypes, NewType}].
+
+%% Adds a new success type, collapsing it with entries that have the same
+%% return type to keep the list short.
+ust_unlimited(SuccTypes, _CallArgs, none) ->
+ %% 'none' is implied since functions can always fail.
+ SuccTypes;
+ust_unlimited([{SuccArgs, Same} | SuccTypes], CallArgs, Same) ->
+ NewArgs = parallel_join(SuccArgs, CallArgs),
+ [{NewArgs, Same} | SuccTypes];
+ust_unlimited([SuccType | SuccTypes], CallArgs, CallRet) ->
+ [SuccType | ust_unlimited(SuccTypes, CallArgs, CallRet)];
+ust_unlimited([], CallArgs, CallRet) ->
+ [{CallArgs, CallRet}].
+
+update_successors(#b_br{bool=#b_literal{val=true},succ=Succ}=Last,
+ Ts, _Ds, Ls, _UsedOnce) ->
+ {Last, update_successor(Succ, Ts, Ls)};
+update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}=Last0,
+ Ts, Ds, Ls0, UsedOnce) ->
+ IsTempVar = cerl_sets:is_element(Bool, UsedOnce),
+ case infer_types_br(Bool, Ts, IsTempVar, Ds) of
+ {#{}=SuccTs, #{}=FailTs} ->
+ Ls1 = update_successor(Succ, SuccTs, Ls0),
+ Ls = update_successor(Fail, FailTs, Ls1),
+ {Last0, Ls};
+ {#{}=SuccTs, none} ->
+ Last = Last0#b_br{bool=#b_literal{val=true},fail=Succ},
+ {Last, update_successor(Succ, SuccTs, Ls0)};
+ {none, #{}=FailTs} ->
+ Last = Last0#b_br{bool=#b_literal{val=true},succ=Fail},
+ {Last, update_successor(Fail, FailTs, Ls0)}
+ end;
+update_successors(#b_switch{arg=#b_var{}=V,fail=Fail0,list=List0}=Last0,
+ Ts, Ds, Ls0, UsedOnce) ->
+ IsTempVar = cerl_sets:is_element(V, UsedOnce),
+
+ {List1, FailTs, Ls1} =
+ update_switch(List0, V, raw_type(V, Ts), Ts, Ds, Ls0, IsTempVar, []),
+
+ case FailTs of
+ none ->
+ %% The fail block is unreachable; swap it with one of the choices.
+ case List1 of
+ [{#b_literal{val=0},_}|_] ->
+ %% Swap with the last choice in order to keep the zero the
+ %% first choice. If the loader can substitute a jump table
+ %% instruction, then a shorter version of the jump table
+ %% instruction can be used if the first value is zero.
+ {List, [{_,Fail}]} = split(length(List1)-1, List1),
+ Last = Last0#b_switch{fail=Fail,list=List},
+ {Last, Ls1};
+ [{_,Fail}|List] ->
+ %% Swap with the first choice in the list.
+ Last = Last0#b_switch{fail=Fail,list=List},
+ {Last, Ls1}
+ end;
+ #{} ->
+ Ls = update_successor(Fail0, FailTs, Ls1),
+ Last = Last0#b_switch{list=List1},
+ {Last, Ls}
+ end;
+update_successors(#b_ret{}=Last, _Ts, _Ds, Ls, _UsedOnce) ->
+ {Last, Ls}.
-update_successor(?BADARG_BLOCK, _Ts, #d{}=D) ->
- %% We KNOW that no variables are used in the ?BADARG_BLOCK,
+update_switch([{Val, Lbl}=Sw | List],
+ V, FailType0, Ts, Ds, Ls0, IsTempVar, Acc) ->
+ FailType = beam_types:subtract(FailType0, raw_type(Val, Ts)),
+ case infer_types_switch(V, Val, Ts, IsTempVar, Ds) of
+ none ->
+ update_switch(List, V, FailType, Ts, Ds, Ls0, IsTempVar, Acc);
+ SwTs ->
+ Ls = update_successor(Lbl, SwTs, Ls0),
+ update_switch(List, V, FailType, Ts, Ds, Ls, IsTempVar, [Sw | Acc])
+ end;
+update_switch([], _V, none, _Ts, _Ds, Ls, _IsTempVar, Acc) ->
+ %% Fail label is unreachable.
+ {reverse(Acc), none, Ls};
+update_switch([], V, FailType, Ts, Ds, Ls, IsTempVar, Acc) ->
+ %% Fail label is reachable, see if we can narrow the type down further.
+ FailTs = case beam_types:get_singleton_value(FailType) of
+ {ok, Value} ->
+ %% This is the only possible value at the fail label, so
+ %% we can infer types as if we matched it directly.
+ Lit = #b_literal{val=Value},
+ infer_types_switch(V, Lit, Ts, IsTempVar, Ds);
+ error when IsTempVar ->
+ ts_remove_var(V, Ts);
+ error ->
+ Ts#{ V := FailType }
+ end,
+ {reverse(Acc), FailTs, Ls}.
+
+update_successor(?EXCEPTION_BLOCK, _Ts, Ls) ->
+ %% We KNOW that no variables are used in the ?EXCEPTION_BLOCK,
%% so there is no need to update the type information. That
%% can be a huge timesaver for huge functions.
- D;
-update_successor(S, Ts0, #d{ls=Ls}=D) ->
+ Ls;
+update_successor(S, Ts0, Ls) ->
case Ls of
- #{S:=Ts1} ->
+ #{ S := Ts1 } ->
Ts = join_types(Ts0, Ts1),
- D#d{ls=Ls#{S:=Ts}};
+ Ls#{ S := Ts };
#{} ->
- D#d{ls=Ls#{S=>Ts0}}
+ Ls#{ S => Ts0 }
end.
-update_types(#b_set{op=Op,dst=Dst,args=Args}, Ts, Ds) ->
- T = type(Op, Args, Ts, Ds),
+update_types(#b_set{op=Op,dst=Dst,anno=Anno,args=Args}, Ts, Ds) ->
+ T = type(Op, Args, Anno, Ts, Ds),
Ts#{Dst=>T}.
-type(phi, Args, Ts, _Ds) ->
- Types = [get_type(A, Ts) || {A,_} <- Args],
- join(Types);
-type({bif,'band'}, Args, Ts, _Ds) ->
- band_type(Args, Ts);
-type({bif,Bif}, Args, Ts, _Ds) ->
- case bif_type(Bif, Args) of
- number ->
- arith_op_type(Args, Ts);
- Type ->
- Type
+type(phi, Args, _Anno, Ts, _Ds) ->
+ Types = [raw_type(A, Ts) || {A,_} <- Args],
+ beam_types:join(Types);
+type({bif,Bif}, Args, _Anno, Ts, _Ds) ->
+ ArgTypes = normalized_types(Args, Ts),
+ {RetType, _, _} = beam_call_types:types(erlang, Bif, ArgTypes),
+ RetType;
+type(bs_init, _Args, _Anno, _Ts, _Ds) ->
+ #t_bitstring{};
+type(bs_extract, [Ctx], _Anno, _Ts, Ds) ->
+ #b_set{op=bs_match,args=Args} = map_get(Ctx, Ds),
+ bs_match_type(Args);
+type(bs_start_match, [_, Src], _Anno, Ts, _Ds) ->
+ case beam_types:meet(#t_bs_matchable{}, raw_type(Src, Ts)) of
+ none ->
+ none;
+ T ->
+ Unit = beam_types:get_bs_matchable_unit(T),
+ #t_bs_context{tail_unit=Unit}
end;
-type(bs_init, _Args, _Ts, _Ds) ->
- {binary, 1};
-type(bs_extract, [Ctx], Ts, _Ds) ->
- #t_bs_match{type=Type} = get_type(Ctx, Ts),
- Type;
-type(bs_match, Args, _Ts, _Ds) ->
- #t_bs_match{type=bs_match_type(Args)};
-type(bs_get_tail, _Args, _Ts, _Ds) ->
- {binary, 1};
+type(bs_match, [#b_literal{val=binary}, Ctx, _Flags,
+ #b_literal{val=all}, #b_literal{val=OpUnit}],
+ _Anno, Ts, _Ds) ->
+
+ %% This is an explicit tail unit test which does not advance the match
+ %% position.
+ CtxType = raw_type(Ctx, Ts),
+ OpType = #t_bs_context{tail_unit=OpUnit},
+
+ beam_types:meet(CtxType, OpType);
+type(bs_match, Args, _Anno, Ts, _Ds) ->
+ [_, Ctx | _] = Args,
+
+ %% Matches advance the current position without testing the tail unit. We
+ %% try to retain unit information by taking the GCD of our current unit and
+ %% the increments we know the match will advance by.
+ #t_bs_context{tail_unit=CtxUnit} = raw_type(Ctx, Ts),
+ OpUnit = bs_match_stride(Args, Ts),
+
+ #t_bs_context{tail_unit=gcd(OpUnit, CtxUnit)};
+type(bs_get_tail, [Ctx], _Anno, Ts, _Ds) ->
+ #t_bs_context{tail_unit=Unit} = raw_type(Ctx, Ts),
+ #t_bitstring{size_unit=Unit};
type(call, [#b_remote{mod=#b_literal{val=Mod},
- name=#b_literal{val=Name}}|Args], Ts, _Ds) ->
- case {Mod,Name,Args} of
- {erlang,setelement,[Pos,Tuple,Arg]} ->
- case {get_type(Pos, Ts),get_type(Tuple, Ts)} of
- {#t_integer{elements={Index,Index}},
- #t_tuple{elements=Es0,size=Size}=T} ->
- %% This is an exact index, update the type of said element
- %% or return 'none' if it's known to be out of bounds.
- Es = set_element_type(Index, get_type(Arg, Ts), Es0),
- case T#t_tuple.exact of
- false ->
- T#t_tuple{size=max(Index, Size),elements=Es};
- true when Index =< Size ->
- T#t_tuple{elements=Es};
- true ->
- none
- end;
- {#t_integer{elements={Min,_}}=IntType,
- #t_tuple{elements=Es0,size=Size}=T} ->
- %% Remove type information for all indices that
- %% falls into the range of the integer.
- Es = remove_element_info(IntType, Es0),
- case T#t_tuple.exact of
- false ->
- T#t_tuple{elements=Es,size=max(Min, Size)};
- true when Min =< Size ->
- T#t_tuple{elements=Es,size=Size};
- true ->
- none
- end;
- {_,#t_tuple{}=T} ->
- %% Position unknown, so we have to discard all element
- %% information.
- T#t_tuple{elements=#{}};
- {#t_integer{elements={Min,_Max}},_} ->
- #t_tuple{size=Min};
- {_,_} ->
- #t_tuple{}
- end;
- {erlang,'++',[LHS,RHS]} ->
- LType = get_type(LHS, Ts),
- RType = get_type(RHS, Ts),
- case LType =:= cons orelse RType =:= cons of
- true ->
- cons;
- false ->
- %% `[] ++ RHS` yields RHS, even if RHS is not a list.
- join(list, RType)
- end;
- {erlang,'--',[_,_]} ->
- list;
- {lists,F,Args} ->
- Types = get_types(Args, Ts),
- lists_function_type(F, Types);
- {math,_,_} ->
- case is_math_bif(Name, length(Args)) of
- false -> any;
- true -> float
- end;
- {_,_,_} ->
- case erl_bifs:is_exit_bif(Mod, Name, length(Args)) of
- true -> none;
- false -> any
- end
+ name=#b_literal{val=Name}}|Args], _Anno, Ts, _Ds) ->
+ ArgTypes = normalized_types(Args, Ts),
+ {RetType, _, _} = beam_call_types:types(Mod, Name, ArgTypes),
+ RetType;
+type(call, [#b_remote{} | _Args], _Anno, _Ts, _Ds) ->
+ %% Remote call with variable Module and/or Function.
+ any;
+type(call, [#b_local{} | _Args], Anno, _Ts, _Ds) ->
+ case Anno of
+ #{ result_type := Type } -> Type;
+ #{} -> any
end;
-type(get_tuple_element, [Tuple, Offset], Ts, _Ds) ->
- #t_tuple{size=Size,elements=Es} = get_type(Tuple, Ts),
+type(call, [#b_var{} | _Args], Anno, _Ts, _Ds) ->
+ case Anno of
+ #{ result_type := Type } -> Type;
+ #{} -> any
+ end;
+type(call, [#b_literal{} | _Args], _Anno, _Ts, _Ds) ->
+ none;
+type(get_hd, [Src], _Anno, Ts, _Ds) ->
+ SrcType = #t_cons{} = normalized_type(Src, Ts), %Assertion.
+ {RetType, _, _} = beam_call_types:types(erlang, hd, [SrcType]),
+ RetType;
+type(get_tl, [Src], _Anno, Ts, _Ds) ->
+ SrcType = #t_cons{} = normalized_type(Src, Ts), %Assertion.
+ {RetType, _, _} = beam_call_types:types(erlang, tl, [SrcType]),
+ RetType;
+type(get_map_element, [_, _]=Args0, _Anno, Ts, _Ds) ->
+ [#t_map{}=Map, Key] = normalized_types(Args0, Ts), %Assertion.
+ {RetType, _, _} = beam_call_types:types(erlang, map_get, [Key, Map]),
+ RetType;
+type(get_tuple_element, [Tuple, Offset], _Anno, Ts, _Ds) ->
+ #t_tuple{size=Size,elements=Es} = normalized_type(Tuple, Ts),
#b_literal{val=N} = Offset,
true = Size > N, %Assertion.
- get_element_type(N + 1, Es);
-type(is_nonempty_list, [_], _Ts, _Ds) ->
- t_boolean();
-type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Ts, _Ds) ->
- t_boolean();
-type(put_map, _Args, _Ts, _Ds) ->
- map;
-type(put_list, _Args, _Ts, _Ds) ->
- cons;
-type(put_tuple, Args, Ts, _Ds) ->
+ beam_types:get_tuple_element(N + 1, Es);
+type(has_map_field, [_, _]=Args0, _Anno, Ts, _Ds) ->
+ [#t_map{}=Map, Key] = normalized_types(Args0, Ts), %Assertion.
+ {RetType, _, _} = beam_call_types:types(erlang, is_map_key, [Key, Map]),
+ RetType;
+type(is_nonempty_list, [_], _Anno, _Ts, _Ds) ->
+ beam_types:make_boolean();
+type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Anno, _Ts, _Ds) ->
+ beam_types:make_boolean();
+type(make_fun, [#b_local{arity=TotalArity} | Env], Anno, _Ts, _Ds) ->
+ RetType = case Anno of
+ #{ result_type := Type } -> Type;
+ #{} -> any
+ end,
+ #t_fun{arity=TotalArity - length(Env), type=RetType};
+type(put_map, [_Kind, Map | Ss], _Anno, Ts, _Ds) ->
+ put_map_type(Map, Ss, Ts);
+type(put_list, [Head, Tail], _Anno, Ts, _Ds) ->
+ HeadType = raw_type(Head, Ts),
+ TailType = raw_type(Tail, Ts),
+ beam_types:make_cons(HeadType, TailType);
+type(put_tuple, Args, _Anno, Ts, _Ds) ->
{Es, _} = foldl(fun(Arg, {Es0, Index}) ->
- Type = get_type(Arg, Ts),
- Es = set_element_type(Index, Type, Es0),
- {Es, Index + 1}
+ Type = raw_type(Arg, Ts),
+ Es = beam_types:set_tuple_element(Index, Type, Es0),
+ {Es, Index + 1}
end, {#{}, 1}, Args),
#t_tuple{exact=true,size=length(Args),elements=Es};
-type(succeeded, [#b_var{}=Src], Ts, Ds) ->
- case maps:get(Src, Ds) of
- #b_set{op={bif,Bif},args=BifArgs} ->
- Types = get_types(BifArgs, Ts),
- case {Bif,Types} of
- {BoolOp,[T1,T2]} when BoolOp =:= 'and'; BoolOp =:= 'or' ->
- case t_is_boolean(T1) andalso t_is_boolean(T2) of
- true -> t_atom(true);
- false -> t_boolean()
- end;
- {byte_size,[{binary,_}]} ->
- t_atom(true);
- {bit_size,[{binary,_}]} ->
- t_atom(true);
- {map_size,[map]} ->
- t_atom(true);
- {'not',[Type]} ->
- case t_is_boolean(Type) of
- true -> t_atom(true);
- false -> t_boolean()
- end;
- {size,[{binary,_}]} ->
- t_atom(true);
- {tuple_size,[#t_tuple{}]} ->
- t_atom(true);
- {_,_} ->
- t_boolean()
- end;
- #b_set{op=get_hd} ->
- t_atom(true);
- #b_set{op=get_tl} ->
- t_atom(true);
- #b_set{op=get_tuple_element} ->
- t_atom(true);
- #b_set{op=wait} ->
- t_atom(false);
- #b_set{} ->
- t_boolean()
- end;
-type(succeeded, [#b_literal{}], _Ts, _Ds) ->
- t_atom(true);
-type(_, _, _, _) -> any.
-
-arith_op_type(Args, Ts) ->
- Types = get_types(Args, Ts),
- foldl(fun(#t_integer{}, unknown) -> t_integer();
- (#t_integer{}, number) -> number;
- (#t_integer{}, float) -> float;
- (#t_integer{}, #t_integer{}) -> t_integer();
- (float, unknown) -> float;
- (float, #t_integer{}) -> float;
- (float, number) -> float;
- (number, unknown) -> number;
- (number, #t_integer{}) -> number;
- (number, float) -> float;
- (any, _) -> number;
- (Same, Same) -> Same;
- (_, _) -> none
- end, unknown, Types).
-
-lists_function_type(F, Types) ->
- case {F,Types} of
- %% Functions that return booleans.
- {all,[_,_]} ->
- t_boolean();
- {any,[_,_]} ->
- t_boolean();
- {keymember,[_,_,_]} ->
- t_boolean();
- {member,[_,_]} ->
- t_boolean();
- {prefix,[_,_]} ->
- t_boolean();
- {suffix,[_,_]} ->
- t_boolean();
-
- %% Functions that return lists.
- {dropwhile,[_,_]} ->
- list;
- {duplicate,[_,_]} ->
- list;
- {filter,[_,_]} ->
- list;
- {flatten,[_]} ->
- list;
- {map,[_Fun,List]} ->
- same_length_type(List);
- {MapFold,[_Fun,_Acc,List]} when MapFold =:= mapfoldl;
- MapFold =:= mapfoldr ->
- #t_tuple{size=2,exact=true,
- elements=#{1=>same_length_type(List)}};
- {partition,[_,_]} ->
- t_two_tuple(list, list);
- {reverse,[List]} ->
- same_length_type(List);
- {sort,[List]} ->
- same_length_type(List);
- {splitwith,[_,_]} ->
- t_two_tuple(list, list);
- {takewhile,[_,_]} ->
- list;
- {unzip,[List]} ->
- ListType = same_length_type(List),
- t_two_tuple(ListType, ListType);
- {usort,[List]} ->
- same_length_type(List);
- {zip,[_,_]} ->
- list;
- {zipwith,[_,_,_]} ->
- list;
- {_,_} ->
- any
- end.
+type(_, _, _, _, _) -> any.
-%% For a lists function that return a list of the same
-%% length as the input list, return the type of the list.
-same_length_type(cons) -> cons;
-same_length_type(nil) -> nil;
-same_length_type(_) -> list.
+put_map_type(Map, Ss, Ts) ->
+ pmt_1(Ss, Ts, normalized_type(Map, Ts)).
-t_two_tuple(Type1, Type2) ->
- #t_tuple{size=2,exact=true,
- elements=#{1=>Type1,2=>Type2}}.
-
-%% will_succeed(TestOperation, Type) -> yes|no|maybe.
-%% Test whether TestOperation applied to an argument of type Type
-%% will succeed. Return yes, no, or maybe.
-%%
-%% Type is a type as described in the comment for verified_type/1 at
-%% the very end of this file, but it will *never* be 'any'.
-
-will_succeed(is_atom, Type) ->
- case Type of
- #t_atom{} -> yes;
- _ -> no
- end;
-will_succeed(is_binary, Type) ->
- case Type of
- {binary,U} when U rem 8 =:= 0 -> yes;
- {binary,_} -> maybe;
- _ -> no
- end;
-will_succeed(is_bitstring, Type) ->
- case Type of
- {binary,_} -> yes;
- _ -> no
- end;
-will_succeed(is_boolean, Type) ->
- case Type of
- #t_atom{elements=any} ->
- maybe;
- #t_atom{elements=Es} ->
- case t_is_boolean(Type) of
- true ->
- yes;
- false ->
- case any(fun is_boolean/1, Es) of
- true -> maybe;
- false -> no
- end
- end;
- _ ->
- no
- end;
-will_succeed(is_float, Type) ->
- case Type of
- float -> yes;
- number -> maybe;
- _ -> no
- end;
-will_succeed(is_integer, Type) ->
- case Type of
- #t_integer{} -> yes;
- number -> maybe;
- _ -> no
- end;
-will_succeed(is_list, Type) ->
- case Type of
- list -> yes;
- cons -> yes;
- _ -> no
- end;
-will_succeed(is_map, Type) ->
- case Type of
- map -> yes;
- _ -> no
- end;
-will_succeed(is_number, Type) ->
- case Type of
- float -> yes;
- #t_integer{} -> yes;
- number -> yes;
- _ -> no
- end;
-will_succeed(is_tuple, Type) ->
- case Type of
- #t_tuple{} -> yes;
- _ -> no
- end;
-will_succeed(_, _) -> maybe.
-
-
-band_type([Other,#b_literal{val=Int}], Ts) when is_integer(Int) ->
- band_type_1(Int, Other, Ts);
-band_type([_,_], _) -> t_integer().
+pmt_1([Key0, Value0 | Ss], Ts, Acc0) ->
+ Key = normalized_type(Key0, Ts),
+ Value = normalized_type(Value0, Ts),
+ {Acc, _, _} = beam_call_types:types(maps, put, [Key, Value, Acc0]),
+ pmt_1(Ss, Ts, Acc);
+pmt_1([], _Ts, Acc) ->
+ Acc.
-band_type_1(Int, OtherSrc, Ts) ->
- Type = band_type_2(Int, 0),
- OtherType = get_type(OtherSrc, Ts),
- meet(Type, OtherType).
+%% We seldom know how far a match operation may advance, but we can often tell
+%% which increment it will advance by.
+bs_match_stride([#b_literal{val=Type} | Args], Ts) ->
+ bs_match_stride(Type, Args, Ts).
-band_type_2(N, Bits) when Bits < 64 ->
- case 1 bsl Bits of
- P when P =:= N + 1 ->
- t_integer(0, N);
- P when P > N + 1 ->
- t_integer();
+bs_match_stride(_, [_,_,Size,#b_literal{val=Unit}], Ts) ->
+ case raw_type(Size, Ts) of
+ #t_integer{elements={Sz, Sz}} when is_integer(Sz) ->
+ Sz * Unit;
_ ->
- band_type_2(N, Bits+1)
+ Unit
end;
-band_type_2(_, _) ->
- %% Negative or large positive number. Give up.
- t_integer().
+bs_match_stride(string, [_,#b_literal{val=String}], _) ->
+ bit_size(String);
+bs_match_stride(utf8, _, _) ->
+ 8;
+bs_match_stride(utf16, _, _) ->
+ 16;
+bs_match_stride(utf32, _, _) ->
+ 32;
+bs_match_stride(_, _, _) ->
+ 1.
+
+-define(UNICODE_MAX, (16#10FFFF)).
bs_match_type([#b_literal{val=Type}|Args]) ->
bs_match_type(Type, Args).
bs_match_type(binary, Args) ->
[_,_,_,#b_literal{val=U}] = Args,
- {binary,U};
+ #t_bitstring{size_unit=U};
bs_match_type(float, _) ->
- float;
+ #t_float{};
bs_match_type(integer, Args) ->
case Args of
[_,
@@ -1180,194 +1694,48 @@ bs_match_type(integer, Args) ->
NumBits = Size * Unit,
case member(unsigned, Flags) of
true ->
- t_integer(0, (1 bsl NumBits)-1);
+ beam_types:make_integer(0, (1 bsl NumBits)-1);
false ->
%% Signed integer. Don't bother.
- t_integer()
+ #t_integer{}
end;
[_|_] ->
- t_integer()
+ #t_integer{}
end;
bs_match_type(skip, _) ->
any;
bs_match_type(string, _) ->
any;
bs_match_type(utf8, _) ->
- ?UNICODE_INT;
+ beam_types:make_integer(0, ?UNICODE_MAX);
bs_match_type(utf16, _) ->
- ?UNICODE_INT;
+ beam_types:make_integer(0, ?UNICODE_MAX);
bs_match_type(utf32, _) ->
- ?UNICODE_INT.
-
-simplify_switch_atom(#t_atom{elements=Atoms}, #b_switch{list=List0}=Sw) ->
- case sort([A || {#b_literal{val=A},_} <- List0]) of
- Atoms ->
- %% All possible atoms are included in the list. The
- %% failure label will never be used.
- [{_,Fail}|List] = List0,
- Sw#b_switch{fail=Fail,list=List};
- _ ->
- Sw
- end.
-
-simplify_switch_int(#b_switch{list=List0}=Sw, {Min,Max}) ->
- List1 = sort(List0),
- Vs = [V || {#b_literal{val=V},_} <- List1],
- case eq_ranges(Vs, Min, Max) of
- true ->
- {_,LastL} = last(List1),
- List = droplast(List1),
- Sw#b_switch{fail=LastL,list=List};
- false ->
- Sw
- end.
-
-eq_ranges([H], H, H) -> true;
-eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max);
-eq_ranges(_, _, _) -> false.
-
-simplify_is_record(I, #t_tuple{exact=Exact,
- size=Size,
- elements=Es},
- RecSize, RecTag, Ts) ->
- TagType = maps:get(1, Es, any),
- TagMatch = case get_literal_from_type(TagType) of
- #b_literal{}=RecTag -> yes;
- #b_literal{} -> no;
- none ->
- %% Is it at all possible for the tag to match?
- case meet(get_type(RecTag, Ts), TagType) of
- none -> no;
- _ -> maybe
- end
- end,
- if
- Size =/= RecSize, Exact; Size > RecSize; TagMatch =:= no ->
- #b_literal{val=false};
- Size =:= RecSize, Exact, TagMatch =:= yes ->
- #b_literal{val=true};
- true ->
- I
- end;
-simplify_is_record(I, any, _Size, _Tag, _Ts) ->
- I;
-simplify_is_record(_I, _Type, _Size, _Tag, _Ts) ->
- #b_literal{val=false}.
-
-simplify_switch_bool(#b_switch{arg=B,fail=Fail,list=List0}, Ts, Ds) ->
- FalseVal = #b_literal{val=false},
- TrueVal = #b_literal{val=true},
- List1 = List0 ++ [{FalseVal,Fail},{TrueVal,Fail}],
- {_,FalseLbl} = keyfind(FalseVal, 1, List1),
- {_,TrueLbl} = keyfind(TrueVal, 1, List1),
- Br = beam_ssa:normalize(#b_br{bool=B,succ=TrueLbl,fail=FalseLbl}),
- simplify_not(Br, Ts, Ds).
-
-simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
- case Ds of
- #{V:=#b_set{op={bif,'not'},args=[Bool]}} ->
- case t_is_boolean(get_type(Bool, Ts)) of
- true ->
- Br = Br0#b_br{bool=Bool,succ=Fail,fail=Succ},
- beam_ssa:normalize(Br);
- false ->
- Br0
- end;
- #{} ->
- Br0
- end;
-simplify_not(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) -> Br.
+ beam_types:make_integer(0, ?UNICODE_MAX).
-%%%
-%%% Calculate the set of variables that are only used once in the
-%%% terminator of the block that defines them. That will allow us to
-%%% discard type information for variables that will never be
-%%% referenced by the successor blocks, potentially improving
-%%% compilation times.
-%%%
+normalized_types(Values, Ts) ->
+ [normalized_type(Val, Ts) || Val <- Values].
-used_once(Linear, Args) ->
- Map0 = used_once_1(reverse(Linear), #{}),
- Map = maps:without(Args, Map0),
- cerl_sets:from_list(maps:keys(Map)).
+normalized_type(V, Ts) ->
+ beam_types:normalize(raw_type(V, Ts)).
-used_once_1([{L,#b_blk{is=Is,last=Last}}|Bs], Uses0) ->
- Uses1 = used_once_last_uses(beam_ssa:used(Last), L, Uses0),
- Uses = used_once_2(reverse(Is), L, Uses1),
- used_once_1(Bs, Uses);
-used_once_1([], Uses) -> Uses.
+argument_types(Values, Ts) ->
+ [argument_type(Val, Ts) || Val <- Values].
-used_once_2([#b_set{dst=Dst}=I|Is], L, Uses0) ->
- Uses = used_once_uses(beam_ssa:used(I), L, Uses0),
- case Uses of
- #{Dst:=[L]} ->
- used_once_2(Is, L, Uses);
- #{} ->
- %% Used more than once or used once in
- %% in another block.
- used_once_2(Is, L, maps:remove(Dst, Uses))
- end;
-used_once_2([], _, Uses) -> Uses.
-
-used_once_uses([V|Vs], L, Uses) ->
- case Uses of
- #{V:=more_than_once} ->
- used_once_uses(Vs, L, Uses);
- #{} ->
- %% Already used or first use is not in
- %% a terminator.
- used_once_uses(Vs, L, Uses#{V=>more_than_once})
- end;
-used_once_uses([], _, Uses) -> Uses.
+-spec argument_type(beam_ssa:value(), type_db()) -> type().
-used_once_last_uses([V|Vs], L, Uses) ->
- case Uses of
- #{V:=[_]} ->
- %% Second time this variable is used.
- used_once_last_uses(Vs, L, Uses#{V:=more_than_once});
- #{V:=more_than_once} ->
- %% Used at least twice before.
- used_once_last_uses(Vs, L, Uses);
- #{} ->
- %% First time this variable is used.
- used_once_last_uses(Vs, L, Uses#{V=>[L]})
- end;
-used_once_last_uses([], _, Uses) -> Uses.
+argument_type(V, Ts) ->
+ beam_types:limit_depth(raw_type(V, Ts)).
+raw_types(Values, Ts) ->
+ [raw_type(Val, Ts) || Val <- Values].
-get_types(Values, Ts) ->
- [get_type(Val, Ts) || Val <- Values].
--spec get_type(beam_ssa:value(), type_db()) -> type().
+-spec raw_type(beam_ssa:value(), type_db()) -> type().
-get_type(#b_var{}=V, Ts) ->
- #{V:=T} = Ts,
- T;
-get_type(#b_literal{val=Val}, _Ts) ->
- if
- is_atom(Val) ->
- t_atom(Val);
- is_float(Val) ->
- float;
- is_integer(Val) ->
- t_integer(Val);
- is_list(Val), Val =/= [] ->
- cons;
- is_map(Val) ->
- map;
- Val =:= {} ->
- #t_tuple{exact=true};
- is_tuple(Val) ->
- {Es, _} = foldl(fun(E, {Es0, Index}) ->
- Type = get_type(#b_literal{val=E}, #{}),
- Es = set_element_type(Index, Type, Es0),
- {Es, Index + 1}
- end, {#{}, 1}, tuple_to_list(Val)),
- #t_tuple{exact=true,size=tuple_size(Val),elements=Es};
- Val =:= [] ->
- nil;
- true ->
- any
- end.
+raw_type(#b_literal{val=Value}, _Ts) ->
+ beam_types:make_type_from_value(Value);
+raw_type(V, Ts) ->
+ map_get(V, Ts).
%% infer_types(Var, Types, #d{}) -> {SuccTypes,FailTypes}
%% Looking at the expression that defines the variable Var, infer
@@ -1390,10 +1758,107 @@ get_type(#b_literal{val=Val}, _Ts) ->
%% 'cons' would give 'nil' as the only possible type. The result of the
%% subtraction for L will be added to FailTypes.
-infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) ->
+infer_types_br(#b_var{}=V, Ts, IsTempVar, Ds) ->
#{V:=#b_set{op=Op,args=Args}} = Ds,
- PosTypes0 = infer_type(Op, Args, Ds),
- NegTypes0 = infer_type_negative(Op, Args, Ds),
+
+ {PosTypes, NegTypes} = infer_type(Op, Args, Ts, Ds),
+
+ SuccTs0 = meet_types(PosTypes, Ts),
+ FailTs0 = subtract_types(NegTypes, Ts),
+
+ case IsTempVar of
+ true ->
+ %% The branch variable is defined in this block and is only
+ %% referenced by this terminator. Therefore, there is no need to
+ %% include it in the type database passed on to the successors of
+ %% of this block.
+ SuccTs = ts_remove_var(V, SuccTs0),
+ FailTs = ts_remove_var(V, FailTs0),
+ {SuccTs, FailTs};
+ false ->
+ SuccTs = infer_br_value(V, true, SuccTs0),
+ FailTs = infer_br_value(V, false, FailTs0),
+ {SuccTs, FailTs}
+ end.
+
+infer_br_value(_V, _Bool, none) ->
+ none;
+infer_br_value(V, Bool, NewTs) ->
+ #{ V := T } = NewTs,
+ case beam_types:is_boolean_type(T) of
+ true ->
+ NewTs#{ V := beam_types:make_atom(Bool) };
+ false ->
+ %% V is a try/catch tag or similar, leave it alone.
+ NewTs
+ end.
+
+infer_types_switch(V, Lit, Ts0, IsTempVar, Ds) ->
+ {PosTypes, _} = infer_type({bif,'=:='}, [V, Lit], Ts0, Ds),
+ Ts = meet_types(PosTypes, Ts0),
+ case IsTempVar of
+ true -> ts_remove_var(V, Ts);
+ false -> Ts
+ end.
+
+ts_remove_var(_V, none) -> none;
+ts_remove_var(V, Ts) -> maps:remove(V, Ts).
+
+infer_type(succeeded, [#b_var{}=Src], Ts, Ds) ->
+ #b_set{op=Op,args=Args} = maps:get(Src, Ds),
+ infer_success_type(Op, Args, Ts, Ds);
+
+%% Type tests are handled separately from other BIFs as we're inferring types
+%% based on their result, so we know that subtraction is safe even if we're
+%% not branching on 'succeeded'.
+infer_type(is_tagged_tuple, [#b_var{}=Src,#b_literal{val=Size},
+ #b_literal{}=Tag], _Ts, _Ds) ->
+ Es = beam_types:set_tuple_element(1, raw_type(Tag, #{}), #{}),
+ T = {Src,#t_tuple{exact=true,size=Size,elements=Es}},
+ {[T], [T]};
+infer_type(is_nonempty_list, [#b_var{}=Src], _Ts, _Ds) ->
+ T = {Src,#t_cons{}},
+ {[T], [T]};
+infer_type({bif,is_atom}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_atom{}},
+ {[T], [T]};
+infer_type({bif,is_binary}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_bitstring{size_unit=8}},
+ {[T], [T]};
+infer_type({bif,is_bitstring}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_bitstring{}},
+ {[T], [T]};
+infer_type({bif,is_boolean}, [Arg], _Ts, _Ds) ->
+ T = {Arg, beam_types:make_boolean()},
+ {[T], [T]};
+infer_type({bif,is_float}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_float{}},
+ {[T], [T]};
+infer_type({bif,is_integer}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_integer{}},
+ {[T], [T]};
+infer_type({bif,is_list}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_list{}},
+ {[T], [T]};
+infer_type({bif,is_map}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_map{}},
+ {[T], [T]};
+infer_type({bif,is_number}, [Arg], _Ts, _Ds) ->
+ T = {Arg, number},
+ {[T], [T]};
+infer_type({bif,is_tuple}, [Arg], _Ts, _Ds) ->
+ T = {Arg, #t_tuple{}},
+ {[T], [T]};
+infer_type({bif,'=:='}, [#b_var{}=LHS,#b_var{}=RHS], Ts, _Ds) ->
+ %% As an example, assume that L1 is known to be 'list', and L2 is
+ %% known to be 'cons'. Then if 'L1 =:= L2' evaluates to 'true', it can
+ %% be inferred that L1 is 'cons' (the meet of 'cons' and 'list').
+ LType = raw_type(LHS, Ts),
+ RType = raw_type(RHS, Ts),
+ Type = beam_types:meet(LType, RType),
+
+ PosTypes = [{V,Type} || {V, OrigType} <- [{LHS, LType}, {RHS, RType}],
+ OrigType =/= Type],
%% We must be careful with types inferred from '=:='.
%%
@@ -1404,219 +1869,72 @@ infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) ->
%%
%% However, it is safe to subtract a type inferred from '=:=' if
%% it is single-valued, e.g. if it is [] or the atom 'true'.
+ NegTypes = case beam_types:is_singleton_type(Type) of
+ true -> PosTypes;
+ false -> []
+ end,
- EqTypes = infer_eq_type(Op, Args, Ts, Ds),
- NegTypes1 = [P || {_,T}=P <- EqTypes, is_singleton_type(T)],
+ {PosTypes, NegTypes};
+infer_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ts, Ds) ->
+ Def = maps:get(Src, Ds),
+ LitType = raw_type(Lit, Ts),
+ PosTypes = [{Src, LitType} | infer_eq_lit(Def, LitType)],
- PosTypes = EqTypes ++ PosTypes0,
- SuccTs = meet_types(PosTypes, Ts),
+ %% Subtraction is only safe if LitType is single-valued.
+ NegTypes = case beam_types:is_singleton_type(LitType) of
+ true -> PosTypes;
+ false -> []
+ end,
- NegTypes = NegTypes0 ++ NegTypes1,
- FailTs = subtract_types(NegTypes, Ts),
+ {PosTypes, NegTypes};
+infer_type(_Op, _Args, _Ts, _Ds) ->
+ {[], []}.
- {SuccTs,FailTs}.
+infer_success_type({bif,Op}, Args, Ts, _Ds) ->
+ ArgTypes = normalized_types(Args, Ts),
-infer_types_switch(V, Lit, Ts, #d{ds=Ds}) ->
- Types = infer_eq_type({bif,'=:='}, [V, Lit], Ts, Ds),
- meet_types(Types, Ts).
+ {_, PosTypes0, CanSubtract} = beam_call_types:types(erlang, Op, ArgTypes),
+ PosTypes = [T || {#b_var{},_}=T <- zip(Args, PosTypes0)],
-infer_eq_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ts, Ds) ->
- Def = maps:get(Src, Ds),
- Type = get_type(Lit, Ts),
- [{Src,Type} | infer_eq_lit(Def, Lit)];
-infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) ->
- %% As an example, assume that L1 is known to be 'list', and L2 is
- %% known to be 'cons'. Then if 'L1 =:= L2' evaluates to 'true', it can
- %% be inferred that L1 is 'cons' (the meet of 'cons' and 'list').
- Type0 = get_type(Arg0, Ts),
- Type1 = get_type(Arg1, Ts),
- Type = meet(Type0, Type1),
- [{V,MeetType} ||
- {V,OrigType,MeetType} <-
- [{Arg0,Type0,Type},{Arg1,Type1,Type}],
- OrigType =/= MeetType];
-infer_eq_type(_Op, _Args, _Ts, _Ds) ->
- [].
+ case CanSubtract of
+ true -> {PosTypes, PosTypes};
+ false -> {PosTypes, []}
+ end;
+infer_success_type(call, [#b_var{}=Fun|Args], _Ts, _Ds) ->
+ T = {Fun, #t_fun{arity=length(Args)}},
+ {[T], []};
+infer_success_type(bs_start_match, [_, #b_var{}=Src], _Ts, _Ds) ->
+ T = {Src,#t_bs_matchable{}},
+ {[T], [T]};
+infer_success_type(bs_match, [#b_literal{val=binary},
+ Ctx, _Flags,
+ #b_literal{val=all},
+ #b_literal{val=OpUnit}],
+ _Ts, _Ds) ->
+ %% This is an explicit tail unit test which does not advance the match
+ %% position, so we know that Ctx has the same unit.
+ T = {Ctx, #t_bs_context{tail_unit=OpUnit}},
+ {[T], [T]};
+infer_success_type(_Op, _Args, _Ts, _Ds) ->
+ {[], []}.
infer_eq_lit(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]},
- #b_literal{val=Size}) when is_integer(Size) ->
+ #t_integer{elements={Size,Size}}) ->
[{Tuple,#t_tuple{exact=true,size=Size}}];
infer_eq_lit(#b_set{op=get_tuple_element,
args=[#b_var{}=Tuple,#b_literal{val=N}]},
- #b_literal{}=Lit) ->
+ LitType) ->
Index = N + 1,
- Es = set_element_type(Index, get_type(Lit, #{}), #{}),
- [{Tuple,#t_tuple{size=Index,elements=Es}}];
-infer_eq_lit(_, _) -> [].
-
-infer_type_negative(Op, Args, Ds) ->
- case is_negative_inference_safe(Op, Args) of
- true ->
- infer_type(Op, Args, Ds);
- false ->
- []
- end.
-
-%% Conservative list of instructions for which negative
-%% inference is safe.
-is_negative_inference_safe(is_nonempty_list, _Args) -> true;
-is_negative_inference_safe(_, _) -> false.
-
-infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) ->
- if
- is_integer(Pos), 1 =< Pos ->
- [{Tuple,#t_tuple{size=Pos}}];
- true ->
+ case beam_types:set_tuple_element(Index, LitType, #{}) of
+ #{ Index := _ }=Es ->
+ [{Tuple,#t_tuple{size=Index,elements=Es}}];
+ #{} ->
+ %% Index was above the element limit; subtraction is not safe.
[]
end;
-infer_type({bif,element}, [#b_var{}=Position,#b_var{}=Tuple], _Ds) ->
- [{Position,t_integer()},{Tuple,#t_tuple{}}];
-infer_type({bif,Bif}, [#b_var{}=Src]=Args, _Ds) ->
- case inferred_bif_type(Bif, Args) of
- any -> [];
- T -> [{Src,T}]
- end;
-infer_type({bif,binary_part}, [#b_var{}=Src,_], _Ds) ->
- [{Src,{binary,8}}];
-infer_type({bif,is_map_key}, [_,#b_var{}=Src], _Ds) ->
- [{Src,map}];
-infer_type({bif,map_get}, [_,#b_var{}=Src], _Ds) ->
- [{Src,map}];
-infer_type({bif,Bif}, [_,_]=Args, _Ds) ->
- case inferred_bif_type(Bif, Args) of
- any -> [];
- T -> [{A,T} || #b_var{}=A <- Args]
- end;
-infer_type({bif,binary_part}, [#b_var{}=Src,Pos,Len], _Ds) ->
- [{Src,{binary,8}}|
- [{V,t_integer()} || #b_var{}=V <- [Pos,Len]]];
-infer_type(bs_start_match, [#b_var{}=Bin], _Ds) ->
- [{Bin,{binary,1}}];
-infer_type(is_nonempty_list, [#b_var{}=Src], _Ds) ->
- [{Src,cons}];
-infer_type(is_tagged_tuple, [#b_var{}=Src,#b_literal{val=Size},
- #b_literal{}=Tag], _Ds) ->
- Es = set_element_type(1, get_type(Tag, #{}), #{}),
- [{Src,#t_tuple{exact=true,size=Size,elements=Es}}];
-infer_type(succeeded, [#b_var{}=Src], Ds) ->
- #b_set{op=Op,args=Args} = maps:get(Src, Ds),
- infer_type(Op, Args, Ds);
-infer_type(_Op, _Args, _Ds) ->
+infer_eq_lit(_, _) ->
[].
-%% bif_type(Name, Args) -> Type
-%% Return the return type for the guard BIF or operator Name with
-%% arguments Args.
-%%
-%% Note that that the following BIFs are handle elsewhere:
-%%
-%% band/2
-
-bif_type(abs, [_]) -> number;
-bif_type(bit_size, [_]) -> t_integer();
-bif_type(byte_size, [_]) -> t_integer();
-bif_type(ceil, [_]) -> t_integer();
-bif_type(float, [_]) -> float;
-bif_type(floor, [_]) -> t_integer();
-bif_type(is_map_key, [_,_]) -> t_boolean();
-bif_type(length, [_]) -> t_integer();
-bif_type(map_size, [_]) -> t_integer();
-bif_type(node, []) -> #t_atom{};
-bif_type(node, [_]) -> #t_atom{};
-bif_type(round, [_]) -> t_integer();
-bif_type(size, [_]) -> t_integer();
-bif_type(trunc, [_]) -> t_integer();
-bif_type(tuple_size, [_]) -> t_integer();
-bif_type('bnot', [_]) -> t_integer();
-bif_type('bor', [_,_]) -> t_integer();
-bif_type('bsl', [_,_]) -> t_integer();
-bif_type('bsr', [_,_]) -> t_integer();
-bif_type('bxor', [_,_]) -> t_integer();
-bif_type('div', [_,_]) -> t_integer();
-bif_type('rem', [_,_]) -> t_integer();
-bif_type('/', [_,_]) -> float;
-bif_type(Name, Args) ->
- Arity = length(Args),
- case erl_internal:new_type_test(Name, Arity) orelse
- erl_internal:bool_op(Name, Arity) orelse
- erl_internal:comp_op(Name, Arity) of
- true ->
- t_boolean();
- false ->
- case erl_internal:arith_op(Name, Arity) of
- true -> number;
- false -> any
- end
- end.
-
-inferred_bif_type(is_atom, [_]) -> t_atom();
-inferred_bif_type(is_binary, [_]) -> {binary,8};
-inferred_bif_type(is_bitstring, [_]) -> {binary,1};
-inferred_bif_type(is_boolean, [_]) -> t_boolean();
-inferred_bif_type(is_float, [_]) -> float;
-inferred_bif_type(is_integer, [_]) -> t_integer();
-inferred_bif_type(is_list, [_]) -> list;
-inferred_bif_type(is_map, [_]) -> map;
-inferred_bif_type(is_number, [_]) -> number;
-inferred_bif_type(is_tuple, [_]) -> #t_tuple{};
-inferred_bif_type(abs, [_]) -> number;
-inferred_bif_type(bit_size, [_]) -> {binary,1};
-inferred_bif_type('bnot', [_]) -> t_integer();
-inferred_bif_type(byte_size, [_]) -> {binary,1};
-inferred_bif_type(ceil, [_]) -> number;
-inferred_bif_type(float, [_]) -> number;
-inferred_bif_type(floor, [_]) -> number;
-inferred_bif_type(hd, [_]) -> cons;
-inferred_bif_type(length, [_]) -> list;
-inferred_bif_type(map_size, [_]) -> map;
-inferred_bif_type('not', [_]) -> t_boolean();
-inferred_bif_type(round, [_]) -> number;
-inferred_bif_type(trunc, [_]) -> number;
-inferred_bif_type(tl, [_]) -> cons;
-inferred_bif_type(tuple_size, [_]) -> #t_tuple{};
-inferred_bif_type('and', [_,_]) -> t_boolean();
-inferred_bif_type('or', [_,_]) -> t_boolean();
-inferred_bif_type('xor', [_,_]) -> t_boolean();
-inferred_bif_type('band', [_,_]) -> t_integer();
-inferred_bif_type('bor', [_,_]) -> t_integer();
-inferred_bif_type('bsl', [_,_]) -> t_integer();
-inferred_bif_type('bsr', [_,_]) -> t_integer();
-inferred_bif_type('bxor', [_,_]) -> t_integer();
-inferred_bif_type('div', [_,_]) -> t_integer();
-inferred_bif_type('rem', [_,_]) -> t_integer();
-inferred_bif_type('+', [_,_]) -> number;
-inferred_bif_type('-', [_,_]) -> number;
-inferred_bif_type('*', [_,_]) -> number;
-inferred_bif_type('/', [_,_]) -> number;
-inferred_bif_type(_, _) -> any.
-
-is_math_bif(cos, 1) -> true;
-is_math_bif(cosh, 1) -> true;
-is_math_bif(sin, 1) -> true;
-is_math_bif(sinh, 1) -> true;
-is_math_bif(tan, 1) -> true;
-is_math_bif(tanh, 1) -> true;
-is_math_bif(acos, 1) -> true;
-is_math_bif(acosh, 1) -> true;
-is_math_bif(asin, 1) -> true;
-is_math_bif(asinh, 1) -> true;
-is_math_bif(atan, 1) -> true;
-is_math_bif(atanh, 1) -> true;
-is_math_bif(erf, 1) -> true;
-is_math_bif(erfc, 1) -> true;
-is_math_bif(exp, 1) -> true;
-is_math_bif(log, 1) -> true;
-is_math_bif(log2, 1) -> true;
-is_math_bif(log10, 1) -> true;
-is_math_bif(sqrt, 1) -> true;
-is_math_bif(atan2, 2) -> true;
-is_math_bif(pow, 2) -> true;
-is_math_bif(ceil, 1) -> true;
-is_math_bif(floor, 1) -> true;
-is_math_bif(fmod, 2) -> true;
-is_math_bif(pi, 0) -> true;
-is_math_bif(_, _) -> false.
-
join_types(Ts0, Ts1) ->
if
map_size(Ts0) < map_size(Ts1) ->
@@ -1630,7 +1948,7 @@ join_types_1([V|Vs], Ts0, Ts1) ->
{#{V:=Same},#{V:=Same}} ->
join_types_1(Vs, Ts0, Ts1);
{#{V:=T0},#{V:=T1}} ->
- case join(T0, T1) of
+ case beam_types:join(T0, T1) of
T1 ->
join_types_1(Vs, Ts0, Ts1);
T ->
@@ -1642,326 +1960,175 @@ join_types_1([V|Vs], Ts0, Ts1) ->
join_types_1([], Ts0, Ts1) ->
maps:merge(Ts0, Ts1).
-join([T1,T2|Ts]) ->
- join([join(T1, T2)|Ts]);
-join([T]) -> T.
-
-get_literal_from_type(#t_atom{elements=[Atom]}) ->
- #b_literal{val=Atom};
-get_literal_from_type(#t_integer{elements={Int,Int}}) ->
- #b_literal{val=Int};
-get_literal_from_type(nil) ->
- #b_literal{val=[]};
-get_literal_from_type(_) -> none.
-
-remove_element_info(#t_integer{elements={Min,Max}}, Es) ->
- foldl(fun(El, Acc) when Min =< El, El =< Max ->
- maps:remove(El, Acc);
- (_El, Acc) -> Acc
- end, Es, maps:keys(Es)).
-
-t_atom() ->
- #t_atom{elements=any}.
-
-t_atom(Atom) when is_atom(Atom) ->
- #t_atom{elements=[Atom]}.
-
-t_boolean() ->
- #t_atom{elements=[false,true]}.
-
-t_integer() ->
- #t_integer{elements=any}.
-
-t_integer(Int) when is_integer(Int) ->
- #t_integer{elements={Int,Int}}.
-
-t_integer(Min, Max) when is_integer(Min), is_integer(Max) ->
- #t_integer{elements={Min,Max}}.
-
-t_is_boolean(#t_atom{elements=[F,T]}) ->
- F =:= false andalso T =:= true;
-t_is_boolean(#t_atom{elements=[B]}) ->
- is_boolean(B);
-t_is_boolean(_) -> false.
-
-t_tuple_size(#t_tuple{size=Size,exact=false}) ->
- {at_least,Size};
-t_tuple_size(#t_tuple{size=Size,exact=true}) ->
- {exact,Size};
-t_tuple_size(_) ->
- none.
-
-is_singleton_type(Type) ->
- get_literal_from_type(Type) =/= none.
-
-get_element_type(Index, Es) ->
- case Es of
- #{ Index := T } -> T;
- #{} -> any
- end.
-
-set_element_type(_Key, none, Es) ->
- Es;
-set_element_type(Key, any, Es) ->
- maps:remove(Key, Es);
-set_element_type(Key, Type, Es) ->
- Es#{ Key => Type }.
-
-%% join(Type1, Type2) -> Type
-%% Return the "join" of Type1 and Type2. The join is a more general
-%% type than Type1 and Type2. For example:
-%%
-%% join(#t_integer{elements=any}, #t_integer=elements={0,3}}) ->
-%% #t_integer{}
-%%
-%% The join for two different types result in 'any', which is
-%% the top element for our type lattice:
-%%
-%% join(#t_integer{}, map) -> any
-
--spec join(type(), type()) -> type().
-
-join(T, T) ->
- verified_type(T);
-join(none, T) ->
- verified_type(T);
-join(T, none) ->
- verified_type(T);
-join(any, _) -> any;
-join(_, any) -> any;
-join(#t_atom{elements=[_|_]=Set1}, #t_atom{elements=[_|_]=Set2}) ->
- Set = ordsets:union(Set1, Set2),
- case ordsets:size(Set) of
- Size when Size =< ?ATOM_SET_SIZE ->
- #t_atom{elements=Set};
- _Size ->
- #t_atom{elements=any}
- end;
-join(#t_atom{elements=any}=T, #t_atom{elements=[_|_]}) -> T;
-join(#t_atom{elements=[_|_]}, #t_atom{elements=any}=T) -> T;
-join({binary,U1}, {binary,U2}) ->
- {binary,gcd(U1, U2)};
-join(#t_integer{}, #t_integer{}) -> t_integer();
-join(list, cons) -> list;
-join(cons, list) -> list;
-join(nil, cons) -> list;
-join(cons, nil) -> list;
-join(nil, list) -> list;
-join(list, nil) -> list;
-join(#t_integer{}, float) -> number;
-join(float, #t_integer{}) -> number;
-join(#t_integer{}, number) -> number;
-join(number, #t_integer{}) -> number;
-join(float, number) -> number;
-join(number, float) -> number;
-join(#t_tuple{size=Sz,exact=ExactA,elements=EsA},
- #t_tuple{size=Sz,exact=ExactB,elements=EsB}) ->
- Exact = ExactA and ExactB,
- Es = join_tuple_elements(Sz, EsA, EsB),
- #t_tuple{size=Sz,exact=Exact,elements=Es};
-join(#t_tuple{size=SzA,elements=EsA}, #t_tuple{size=SzB,elements=EsB}) ->
- Sz = min(SzA, SzB),
- Es = join_tuple_elements(Sz, EsA, EsB),
- #t_tuple{size=Sz,elements=Es};
-join(_T1, _T2) ->
- %%io:format("~p ~p\n", [_T1,_T2]),
- any.
-
-join_tuple_elements(MinSize, EsA, EsB) ->
- Es0 = join_elements(EsA, EsB),
- maps:filter(fun(Index, _Type) -> Index =< MinSize end, Es0).
-
-join_elements(Es1, Es2) ->
- Keys = if
- map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
- map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
- end,
- join_elements_1(Keys, Es1, Es2, #{}).
-
-join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
- case {Es1, Es2} of
- {#{ Key := Type1 }, #{ Key := Type2 }} ->
- Acc = set_element_type(Key, join(Type1, Type2), Acc0),
- join_elements_1(Keys, Es1, Es2, Acc);
- {#{}, #{}} ->
- join_elements_1(Keys, Es1, Es2, Acc0)
- end;
-join_elements_1([], _Es1, _Es2, Acc) ->
- Acc.
-
-gcd(A, B) ->
- case A rem B of
- 0 -> B;
- X -> gcd(B, X)
- end.
-
meet_types([{V,T0}|Vs], Ts) ->
#{V:=T1} = Ts,
- case meet(T0, T1) of
+ case beam_types:meet(T0, T1) of
+ none -> none;
T1 -> meet_types(Vs, Ts);
T -> meet_types(Vs, Ts#{V:=T})
end;
meet_types([], Ts) -> Ts.
-meet([T1,T2|Ts]) ->
- meet([meet(T1, T2)|Ts]);
-meet([T]) -> T.
-
subtract_types([{V,T0}|Vs], Ts) ->
#{V:=T1} = Ts,
- case subtract(T1, T0) of
+ case beam_types:subtract(T1, T0) of
+ none -> none;
T1 -> subtract_types(Vs, Ts);
T -> subtract_types(Vs, Ts#{V:=T})
end;
subtract_types([], Ts) -> Ts.
-%% subtract(Type1, Type2) -> Type.
-%% Subtract Type2 from Type1. Example:
-%%
-%% subtract(list, cons) -> nil
+parallel_join([A | As], [B | Bs]) ->
+ [beam_types:join(A, B) | parallel_join(As, Bs)];
+parallel_join([], []) ->
+ [].
-subtract(#t_atom{elements=[_|_]=Set0}, #t_atom{elements=[_|_]=Set1}) ->
- case ordsets:subtract(Set0, Set1) of
- [] -> none;
- [_|_]=Set -> #t_atom{elements=Set}
- end;
-subtract(number, float) -> #t_integer{};
-subtract(number, #t_integer{elements=any}) -> float;
-subtract(list, cons) -> nil;
-subtract(list, nil) -> cons;
-subtract(T, _) -> T.
-
-%% meet(Type1, Type2) -> Type
-%% Return the "meet" of Type1 and Type2. The meet is a narrower
-%% type than Type1 and Type2. For example:
-%%
-%% meet(#t_integer{elements=any}, #t_integer{elements={0,3}}) ->
-%% #t_integer{elements={0,3}}
-%%
-%% The meet for two different types result in 'none', which is
-%% the bottom element for our type lattice:
-%%
-%% meet(#t_integer{}, map) -> none
+gcd(A, B) ->
+ case A rem B of
+ 0 -> B;
+ X -> gcd(B, X)
+ end.
+
+%%%
+%%% Helpers
+%%%
--spec meet(type(), type()) -> type().
+init_metadata(FuncId, Linear, Params) ->
+ {RetCounter, Map0} = init_metadata_1(reverse(Linear), 0, #{}),
+ Map = maps:without(Params, Map0),
+ UsedOnce = cerl_sets:from_list(maps:keys(Map)),
+
+ #metadata{ func_id = FuncId,
+ limit_return = (RetCounter >= ?RETURN_LIMIT),
+ params = Params,
+ used_once = UsedOnce }.
+
+init_metadata_1([{L,#b_blk{is=Is,last=Last}} | Bs], RetCounter0, Uses0) ->
+ %% Track the number of return terminators in use. See ?RETURN_LIMIT for
+ %% details.
+ RetCounter = case Last of
+ #b_ret{} -> RetCounter0 + 1;
+ _ -> RetCounter0
+ end,
+
+ %% Calculate the set of variables that are only used once in the terminator
+ %% of the block that defines them. That will allow us to discard type
+ %% information discard type information for variables that will never be
+ %% referenced by the successor blocks, potentially improving compilation
+ %% times.
-meet(T, T) ->
- verified_type(T);
-meet(#t_atom{elements=[_|_]=Set1}, #t_atom{elements=[_|_]=Set2}) ->
- case ordsets:intersection(Set1, Set2) of
- [] ->
- none;
- [_|_]=Set ->
- #t_atom{elements=Set}
+ Uses1 = used_once_last_uses(beam_ssa:used(Last), L, Uses0),
+ Uses = used_once_2(reverse(Is), L, Uses1),
+ init_metadata_1(Bs, RetCounter, Uses);
+init_metadata_1([], RetCounter, Uses) ->
+ {RetCounter, Uses}.
+
+used_once_2([#b_set{dst=Dst}=I|Is], L, Uses0) ->
+ Uses = used_once_uses(beam_ssa:used(I), L, Uses0),
+ case Uses of
+ #{Dst:=[L]} ->
+ used_once_2(Is, L, Uses);
+ #{} ->
+ %% Used more than once or used once in
+ %% in another block.
+ used_once_2(Is, L, maps:remove(Dst, Uses))
end;
-meet(#t_atom{elements=[_|_]}=T, #t_atom{elements=any}) ->
- T;
-meet(#t_atom{elements=any}, #t_atom{elements=[_|_]}=T) ->
- T;
-meet(#t_integer{elements={_,_}}=T, #t_integer{elements=any}) ->
- T;
-meet(#t_integer{elements=any}, #t_integer{elements={_,_}}=T) ->
- T;
-meet(#t_integer{elements={Min1,Max1}},
- #t_integer{elements={Min2,Max2}}) ->
- #t_integer{elements={max(Min1, Min2),min(Max1, Max2)}};
-meet(#t_integer{}=T, number) -> T;
-meet(float=T, number) -> T;
-meet(number, #t_integer{}=T) -> T;
-meet(number, float=T) -> T;
-meet(list, cons) -> cons;
-meet(list, nil) -> nil;
-meet(cons, list) -> cons;
-meet(nil, list) -> nil;
-meet(#t_tuple{}=T1, #t_tuple{}=T2) ->
- meet_tuples(T1, T2);
-meet({binary,U1}, {binary,U2}) ->
- {binary,max(U1, U2)};
-meet(any, T) ->
- verified_type(T);
-meet(T, any) ->
- verified_type(T);
-meet(_, _) ->
- %% Inconsistent types. There will be an exception at runtime.
- none.
-
-meet_tuples(#t_tuple{size=Sz1,exact=true},
- #t_tuple{size=Sz2,exact=true}) when Sz1 =/= Sz2 ->
- none;
-meet_tuples(#t_tuple{size=Sz1,exact=Ex1,elements=Es1},
- #t_tuple{size=Sz2,exact=Ex2,elements=Es2}) ->
- Size = max(Sz1, Sz2),
- Exact = Ex1 or Ex2,
- case meet_elements(Es1, Es2) of
- none ->
- none;
- Es ->
- #t_tuple{size=Size,exact=Exact,elements=Es}
- end.
+used_once_2([], _, Uses) -> Uses.
-meet_elements(Es1, Es2) ->
- Keys = maps:keys(Es1) ++ maps:keys(Es2),
- meet_elements_1(Keys, Es1, Es2, #{}).
+used_once_uses([V|Vs], L, Uses) ->
+ case Uses of
+ #{V:=more_than_once} ->
+ used_once_uses(Vs, L, Uses);
+ #{} ->
+ %% Already used or first use is not in
+ %% a terminator.
+ used_once_uses(Vs, L, Uses#{V=>more_than_once})
+ end;
+used_once_uses([], _, Uses) -> Uses.
-meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
- case {Es1, Es2} of
- {#{ Key := Type1 }, #{ Key := Type2 }} ->
- case meet(Type1, Type2) of
- none -> none;
- Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
- end;
- {#{ Key := Type1 }, _} ->
- meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
- {_, #{ Key := Type2 }} ->
- meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+used_once_last_uses([V|Vs], L, Uses) ->
+ case Uses of
+ #{V:=[_]} ->
+ %% Second time this variable is used.
+ used_once_last_uses(Vs, L, Uses#{V:=more_than_once});
+ #{V:=more_than_once} ->
+ %% Used at least twice before.
+ used_once_last_uses(Vs, L, Uses);
+ #{} ->
+ %% First time this variable is used.
+ used_once_last_uses(Vs, L, Uses#{V=>[L]})
end;
-meet_elements_1([], _Es1, _Es2, Acc) ->
- Acc.
+used_once_last_uses([], _, Uses) -> Uses.
-%% verified_type(Type) -> Type
-%% Returns the passed in type if it is one of the defined types.
-%% Crashes if there is anything wrong with the type.
%%
-%% Here are all possible types:
+%% Ordered worklist used in signatures/2.
%%
-%% any Any Erlang term (top element for the type lattice).
+%% This is equivalent to consing (wl_add) and appending (wl_defer_list)
+%% to a regular list, but avoids uneccessary work by reordering elements.
%%
-%% #t_atom{} Any atom or some specific atoms.
-%% {binary,Unit} Binary/bitstring aligned to unit Unit.
-%% float Floating point number.
-%% #t_integer{} Integer
-%% list Empty or nonempty list.
-%% map Map.
-%% nil Empty list.
-%% cons Cons (nonempty list).
-%% number A number (float or integer).
-%% #t_tuple{} Tuple.
+%% We can do this since a function only needs to be visited *once* for all
+%% prior updates to take effect, so if an element is added to the front, then
+%% all earlier instances of the same element are redundant.
%%
-%% none No type (bottom element for the type lattice).
-
--spec verified_type(T) -> T when
- T :: type().
-
-verified_type(any=T) -> T;
-verified_type(none=T) -> T;
-verified_type(#t_atom{elements=any}=T) -> T;
-verified_type(#t_atom{elements=[_|_]}=T) -> T;
-verified_type({binary,U}=T) when is_integer(U) -> T;
-verified_type(#t_integer{elements=any}=T) -> T;
-verified_type(#t_integer{elements={Min,Max}}=T)
- when is_integer(Min), is_integer(Max) -> T;
-verified_type(list=T) -> T;
-verified_type(map=T) -> T;
-verified_type(nil=T) -> T;
-verified_type(cons=T) -> T;
-verified_type(number=T) -> T;
-verified_type(#t_tuple{size=Size,elements=Es}=T) ->
- %% All known elements must have a valid index and type. 'any' is prohibited
- %% since it's implicit and should never be present in the map.
- maps:fold(fun(Index, Element, _) when is_integer(Index),
- 1 =< Index, Index =< Size,
- Element =/= any, Element =/= none ->
- verified_type(Element)
- end, [], Es),
- T;
-verified_type(float=T) -> T.
+
+-record(worklist,
+ { counter = 0 :: integer(),
+ elements = gb_trees:empty() :: gb_trees:tree(integer(), term()),
+ indexes = #{} :: #{ term() => integer() } }).
+
+-type worklist() :: #worklist{}.
+
+wl_new() -> #worklist{}.
+
+%% Adds an element to the worklist, or moves it to the front if it's already
+%% present.
+wl_add(Element, #worklist{counter=Counter,elements=Es,indexes=Is}) ->
+ case Is of
+ #{ Element := Index } ->
+ wl_add_1(Element, Counter, gb_trees:delete(Index, Es), Is);
+ #{} ->
+ wl_add_1(Element, Counter, Es, Is)
+ end.
+
+wl_add_1(Element, Counter0, Es0, Is0) ->
+ Counter = Counter0 + 1,
+ Es = gb_trees:insert(Counter, Element, Es0),
+ Is = Is0#{ Element => Counter },
+ #worklist{counter=Counter,elements=Es,indexes=Is}.
+
+%% All mutations bump the counter, so we can check for changes without a deep
+%% comparison.
+wl_changed(#worklist{counter=Same}, #worklist{counter=Same}) -> false;
+wl_changed(#worklist{}, #worklist{}) -> true.
+
+%% Adds the given elements to the back of the worklist, skipping the elements
+%% that are already present. This lets us append elements arbitrarly after the
+%% current front without changing the work order.
+wl_defer_list(Elements, #worklist{counter=Counter,elements=Es,indexes=Is}) ->
+ wl_defer_list_1(Elements, Counter, Es, Is).
+
+wl_defer_list_1([Element | Elements], Counter0, Es0, Is0) ->
+ case Is0 of
+ #{ Element := _ } ->
+ wl_defer_list_1(Elements, Counter0, Es0, Is0);
+ #{} ->
+ Counter = Counter0 + 1,
+ Es = gb_trees:insert(-Counter, Element, Es0),
+ Is = Is0#{ Element => -Counter },
+ wl_defer_list_1(Elements, Counter, Es, Is)
+ end;
+wl_defer_list_1([], Counter, Es, Is) ->
+ #worklist{counter=Counter,elements=Es,indexes=Is}.
+
+wl_next(#worklist{indexes=Is}) when Is =:= #{} ->
+ empty;
+wl_next(#worklist{elements=Es,indexes=Is}) when Is =/= #{} ->
+ {_Key, Element} = gb_trees:largest(Es),
+ {ok, Element}.
+
+%% Removes the front of the worklist.
+wl_pop(Element, #worklist{counter=Counter0,elements=Es0,indexes=Is0}=Wl) ->
+ Counter = Counter0 + 1,
+ {_Key, Element, Es} = gb_trees:take_largest(Es0), %Assertion.
+ Is = maps:remove(Element, Is0),
+ Wl#worklist{counter=Counter,elements=Es,indexes=Is}.
diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl
index acf3838da4..ddf8e6f89c 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.erl
@@ -208,6 +208,9 @@ remap([{block,Bl0}|Is], Map, Acc) ->
remap([{bs_get_tail,Src,Dst,Live}|Is], Map, Acc) ->
I = {bs_get_tail,Map(Src),Map(Dst),Live},
remap(Is, Map, [I|Acc]);
+remap([{bs_start_match4,Fail,Live,Src,Dst}|Is], Map, Acc) ->
+ I = {bs_start_match4,Fail,Live,Map(Src),Map(Dst)},
+ remap(Is, Map, [I|Acc]);
remap([{bs_set_position,Src1,Src2}|Is], Map, Acc) ->
I = {bs_set_position,Map(Src1),Map(Src2)},
remap(Is, Map, [I|Acc]);
@@ -244,6 +247,9 @@ remap([{make_fun2,_,_,_,_}=I|T], Map, Acc) ->
remap([{deallocate,N}|Is], Map, Acc) ->
I = {deallocate,Map({frame_size,N})},
remap(Is, Map, [I|Acc]);
+remap([{swap,Reg1,Reg2}|Is], Map, Acc) ->
+ I = {swap,Map(Reg1),Map(Reg2)},
+ remap(Is, Map, [I|Acc]);
remap([{test,Name,Fail,Ss}|Is], Map, Acc) ->
I = {test,Name,Fail,[Map(S) || S <- Ss]},
remap(Is, Map, [I|Acc]);
@@ -378,10 +384,17 @@ frame_size([{deallocate,N}|_], _) ->
N;
frame_size([{line,_}|Is], Safe) ->
frame_size(Is, Safe);
+frame_size([{bs_start_match4,Fail,_,_,_}|Is], Safe) ->
+ case Fail of
+ {f,L} -> frame_size_branch(L, Is, Safe);
+ _ -> frame_size(Is, Safe)
+ end;
frame_size([{bs_set_position,_,_}|Is], Safe) ->
frame_size(Is, Safe);
frame_size([{bs_get_tail,_,_,_}|Is], Safe) ->
frame_size(Is, Safe);
+frame_size([{swap,_,_}|Is], Safe) ->
+ frame_size(Is, Safe);
frame_size(_, _) -> throw(not_possible).
frame_size_branch(0, Is, Safe) ->
@@ -417,6 +430,9 @@ is_not_used(Y, [{bs_init,_,_,_,Ss,Dst}|Is]) ->
is_not_used_ss_dst(Y, Ss, Dst, Is);
is_not_used(Y, [{bs_put,{f,_},_,Ss}|Is]) ->
not member(Y, Ss) andalso is_not_used(Y, Is);
+is_not_used(Y, [{bs_start_match4,_Fail,_Live,Src,Dst}|Is]) ->
+ Y =/= Src andalso Y =/= Dst andalso
+ is_not_used(Y, Is);
is_not_used(Y, [{bs_set_position,Src1,Src2}|Is]) ->
Y =/= Src1 andalso Y =/= Src2 andalso
is_not_used(Y, Is);
@@ -444,6 +460,8 @@ is_not_used(Y, [{line,_}|Is]) ->
is_not_used(Y, Is);
is_not_used(Y, [{make_fun2,_,_,_,_}|Is]) ->
is_not_used(Y, Is);
+is_not_used(Y, [{swap,Reg1,Reg2}|Is]) ->
+ Y =/= Reg1 andalso Y =/= Reg2 andalso is_not_used(Y, Is);
is_not_used(Y, [{test,_,_,Ss}|Is]) ->
not member(Y, Ss) andalso is_not_used(Y, Is);
is_not_used(Y, [{test,_Op,{f,_},_Live,Ss,Dst}|Is]) ->
diff --git a/lib/compiler/src/beam_types.erl b/lib/compiler/src/beam_types.erl
new file mode 100644
index 0000000000..5577fe79d8
--- /dev/null
+++ b/lib/compiler/src/beam_types.erl
@@ -0,0 +1,1127 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_types).
+
+-define(BEAM_TYPES_INTERNAL, true).
+-include("beam_types.hrl").
+
+-import(lists, [foldl/3, reverse/1]).
+
+-export([meet/1, meet/2, join/1, join/2, subtract/2]).
+
+-export([is_boolean_type/1,
+ get_bs_matchable_unit/1,
+ is_bs_matchable_type/1,
+ get_singleton_value/1,
+ is_singleton_type/1,
+ normalize/1]).
+
+-export([get_tuple_element/2, set_tuple_element/3]).
+
+-export([make_type_from_value/1]).
+
+-export([make_atom/1,
+ make_boolean/0,
+ make_cons/2,
+ make_float/1,
+ make_float/2,
+ make_integer/1,
+ make_integer/2]).
+
+-export([limit_depth/1]).
+
+%% This is exported to help catch errors in property test generators and is not
+%% meant to be used outside of test suites.
+-export([verified_type/1]).
+
+-define(IS_LIST_TYPE(N),
+ is_record(N, t_list) orelse
+ is_record(N, t_cons) orelse
+ N =:= nil).
+
+-define(IS_NUMBER_TYPE(N),
+ N =:= number orelse
+ is_record(N, t_float) orelse
+ is_record(N, t_integer)).
+
+%% Folds meet/2 over a list.
+
+-spec meet([type()]) -> type().
+
+meet([T1, T2 | Ts]) ->
+ meet([meet(T1, T2) | Ts]);
+meet([T]) -> T.
+
+%% Return the "meet" of Type1 and Type2, which is more specific than Type1 and
+%% Type2. This is identical to glb/2 but can operate on and produce unions.
+%%
+%% A = #t_union{list=nil, number=[number], other=[#t_map{}]}
+%% B = #t_union{number=[#t_integer{}], other=[#t_map{}]}
+%%
+%% meet(A, B) ->
+%% #t_union{number=[#t_integer{}], other=[#t_map{}]}
+%%
+%% The meet of two different types result in 'none', which is the bottom
+%% element for our type lattice:
+%%
+%% meet(#t_integer{}, #t_map{}) -> none
+
+-spec meet(type(), type()) -> type().
+
+meet(T, T) ->
+ verified_type(T);
+meet(any, T) ->
+ verified_type(T);
+meet(T, any) ->
+ verified_type(T);
+meet(#t_union{}=A, B) ->
+ meet_unions(A, B);
+meet(A, #t_union{}=B) ->
+ meet_unions(B, A);
+meet(A, B) ->
+ glb(A, B).
+
+meet_unions(#t_union{atom=AtomA,list=ListA,number=NumberA,
+ tuple_set=TSetA,other=OtherA},
+ #t_union{atom=AtomB,list=ListB,number=NumberB,
+ tuple_set=TSetB,other=OtherB}) ->
+ Union = #t_union{atom=glb(AtomA, AtomB),
+ list=glb(ListA, ListB),
+ number=glb(NumberA, NumberB),
+ tuple_set=meet_tuple_sets(TSetA, TSetB),
+ other=glb(OtherA, OtherB)},
+ shrink_union(Union);
+meet_unions(#t_union{atom=AtomA}, #t_atom{}=B) ->
+ case glb(AtomA, B) of
+ none -> none;
+ Atom -> Atom
+ end;
+meet_unions(#t_union{number=NumberA}, B) when ?IS_NUMBER_TYPE(B) ->
+ case glb(NumberA, B) of
+ none -> none;
+ Number -> Number
+ end;
+meet_unions(#t_union{list=ListA}, B) when ?IS_LIST_TYPE(B) ->
+ case glb(ListA, B) of
+ none -> none;
+ List -> List
+ end;
+meet_unions(#t_union{tuple_set=Tuples}, #t_tuple{}=B) ->
+ Set = meet_tuple_sets(Tuples, new_tuple_set(B)),
+ shrink_union(#t_union{tuple_set=Set});
+meet_unions(#t_union{other=OtherA}, OtherB) ->
+ case glb(OtherA, OtherB) of
+ none -> none;
+ Other -> Other
+ end.
+
+meet_tuple_sets(none, _) ->
+ none;
+meet_tuple_sets(_, none) ->
+ none;
+meet_tuple_sets(#t_tuple{}=A, #t_tuple{}=B) ->
+ new_tuple_set(glb(A, B));
+meet_tuple_sets(#t_tuple{}=Tuple, Records) ->
+ mts_tuple(Records, Tuple, []);
+meet_tuple_sets(Records, #t_tuple{}=Tuple) ->
+ meet_tuple_sets(Tuple, Records);
+meet_tuple_sets(RecordsA, RecordsB) ->
+ mts_records(RecordsA, RecordsB).
+
+mts_tuple([{Key, Type} | Records], Tuple, Acc) ->
+ case glb(Type, Tuple) of
+ none -> mts_tuple(Records, Tuple, Acc);
+ T -> mts_tuple(Records, Tuple, [{Key, T} | Acc])
+ end;
+mts_tuple([], _Tuple, [_|_]=Acc) ->
+ reverse(Acc);
+mts_tuple([], _Tuple, []) ->
+ none.
+
+mts_records(RecordsA, RecordsB) ->
+ mts_records(RecordsA, RecordsB, []).
+
+mts_records([{Key, A} | RsA], [{Key, B} | RsB], Acc) ->
+ case glb(A, B) of
+ none -> mts_records(RsA, RsB, Acc);
+ T -> mts_records(RsA, RsB, [{Key, T} | Acc])
+ end;
+mts_records([{KeyA, _} | _ ]=RsA, [{KeyB, _} | RsB], Acc) when KeyA > KeyB ->
+ mts_records(RsA, RsB, Acc);
+mts_records([{KeyA, _} | RsA], [{KeyB, _} | _] = RsB, Acc) when KeyA < KeyB ->
+ mts_records(RsA, RsB, Acc);
+mts_records(_RsA, [], [_|_]=Acc) ->
+ reverse(Acc);
+mts_records([], _RsB, [_|_]=Acc) ->
+ reverse(Acc);
+mts_records(_RsA, _RsB, []) ->
+ none.
+
+%% Folds join/2 over a list.
+
+-spec join([type()]) -> type().
+
+join([T1, T2| Ts]) ->
+ join([join(T1, T2) | Ts]);
+join([T]) -> T.
+
+%% Return the "join" of Type1 and Type2, which is more general than Type1 and
+%% Type2. This is identical to lub/2 but can operate on and produce unions.
+%%
+%% join(#t_integer{}, #t_map{}) -> #t_union{number=[#t_integer{}],
+%% other=[#t_map{}]}
+
+-spec join(type(), type()) -> type().
+
+join(T, T) -> T;
+join(_T, any) -> any;
+join(any, _T) -> any;
+join(T, none) -> T;
+join(none, T) -> T;
+
+join(#t_union{}=A, B) ->
+ join_unions(A, B);
+join(A, #t_union{}=B) ->
+ join_unions(B, A);
+
+%% Union creation...
+join(#t_atom{}=A, #t_atom{}=B) ->
+ lub(A, B);
+join(#t_atom{}=A, B) when ?IS_LIST_TYPE(B) ->
+ #t_union{atom=A,list=B};
+join(#t_atom{}=A, B) when ?IS_NUMBER_TYPE(B) ->
+ #t_union{atom=A,number=B};
+join(#t_atom{}=A, #t_tuple{}=B) ->
+ #t_union{atom=A,tuple_set=new_tuple_set(B)};
+join(#t_atom{}=A, B) ->
+ #t_union{atom=A,other=B};
+join(A, #t_atom{}=B) ->
+ join(B, A);
+
+join(A, B) when ?IS_LIST_TYPE(A), ?IS_LIST_TYPE(B) ->
+ lub(A, B);
+join(A, B) when ?IS_LIST_TYPE(A), ?IS_NUMBER_TYPE(B) ->
+ #t_union{list=A,number=B};
+join(A, #t_tuple{}=B) when ?IS_LIST_TYPE(A) ->
+ #t_union{list=A,tuple_set=new_tuple_set(B)};
+join(A, B) when ?IS_LIST_TYPE(A) ->
+ #t_union{list=A,other=B};
+join(A, B) when ?IS_LIST_TYPE(B) ->
+ join(B, A);
+
+join(A, B) when ?IS_NUMBER_TYPE(A), ?IS_NUMBER_TYPE(B) ->
+ lub(A, B);
+join(A, #t_tuple{}=B) when ?IS_NUMBER_TYPE(A) ->
+ #t_union{number=A,tuple_set=new_tuple_set(B)};
+join(A, B) when ?IS_NUMBER_TYPE(A) ->
+ #t_union{number=A,other=B};
+join(A, B) when ?IS_NUMBER_TYPE(B) ->
+ join(B, A);
+
+join(#t_tuple{}=A, #t_tuple{}=B) ->
+ case {record_key(A), record_key(B)} of
+ {Same, Same} ->
+ lub(A, B);
+ {none, _Key} ->
+ lub(A, B);
+ {_Key, none} ->
+ lub(A, B);
+ {KeyA, KeyB} when KeyA < KeyB ->
+ #t_union{tuple_set=[{KeyA, A}, {KeyB, B}]};
+ {KeyA, KeyB} when KeyA > KeyB ->
+ #t_union{tuple_set=[{KeyB, B}, {KeyA, A}]}
+ end;
+join(#t_tuple{}=A, B) ->
+ %% All other combinations have been tried already, so B must be 'other'
+ #t_union{tuple_set=new_tuple_set(A),other=B};
+join(A, #t_tuple{}=B) ->
+ join(B, A);
+
+join(A, B) ->
+ lub(A, B).
+
+join_unions(#t_union{atom=AtomA,list=ListA,number=NumberA,
+ tuple_set=TSetA,other=OtherA},
+ #t_union{atom=AtomB,list=ListB,number=NumberB,
+ tuple_set=TSetB,other=OtherB}) ->
+ Union = #t_union{atom=lub(AtomA, AtomB),
+ list=lub(ListA, ListB),
+ number=lub(NumberA, NumberB),
+ tuple_set=join_tuple_sets(TSetA, TSetB),
+ other=lub(OtherA, OtherB)},
+ shrink_union(Union);
+join_unions(#t_union{atom=AtomA}=A, #t_atom{}=B) ->
+ A#t_union{atom=lub(AtomA, B)};
+join_unions(#t_union{list=ListA}=A, B) when ?IS_LIST_TYPE(B) ->
+ A#t_union{list=lub(ListA, B)};
+join_unions(#t_union{number=NumberA}=A, B) when ?IS_NUMBER_TYPE(B) ->
+ A#t_union{number=lub(NumberA, B)};
+join_unions(#t_union{tuple_set=TSetA}=A, #t_tuple{}=B) ->
+ Set = join_tuple_sets(TSetA, new_tuple_set(B)),
+ shrink_union(A#t_union{tuple_set=Set});
+join_unions(#t_union{other=OtherA}=A, B) ->
+ case lub(OtherA, B) of
+ any -> any;
+ T -> A#t_union{other=T}
+ end.
+
+join_tuple_sets(A, none) ->
+ A;
+join_tuple_sets(none, B) ->
+ B;
+join_tuple_sets(#t_tuple{}=A, #t_tuple{}=B) ->
+ lub(A, B);
+join_tuple_sets(#t_tuple{}=Tuple, Records) ->
+ jts_tuple(Records, Tuple);
+join_tuple_sets(Records, #t_tuple{}=Tuple) ->
+ join_tuple_sets(Tuple, Records);
+join_tuple_sets(RecordsA, RecordsB) ->
+ jts_records(RecordsA, RecordsB).
+
+jts_tuple([{_Key, Tuple} | Records], Acc) ->
+ jts_tuple(Records, lub(Tuple, Acc));
+jts_tuple([], Acc) ->
+ Acc.
+
+jts_records(RsA, RsB) ->
+ jts_records(RsA, RsB, 0, []).
+
+jts_records([], [], _N, Acc) ->
+ reverse(Acc);
+jts_records(RsA, RsB, N, Acc) when N > ?TUPLE_SET_LIMIT ->
+ A = normalize_tuple_set(RsA, none),
+ B = normalize_tuple_set(RsB, A),
+ #t_tuple{} = normalize_tuple_set(Acc, B);
+jts_records([{Key, A} | RsA], [{Key, B} | RsB], N, Acc) ->
+ jts_records(RsA, RsB, N + 1, [{Key, lub(A, B)} | Acc]);
+jts_records([{KeyA, _} | _]=RsA, [{KeyB, B} | RsB], N, Acc) when KeyA > KeyB ->
+ jts_records(RsA, RsB, N + 1, [{KeyB, B} | Acc]);
+jts_records([{KeyA, A} | RsA], [{KeyB, _} | _] = RsB, N, Acc) when KeyA < KeyB ->
+ jts_records(RsA, RsB, N + 1, [{KeyA, A} | Acc]);
+jts_records([{KeyA, A} | RsA], [], N, Acc) ->
+ jts_records(RsA, [], N + 1, [{KeyA, A} | Acc]);
+jts_records([], [{KeyB, B} | RsB], N, Acc) ->
+ jts_records([], RsB, N + 1, [{KeyB, B} | Acc]).
+
+%% Subtract Type2 from Type1. Example:
+%% subtract(list, cons) -> nil
+
+-spec subtract(type(), type()) -> type().
+
+subtract(#t_atom{elements=[_|_]=Set0}, #t_atom{elements=[_|_]=Set1}) ->
+ case ordsets:subtract(Set0, Set1) of
+ [] -> none;
+ [_|_]=Set -> #t_atom{elements=Set}
+ end;
+subtract(#t_bitstring{size_unit=UnitA}=T, #t_bs_matchable{tail_unit=UnitB}) ->
+ subtract_matchable(T, UnitA, UnitB);
+subtract(#t_bitstring{size_unit=UnitA}=T, #t_bitstring{size_unit=UnitB}) ->
+ subtract_matchable(T, UnitA, UnitB);
+subtract(#t_bs_context{tail_unit=UnitA}=T, #t_bs_matchable{tail_unit=UnitB}) ->
+ subtract_matchable(T, UnitA, UnitB);
+subtract(#t_bs_context{tail_unit=UnitA}=T, #t_bs_context{tail_unit=UnitB}) ->
+ subtract_matchable(T, UnitA, UnitB);
+subtract(#t_integer{elements={Min, Max}}, #t_integer{elements={N,N}}) ->
+ if
+ Min =:= N, Max =:= N ->
+ none;
+ Min =/= N, Max =/= N ->
+ #t_integer{elements={Min, Max}};
+ Min =:= N ->
+ #t_integer{elements={Min + 1, Max}};
+ Max =:= N ->
+ #t_integer{elements={Min, Max - 1}}
+ end;
+subtract(number, #t_float{elements=any}) -> #t_integer{};
+subtract(number, #t_integer{elements=any}) -> #t_float{};
+
+%% A list is essentially `#t_cons{} | nil`, so we're left with nil if we
+%% subtract a cons cell that is more general than the one in the list.
+subtract(#t_list{type=TypeA,terminator=TermA}=T,
+ #t_cons{type=TypeB,terminator=TermB}) ->
+ case {meet(TypeA, TypeB), meet(TermA, TermB)} of
+ {TypeA, TermA} -> nil;
+ _ -> T
+ end;
+subtract(#t_list{type=Type,terminator=Term}, nil) ->
+ #t_cons{type=Type,terminator=Term};
+
+subtract(#t_union{atom=Atom}=A, #t_atom{}=B)->
+ shrink_union(A#t_union{atom=subtract(Atom, B)});
+subtract(#t_union{number=Number}=A, B) when ?IS_NUMBER_TYPE(B) ->
+ shrink_union(A#t_union{number=subtract(Number, B)});
+subtract(#t_union{list=List}=A, B) when ?IS_LIST_TYPE(B) ->
+ shrink_union(A#t_union{list=subtract(List, B)});
+subtract(#t_union{tuple_set=[_|_]=Records0}=A, #t_tuple{}=B) ->
+ %% Filter out all records that are more specific than B.
+ NewSet = case [{Key, T} || {Key, T} <- Records0, meet(T, B) =/= T] of
+ [_|_]=Records -> Records;
+ [] -> none
+ end,
+ shrink_union(A#t_union{tuple_set=NewSet});
+subtract(#t_union{tuple_set=#t_tuple{}=Tuple}=A, #t_tuple{}=B) ->
+ %% Exclude Tuple if it's more specific than B.
+ case meet(Tuple, B) of
+ Tuple -> shrink_union(A#t_union{tuple_set=none});
+ _ -> A
+ end;
+subtract(#t_union{other=Other}=A, B) ->
+ shrink_union(A#t_union{other=subtract(Other, B)});
+
+subtract(A, B) ->
+ %% There's nothing left if A is more specific than B.
+ case meet(A, B) of
+ A -> none;
+ _Other -> A
+ end.
+
+subtract_matchable(T, UnitA, UnitB) ->
+ if
+ UnitA rem UnitB =:= 0 -> none;
+ UnitA rem UnitB =/= 0 -> T
+ end.
+
+%%%
+%%% Type operators
+%%%
+
+-spec get_bs_matchable_unit(type()) -> pos_integer() | error.
+get_bs_matchable_unit(#t_bitstring{size_unit=Unit}) ->
+ Unit;
+get_bs_matchable_unit(#t_bs_context{tail_unit=Unit}) ->
+ Unit;
+get_bs_matchable_unit(#t_bs_matchable{tail_unit=Unit}) ->
+ Unit;
+get_bs_matchable_unit(_) ->
+ error.
+
+-spec is_bs_matchable_type(type()) -> boolean().
+is_bs_matchable_type(Type) ->
+ get_bs_matchable_unit(Type) =/= error.
+
+-spec get_singleton_value(Type) -> Result when
+ Type :: type(),
+ Result :: {ok, term()} | error.
+get_singleton_value(#t_atom{elements=[Atom]}) ->
+ {ok, Atom};
+get_singleton_value(#t_float{elements={Float,Float}}) ->
+ {ok, Float};
+get_singleton_value(#t_integer{elements={Int,Int}}) ->
+ {ok, Int};
+get_singleton_value(#t_map{super_key=none,super_value=none}) ->
+ {ok, #{}};
+get_singleton_value(#t_tuple{exact=true,size=Size,elements=Es}) ->
+ case gsv_elements(Size, Es, []) of
+ Values when is_list(Values) ->
+ {ok, list_to_tuple(Values)};
+ error ->
+ error
+ end;
+get_singleton_value(nil) ->
+ {ok, []};
+get_singleton_value(_) ->
+ error.
+
+gsv_elements(0, _Es, Acc) ->
+ %% The elements were added right-to-left, so it's already in order.
+ Acc;
+gsv_elements(N, Es, Acc) ->
+ ElementType = get_tuple_element(N, Es),
+ case get_singleton_value(ElementType) of
+ {ok, Value} -> gsv_elements(N - 1, Es, [Value | Acc]);
+ error -> error
+ end.
+
+-spec is_singleton_type(type()) -> boolean().
+is_singleton_type(Type) ->
+ get_singleton_value(Type) =/= error.
+
+-spec is_boolean_type(type()) -> boolean().
+is_boolean_type(#t_atom{elements=[F,T]}) ->
+ F =:= false andalso T =:= true;
+is_boolean_type(#t_atom{elements=[B]}) ->
+ is_boolean(B);
+is_boolean_type(#t_union{}=T) ->
+ is_boolean_type(normalize(T));
+is_boolean_type(_) ->
+ false.
+
+-spec set_tuple_element(Index, Type, Elements) -> Elements when
+ Index :: pos_integer(),
+ Type :: type(),
+ Elements :: tuple_elements().
+set_tuple_element(Index, _Type, Es) when Index > ?TUPLE_ELEMENT_LIMIT ->
+ Es;
+set_tuple_element(_Index, none, Es) ->
+ Es;
+set_tuple_element(Index, any, Es) ->
+ maps:remove(Index, Es);
+set_tuple_element(Index, Type, Es) ->
+ Es#{ Index => Type }.
+
+-spec get_tuple_element(Index, Elements) -> type() when
+ Index :: pos_integer(),
+ Elements :: tuple_elements().
+get_tuple_element(Index, Es) ->
+ case Es of
+ #{ Index := T } -> T;
+ #{} -> any
+ end.
+
+-spec normalize(type()) -> normal_type().
+normalize(#t_union{atom=Atom,list=List,number=Number,
+ tuple_set=Tuples,other=Other}) ->
+ A = lub(Atom, List),
+ B = lub(A, Number),
+ C = lub(B, Other),
+ normalize_tuple_set(Tuples, C);
+normalize(T) ->
+ verified_normal_type(T).
+
+normalize_tuple_set([{_, A} | Records], B) ->
+ normalize_tuple_set(Records, lub(A, B));
+normalize_tuple_set([], B) ->
+ B;
+normalize_tuple_set(A, B) ->
+ lub(A, B).
+
+%%%
+%%% Type constructors
+%%%
+
+-spec make_type_from_value(term()) -> type().
+make_type_from_value(Value) ->
+ mtfv_1(Value).
+
+mtfv_1(A) when is_atom(A) ->
+ #t_atom{elements=[A]};
+mtfv_1(B) when is_bitstring(B) ->
+ case bit_size(B) of
+ 0 ->
+ %% This is a bit of a hack, but saying that empty binaries have a
+ %% unit of 8 helps us get rid of is_binary/1 checks.
+ #t_bitstring{size_unit=8};
+ Size ->
+ #t_bitstring{size_unit=Size}
+ end;
+mtfv_1(F) when is_float(F) ->
+ make_float(F);
+mtfv_1(F) when is_function(F) ->
+ {arity, Arity} = erlang:fun_info(F, arity),
+ #t_fun{arity=Arity};
+mtfv_1(I) when is_integer(I) ->
+ make_integer(I);
+mtfv_1(L) when is_list(L) ->
+ case L of
+ [_|_] -> mtfv_cons(L, none);
+ [] -> nil
+ end;
+mtfv_1(M) when is_map(M) ->
+ {SKey, SValue} =
+ maps:fold(fun(Key, Value, {SKey0, SValue0}) ->
+ SKey = join(mtfv_1(Key), SKey0),
+ SValue = join(mtfv_1(Value), SValue0),
+ {SKey, SValue}
+ end, {none, none}, M),
+ #t_map{super_key=SKey,super_value=SValue};
+mtfv_1(T) when is_tuple(T) ->
+ {Es,_} = foldl(fun(Val, {Es0, Index}) ->
+ Type = mtfv_1(Val),
+ Es = set_tuple_element(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, tuple_to_list(T)),
+ #t_tuple{exact=true,size=tuple_size(T),elements=Es};
+mtfv_1(_Term) ->
+ any.
+
+mtfv_cons([Head | Tail], Type) ->
+ mtfv_cons(Tail, join(mtfv_1(Head), Type));
+mtfv_cons(Terminator, Type) ->
+ #t_cons{type=Type,terminator=mtfv_1(Terminator)}.
+
+-spec make_atom(atom()) -> type().
+make_atom(Atom) when is_atom(Atom) ->
+ #t_atom{elements=[Atom]}.
+
+-spec make_boolean() -> type().
+make_boolean() ->
+ #t_atom{elements=[false,true]}.
+
+-spec make_cons(type(), type()) -> type().
+make_cons(Head0, Tail) ->
+ case meet(Tail, #t_cons{}) of
+ #t_cons{type=Type0,terminator=Term0} ->
+ %% Propagate element and terminator types. Note that if the tail is
+ %% the union of a list and something else, the new list could be
+ %% terminated by the other types in the union.
+ Type = join(Head0, Type0),
+ Term = join(subtract(Tail, #t_cons{}), Term0),
+ #t_cons{type=Type,terminator=Term};
+ _ ->
+ %% Tail can't be a cons cell, so we know it terminates the list.
+ #t_cons{type=Head0,terminator=Tail}
+ end.
+
+-spec make_float(float()) -> type().
+make_float(Float) when is_float(Float) ->
+ make_float(Float, Float).
+
+-spec make_float(float(), float()) -> type().
+make_float(Min, Max) when is_float(Min), is_float(Max), Min =< Max ->
+ #t_float{elements={Min, Max}}.
+
+-spec make_integer(integer()) -> type().
+make_integer(Int) when is_integer(Int) ->
+ make_integer(Int, Int).
+
+-spec make_integer(Min, Max) -> type() when
+ Min :: integer(),
+ Max :: integer().
+make_integer(Min, Max) when is_integer(Min), is_integer(Max), Min =< Max ->
+ #t_integer{elements={Min,Max}}.
+
+-spec limit_depth(type()) -> type().
+
+limit_depth(Type) ->
+ limit_depth(Type, ?MAX_TYPE_DEPTH).
+
+limit_depth(#t_cons{}=T, Depth) ->
+ limit_depth_list(T, Depth);
+limit_depth(#t_list{}=T, Depth) ->
+ limit_depth_list(T, Depth);
+limit_depth(#t_tuple{}=T, Depth) ->
+ limit_depth_tuple(T, Depth);
+limit_depth(#t_fun{}=T, Depth) ->
+ limit_depth_fun(T, Depth);
+limit_depth(#t_map{}=T, Depth) ->
+ limit_depth_map(T, Depth);
+limit_depth(#t_union{list=List0,tuple_set=TupleSet0,other=Other0}=U, Depth) ->
+ TupleSet = limit_depth_tuple(TupleSet0, Depth),
+ List = limit_depth_list(List0, Depth),
+ Other = limit_depth(Other0, Depth),
+ shrink_union(U#t_union{list=List,tuple_set=TupleSet,other=Other});
+limit_depth(Type, _Depth) ->
+ Type.
+
+limit_depth_fun(#t_fun{type=Type0}=T, Depth) ->
+ Type = if
+ Depth > 0 -> limit_depth(Type0, Depth - 1);
+ Depth =< 0 -> any
+ end,
+ T#t_fun{type=Type}.
+
+limit_depth_list(#t_cons{type=Type0,terminator=Term0}=T, Depth) ->
+ {Type, Term} = limit_depth_list_1(Type0, Term0, Depth),
+ T#t_cons{type=Type,terminator=Term};
+limit_depth_list(#t_list{type=Type0,terminator=Term0}=T, Depth) ->
+ {Type, Term} = limit_depth_list_1(Type0, Term0, Depth),
+ T#t_list{type=Type,terminator=Term};
+limit_depth_list(nil, _Depth) ->
+ nil;
+limit_depth_list(none, _Depth) ->
+ none.
+
+limit_depth_list_1(Type0, Terminator0, Depth) when Depth > 0 ->
+ Type = limit_depth(Type0, Depth - 1),
+ Terminator = limit_depth(Terminator0, Depth - 1),
+ {Type, Terminator};
+limit_depth_list_1(_Type, _Terminator, Depth) when Depth =< 0 ->
+ {any, any}.
+
+limit_depth_map(#t_map{ super_key=SKey0,
+ super_value=SValue0 }, Depth) when Depth > 0 ->
+ SKey = limit_depth(SKey0, Depth - 1),
+ SValue = limit_depth(SValue0, Depth - 1),
+ #t_map{super_key=SKey,super_value=SValue};
+limit_depth_map(#t_map{}, Depth) when Depth =< 0 ->
+ #t_map{}.
+
+limit_depth_tuple(#t_tuple{elements=Es0}=T, Depth) ->
+ if
+ Depth > 0 ->
+ Es = maps:map(fun(_, E) -> limit_depth(E, Depth - 1) end, Es0),
+ T#t_tuple{elements=Es};
+ Depth =< 0 ->
+ #t_tuple{elements=#{}}
+ end;
+limit_depth_tuple([{{MinSize,_},_}|_], Depth) when Depth =< 0 ->
+ %% Preserve the minimum size of the tuple set.
+ #t_tuple{exact=false,size=MinSize};
+limit_depth_tuple([{SzTag,Tuple}|Ts], Depth) ->
+ [{SzTag, limit_depth_tuple(Tuple, Depth)} | limit_depth_tuple(Ts, Depth)];
+limit_depth_tuple([], _Depth) ->
+ [];
+limit_depth_tuple(none, _Depth) ->
+ none.
+
+%%%
+%%% Helpers
+%%%
+
+%% Return the greatest lower bound of the types Type1 and Type2. The GLB is a
+%% more specific type than Type1 and Type2, and is always a normal type.
+%%
+%% glb(#t_integer{elements=any}, #t_integer{elements={0,3}}) ->
+%% #t_integer{elements={0,3}}
+%%
+%% The GLB of two different types result in 'none', which is the bottom
+%% element for our type lattice:
+%%
+%% glb(#t_integer{}, #t_map{}) -> none
+
+-spec glb(normal_type(), normal_type()) -> normal_type().
+
+glb(T, T) ->
+ verified_normal_type(T);
+glb(any, T) ->
+ verified_normal_type(T);
+glb(T, any) ->
+ verified_normal_type(T);
+glb(#t_atom{elements=[_|_]=Set1}, #t_atom{elements=[_|_]=Set2}) ->
+ case ordsets:intersection(Set1, Set2) of
+ [] ->
+ none;
+ [_|_]=Set ->
+ #t_atom{elements=Set}
+ end;
+glb(#t_atom{elements=[_|_]}=T, #t_atom{elements=any}) ->
+ T;
+glb(#t_atom{elements=any}, #t_atom{elements=[_|_]}=T) ->
+ T;
+glb(#t_bitstring{size_unit=U1}, #t_bitstring{size_unit=U2}) ->
+ #t_bitstring{size_unit=U1 * U2 div gcd(U1, U2)};
+glb(#t_bitstring{size_unit=UnitA}=T, #t_bs_matchable{tail_unit=UnitB}) ->
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ T#t_bitstring{size_unit=Unit};
+glb(#t_bs_context{tail_unit=UnitA,slots=SlotCountA,valid=ValidSlotsA},
+ #t_bs_context{tail_unit=UnitB,slots=SlotCountB,valid=ValidSlotsB}) ->
+ CommonSlotMask = (1 bsl min(SlotCountA, SlotCountB)) - 1,
+ CommonSlotsA = ValidSlotsA band CommonSlotMask,
+ CommonSlotsB = ValidSlotsB band CommonSlotMask,
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ if
+ CommonSlotsA =:= CommonSlotsB ->
+ #t_bs_context{tail_unit=Unit,
+ slots=max(SlotCountA, SlotCountB),
+ valid=ValidSlotsA bor ValidSlotsB};
+ CommonSlotsA =/= CommonSlotsB ->
+ none
+ end;
+glb(#t_bs_context{tail_unit=UnitA}=T, #t_bs_matchable{tail_unit=UnitB}) ->
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ T#t_bs_context{tail_unit=Unit};
+glb(#t_bs_matchable{tail_unit=UnitA}, #t_bs_matchable{tail_unit=UnitB}) ->
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ #t_bs_matchable{tail_unit=Unit};
+glb(#t_bs_matchable{tail_unit=UnitA}, #t_bitstring{size_unit=UnitB}=T) ->
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ T#t_bitstring{size_unit=Unit};
+glb(#t_bs_matchable{tail_unit=UnitA}, #t_bs_context{tail_unit=UnitB}=T) ->
+ Unit = UnitA * UnitB div gcd(UnitA, UnitB),
+ T#t_bs_context{tail_unit=Unit};
+glb(#t_cons{type=TypeA,terminator=TermA},
+ #t_cons{type=TypeB,terminator=TermB}) ->
+ %% Note the use of meet/2; elements don't need to be normal types.
+ case {meet(TypeA, TypeB), meet(TermA, TermB)} of
+ {none, _} -> none;
+ {_, none} -> none;
+ {Type, Term} -> #t_cons{type=Type,terminator=Term}
+ end;
+glb(#t_cons{type=TypeA,terminator=TermA},
+ #t_list{type=TypeB,terminator=TermB}) ->
+ case {meet(TypeA, TypeB), meet(TermA, TermB)} of
+ {none, _} -> none;
+ {_, none} -> none;
+ {Type, Term} -> #t_cons{type=Type,terminator=Term}
+ end;
+glb(#t_float{}=T, #t_float{elements=any}) ->
+ T;
+glb(#t_float{elements=any}, #t_float{}=T) ->
+ T;
+glb(#t_float{elements={MinA,MaxA}}, #t_float{elements={MinB,MaxB}})
+ when MinA >= MinB, MinA =< MaxB;
+ MinB >= MinA, MinB =< MaxA ->
+ true = MinA =< MaxA andalso MinB =< MaxB, %Assertion.
+ #t_float{elements={max(MinA, MinB),min(MaxA, MaxB)}};
+glb(#t_fun{arity=Same,type=TypeA}, #t_fun{arity=Same,type=TypeB}=T) ->
+ T#t_fun{type=meet(TypeA, TypeB)};
+glb(#t_fun{arity=any,type=TypeA}, #t_fun{type=TypeB}=T) ->
+ T#t_fun{type=meet(TypeA, TypeB)};
+glb(#t_fun{type=TypeA}=T, #t_fun{arity=any,type=TypeB}) ->
+ T#t_fun{type=meet(TypeA, TypeB)};
+glb(#t_integer{elements={_,_}}=T, #t_integer{elements=any}) ->
+ T;
+glb(#t_integer{elements=any}, #t_integer{elements={_,_}}=T) ->
+ T;
+glb(#t_integer{elements={MinA,MaxA}}, #t_integer{elements={MinB,MaxB}})
+ when MinA >= MinB, MinA =< MaxB;
+ MinB >= MinA, MinB =< MaxA ->
+ true = MinA =< MaxA andalso MinB =< MaxB, %Assertion.
+ #t_integer{elements={max(MinA, MinB),min(MaxA, MaxB)}};
+glb(#t_integer{}=T, number) ->
+ T;
+glb(#t_float{}=T, number) ->
+ T;
+glb(#t_list{type=TypeA,terminator=TermA},
+ #t_list{type=TypeB,terminator=TermB}) ->
+ %% A list is a union of `[type() | _]` and `[]`, so we're left with
+ %% nil when the element types are incompatible.
+ case {meet(TypeA, TypeB), meet(TermA, TermB)} of
+ {none, _} -> nil;
+ {_, none} -> nil;
+ {Type, Term} -> #t_list{type=Type,terminator=Term}
+ end;
+glb(#t_list{}=A, #t_cons{}=B) ->
+ glb(B, A);
+glb(#t_list{}, nil) ->
+ nil;
+glb(nil, #t_list{}) ->
+ nil;
+glb(number, #t_integer{}=T) ->
+ T;
+glb(number, #t_float{}=T) ->
+ T;
+glb(#t_map{super_key=SKeyA,super_value=SValueA},
+ #t_map{super_key=SKeyB,super_value=SValueB}) ->
+ %% Note the use of meet/2; elements don't need to be normal types.
+ SKey = meet(SKeyA, SKeyB),
+ SValue = meet(SValueA, SValueB),
+ #t_map{super_key=SKey,super_value=SValue};
+glb(#t_tuple{}=T1, #t_tuple{}=T2) ->
+ glb_tuples(T1, T2);
+glb(_, _) ->
+ %% Inconsistent types. There will be an exception at runtime.
+ none.
+
+glb_tuples(#t_tuple{size=Sz1,exact=Ex1}, #t_tuple{size=Sz2,exact=Ex2})
+ when Ex1, Sz1 < Sz2;
+ Ex2, Sz2 < Sz1 ->
+ none;
+glb_tuples(#t_tuple{size=Sz1,exact=Ex1,elements=Es1},
+ #t_tuple{size=Sz2,exact=Ex2,elements=Es2}) ->
+ Size = max(Sz1, Sz2),
+ Exact = Ex1 or Ex2,
+ case glb_elements(Es1, Es2) of
+ none ->
+ none;
+ Es ->
+ #t_tuple{size=Size,exact=Exact,elements=Es}
+ end.
+
+glb_elements(Es1, Es2) ->
+ Keys = maps:keys(Es1) ++ maps:keys(Es2),
+ glb_elements_1(Keys, Es1, Es2, #{}).
+
+glb_elements_1([Key | Keys], Es1, Es2, Acc) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ %% Note the use of meet/2; elements don't need to be normal types.
+ case meet(Type1, Type2) of
+ none -> none;
+ Type -> glb_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
+ end;
+ {#{ Key := Type1 }, _} ->
+ glb_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
+ {_, #{ Key := Type2 }} ->
+ glb_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+ end;
+glb_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
+%% Return the least upper bound of the types Type1 and Type2. The LUB is a more
+%% general type than Type1 and Type2, and is always a normal type.
+%%
+%% For example:
+%%
+%% lub(#t_integer{elements=any}, #t_integer=elements={0,3}}) ->
+%% #t_integer{}
+%%
+%% The LUB for two different types result in 'any' (not a union type!), which
+%% is the top element for our type lattice:
+%%
+%% lub(#t_integer{}, #t_map{}) -> any
+
+-spec lub(normal_type(), normal_type()) -> normal_type().
+
+lub(T, T) ->
+ verified_normal_type(T);
+lub(none, T) ->
+ verified_normal_type(T);
+lub(T, none) ->
+ verified_normal_type(T);
+lub(any, _) ->
+ any;
+lub(_, any) ->
+ any;
+lub(#t_atom{elements=[_|_]=Set1}, #t_atom{elements=[_|_]=Set2}) ->
+ Set = ordsets:union(Set1, Set2),
+ case ordsets:size(Set) of
+ Size when Size =< ?ATOM_SET_SIZE ->
+ #t_atom{elements=Set};
+ _Size ->
+ #t_atom{elements=any}
+ end;
+lub(#t_atom{elements=any}=T, #t_atom{elements=[_|_]}) -> T;
+lub(#t_atom{elements=[_|_]}, #t_atom{elements=any}=T) -> T;
+lub(#t_bitstring{size_unit=U1}, #t_bitstring{size_unit=U2}) ->
+ #t_bitstring{size_unit=gcd(U1, U2)};
+lub(#t_bitstring{size_unit=U1}, #t_bs_context{tail_unit=U2}) ->
+ #t_bs_matchable{tail_unit=gcd(U1, U2)};
+lub(#t_bitstring{size_unit=UnitA}, #t_bs_matchable{tail_unit=UnitB}) ->
+ lub_bs_matchable(UnitA, UnitB);
+lub(#t_bs_context{tail_unit=UnitA,slots=SlotsA,valid=ValidA},
+ #t_bs_context{tail_unit=UnitB,slots=SlotsB,valid=ValidB}) ->
+ #t_bs_context{tail_unit=gcd(UnitA, UnitB),
+ slots=min(SlotsA, SlotsB),
+ valid=ValidA band ValidB};
+lub(#t_bs_context{tail_unit=U1}, #t_bitstring{size_unit=U2}) ->
+ #t_bs_matchable{tail_unit=gcd(U1, U2)};
+lub(#t_bs_context{tail_unit=UnitA}, #t_bs_matchable{tail_unit=UnitB}) ->
+ lub_bs_matchable(UnitA, UnitB);
+lub(#t_bs_matchable{tail_unit=UnitA}, #t_bs_matchable{tail_unit=UnitB}) ->
+ lub_bs_matchable(UnitA, UnitB);
+lub(#t_bs_matchable{tail_unit=UnitA}, #t_bitstring{size_unit=UnitB}) ->
+ lub_bs_matchable(UnitA, UnitB);
+lub(#t_bs_matchable{tail_unit=UnitA}, #t_bs_context{tail_unit=UnitB}) ->
+ lub_bs_matchable(UnitA, UnitB);
+lub(#t_cons{type=TypeA,terminator=TermA},
+ #t_cons{type=TypeB,terminator=TermB}) ->
+ %% Note the use of join/2; elements don't need to be normal types.
+ #t_cons{type=join(TypeA,TypeB),terminator=join(TermA, TermB)};
+lub(#t_cons{type=TypeA,terminator=TermA},
+ #t_list{type=TypeB,terminator=TermB}) ->
+ #t_list{type=join(TypeA,TypeB),terminator=join(TermA, TermB)};
+lub(#t_cons{type=Type,terminator=Term}, nil) ->
+ #t_list{type=Type,terminator=Term};
+lub(#t_float{elements={MinA,MaxA}},
+ #t_float{elements={MinB,MaxB}}) ->
+ #t_float{elements={min(MinA,MinB),max(MaxA,MaxB)}};
+lub(#t_float{}, #t_float{}) ->
+ #t_float{};
+lub(#t_float{}, #t_integer{}) ->
+ number;
+lub(#t_float{}, number) ->
+ number;
+lub(#t_fun{arity=Same,type=TypeA}, #t_fun{arity=Same,type=TypeB}) ->
+ #t_fun{arity=Same,type=join(TypeA, TypeB)};
+lub(#t_fun{type=TypeA}, #t_fun{type=TypeB}) ->
+ #t_fun{type=join(TypeA, TypeB)};
+lub(#t_integer{elements={MinA,MaxA}},
+ #t_integer{elements={MinB,MaxB}}) ->
+ #t_integer{elements={min(MinA,MinB),max(MaxA,MaxB)}};
+lub(#t_integer{}, #t_integer{}) ->
+ #t_integer{};
+lub(#t_integer{}, #t_float{}) ->
+ number;
+lub(#t_integer{}, number) ->
+ number;
+lub(#t_list{type=TypeA,terminator=TermA},
+ #t_list{type=TypeB,terminator=TermB}) ->
+ #t_list{type=join(TypeA, TypeB),terminator=join(TermA, TermB)};
+lub(#t_list{}=A, #t_cons{}=B) ->
+ lub(B, A);
+lub(nil=A, #t_cons{}=B) ->
+ lub(B, A);
+lub(nil, #t_list{}=T) ->
+ T;
+lub(#t_list{}=T, nil) ->
+ T;
+lub(number, #t_integer{}) ->
+ number;
+lub(number, #t_float{}) ->
+ number;
+lub(#t_map{super_key=SKeyA,super_value=SValueA},
+ #t_map{super_key=SKeyB,super_value=SValueB}) ->
+ %% Note the use of join/2; elements don't need to be normal types.
+ SKey = join(SKeyA, SKeyB),
+ SValue = join(SValueA, SValueB),
+ #t_map{super_key=SKey,super_value=SValue};
+lub(#t_tuple{size=Sz,exact=ExactA,elements=EsA},
+ #t_tuple{size=Sz,exact=ExactB,elements=EsB}) ->
+ Exact = ExactA and ExactB,
+ Es = lub_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,exact=Exact,elements=Es};
+lub(#t_tuple{size=SzA,elements=EsA}, #t_tuple{size=SzB,elements=EsB}) ->
+ Sz = min(SzA, SzB),
+ Es = lub_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,elements=Es};
+lub(_T1, _T2) ->
+ %%io:format("~p ~p\n", [_T1,_T2]),
+ any.
+
+lub_bs_matchable(UnitA, UnitB) ->
+ #t_bs_matchable{tail_unit=gcd(UnitA, UnitB)}.
+
+lub_tuple_elements(MinSize, EsA, EsB) ->
+ Es0 = lub_elements(EsA, EsB),
+ maps:filter(fun(Index, _Type) -> Index =< MinSize end, Es0).
+
+lub_elements(Es1, Es2) ->
+ Keys = if
+ map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
+ map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
+ end,
+ lub_elements_1(Keys, Es1, Es2, #{}).
+
+lub_elements_1([Key | Keys], Es1, Es2, Acc0) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ %% Note the use of join/2; elements don't need to be normal types.
+ Acc = set_tuple_element(Key, join(Type1, Type2), Acc0),
+ lub_elements_1(Keys, Es1, Es2, Acc);
+ {#{}, #{}} ->
+ lub_elements_1(Keys, Es1, Es2, Acc0)
+ end;
+lub_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
+%%
+
+gcd(A, B) ->
+ case A rem B of
+ 0 -> B;
+ X -> gcd(B, X)
+ end.
+
+%%
+
+record_key(#t_tuple{exact=true,size=Size,elements=#{ 1 := Tag }}) ->
+ case is_singleton_type(Tag) of
+ true -> {Size, Tag};
+ false -> none
+ end;
+record_key(_) ->
+ none.
+
+new_tuple_set(T) ->
+ case record_key(T) of
+ none -> T;
+ Key -> [{Key, T}]
+ end.
+
+%%
+
+shrink_union(#t_union{other=any}) ->
+ any;
+shrink_union(#t_union{atom=Atom,list=none,number=none,
+ tuple_set=none,other=none}) ->
+ Atom;
+shrink_union(#t_union{atom=none,list=List,number=none,
+ tuple_set=none,other=none}) ->
+ List;
+shrink_union(#t_union{atom=none,list=none,number=Number,
+ tuple_set=none,other=none}) ->
+ Number;
+shrink_union(#t_union{atom=none,list=none,number=none,
+ tuple_set=#t_tuple{}=Tuple,other=none}) ->
+ Tuple;
+shrink_union(#t_union{atom=none,list=none,number=none,
+ tuple_set=[{_Key, Record}],other=none}) ->
+ #t_tuple{} = Record; %Assertion.
+shrink_union(#t_union{atom=none,list=none,number=none,
+ tuple_set=none,other=Other}) ->
+ Other;
+shrink_union(#t_union{}=T) ->
+ T.
+
+%% Verifies that the given type is well-formed.
+
+-spec verified_type(T) -> T when
+ T :: type().
+
+verified_type(#t_union{atom=Atom,
+ list=List,
+ number=Number,
+ tuple_set=TSet,
+ other=Other}=T) ->
+ _ = verified_normal_type(Atom),
+ _ = verified_normal_type(List),
+ _ = verified_normal_type(Number),
+ _ = verify_tuple_set(TSet),
+ _ = verified_normal_type(Other),
+ T;
+verified_type(T) ->
+ verified_normal_type(T).
+
+verify_tuple_set([_|_]=T) ->
+ _ = verify_tuple_set_1(T, 0),
+ T;
+verify_tuple_set(#t_tuple{}=T) ->
+ none = record_key(T), %Assertion.
+ T;
+verify_tuple_set(none=T) ->
+ T.
+
+verify_tuple_set_1([{_Tag, Record} | Records], Size) ->
+ true = Size =< ?TUPLE_SET_LIMIT, %Assertion.
+ _ = verified_normal_type(Record),
+ verify_tuple_set_1(Records, Size + 1);
+verify_tuple_set_1([], _Size) ->
+ ok.
+
+-spec verified_normal_type(T) -> T when
+ T :: normal_type().
+
+verified_normal_type(any=T) -> T;
+verified_normal_type(none=T) -> T;
+verified_normal_type(#t_atom{elements=any}=T) -> T;
+verified_normal_type(#t_atom{elements=[_|_]}=T) -> T;
+verified_normal_type(#t_bitstring{size_unit=U}=T)
+ when is_integer(U), U >= 1 ->
+ T;
+verified_normal_type(#t_bs_context{tail_unit=U}=T)
+ when is_integer(U), U >= 1 ->
+ T;
+verified_normal_type(#t_bs_matchable{tail_unit=U}=T)
+ when is_integer(U), U >= 1 ->
+ T;
+verified_normal_type(#t_cons{type=Type,terminator=Term}=T) ->
+ _ = verified_type(Type),
+ _ = verified_type(Term),
+ T;
+verified_normal_type(#t_fun{arity=Arity,type=ReturnType}=T)
+ when Arity =:= any; is_integer(Arity) ->
+ _ = verified_type(ReturnType),
+ T;
+verified_normal_type(#t_float{}=T) -> T;
+verified_normal_type(#t_integer{elements=any}=T) -> T;
+verified_normal_type(#t_integer{elements={Min,Max}}=T)
+ when is_integer(Min), is_integer(Max), Min =< Max ->
+ T;
+verified_normal_type(#t_list{type=Type,terminator=Term}=T) ->
+ _ = verified_type(Type),
+ _ = verified_type(Term),
+ T;
+verified_normal_type(#t_map{}=T) -> T;
+verified_normal_type(nil=T) -> T;
+verified_normal_type(number=T) -> T;
+verified_normal_type(#t_tuple{size=Size,elements=Es}=T) ->
+ %% All known elements must have a valid index and type (which may be a
+ %% union). 'any' is prohibited since it's implicit and should never be
+ %% present in the map, and a 'none' element ought to have reduced the
+ %% entire tuple to 'none'.
+ maps:fold(fun(Index, Element, _) when is_integer(Index),
+ 1 =< Index, Index =< Size,
+ Index =< ?TUPLE_ELEMENT_LIMIT,
+ Element =/= any, Element =/= none ->
+ verified_type(Element)
+ end, [], Es),
+ T.
diff --git a/lib/compiler/src/beam_types.hrl b/lib/compiler/src/beam_types.hrl
new file mode 100644
index 0000000000..c20e1ce7a0
--- /dev/null
+++ b/lib/compiler/src/beam_types.hrl
@@ -0,0 +1,154 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Common term types for passes operating on beam SSA and assembly. Helper
+%% functions for wrangling these can be found in beam_types.erl
+%%
+%% The type lattice is as follows:
+%%
+%% any Any Erlang term (top element).
+%%
+%% - #t_atom{} Atom, or a set thereof.
+%% - #t_bs_matchable{} Binary-matchable types.
+%% - #t_bitstring{} Bitstring.
+%% - #t_bs_context{} Match context.
+%% - #t_fun{} Fun.
+%% - #t_map{} Map.
+%% - number Any number.
+%% -- #t_float{} Floating point number.
+%% -- #t_integer{} Integer.
+%% - #t_list{} Any list.
+%% -- #t_cons{} Cons (nonempty list).
+%% -- nil The empty list.
+%% - #t_tuple{} Tuple.
+%%
+%% none No type (bottom element).
+%%
+%% We also use #t_union{} to represent conflicting types produced by certain
+%% expressions, e.g. the "#t_atom{} or #t_tuple{}" of lists:keyfind/3, which is
+%% very useful for preserving type information when we would otherwise have
+%% reduced it to 'any'. Since few operations can make direct use of this extra
+%% type information, types should generally be normalized to one of the above
+%% before use.
+%%
+%% When adding a new type it's important that the lattice stays consistent [1].
+%% In brief, the following properties must hold:
+%%
+%% * All types must be unambiguous; any given value must narrow down to a
+%% single type, and multiple supertypes are not allowed.
+%%
+%% * `meet` is used when we know more about a value (e.g. type tests), so it
+%% must not return a more general type than either of its arguments. In other
+%% words, we're only allowed to *add* knowledge in a `meet`.
+%%
+%% * `join` is used when we know less about a value (e.g. phi node), so it
+%% must not return a more specific type than either of its arguments. In
+%% other words we're only allowed to *remove* knowledge in a `join`.
+%%
+%% * Both `join` and `meet` must be commutative, associative, and idempotent.
+%%
+%% Maintaining the above may seem trivial but subtle errors can creep in when
+%% adding fields or restrictions to a type. ?TUPLE_ELEMENT_LIMIT is a great
+%% example of this.
+%%
+%% The property test suite ensures that the above holds, so don't forget to
+%% add your new types there. You should also consider increasing ?REPETITIONS
+%% during development to ensure it hits all nooks and crannies.
+%%
+%% [1] https://en.wikipedia.org/wiki/Lattice_(order)#General_lattice
+
+-define(ATOM_SET_SIZE, 5).
+
+-record(t_atom, {elements=any :: 'any' | ordsets:ordset(atom())}).
+-record(t_bitstring, {size_unit=1 :: pos_integer()}).
+-record(t_bs_context, {tail_unit=1 :: pos_integer(),
+ slots=0 :: non_neg_integer(),
+ valid=0 :: non_neg_integer()}).
+-record(t_bs_matchable, {tail_unit=1}).
+-record(t_float, {elements=any :: 'any' | {float(),float()}}).
+-record(t_fun, {arity=any :: arity() | 'any',
+ type=any :: type() }).
+-record(t_integer, {elements=any :: 'any' | {integer(),integer()}}).
+
+%% `super_key` and `super_value` are the join of all key and value types.
+%%
+%% Note that we don't track specific elements as we have no obvious way to
+%% limit them. See ?TUPLE_ELEMENT_LIMIT for details.
+-record(t_map, {super_key=any :: type(),
+ super_value=any :: type()}).
+
+%% `type` is the join of all list elements, and `terminator` is the tail of the
+%% last cons cell ('nil' for proper lists).
+%%
+%% Note that `type` may not be updated unless the entire list is known, and
+%% that the terminator being known is not a guarantee that the rest of the list
+%% is.
+-record(t_cons, {type=any :: type(), terminator=any :: type()}).
+-record(t_list, {type=any :: type(), terminator=any :: type()}).
+
+-record(t_tuple, {size=0 :: integer(),
+ exact=false :: boolean(),
+ elements=#{} :: tuple_elements()}).
+
+%% Known element types, where the key is a 1-based integer index. Unknown
+%% elements are assumed to be 'any', and indexes above ?TUPLE_ELEMENT_LIMIT are
+%% ignored for performance reasons.
+%%
+%% Cutting off all indexes above a certain limit may seem strange, but is
+%% required to ensure that a meet of two types always returns a type that's at
+%% least as specific as either type. Consider the following types:
+%%
+%% A = #t_tuple{elements=#{ ... elements 1 .. 6 ... }}
+%% B = #t_tuple{elements=#{ ... elements 7 .. 13 ... }}
+%%
+%% If we'd collapse types once a tuple has more than 12 elements, meet(A, B)
+%% would suddenly be less specific than either A or B. Ignoring all elements
+%% above a certain index avoids this problem, at the small price of losing type
+%% information in huge tuples.
+
+-define(TUPLE_ELEMENT_LIMIT, 12).
+-type tuple_elements() :: #{ Key :: pos_integer() => type() }.
+
+-type normal_type() :: any | none |
+ number | #t_float{} | #t_integer{} |
+ #t_atom{} |
+ #t_bitstring{} | #t_bs_context{} | #t_bs_matchable{} |
+ #t_fun{} |
+ #t_list{} | #t_cons{} | nil |
+ #t_map{} |
+ #t_tuple{}.
+
+-type record_key() :: {Arity :: integer(), Tag :: normal_type() }.
+-type record_set() :: ordsets:ordset({record_key(), #t_tuple{}}).
+-type tuple_set() :: #t_tuple{} | record_set().
+
+-record(t_union, {atom=none :: none | #t_atom{},
+ list=none :: none | #t_list{} | #t_cons{} | nil,
+ number=none :: none | number | #t_float{} | #t_integer{},
+ tuple_set=none :: none | tuple_set(),
+ other=none :: normal_type()}).
+
+-type type() :: #t_union{} | normal_type().
+
+-ifdef(BEAM_TYPES_INTERNAL).
+%% Internal constants used by beam_types.erl and its whitebox tests
+-define(TUPLE_SET_LIMIT, 12).
+-define(MAX_TYPE_DEPTH, 4).
+-endif.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 6e6574c0b3..9bf18911c5 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -88,11 +88,12 @@ split_even(Rs) -> split_even(Rs, [], []).
%%%
%%% Local functions.
%%%
-
replace_labels_1([{test,Test,{f,Lbl},Ops}|Is], Acc, D, Fb) ->
- replace_labels_1(Is, [{test,Test,{f,label(Lbl, D, Fb)},Ops}|Acc], D, Fb);
+ I = {test,Test,{f,label(Lbl, D, Fb)},Ops},
+ replace_labels_1(Is, [I | Acc], D, Fb);
replace_labels_1([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D, Fb) ->
- replace_labels_1(Is, [{test,Test,{f,label(Lbl, D, Fb)},Live,Ops,Dst}|Acc], D, Fb);
+ I = {test,Test,{f,label(Lbl, D, Fb)},Live,Ops,Dst},
+ replace_labels_1(Is, [I | Acc], D, Fb);
replace_labels_1([{select,I,R,{f,Fail0},Vls0}|Is], Acc, D, Fb) ->
Vls = map(fun ({f,L}) -> {f,label(L, D, Fb)};
(Other) -> Other
@@ -134,6 +135,9 @@ replace_labels_1([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D, Fb)
replace_labels_1(Is, [{I,{f,label(Lbl, D, Fb)},Op,Src,Dst,Live,List}|Acc], D, Fb);
replace_labels_1([{get_map_elements=I,{f,Lbl},Src,List}|Is], Acc, D, Fb) when Lbl =/= 0 ->
replace_labels_1(Is, [{I,{f,label(Lbl, D, Fb)},Src,List}|Acc], D, Fb);
+replace_labels_1([{bs_start_match4,{f,Lbl},Live,Src,Dst}|Is], Acc, D, Fb) ->
+ I = {bs_start_match4,{f,label(Lbl, D, Fb)},Live,Src,Dst},
+ replace_labels_1(Is, [I | Acc], D, Fb);
replace_labels_1([I|Is], Acc, D, Fb) ->
replace_labels_1(Is, [I|Acc], D, Fb);
replace_labels_1([], Acc, _, _) -> Acc.
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 12aaa01b6b..8a71ac35e3 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -19,6 +19,10 @@
-module(beam_validator).
+-include("beam_types.hrl").
+
+-define(UNICODE_MAX, (16#10FFFF)).
+
-compile({no_auto_import,[min/2]}).
%% Avoid warning for local function error/1 clashing with autoimported BIF.
@@ -26,7 +30,6 @@
%% Interface for compiler.
-export([module/2, format_error/1]).
--export([type_anno/1, type_anno/2, type_anno/4]).
-import(lists, [dropwhile/2,foldl/3,member/2,reverse/1,sort/1,zip/2]).
@@ -45,34 +48,6 @@ module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)
{error,[{atom_to_list(Mod),Es}]}
end.
-%% Provides a stable interface for type annotations, used by certain passes to
-%% indicate that we can safely assume that a register has a given type.
--spec type_anno(term()) -> term().
-type_anno(atom) -> {atom,[]};
-type_anno(bool) -> bool;
-type_anno({binary,_}) -> binary;
-type_anno(cons) -> cons;
-type_anno(float) -> {float,[]};
-type_anno(integer) -> {integer,[]};
-type_anno(list) -> list;
-type_anno(map) -> map;
-type_anno(match_context) -> match_context;
-type_anno(number) -> number;
-type_anno(nil) -> nil.
-
--spec type_anno(term(), term()) -> term().
-type_anno(atom, Value) when is_atom(Value) -> {atom, Value};
-type_anno(float, Value) when is_float(Value) -> {float, Value};
-type_anno(integer, Value) when is_integer(Value) -> {integer, Value}.
-
--spec type_anno(term(), term(), term(), term()) -> term().
-type_anno(tuple, Size, Exact, Elements) when is_integer(Size), Size >= 0,
- is_map(Elements) ->
- case Exact of
- true -> {tuple, Size, Elements};
- false -> {tuple, [Size], Elements}
- end.
-
-spec format_error(term()) -> iolist().
format_error({{_M,F,A},{I,Off,limit}}) ->
@@ -119,7 +94,7 @@ format_error(Error) ->
%% format as used in the compiler and in .S files.
validate(Module, Fs) ->
- Ft = index_parameter_types(Fs, []),
+ Ft = build_function_table(Fs, []),
validate_0(Module, Fs, Ft).
validate_0(_Module, [], _) -> [];
@@ -127,17 +102,24 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
try validate_1(Code, Name, Ar, Entry, Ft) of
_ -> validate_0(Module, Fs, Ft)
catch
- throw:Error ->
- %% Controlled error.
- [Error|validate_0(Module, Fs, Ft)];
+ throw:Error ->
+ %% Controlled error.
+ [Error|validate_0(Module, Fs, Ft)];
Class:Error:Stack ->
%% Crash.
io:fwrite("Function: ~w/~w\n", [Name,Ar]),
erlang:raise(Class, Error, Stack)
end.
+-record(t_abstract, {kind}).
+
+%% The types are the same as in 'beam_types.hrl', with the addition of
+%% #t_abstract{} that describes tuples under construction, match context
+%% positions, and so on.
+-type validator_type() :: #t_abstract{} | type().
+
-record(value_ref, {id :: index()}).
--record(value, {op :: term(), args :: [argument()], type :: type()}).
+-record(value, {op :: term(), args :: [argument()], type :: validator_type()}).
-type argument() :: #value_ref{} | literal().
@@ -149,34 +131,28 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
{literal, term()} |
nil.
--type tuple_sz() :: [non_neg_integer()] | %% Inexact
- non_neg_integer(). %% Exact.
-
-%% Match context type.
--record(ms,
- {id=make_ref() :: reference(), %Unique ID.
- valid=0 :: non_neg_integer(), %Valid slots
- slots=0 :: non_neg_integer() %Number of slots
- }).
-
--type type() :: binary |
- cons |
- list |
- map |
- nil |
- #ms{} |
- ms_position |
- none |
- number |
- term |
- tuple_in_progress |
- {tuple, tuple_sz(), #{ literal() => type() }} |
- literal().
-
+%% Register tags describe the state of the register rather than the value they
+%% contain (if any).
+%%
+%% initialized The register has been initialized with some valid term
+%% so that it is safe to pass to the garbage collector.
+%% NOT safe to use in any other way (will not crash the
+%% emulator, but clearly points to a bug in the compiler).
+%%
+%% uninitialized The register contains any old garbage and can not be
+%% passed to the garbage collector.
+%%
+%% {catchtag,[Lbl]} A special term used within a catch. Must only be used
+%% by the catch instructions; NOT safe to use in other
+%% instructions.
+%%
+%% {trytag,[Lbl]} A special term used within a try block. Must only be
+%% used by the catch instructions; NOT safe to use in other
+%% instructions.
-type tag() :: initialized |
uninitialized |
- {catchtag, [label()]} |
- {trytag, [label()]}.
+ {catchtag, ordsets:ordset(label())} |
+ {trytag, ordsets:ordset(label())}.
-type x_regs() :: #{ {x, index()} => #value_ref{} }.
-type y_regs() :: #{ {y, index()} => tag() | #value_ref{} }.
@@ -200,11 +176,11 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
numy=none :: none | undecided | index(),
%% Available heap size.
h=0,
- %Available heap size for floats.
+ %%Available heap size for floats.
hf=0,
%% Floating point state.
fls=undefined,
- %% List of hot catch/try labels
+ %% List of hot catch/try tags
ct=[],
%% Previous instruction was setelement/3.
setelem=false,
@@ -230,36 +206,34 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
branched=gb_trees:empty() :: branched_tab(),
%% All defined labels
labels=gb_sets:empty() :: label_set(),
- %% Argument information of other functions in the module
+ %% Information of other functions in the module
ft=gb_trees:empty() :: ft_tab(),
%% Counter for #value_ref{} creation
ref_ctr=0 :: index()
}).
-index_parameter_types([{function,_,_,Entry,Code0}|Fs], Acc0) ->
+build_function_table([{function,_,Arity,Entry,Code0}|Fs], Acc0) ->
Code = dropwhile(fun({label,L}) when L =:= Entry -> false;
(_) -> true
end, Code0),
case Code of
[{label,Entry}|Is] ->
- Acc = index_parameter_types_1(Is, Entry, Acc0),
- index_parameter_types(Fs, Acc);
+ Info = #{ arity => Arity,
+ parameter_info => find_parameter_info(Is, #{}) },
+ build_function_table(Fs, [{Entry, Info} | Acc0]);
_ ->
- %% Something serious is wrong. Ignore it for now.
+ %% Something is seriously wrong. Ignore it for now.
%% It will be detected and diagnosed later.
- index_parameter_types(Fs, Acc0)
+ build_function_table(Fs, Acc0)
end;
-index_parameter_types([], Acc) ->
+build_function_table([], Acc) ->
gb_trees:from_orddict(sort(Acc)).
-index_parameter_types_1([{'%', {type_info, Reg, Type0}} | Is], Entry, Acc) ->
- Type = case Type0 of
- match_context -> #ms{};
- _ -> Type0
- end,
- Key = {Entry, Reg},
- index_parameter_types_1(Is, Entry, [{Key, Type} | Acc]);
-index_parameter_types_1(_, _, Acc) ->
+find_parameter_info([{'%', {var_info, Reg, Info}} | Is], Acc) ->
+ find_parameter_info(Is, Acc#{ Reg => Info });
+find_parameter_info([{'%', _} | Is], Acc) ->
+ find_parameter_info(Is, Acc);
+find_parameter_info(_, Acc) ->
Acc.
validate_1(Is, Name, Arity, Entry, Ft) ->
@@ -278,7 +252,7 @@ validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1, Ft) ->
EntryOK ->
Vst0 = init_vst(Arity, Ls1, Ls2, Ft),
MFA = {Mod,Name,Arity},
- Vst = valfun(Is, MFA, Offset, Vst0),
+ Vst = validate_instrs(Is, MFA, Offset, Vst0),
validate_fun_info_branches(Ls1, MFA, Vst);
true ->
error({{Mod,Name,Arity},{first(Is),Offset,no_entry_label}})
@@ -331,12 +305,12 @@ init_vst(Arity, Ls1, Ls2, Ft) ->
init_function_args(-1, Vst) ->
Vst;
init_function_args(X, Vst) ->
- init_function_args(X - 1, create_term(term, argument, [], {x,X}, Vst)).
+ init_function_args(X - 1, create_term(any, argument, [], {x,X}, Vst)).
kill_heap_allocation(St) ->
St#st{h=0,hf=0}.
-valfun([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) ->
+validate_instrs([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) ->
Targets = gb_trees:keys(Targets0),
Labels = gb_sets:to_list(Labels0),
case Targets -- Labels of
@@ -345,109 +319,134 @@ valfun([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) ->
Error = {undef_labels,Undef},
error({MFA,Error})
end;
-valfun([I|Is], MFA, Offset, Vst0) ->
- valfun(Is, MFA, Offset+1,
+validate_instrs([I|Is], MFA, Offset, Vst0) ->
+ validate_instrs(Is, MFA, Offset+1,
try
- Vst = val_dsetel(I, Vst0),
- valfun_1(I, Vst)
+ Vst = validate_mutation(I, Vst0),
+ vi_safe(I, Vst)
catch Error ->
error({MFA,{I,Offset,Error}})
end).
-%% Instructions that are allowed in dead code or when failing,
-%% that is while the state is undecided in some way.
-valfun_1({label,Lbl}, #vst{current=St0,
- ref_ctr=Counter0,
- branched=B,
- labels=Lbls}=Vst) ->
+%%%
+%%% vi_safe/2 handles instructions that will never throw an exception, and can
+%%% thus be used when the state is undecided in some way.
+%%%
+vi_safe({label,Lbl}, #vst{current=St0,
+ ref_ctr=Counter0,
+ branched=B,
+ labels=Lbls}=Vst) ->
{St, Counter} = merge_states(Lbl, St0, B, Counter0),
Vst#vst{current=St,
ref_ctr=Counter,
branched=gb_trees:enter(Lbl, St, B),
labels=gb_sets:add(Lbl, Lbls)};
-valfun_1(_I, #vst{current=none}=Vst) ->
- %% Ignore instructions after erlang:error/1,2, which
- %% the original R10B compiler thought would return.
+vi_safe(_I, #vst{current=none}=Vst) ->
+ %% Ignore all unreachable code.
Vst;
-valfun_1({badmatch,Src}, Vst) ->
- assert_durable_term(Src, Vst),
- verify_y_init(Vst),
- kill_state(Vst);
-valfun_1({case_end,Src}, Vst) ->
- assert_durable_term(Src, Vst),
- verify_y_init(Vst),
- kill_state(Vst);
-valfun_1(if_end, Vst) ->
- verify_y_init(Vst),
- kill_state(Vst);
-valfun_1({try_case_end,Src}, Vst) ->
- verify_y_init(Vst),
- assert_durable_term(Src, Vst),
- kill_state(Vst);
-%% Instructions that cannot cause exceptions
-valfun_1({bs_get_tail,Ctx,Dst,Live}, Vst0) ->
- bsm_validate_context(Ctx, Vst0),
+vi_safe({bs_get_tail,Ctx,Dst,Live}, Vst0) ->
+ assert_type(#t_bs_context{}, Ctx, Vst0),
verify_live(Live, Vst0),
verify_y_init(Vst0),
+
+ #t_bs_context{tail_unit=Unit} = get_raw_type(Ctx, Vst0),
+
Vst = prune_x_regs(Live, Vst0),
- extract_term(binary, bs_get_tail, [Ctx], Dst, Vst, Vst0);
-valfun_1(bs_init_writable=I, Vst) ->
+ extract_term(#t_bitstring{size_unit=Unit}, bs_get_tail, [Ctx], Dst,
+ Vst, Vst0);
+vi_safe(bs_init_writable=I, Vst) ->
call(I, 1, Vst);
-valfun_1(build_stacktrace=I, Vst) ->
+vi_safe(build_stacktrace=I, Vst) ->
call(I, 1, Vst);
-valfun_1({move,Src,Dst}, Vst) ->
+vi_safe({move,Src,Dst}, Vst) ->
assign(Src, Dst, Vst);
-valfun_1({fmove,Src,{fr,_}=Dst}, Vst) ->
- assert_type(float, Src, Vst),
+vi_safe({swap,RegA,RegB}, Vst0) ->
+ assert_movable(RegA, Vst0),
+ assert_movable(RegB, Vst0),
+
+ %% We don't expect fragile registers to be swapped.
+ %% Therefore, we can conservatively make both registers
+ %% fragile if one of the register is fragile instead of
+ %% swapping the fragility of the registers.
+ Sources = [RegA,RegB],
+ Vst1 = propagate_fragility(RegA, Sources, Vst0),
+ Vst2 = propagate_fragility(RegB, Sources, Vst1),
+
+ %% Swap the value references.
+ VrefA = get_reg_vref(RegA, Vst2),
+ VrefB = get_reg_vref(RegB, Vst2),
+ Vst = set_reg_vref(VrefB, RegA, Vst2),
+ set_reg_vref(VrefA, RegB, Vst);
+vi_safe({fmove,Src,{fr,_}=Dst}, Vst) ->
+ assert_type(#t_float{}, Src, Vst),
set_freg(Dst, Vst);
-valfun_1({fmove,{fr,_}=Src,Dst}, Vst0) ->
+vi_safe({fmove,{fr,_}=Src,Dst}, Vst0) ->
assert_freg_set(Src, Vst0),
assert_fls(checked, Vst0),
Vst = eat_heap_float(Vst0),
- create_term({float,[]}, fmove, [], Dst, Vst);
-valfun_1({kill,Reg}, Vst) ->
+ create_term(#t_float{}, fmove, [], Dst, Vst);
+vi_safe({kill,Reg}, Vst) ->
create_tag(initialized, kill, [], Reg, Vst);
-valfun_1({init,Reg}, Vst) ->
+vi_safe({init,Reg}, Vst) ->
create_tag(initialized, init, [], Reg, Vst);
-valfun_1({test_heap,Heap,Live}, Vst) ->
+vi_safe({test_heap,Heap,Live}, Vst) ->
test_heap(Heap, Live, Vst);
-valfun_1({bif,Op,{f,_},Ss,Dst}=I, Vst) ->
- case is_bif_safe(Op, length(Ss)) of
- false ->
- %% Since the BIF can fail, make sure that any catch state
- %% is updated.
- valfun_2(I, Vst);
- true ->
- %% It can't fail, so we finish handling it here (not updating
- %% catch state).
- validate_src(Ss, Vst),
- Type = bif_return_type(Op, Ss, Vst),
- extract_term(Type, {bif,Op}, Ss, Dst, Vst)
+vi_safe({bif,Op,{f,0},Ss,Dst}=I, Vst0) ->
+ case will_bif_succeed(Op, Ss, Vst0) of
+ yes ->
+ %% This BIF cannot fail, handle it here without updating catch
+ %% state.
+ validate_bif(Op, cannot_fail, Ss, Dst, Vst0);
+ no ->
+ %% The stack will be scanned, so Y registers must be initialized.
+ Vst = branch_exception(Vst0),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ maybe ->
+ %% The BIF can fail, make sure that any catch state is updated.
+ Vst = branch_exception(Vst0),
+ vi_float(I, Vst)
+ end;
+vi_safe({gc_bif,Op,{f,0},Live,Ss,Dst}=I, Vst0) ->
+ case will_bif_succeed(Op, Ss, Vst0) of
+ yes ->
+ validate_gc_bif(Op, cannot_fail, Ss, Dst, Live, Vst0);
+ no ->
+ Vst = branch_exception(Vst0),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ maybe ->
+ Vst = branch_exception(Vst0),
+ assert_float_checked(Vst),
+ vi_float(I, Vst)
end;
%% Put instructions.
-valfun_1({put_list,A,B,Dst}, Vst0) ->
- assert_term(A, Vst0),
- assert_term(B, Vst0),
+vi_safe({put_list,A,B,Dst}, Vst0) ->
Vst = eat_heap(2, Vst0),
- create_term(cons, put_list, [A, B], Dst, Vst);
-valfun_1({put_tuple2,Dst,{list,Elements}}, Vst0) ->
+
+ Head = get_term_type(A, Vst),
+ Tail = get_term_type(B, Vst),
+
+ create_term(beam_types:make_cons(Head, Tail), put_list, [A, B], Dst, Vst);
+vi_safe({put_tuple2,Dst,{list,Elements}}, Vst0) ->
_ = [assert_term(El, Vst0) || El <- Elements],
Size = length(Elements),
Vst = eat_heap(Size+1, Vst0),
{Es,_} = foldl(fun(Val, {Es0, Index}) ->
Type = get_term_type(Val, Vst0),
- Es = set_element_type({integer,Index}, Type, Es0),
+ Es = beam_types:set_tuple_element(Index, Type, Es0),
{Es, Index + 1}
end, {#{}, 1}, Elements),
- Type = {tuple,Size,Es},
+ Type = #t_tuple{exact=true,size=Size,elements=Es},
create_term(Type, put_tuple2, [], Dst, Vst);
-valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
+vi_safe({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
Vst1 = eat_heap(1, Vst0),
- Vst = create_term(tuple_in_progress, put_tuple, [], Dst, Vst1),
+ Vst = create_term(#t_abstract{kind=unfinished_tuple}, put_tuple, [],
+ Dst, Vst1),
#vst{current=St0} = Vst,
St = St0#st{puts_left={Sz,{Dst,Sz,#{}}}},
Vst#vst{current=St};
-valfun_1({put,Src}, Vst0) ->
+vi_safe({put,Src}, Vst0) ->
assert_term(Src, Vst0),
Vst = eat_heap(1, Vst0),
#vst{current=St0} = Vst,
@@ -455,35 +454,46 @@ valfun_1({put,Src}, Vst0) ->
#st{puts_left=none} ->
error(not_building_a_tuple);
#st{puts_left={1,{Dst,Sz,Es0}}} ->
- Es = Es0#{ {integer,Sz} => get_term_type(Src, Vst0) },
+ ElementType = get_term_type(Src, Vst0),
+ Es = beam_types:set_tuple_element(Sz, ElementType, Es0),
St = St0#st{puts_left=none},
- create_term({tuple,Sz,Es}, put_tuple, [], Dst, Vst#vst{current=St});
+ Type = #t_tuple{exact=true,size=Sz,elements=Es},
+ create_term(Type, put_tuple, [], Dst, Vst#vst{current=St});
#st{puts_left={PutsLeft,{Dst,Sz,Es0}}} when is_integer(PutsLeft) ->
Index = Sz - PutsLeft + 1,
- Es = Es0#{ {integer,Index} => get_term_type(Src, Vst0) },
+ ElementType = get_term_type(Src, Vst0),
+ Es = beam_types:set_tuple_element(Index, ElementType, Es0),
St = St0#st{puts_left={PutsLeft-1,{Dst,Sz,Es}}},
Vst#vst{current=St}
end;
+%% This instruction never fails, though it may be invalid in some contexts; see
+%% validate_mutation/2
+vi_safe({set_tuple_element,Src,Tuple,N}, Vst) ->
+ I = N + 1,
+ assert_term(Src, Vst),
+ assert_type(#t_tuple{size=I}, Tuple, Vst),
+ %% Manually update the tuple type; we can't rely on the ordinary update
+ %% helpers as we must support overwriting (rather than just widening or
+ %% narrowing) known elements, and we can't use extract_term either since
+ %% the source tuple may be aliased.
+ #t_tuple{elements=Es0}=Type = normalize(get_term_type(Tuple, Vst)),
+ Es = beam_types:set_tuple_element(I, get_term_type(Src, Vst), Es0),
+ override_type(Type#t_tuple{elements=Es}, Tuple, Vst);
%% Instructions for optimization of selective receives.
-valfun_1({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) ->
+vi_safe({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) ->
set_receive_marker(initialized, Vst);
-valfun_1({recv_set,{f,Fail}}, Vst) when is_integer(Fail) ->
+vi_safe({recv_set,{f,Fail}}, Vst) when is_integer(Fail) ->
set_receive_marker(committed, Vst);
%% Misc.
-valfun_1(remove_message, Vst0) ->
+vi_safe(remove_message, Vst0) ->
Vst = set_receive_marker(none, Vst0),
%% The message term is no longer fragile. It can be used
%% without restrictions.
remove_fragility(Vst);
-valfun_1({'%', {type_info, Reg, match_context}}, Vst) ->
- update_type(fun meet/2, #ms{}, Reg, Vst);
-valfun_1({'%', {type_info, Reg, Type}}, Vst) ->
- %% Explicit type information inserted by optimization passes to indicate
- %% that Reg has a certain type, so that we can accept cross-function type
- %% optimizations.
- update_type(fun meet/2, Type, Reg, Vst);
-valfun_1({'%', {remove_fragility, Reg}}, Vst) ->
+vi_safe({'%', {var_info, Reg, Info}}, Vst) ->
+ validate_var_info(Info, Reg, Vst);
+vi_safe({'%', {remove_fragility, Reg}}, Vst) ->
%% This is a hack to make prim_eval:'receive'/2 work.
%%
%% Normally it's illegal to pass fragile terms as a function argument as we
@@ -491,40 +501,50 @@ valfun_1({'%', {remove_fragility, Reg}}, Vst) ->
%% prim_eval:'receive'/2 won't leak the term, nor cause a GC since it's
%% disabled while matching messages.
remove_fragility(Reg, Vst);
-valfun_1({'%',_}, Vst) ->
+vi_safe({'%',_}, Vst) ->
Vst;
-valfun_1({line,_}, Vst) ->
+vi_safe({line,_}, Vst) ->
Vst;
-%% Exception generating calls
-valfun_1({call_ext,Live,Func}=I, Vst) ->
- case call_return_type(Func, Vst) of
- exception ->
- verify_live(Live, Vst),
- %% The stack will be scanned, so Y registers
- %% must be initialized.
- verify_y_init(Vst),
- kill_state(Vst);
- _ ->
- valfun_2(I, Vst)
- end;
-valfun_1(_I, #vst{current=#st{ct=undecided}}) ->
+
+%%
+%% Calls; these may be okay when the try/catch state or stack is undecided,
+%% depending on whether they always succeed or always fail.
+%%
+vi_safe({apply,Live}, Vst) ->
+ validate_body_call(apply, Live+2, Vst);
+vi_safe({apply_last,Live,N}, Vst) ->
+ validate_tail_call(N, apply, Live+2, Vst);
+vi_safe({call,Live,Func}, Vst) ->
+ validate_body_call(Func, Live, Vst);
+vi_safe({call_ext,Live,Func}, Vst) ->
+ validate_body_call(Func, Live, Vst);
+vi_safe({call_only,Live,Func}, Vst) ->
+ validate_tail_call(none, Func, Live, Vst);
+vi_safe({call_ext_only,Live,Func}, Vst) ->
+ validate_tail_call(none, Func, Live, Vst);
+vi_safe({call_last,Live,Func,N}, Vst) ->
+ validate_tail_call(N, Func, Live, Vst);
+vi_safe({call_ext_last,Live,Func,N}, Vst) ->
+ validate_tail_call(N, Func, Live, Vst);
+vi_safe(_I, #vst{current=#st{ct=undecided}}) ->
error(unknown_catch_try_state);
%%
%% Allocate and deallocate, et.al
-valfun_1({allocate,Stk,Live}, Vst) ->
+%%
+vi_safe({allocate,Stk,Live}, Vst) ->
allocate(uninitialized, Stk, 0, Live, Vst);
-valfun_1({allocate_heap,Stk,Heap,Live}, Vst) ->
+vi_safe({allocate_heap,Stk,Heap,Live}, Vst) ->
allocate(uninitialized, Stk, Heap, Live, Vst);
-valfun_1({allocate_zero,Stk,Live}, Vst) ->
+vi_safe({allocate_zero,Stk,Live}, Vst) ->
allocate(initialized, Stk, 0, Live, Vst);
-valfun_1({allocate_heap_zero,Stk,Heap,Live}, Vst) ->
+vi_safe({allocate_heap_zero,Stk,Heap,Live}, Vst) ->
allocate(initialized, Stk, Heap, Live, Vst);
-valfun_1({deallocate,StkSize}, #vst{current=#st{numy=StkSize}}=Vst) ->
+vi_safe({deallocate,StkSize}, #vst{current=#st{numy=StkSize}}=Vst) ->
verify_no_ct(Vst),
deallocate(Vst);
-valfun_1({deallocate,_}, #vst{current=#st{numy=NumY}}) ->
+vi_safe({deallocate,_}, #vst{current=#st{numy=NumY}}) ->
error({allocated,NumY});
-valfun_1({trim,N,Remaining}, #vst{current=St0}=Vst) ->
+vi_safe({trim,N,Remaining}, #vst{current=St0}=Vst) ->
#st{numy=NumY} = St0,
if
N =< NumY, N+Remaining =:= NumY ->
@@ -533,13 +553,13 @@ valfun_1({trim,N,Remaining}, #vst{current=St0}=Vst) ->
error({trim,N,Remaining,allocated,NumY})
end;
%% Catch & try.
-valfun_1({'catch',Dst,{f,Fail}}, Vst) when Fail =/= none ->
+vi_safe({'catch',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(catchtag, Dst, Fail, Vst);
-valfun_1({'try',Dst,{f,Fail}}, Vst) when Fail =/= none ->
+vi_safe({'try',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(trytag, Dst, Fail, Vst);
-valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+vi_safe({catch_end,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
case get_tag_type(Reg, Vst0) of
- {catchtag,Fail} ->
+ {catchtag,_Fail}=Tag ->
%% Kill the catch tag and receive marker.
%%
%% The marker is only cleared when an exception is thrown, but it's
@@ -548,164 +568,116 @@ valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
Vst = set_receive_marker(none, Vst1),
%% {x,0} contains the caught term, if any.
- create_term(term, catch_end, [], {x,0}, Vst);
+ create_term(any, catch_end, [], {x,0}, Vst);
Type ->
error({wrong_tag_type,Type})
end;
-valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst) ->
+vi_safe({try_end,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst) ->
case get_tag_type(Reg, Vst) of
- {trytag,Fail} ->
+ {trytag,_Fail}=Tag ->
%% Kill the catch tag. Note that x registers and the receive marker
%% are unaffected.
kill_catch_tag(Reg, Vst);
Type ->
error({wrong_tag_type,Type})
end;
-valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+vi_safe({try_case,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
case get_tag_type(Reg, Vst0) of
- {trytag,Fail} ->
+ {trytag,_Fail}=Tag ->
%% Kill the catch tag, all x registers, and the receive marker.
Vst1 = kill_catch_tag(Reg, Vst0),
Vst2 = prune_x_regs(0, Vst1),
Vst3 = set_receive_marker(none, Vst2),
%% Class:Error:Stacktrace
- Vst4 = create_term({atom,[]}, try_case, [], {x,0}, Vst3),
- Vst = create_term(term, try_case, [], {x,1}, Vst4),
- create_term(term, try_case, [], {x,2}, Vst);
+ Vst4 = create_term(#t_atom{}, try_case, [], {x,0}, Vst3),
+ Vst = create_term(any, try_case, [], {x,1}, Vst4),
+ create_term(any, try_case, [], {x,2}, Vst);
Type ->
error({wrong_tag_type,Type})
end;
-valfun_1({get_list,Src,D1,D2}, Vst0) ->
+%% Simple getters that can't fail.
+vi_safe({get_list,Src,D1,D2}, Vst0) ->
assert_not_literal(Src),
- assert_type(cons, Src, Vst0),
- Vst = extract_term(term, get_hd, [Src], D1, Vst0),
- extract_term(term, get_tl, [Src], D2, Vst);
-valfun_1({get_hd,Src,Dst}, Vst) ->
+ assert_type(#t_cons{}, Src, Vst0),
+
+ SrcType = get_term_type(Src, Vst0),
+ {HeadType, _, _} = beam_call_types:types(erlang, hd, [SrcType]),
+ {TailType, _, _} = beam_call_types:types(erlang, tl, [SrcType]),
+
+ Vst = extract_term(HeadType, get_hd, [Src], D1, Vst0),
+ extract_term(TailType, get_tl, [Src], D2, Vst, Vst0);
+vi_safe({get_hd,Src,Dst}, Vst) ->
assert_not_literal(Src),
- assert_type(cons, Src, Vst),
- extract_term(term, get_hd, [Src], Dst, Vst);
-valfun_1({get_tl,Src,Dst}, Vst) ->
+ assert_type(#t_cons{}, Src, Vst),
+
+ SrcType = get_term_type(Src, Vst),
+ {HeadType, _, _} = beam_call_types:types(erlang, hd, [SrcType]),
+
+ extract_term(HeadType, get_hd, [Src], Dst, Vst);
+vi_safe({get_tl,Src,Dst}, Vst) ->
assert_not_literal(Src),
- assert_type(cons, Src, Vst),
- extract_term(term, get_tl, [Src], Dst, Vst);
-valfun_1({get_tuple_element,Src,N,Dst}, Vst) ->
+ assert_type(#t_cons{}, Src, Vst),
+
+ SrcType = get_term_type(Src, Vst),
+ {TailType, _, _} = beam_call_types:types(erlang, tl, [SrcType]),
+
+ extract_term(TailType, get_tl, [Src], Dst, Vst);
+vi_safe({get_tuple_element,Src,N,Dst}, Vst) ->
+ Index = N+1,
assert_not_literal(Src),
- assert_type({tuple_element,N+1}, Src, Vst),
- Index = {integer,N+1},
- Type = get_element_type(Index, Src, Vst),
- extract_term(Type, {bif,element}, [Index, Src], Dst, Vst);
-valfun_1({jump,{f,Lbl}}, Vst) ->
+ assert_type(#t_tuple{size=Index}, Src, Vst),
+ #t_tuple{elements=Es} = normalize(get_term_type(Src, Vst)),
+ Type = beam_types:get_tuple_element(Index, Es),
+ extract_term(Type, {bif,element}, [{integer,Index}, Src], Dst, Vst);
+vi_safe({jump,{f,Lbl}}, Vst) ->
branch(Lbl, Vst,
fun(SuccVst) ->
%% The next instruction is never executed.
kill_state(SuccVst)
end);
-valfun_1(return, Vst) ->
+vi_safe(return, Vst) ->
assert_durable_term({x,0}, Vst),
- verify_return(Vst),
- kill_state(Vst);
+ verify_return(Vst);
-valfun_1({set_tuple_element,Src,Tuple,N}, Vst) ->
- I = N + 1,
- assert_term(Src, Vst),
- assert_type({tuple_element,I}, Tuple, Vst),
- %% Manually update the tuple type; we can't rely on the ordinary update
- %% helpers as we must support overwriting (rather than just widening or
- %% narrowing) known elements, and we can't use extract_term either since
- %% the source tuple may be aliased.
- {tuple, Sz, Es0} = get_term_type(Tuple, Vst),
- Es = set_element_type({integer,I}, get_term_type(Src, Vst), Es0),
- override_type({tuple, Sz, Es}, Tuple, Vst);
+%%
+%% Matching and test instructions.
+%%
-%% Match instructions.
-valfun_1({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
+vi_safe({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
assert_term(Src, Vst),
assert_choices(Choices),
validate_select_val(Fail, Choices, Src, Vst);
-valfun_1({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
- assert_type(tuple, Tuple, Vst),
+vi_safe({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
+ assert_type(#t_tuple{}, Tuple, Vst),
assert_arities(Choices),
validate_select_tuple_arity(Fail, Choices, Tuple, Vst);
-
-%% New bit syntax matching instructions.
-valfun_1({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) ->
- validate_bs_start_match(Fail, Live, bsm_match_state(), Src, Dst, Vst);
-valfun_1({test,bs_start_match2,{f,Fail},Live,[Src,Slots],Dst}, Vst) ->
- validate_bs_start_match(Fail, Live, bsm_match_state(Slots), Src, Dst, Vst);
-valfun_1({test,bs_match_string,{f,Fail},[Ctx,_,_]}, Vst) ->
- bsm_validate_context(Ctx, Vst),
- branch(Fail, Vst, fun(V) -> V end);
-valfun_1({test,bs_skip_bits2,{f,Fail},[Ctx,Src,_,_]}, Vst) ->
- bsm_validate_context(Ctx, Vst),
- assert_term(Src, Vst),
- branch(Fail, Vst, fun(V) -> V end);
-valfun_1({test,bs_test_tail2,{f,Fail},[Ctx,_]}, Vst) ->
- bsm_validate_context(Ctx, Vst),
- branch(Fail, Vst, fun(V) -> V end);
-valfun_1({test,bs_test_unit,{f,Fail},[Ctx,_]}, Vst) ->
- bsm_validate_context(Ctx, Vst),
- branch(Fail, Vst, fun(V) -> V end);
-valfun_1({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) ->
- validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_1({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) ->
- validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_1({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) ->
- validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_1({test,bs_get_integer2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_1({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, {float, []}, Dst, Vst);
-valfun_1({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, binary, Dst, Vst);
-valfun_1({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_1({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_1({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
- validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst);
-valfun_1({bs_save2,Ctx,SavePoint}, Vst) ->
- bsm_save(Ctx, SavePoint, Vst);
-valfun_1({bs_restore2,Ctx,SavePoint}, Vst) ->
- bsm_restore(Ctx, SavePoint, Vst);
-valfun_1({bs_get_position, Ctx, Dst, Live}, Vst0) ->
- bsm_validate_context(Ctx, Vst0),
- verify_live(Live, Vst0),
- verify_y_init(Vst0),
- Vst = prune_x_regs(Live, Vst0),
- create_term(ms_position, bs_get_position, [Ctx], Dst, Vst, Vst0);
-valfun_1({bs_set_position, Ctx, Pos}, Vst) ->
- bsm_validate_context(Ctx, Vst),
- assert_type(ms_position, Pos, Vst),
- Vst;
-valfun_1({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
- assert_type(map, Src, Vst),
- assert_unique_map_keys(List),
- branch(Lbl, Vst, fun(V) -> V end);
-valfun_1({test,is_atom,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {atom,[]}, Src, Vst);
-valfun_1({test,is_binary,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, binary, Src, Vst);
-valfun_1({test,is_bitstr,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, binary, Src, Vst);
-valfun_1({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, bool, Src, Vst);
-valfun_1({test,is_float,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {float,[]}, Src, Vst);
-valfun_1({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {tuple,[0],#{}}, Src, Vst);
-valfun_1({test,is_integer,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {integer,[]}, Src, Vst);
-valfun_1({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, cons, Src, Vst);
-valfun_1({test,is_number,{f,Lbl},[Src]}, Vst) ->
+vi_safe({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
+ verify_has_map_fields(Lbl, Src, List, Vst);
+vi_safe({test,is_atom,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_atom{}, Src, Vst);
+vi_safe({test,is_binary,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_bitstring{size_unit=8}, Src, Vst);
+vi_safe({test,is_bitstr,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_bitstring{}, Src, Vst);
+vi_safe({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, beam_types:make_boolean(), Src, Vst);
+vi_safe({test,is_float,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_float{}, Src, Vst);
+vi_safe({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_tuple{}, Src, Vst);
+vi_safe({test,is_integer,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_integer{}, Src, Vst);
+vi_safe({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_cons{}, Src, Vst);
+vi_safe({test,is_number,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, number, Src, Vst);
-valfun_1({test,is_list,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, list, Src, Vst);
-valfun_1({test,is_map,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, map, Src, Vst);
-valfun_1({test,is_nil,{f,Lbl},[Src]}, Vst) ->
+vi_safe({test,is_list,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_list{}, Src, Vst);
+vi_safe({test,is_map,{f,Lbl},[Src]}, Vst) ->
+ type_test(Lbl, #t_map{}, Src, Vst);
+vi_safe({test,is_nil,{f,Lbl},[Src]}, Vst) ->
%% is_nil is an exact check against the 'nil' value, and should not be
%% treated as a simple type test.
assert_term(Src, Vst),
@@ -716,15 +688,16 @@ valfun_1({test,is_nil,{f,Lbl},[Src]}, Vst) ->
fun(SuccVst) ->
update_eq_types(Src, nil, SuccVst)
end);
-valfun_1({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
- assert_type(tuple, Tuple, Vst),
- Type = {tuple, Sz, #{}},
+vi_safe({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
+ assert_type(#t_tuple{}, Tuple, Vst),
+ Type = #t_tuple{exact=true,size=Sz},
type_test(Lbl, Type, Tuple, Vst);
-valfun_1({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) ->
+vi_safe({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) ->
assert_term(Src, Vst),
- Type = {tuple, Sz, #{ {integer,1} => Atom }},
+ Es = #{ 1 => get_literal_type(Atom) },
+ Type = #t_tuple{exact=true,size=Sz,elements=Es},
type_test(Lbl, Type, Src, Vst);
-valfun_1({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+vi_safe({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
validate_src(Ss, Vst),
branch(Lbl, Vst,
fun(FailVst) ->
@@ -733,7 +706,7 @@ valfun_1({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
fun(SuccVst) ->
update_eq_types(Src, Val, SuccVst)
end);
-valfun_1({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+vi_safe({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
validate_src(Ss, Vst),
branch(Lbl, Vst,
fun(FailVst) ->
@@ -742,63 +715,220 @@ valfun_1({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
fun(SuccVst) ->
update_ne_types(Src, Val, SuccVst)
end);
-valfun_1({test,_Op,{f,Lbl},Src}, Vst) ->
+%%
+%% New bit syntax matching instructions.
+%%
+vi_safe({bs_start_match4,Fail,Live,Src,Dst}, Vst) ->
+ validate_bs_start_match(Fail, Live, 0, Src, Dst, Vst);
+vi_safe({test,bs_start_match3,{f,_}=Fail,Live,[Src],Dst}, Vst) ->
+ validate_bs_start_match(Fail, Live, 0, Src, Dst, Vst);
+vi_safe({test,bs_start_match2,{f,_}=Fail,Live,[Src,Slots],Dst}, Vst) ->
+ validate_bs_start_match(Fail, Live, Slots, Src, Dst, Vst);
+%%
+%% Bit syntax positioning
+%%
+vi_safe({bs_save2,Ctx,SavePoint}, Vst) ->
+ bsm_save(Ctx, SavePoint, Vst);
+vi_safe({bs_restore2,Ctx,SavePoint}, Vst) ->
+ bsm_restore(Ctx, SavePoint, Vst);
+vi_safe({bs_get_position, Ctx, Dst, Live}, Vst0) ->
+ assert_type(#t_bs_context{}, Ctx, Vst0),
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+ Vst = prune_x_regs(Live, Vst0),
+ create_term(#t_abstract{kind=ms_position}, bs_get_position, [Ctx],
+ Dst, Vst, Vst0);
+vi_safe({bs_set_position, Ctx, Pos}, Vst) ->
+ assert_type(#t_bs_context{}, Ctx, Vst),
+ assert_type(#t_abstract{kind=ms_position}, Pos, Vst),
+ Vst;
+%%
+%% Bit syntax matching
+%%
+vi_safe({test,bs_match_string,{f,Fail},[Ctx,Rem,{string,String}]}, Vst) ->
+ true = is_bitstring(String), %Assertion.
+ Stride = bit_size(String) + Rem,
+ validate_bs_skip(Fail, Ctx, Stride, Vst);
+vi_safe({test,bs_skip_bits2,{f,Fail},[Ctx,Size,Unit,_Flags]}, Vst) ->
+ assert_term(Size, Vst),
+
+ Stride = case get_raw_type(Size, Vst) of
+ #t_integer{elements={Same,Same}} -> Same * Unit;
+ _ -> Unit
+ end,
+
+ validate_bs_skip(Fail, Ctx, Stride, Vst);
+vi_safe({test,bs_test_tail2,{f,Fail},[Ctx,_Size]}, Vst) ->
+ assert_type(#t_bs_context{}, Ctx, Vst),
+ branch(Fail, Vst, fun(V) -> V end);
+vi_safe({test,bs_test_unit,{f,Fail},[Ctx,Unit]}, Vst) ->
+ assert_type(#t_bs_context{}, Ctx, Vst),
+ type_test(Fail, #t_bs_context{tail_unit=Unit}, Ctx, Vst);
+vi_safe({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) ->
+ validate_bs_skip(Fail, Ctx, 8, Live, Vst);
+vi_safe({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) ->
+ validate_bs_skip(Fail, Ctx, 16, Live, Vst);
+vi_safe({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) ->
+ validate_bs_skip(Fail, Ctx, 32, Live, Vst);
+vi_safe({test,bs_get_integer2=Op,{f,Fail},Live,
+ [Ctx,{integer,Size},Unit,{field_flags,Flags}],Dst},Vst) ->
+ NumBits = Size * Unit,
+ Type = case member(unsigned, Flags) of
+ true when NumBits =< 64 ->
+ beam_types:make_integer(0, (1 bsl NumBits)-1);
+ _ ->
+ %% Signed integer or way too large, don't bother.
+ #t_integer{}
+ end,
+ validate_bs_get(Op, Fail, Ctx, Live, NumBits, Type, Dst, Vst);
+vi_safe({test,bs_get_integer2=Op,{f,Fail},Live,
+ [Ctx,_Size,Unit,_Flags],Dst},Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, Unit, #t_integer{}, Dst, Vst);
+vi_safe({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
+ validate_bs_get(Op, Fail, Ctx, Live, 1, #t_float{}, Dst, Vst);
+vi_safe({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,Unit,_],Dst}, Vst) ->
+ Type = #t_bitstring{size_unit=Unit},
+ validate_bs_get(Op, Fail, Ctx, Live, Unit, Type, Dst, Vst);
+vi_safe({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ Type = beam_types:make_integer(0, ?UNICODE_MAX),
+ validate_bs_get(Op, Fail, Ctx, Live, 8, Type, Dst, Vst);
+vi_safe({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ Type = beam_types:make_integer(0, ?UNICODE_MAX),
+ validate_bs_get(Op, Fail, Ctx, Live, 16, Type, Dst, Vst);
+vi_safe({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+ Type = beam_types:make_integer(0, ?UNICODE_MAX),
+ validate_bs_get(Op, Fail, Ctx, Live, 32, Type, Dst, Vst);
+
+vi_safe({test,_Op,{f,Lbl},Src}, Vst) ->
%% is_pid, is_reference, et cetera.
validate_src(Src, Vst),
branch(Lbl, Vst, fun(V) -> V end);
-
-%% Map instructions.
-valfun_1({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+vi_safe({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
-valfun_1({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
+vi_safe({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
verify_get_map(Fail, Src, List, Vst);
+vi_safe(I, Vst0) ->
+ Vst = branch_exception(Vst0),
+ vi_float(I, Vst).
+
+validate_var_info([{fun_type, Type} | Info], Reg, Vst0) ->
+ %% Explicit type information inserted after make_fun2 instructions to mark
+ %% the return type of the created fun.
+ Vst = update_type(fun meet/2, #t_fun{type=Type}, Reg, Vst0),
+ validate_var_info(Info, Reg, Vst);
+validate_var_info([{type, none} | _Info], _Reg, Vst) ->
+ %% Unreachable code, typically after a call that never returns.
+ kill_state(Vst);
+validate_var_info([{type, Type} | Info], Reg, Vst0) ->
+ %% Explicit type information inserted by optimization passes to indicate
+ %% that Reg has a certain type, so that we can accept cross-function type
+ %% optimizations.
+ Vst = update_type(fun meet/2, Type, Reg, Vst0),
+ validate_var_info(Info, Reg, Vst);
+validate_var_info([_ | Info], Reg, Vst) ->
+ validate_var_info(Info, Reg, Vst);
+validate_var_info([], _Reg, Vst) ->
+ Vst.
+
+validate_tail_call(Deallocate, Func, Live, #vst{current=#st{numy=NumY}}=Vst0) ->
+ assert_float_checked(Vst0),
+ case will_call_succeed(Func, Vst0) of
+ yes when Deallocate =:= NumY ->
+ %% This call cannot fail, handle it without updating catch state.
+ tail_call(Func, Live, Vst0);
+ maybe when Deallocate =:= NumY ->
+ %% The call can fail, make sure that any catch state is updated.
+ Vst = branch_exception(Vst0),
+ tail_call(Func, Live, Vst);
+ no ->
+ %% The stack will be scanned, so Y registers must be initialized.
+ %%
+ %% Note that the compiler is allowed to emit garbage values for
+ %% "Deallocate" as we know that it will not be used in this case.
+ Vst = branch_exception(Vst0),
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ _ when Deallocate =/= NumY ->
+ error({allocated, NumY})
+ end.
+
+validate_body_call(Func, Live,
+ #vst{current=#st{numy=NumY}}=Vst0) when is_integer(NumY)->
+ assert_float_checked(Vst0),
+ case will_call_succeed(Func, Vst0) of
+ yes ->
+ call(Func, Live, Vst0);
+ maybe ->
+ Vst = branch_exception(Vst0),
+ call(Func, Live, Vst);
+ no ->
+ Vst = branch_exception(Vst0),
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ kill_state(Vst)
+ end;
+validate_body_call(_, _, #vst{current=#st{numy=NumY}}) ->
+ error({allocated, NumY}).
-valfun_1(I, Vst) ->
- valfun_2(I, Vst).
+assert_float_checked(Vst) ->
+ case get_fls(Vst) of
+ undefined -> ok;
+ checked -> ok;
+ Fls -> error({unsafe_instruction,{float_error_state,Fls}})
+ end.
-init_try_catch_branch(Tag, Dst, Fail, Vst0) ->
- Vst1 = create_tag({Tag,[Fail]}, 'try_catch', [], Dst, Vst0),
- #vst{current=#st{ct=Fails}=St0} = Vst1,
- St = St0#st{ct=[[Fail]|Fails]},
- Vst = Vst0#vst{current=St},
+init_try_catch_branch(Kind, Dst, Fail, Vst0) ->
+ Tag = {Kind, [Fail]},
+ Vst = create_tag(Tag, 'try_catch', [], Dst, Vst0),
branch(Fail, Vst,
fun(CatchVst0) ->
- #vst{current=#st{ys=Ys}} = CatchVst0,
- CatchVst = maps:fold(fun init_catch_handler_1/3,
- CatchVst0, Ys),
- %% The receive marker is cleared on exceptions.
- set_receive_marker(none, CatchVst)
+ %% We add the tag here because branch/4 rejects jumps to
+ %% labels referenced by try tags.
+ #vst{current=#st{ct=Tags,ys=Ys}=St0} = CatchVst0,
+ St = St0#st{ct=[Tag|Tags]},
+ CatchVst1 = CatchVst0#vst{current=St},
+
+ %% The receive marker is cleared on exceptions.
+ CatchVst = set_receive_marker(none, CatchVst1),
+
+ maps:fold(fun init_catch_handler_1/3, CatchVst, Ys)
end,
- fun(SuccVst) ->
- %% All potentially-throwing instructions after this
- %% one will implicitly branch to the fail label;
- %% see valfun_2/2
- SuccVst
+ fun(SuccVst0) ->
+ #vst{current=#st{ct=Tags}=St0} = SuccVst0,
+ St = St0#st{ct=[Tag|Tags]},
+ SuccVst = SuccVst0#vst{current=St},
+
+ %% All potentially-throwing instructions after this one will
+ %% implicitly branch to the current try/catch handler; see
+ %% the base case of vi_safe/2
+ SuccVst
end).
%% Set the initial state at the try/catch label. Assume that Y registers
%% contain terms or try/catch tags.
init_catch_handler_1(Reg, initialized, Vst) ->
- create_term(term, 'catch_handler', [], Reg, Vst);
+ create_term(any, 'catch_handler', [], Reg, Vst);
init_catch_handler_1(Reg, uninitialized, Vst) ->
- create_term(term, 'catch_handler', [], Reg, Vst);
+ create_term(any, 'catch_handler', [], Reg, Vst);
init_catch_handler_1(_, _, Vst) ->
Vst.
-valfun_2(I, #vst{current=#st{ct=[[Fail]|_]}}=Vst) when is_integer(Fail) ->
+branch_exception(#vst{current=#st{ct=[{_,[Fail]}|_]}}=Vst)
+ when is_integer(Fail) ->
%% We have an active try/catch tag and we can jump there from this
%% instruction, so we need to update the branched state of the try/catch
%% handler.
- valfun_3(I, branch_state(Fail, Vst));
-valfun_2(I, #vst{current=#st{ct=[]}}=Vst) ->
- valfun_3(I, Vst);
-valfun_2(_, _) ->
+ fork_state(Fail, Vst);
+branch_exception(#vst{current=#st{ct=[]}}=Vst) ->
+ Vst;
+branch_exception(_) ->
error(ambiguous_catch_try_state).
%% Handle the remaining floating point instructions here.
%% Floating point.
-valfun_3({fconv,Src,{fr,_}=Dst}, Vst) ->
+vi_float({fconv,Src,{fr,_}=Dst}, Vst) ->
assert_term(Src, Vst),
%% An exception is raised on error, hence branching to 0.
@@ -807,184 +937,127 @@ valfun_3({fconv,Src,{fr,_}=Dst}, Vst) ->
SuccVst = update_type(fun meet/2, number, Src, SuccVst0),
set_freg(Dst, SuccVst)
end);
-valfun_3({bif,fadd,_,[_,_]=Ss,Dst}, Vst) ->
+vi_float({bif,fadd,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) ->
+vi_float({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fmul,_,[_,_]=Ss,Dst}, Vst) ->
+vi_float({bif,fmul,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fnegate,_,[_]=Ss,Dst}, Vst) ->
+vi_float({bif,fnegate,_,[_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fsub,_,[_,_]=Ss,Dst}, Vst) ->
+vi_float({bif,fsub,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3(fclearerror, Vst) ->
+vi_float(fclearerror, Vst) ->
case get_fls(Vst) of
- undefined -> ok;
- checked -> ok;
- Fls -> error({bad_floating_point_state,Fls})
+ undefined -> ok;
+ checked -> ok;
+ Fls -> error({bad_floating_point_state,Fls})
end,
set_fls(cleared, Vst);
-valfun_3({fcheckerror,_}, Vst) ->
+vi_float({fcheckerror,_}, Vst) ->
assert_fls(cleared, Vst),
set_fls(checked, Vst);
-valfun_3(I, Vst) ->
- %% The instruction is not a float instruction.
- case get_fls(Vst) of
- undefined ->
- valfun_4(I, Vst);
- checked ->
- valfun_4(I, Vst);
- Fls ->
- error({unsafe_instruction,{float_error_state,Fls}})
- end.
-
-%% Instructions that can cause exceptions.
-valfun_4({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
- verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
-valfun_4({apply,Live}, Vst) ->
- call(apply, Live+2, Vst);
-valfun_4({apply_last,Live,_}, Vst) ->
- tail_call(apply, Live+2, Vst);
-valfun_4({call_fun,Live}, Vst) ->
- validate_src([{x,Live}], Vst),
- call('fun', Live+1, Vst);
-valfun_4({call,Live,Func}, Vst) ->
- call(Func, Live, Vst);
-valfun_4({call_ext,Live,Func}, Vst) ->
- %% Exception BIFs has already been taken care of above.
- call(Func, Live, Vst);
-valfun_4({call_only,Live,Func}, Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_ext_only,Live,Func}, Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_last,Live,Func,StkSize}, #vst{current=#st{numy=StkSize}}=Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->
- error({allocated,NumY});
-valfun_4({call_ext_last,Live,Func,StkSize},
- #vst{current=#st{numy=StkSize}}=Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_ext_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->
- error({allocated,NumY});
-valfun_4({make_fun2,_,_,_,Live}, Vst) ->
- call(make_fun, Live, Vst);
-%% Other BIFs
-valfun_4({bif,element,{f,Fail},[Pos,Src],Dst}, Vst) ->
- branch(Fail, Vst,
- fun(SuccVst0) ->
- PosType = get_term_type(Pos, SuccVst0),
- TupleType = {tuple,[get_tuple_size(PosType)],#{}},
+vi_float(I, Vst) ->
+ assert_float_checked(Vst),
+ vi_throwing(I, Vst).
- SuccVst1 = update_type(fun meet/2, TupleType,
- Src, SuccVst0),
- SuccVst = update_type(fun meet/2, {integer,[]},
- Pos, SuccVst1),
-
- ElementType = get_element_type(PosType, Src, SuccVst),
- extract_term(ElementType, {bif,element}, [Pos,Src],
- Dst, SuccVst)
- end);
-valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) ->
- validate_src(Src, Vst),
+%%%
+%%% vi_throwing/2 handles instructions that can cause exceptions.
+%%%
+vi_throwing({badmatch,Src}, Vst) ->
+ assert_durable_term(Src, Vst),
+ verify_y_init(Vst),
kill_state(Vst);
-valfun_4(raw_raise=I, Vst) ->
- call(I, 3, Vst);
-valfun_4({bif,Op,{f,Fail},[Src]=Ss,Dst}, Vst) when Op =:= hd; Op =:= tl ->
- assert_term(Src, Vst),
- branch(Fail, Vst,
- fun(FailVst) ->
- update_type(fun subtract/2, cons, Src, FailVst)
- end,
- fun(SuccVst0) ->
- SuccVst = update_type(fun meet/2, cons, Src, SuccVst0),
- extract_term(term, {bif,Op}, Ss, Dst, SuccVst)
- end);
-valfun_4({bif,Op,{f,Fail},Ss,Dst}, Vst) ->
- validate_src(Ss, Vst),
- branch(Fail, Vst,
+vi_throwing({case_end,Src}, Vst) ->
+ assert_durable_term(Src, Vst),
+ verify_y_init(Vst),
+ kill_state(Vst);
+vi_throwing(if_end, Vst) ->
+ verify_y_init(Vst),
+ kill_state(Vst);
+vi_throwing({try_case_end,Src}, Vst) ->
+ verify_y_init(Vst),
+ assert_durable_term(Src, Vst),
+ kill_state(Vst);
+vi_throwing({call_fun,Live}, Vst) ->
+ Fun = {x,Live},
+ assert_term(Fun, Vst),
+
+ %% An exception is raised on error, hence branching to 0.
+ branch(0, Vst,
fun(SuccVst0) ->
- %% Infer argument types. Note that we can't subtract
- %% types as the BIF could fail for reasons other than
- %% bad argument types.
- ArgTypes = bif_arg_types(Op, Ss),
- SuccVst = foldl(fun({Arg, T}, V) ->
- update_type(fun meet/2, T, Arg, V)
- end, SuccVst0, zip(Ss, ArgTypes)),
- Type = bif_return_type(Op, Ss, SuccVst),
- extract_term(Type, {bif,Op}, Ss, Dst, SuccVst)
+ SuccVst = update_type(fun meet/2, #t_fun{arity=Live},
+ Fun, SuccVst0),
+ validate_body_call('fun', Live+1, SuccVst)
end);
-valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) ->
- validate_src(Ss, Vst0),
- verify_live(Live, Vst0),
- verify_y_init(Vst0),
+vi_throwing({make_fun2,{f,Lbl},_,_,NumFree}, #vst{ft=Ft}=Vst0) ->
+ #{ arity := Arity0 } = gb_trees:get(Lbl, Ft),
+ Arity = Arity0 - NumFree,
- %% Heap allocations and X registers are killed regardless of whether we
- %% fail or not, as we may fail after GC.
- St = kill_heap_allocation(St0),
- Vst = prune_x_regs(Live, Vst0#vst{current=St}),
-
- branch(Fail, Vst,
- fun(SuccVst0) ->
- ArgTypes = bif_arg_types(Op, Ss),
- SuccVst = foldl(fun({Arg, T}, V) ->
- update_type(fun meet/2, T, Arg, V)
- end, SuccVst0, zip(Ss, ArgTypes)),
+ true = Arity >= 0, %Assertion.
- Type = bif_return_type(Op, Ss, SuccVst),
+ Vst = prune_x_regs(NumFree, Vst0),
+ verify_call_args(make_fun, NumFree, Vst),
+ verify_y_init(Vst),
- %% We're passing Vst0 as the original because the
- %% registers were pruned before the branch.
- extract_term(Type, {gc_bif,Op}, Ss, Dst, SuccVst, Vst0)
- end);
-valfun_4({loop_rec,{f,Fail},Dst}, Vst) ->
+ create_term(#t_fun{arity=Arity}, make_fun, [], {x,0}, Vst);
+%% Other BIFs
+vi_throwing({bif,raise,{f,0},Src,_Dst}, Vst) ->
+ validate_src(Src, Vst),
+ kill_state(Vst);
+vi_throwing(raw_raise=I, Vst) ->
+ call(I, 3, Vst);
+vi_throwing({bif,Op,{f,Fail},Ss,Dst}, Vst) ->
+ validate_bif(Op, Fail, Ss, Dst, Vst);
+vi_throwing({gc_bif,Op,{f,Fail},Live,Ss,Dst}, Vst) ->
+ validate_gc_bif(Op, Fail, Ss, Dst, Live, Vst);
+vi_throwing({loop_rec,{f,Fail},Dst}, Vst) ->
%% This term may not be part of the root set until remove_message/0 is
%% executed. If control transfers to the loop_rec_end/1 instruction, no
%% part of this term must be stored in a Y register.
branch(Fail, Vst,
fun(SuccVst0) ->
- {Ref, SuccVst} = new_value(term, loop_rec, [], SuccVst0),
+ {Ref, SuccVst} = new_value(any, loop_rec, [], SuccVst0),
mark_fragile(Dst, set_reg_vref(Ref, Dst, SuccVst))
end);
-valfun_4({wait,_}, Vst) ->
+vi_throwing({wait,_}, Vst) ->
verify_y_init(Vst),
kill_state(Vst);
-valfun_4({wait_timeout,_,Src}, Vst) ->
+vi_throwing({wait_timeout,_,Src}, Vst) ->
%% Note that the receive marker is not cleared since we may re-enter the
%% loop while waiting. If we time out we'll be transferred to a timeout
%% instruction that clears the marker.
assert_term(Src, Vst),
verify_y_init(Vst),
prune_x_regs(0, Vst);
-valfun_4({loop_rec_end,_}, Vst) ->
+vi_throwing({loop_rec_end,_}, Vst) ->
verify_y_init(Vst),
kill_state(Vst);
-valfun_4(timeout, Vst0) ->
+vi_throwing(timeout, Vst0) ->
Vst = set_receive_marker(none, Vst0),
prune_x_regs(0, Vst);
-valfun_4(send, Vst) ->
+vi_throwing(send, Vst) ->
call(send, 2, Vst);
-
-%% Other test instructions.
-valfun_4({bs_add,{f,Fail},[A,B,_],Dst}, Vst) ->
+vi_throwing({bs_add,{f,Fail},[A,B,_],Dst}, Vst) ->
assert_term(A, Vst),
assert_term(B, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- create_term({integer,[]}, bs_add, [A, B], Dst, SuccVst)
+ create_term(#t_integer{}, bs_add, [A, B], Dst, SuccVst)
end);
-valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
+vi_throwing({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- create_term({integer,[]}, bs_utf8_size, [A], Dst, SuccVst)
+ create_term(#t_integer{}, bs_utf8_size, [A], Dst, SuccVst)
end);
-valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
+vi_throwing({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- create_term({integer,[]}, bs_utf16_size, [A], Dst, SuccVst)
+ create_term(#t_integer{}, bs_utf16_size, [A], Dst, SuccVst)
end);
-valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
+vi_throwing({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
if
@@ -997,9 +1070,10 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
branch(Fail, Vst,
fun(SuccVst0) ->
SuccVst = prune_x_regs(Live, SuccVst0),
- create_term(binary, bs_init2, [], Dst, SuccVst, SuccVst0)
+ create_term(#t_bitstring{size_unit=8}, bs_init2, [], Dst,
+ SuccVst, SuccVst0)
end);
-valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
+vi_throwing({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
if
@@ -1012,9 +1086,9 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
branch(Fail, Vst,
fun(SuccVst0) ->
SuccVst = prune_x_regs(Live, SuccVst0),
- create_term(binary, bs_init_bits, [], Dst, SuccVst)
+ create_term(#t_bitstring{}, bs_init_bits, [], Dst, SuccVst)
end);
-valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
+vi_throwing({bs_append,{f,Fail},Bits,Heap,Live,Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
assert_term(Bits, Vst0),
@@ -1023,62 +1097,81 @@ valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
branch(Fail, Vst,
fun(SuccVst0) ->
SuccVst = prune_x_regs(Live, SuccVst0),
- create_term(binary, bs_append, [Bin], Dst, SuccVst, SuccVst0)
+ create_term(#t_bitstring{size_unit=Unit}, bs_append,
+ [Bin], Dst, SuccVst, SuccVst0)
end);
-valfun_4({bs_private_append,{f,Fail},Bits,_Unit,Bin,_Flags,Dst}, Vst) ->
+vi_throwing({bs_private_append,{f,Fail},Bits,Unit,Bin,_Flags,Dst}, Vst) ->
assert_term(Bits, Vst),
assert_term(Bin, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- create_term(binary, bs_private_append, [Bin], Dst, SuccVst)
+ create_term(#t_bitstring{size_unit=Unit}, bs_private_append,
+ [Bin], Dst, SuccVst)
end);
-valfun_4({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
+vi_throwing({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
Vst;
-valfun_4({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) ->
+vi_throwing({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, binary, Src, SuccVst)
+ update_type(fun meet/2, #t_bitstring{}, Src, SuccVst)
end);
-valfun_4({bs_put_float,{f,Fail},Sz,_,_,Src}, Vst) ->
+vi_throwing({bs_put_float,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, {float,[]}, Src, SuccVst)
+ update_type(fun meet/2, #t_float{}, Src, SuccVst)
end);
-valfun_4({bs_put_integer,{f,Fail},Sz,_,_,Src}, Vst) ->
+vi_throwing({bs_put_integer,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, {integer,[]}, Src, SuccVst)
+ update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf8,{f,Fail},_,Src}, Vst) ->
+vi_throwing({bs_put_utf8,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, {integer,[]}, Src, SuccVst)
+ update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf16,{f,Fail},_,Src}, Vst) ->
+vi_throwing({bs_put_utf16,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, {integer,[]}, Src, SuccVst)
+ update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf32,{f,Fail},_,Src}, Vst) ->
+vi_throwing({bs_put_utf32,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
- update_type(fun meet/2, {integer,[]}, Src, SuccVst)
+ update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4(_, _) ->
+%% Map instructions.
+vi_throwing({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+ verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
+vi_throwing(_, _) ->
error(unknown_instruction).
+verify_has_map_fields(Lbl, Src, List, Vst) ->
+ assert_type(#t_map{}, Src, Vst),
+ assert_unique_map_keys(List),
+ verify_map_fields(List, Src, Lbl, Vst).
+
+verify_map_fields([Key | Keys], Map, Lbl, Vst) ->
+ assert_term(Key, Vst),
+ case bif_types(map_get, [Key, Map], Vst) of
+ {none, _, _} -> kill_state(Vst);
+ {_, _, _} -> verify_map_fields(Keys, Map, Lbl, Vst)
+ end;
+verify_map_fields([], _Map, Lbl, Vst) ->
+ branch(Lbl, Vst, fun(V) -> V end).
+
verify_get_map(Fail, Src, List, Vst0) ->
assert_not_literal(Src), %OTP 22.
- assert_type(map, Src, Vst0),
+ assert_type(#t_map{}, Src, Vst0),
branch(Fail, Vst0,
fun(FailVst) ->
@@ -1102,7 +1195,7 @@ verify_get_map(Fail, Src, List, Vst0) ->
clobber_map_vals([Key,Dst|T], Map, Vst0) ->
case is_reg_initialized(Dst, Vst0) of
true ->
- Vst = extract_term(term, {bif,map_get}, [Key, Map], Dst, Vst0),
+ Vst = extract_term(any, {bif,map_get}, [Key, Map], Dst, Vst0),
clobber_map_vals(T, Map, Vst);
false ->
clobber_map_vals(T, Map, Vst0)
@@ -1125,15 +1218,20 @@ extract_map_keys([Key,_Val|T]) ->
[Key|extract_map_keys(T)];
extract_map_keys([]) -> [].
-extract_map_vals([Key,Dst|Vs], Map, Vst0, Vsti0) ->
+extract_map_vals([Key, Dst | Vs], Map, Vst0, Vsti0) ->
assert_term(Key, Vst0),
- Vsti = extract_term(term, {bif,map_get}, [Key, Map], Dst, Vsti0),
- extract_map_vals(Vs, Map, Vst0, Vsti);
+ case bif_types(map_get, [Key, Map], Vst0) of
+ {none, _, _} ->
+ kill_state(Vsti0);
+ {DstType, _, _} ->
+ Vsti = extract_term(DstType, {bif,map_get}, [Key, Map], Dst, Vsti0),
+ extract_map_vals(Vs, Map, Vst0, Vsti)
+ end;
extract_map_vals([], _Map, _Vst0, Vst) ->
Vst.
verify_put_map(Op, Fail, Src, Dst, Live, List, Vst0) ->
- assert_type(map, Src, Vst0),
+ assert_type(#t_map{}, Src, Vst0),
verify_live(Live, Vst0),
verify_y_init(Vst0),
_ = [assert_term(Term, Vst0) || Term <- List],
@@ -1144,9 +1242,22 @@ verify_put_map(Op, Fail, Src, Dst, Live, List, Vst0) ->
SuccVst = prune_x_regs(Live, SuccVst0),
Keys = extract_map_keys(List),
assert_unique_map_keys(Keys),
- create_term(map, Op, [Src], Dst, SuccVst, SuccVst0)
+
+ Type = put_map_type(Src, List, Vst),
+ create_term(Type, Op, [Src], Dst, SuccVst, SuccVst0)
end).
+put_map_type(Map0, List, Vst) ->
+ Map = normalize(get_term_type(Map0, Vst)),
+ pmt_1(List, Vst, Map).
+
+pmt_1([Key0, Value0 | List], Vst, Acc0) ->
+ Key = normalize(get_term_type(Key0, Vst)),
+ Value = normalize(get_term_type(Value0, Vst)),
+ {Acc, _, _} = beam_call_types:types(maps, put, [Key, Value, Acc0]),
+ pmt_1(List, Vst, Acc);
+pmt_1([], _Vst, Acc) ->
+ Acc.
%%
%% Common code for validating returns, whether naked or as part of a tail call.
@@ -1172,66 +1283,162 @@ verify_return(#vst{current=#st{recv_marker=Mark}}) when Mark =/= none ->
error({return_with_receive_marker,Mark});
verify_return(Vst) ->
verify_no_ct(Vst),
- ok.
+ kill_state(Vst).
+
+%%
+%% Common code for validating BIFs.
+%%
+%% OrigVst is the state we entered the instruction with, which is needed for
+%% gc_bifs as X registers are pruned prior to calling this function, which may
+%% have clobbered the sources.
+%%
+
+validate_bif(Op, Fail, Ss, Dst, Vst) ->
+ validate_src(Ss, Vst),
+ validate_bif_1(bif, Op, Fail, Ss, Dst, Vst, Vst).
+
+validate_gc_bif(Op, Fail, Ss, Dst, Live, #vst{current=St0}=Vst0) ->
+ validate_src(Ss, Vst0),
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+
+ %% Heap allocations and X registers are killed regardless of whether we
+ %% fail or not, as we may fail after GC.
+ St = kill_heap_allocation(St0),
+ Vst = prune_x_regs(Live, Vst0#vst{current=St}),
+ validate_src(Ss, Vst),
+
+ validate_bif_1(gc_bif, Op, Fail, Ss, Dst, Vst, Vst).
+
+validate_bif_1(Kind, Op, cannot_fail, Ss, Dst, OrigVst, Vst0) ->
+ %% This BIF explicitly cannot fail; it will not jump to a guard nor throw
+ %% an exception. Validation will fail if it returns 'none' or has a type
+ %% conflict on one of its arguments.
+
+ {Type, ArgTypes, _CanSubtract} = bif_types(Op, Ss, Vst0),
+ ZippedArgs = zip(Ss, ArgTypes),
+
+ Vst = foldl(fun({A, T}, V) ->
+ update_type(fun meet/2, T, A, V)
+ end, Vst0, ZippedArgs),
+
+ true = Type =/= none, %Assertion.
+
+ extract_term(Type, {Kind, Op}, Ss, Dst, Vst, OrigVst);
+validate_bif_1(Kind, Op, Fail, Ss, Dst, OrigVst, Vst) ->
+ {Type, ArgTypes, CanSubtract} = bif_types(Op, Ss, Vst),
+ ZippedArgs = zip(Ss, ArgTypes),
+
+ FailFun = case CanSubtract of
+ true ->
+ fun(FailVst0) ->
+ foldl(fun({A, T}, V) ->
+ update_type(fun subtract/2, T, A, V)
+ end, FailVst0, ZippedArgs)
+ end;
+ false ->
+ fun(S) -> S end
+ end,
+ SuccFun = fun(SuccVst0) ->
+ SuccVst = foldl(fun({A, T}, V) ->
+ update_type(fun meet/2, T, A, V)
+ end, SuccVst0, ZippedArgs),
+ extract_term(Type, {Kind, Op}, Ss, Dst, SuccVst, OrigVst)
+ end,
+
+ branch(Fail, Vst, FailFun, SuccFun).
%%
%% Common code for validating bs_start_match* instructions.
%%
-validate_bs_start_match(Fail, Live, Type, Src, Dst, Vst) ->
- verify_live(Live, Vst),
- verify_y_init(Vst),
+validate_bs_start_match({atom,resume}, Live, 0, Src, Dst, Vst0) ->
+ assert_type(#t_bs_context{}, Src, Vst0),
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+
+ Vst = assign(Src, Dst, Vst0),
+ prune_x_regs(Live, Vst);
+validate_bs_start_match({atom,no_fail}, Live, Slots, Src, Dst, Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+
+ Vst1 = update_type(fun meet/2, #t_bs_matchable{}, Src, Vst0),
+
+ %% Retain the current unit, if known.
+ SrcType = get_movable_term_type(Src, Vst1),
+ TailUnit = beam_types:get_bs_matchable_unit(SrcType),
- %% #ms{} can represent either a match context or a term, so we have to mark
- %% the source as a term if it fails with a match context as an input. This
- %% hack is only needed until we get proper union types.
+ CtxType = #t_bs_context{slots=Slots,tail_unit=TailUnit},
+
+ Vst = prune_x_regs(Live, Vst1),
+ extract_term(CtxType, bs_start_match, [Src], Dst, Vst, Vst0);
+validate_bs_start_match({f,Fail}, Live, Slots, Src, Dst, Vst) ->
branch(Fail, Vst,
fun(FailVst) ->
- case get_movable_term_type(Src, FailVst) of
- #ms{} -> override_type(term, Src, FailVst);
- _ -> FailVst
- end
+ update_type(fun subtract/2, #t_bs_matchable{}, Src, FailVst)
end,
- fun(SuccVst0) ->
- SuccVst1 = update_type(fun meet/2, binary,
- Src, SuccVst0),
- SuccVst = prune_x_regs(Live, SuccVst1),
- extract_term(Type, bs_start_match, [Src], Dst,
- SuccVst, SuccVst0)
+ fun(SuccVst) ->
+ validate_bs_start_match({atom,no_fail}, Live, Slots,
+ Src, Dst, SuccVst)
end).
%%
%% Common code for validating bs_get* instructions.
%%
-validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst) ->
- bsm_validate_context(Ctx, Vst),
+validate_bs_get(Op, Fail, Ctx, Live, Stride, Type, Dst, Vst) ->
+ assert_type(#t_bs_context{}, Ctx, Vst),
verify_live(Live, Vst),
verify_y_init(Vst),
+ #t_bs_context{tail_unit=TailUnit} = get_raw_type(Ctx, Vst),
+ CtxType = #t_bs_context{tail_unit=gcd(Stride, TailUnit)},
+
branch(Fail, Vst,
fun(SuccVst0) ->
- SuccVst = prune_x_regs(Live, SuccVst0),
+ SuccVst1 = update_type(fun meet/2, CtxType, Ctx, SuccVst0),
+ SuccVst = prune_x_regs(Live, SuccVst1),
extract_term(Type, Op, [Ctx], Dst, SuccVst, SuccVst0)
end).
%%
-%% Common code for validating bs_skip_utf* instructions.
+%% Common code for validating bs_skip* instructions.
%%
-validate_bs_skip_utf(Fail, Ctx, Live, Vst) ->
- bsm_validate_context(Ctx, Vst),
+validate_bs_skip(Fail, Ctx, Stride, Vst) ->
+ validate_bs_skip(Fail, Ctx, Stride, no_live, Vst).
+
+validate_bs_skip(Fail, Ctx, Stride, Live, Vst) ->
+ assert_type(#t_bs_context{}, Ctx, Vst),
+
+ #t_bs_context{tail_unit=TailUnit} = get_raw_type(Ctx, Vst),
+ CtxType = #t_bs_context{tail_unit=gcd(Stride, TailUnit)},
+
+ validate_bs_skip_1(Fail, Ctx, CtxType, Live, Vst).
+
+validate_bs_skip_1(Fail, Ctx, CtxType, no_live, Vst) ->
+ branch(Fail, Vst,
+ fun(SuccVst0) ->
+ update_type(fun meet/2, CtxType, Ctx, SuccVst0)
+ end);
+validate_bs_skip_1(Fail, Ctx, CtxType, Live, Vst) ->
verify_y_init(Vst),
verify_live(Live, Vst),
-
branch(Fail, Vst,
- fun(SuccVst) ->
+ fun(SuccVst0) ->
+ SuccVst = update_type(fun meet/2, CtxType, Ctx, SuccVst0),
prune_x_regs(Live, SuccVst)
end).
-
%%
%% Common code for is_$type instructions.
%%
+type_test(Fail, #t_bs_context{}=Type, Reg, Vst) ->
+ assert_movable(Reg, Vst),
+ type_test_1(Fail, Type, Reg, Vst);
type_test(Fail, Type, Reg, Vst) ->
assert_term(Reg, Vst),
+ type_test_1(Fail, Type, Reg, Vst).
+
+type_test_1(Fail, Type, Reg, Vst) ->
branch(Fail, Vst,
fun(FailVst) ->
update_type(fun subtract/2, Type, Reg, FailVst)
@@ -1247,21 +1454,27 @@ type_test(Fail, Type, Reg, Vst) ->
%%
%% Note that #vst.current will be 'none' if the instruction is unreachable.
%%
-val_dsetel({move,_,_}, Vst) ->
+
+validate_mutation(I, Vst) ->
+ vm_1(I, Vst).
+
+vm_1({move,_,_}, Vst) ->
Vst;
-val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=#st{}=St}=Vst) ->
+vm_1({swap,_,_}, Vst) ->
+ Vst;
+vm_1({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=#st{}=St}=Vst) ->
Vst#vst{current=St#st{setelem=true}};
-val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->
+vm_1({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->
error(illegal_context_for_set_tuple_element);
-val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=true}}=Vst) ->
+vm_1({set_tuple_element,_,_,_}, #vst{current=#st{setelem=true}}=Vst) ->
Vst;
-val_dsetel({get_tuple_element,_,_,_}, Vst) ->
+vm_1({get_tuple_element,_,_,_}, Vst) ->
Vst;
-val_dsetel({line,_}, Vst) ->
+vm_1({line,_}, Vst) ->
Vst;
-val_dsetel(_, #vst{current=#st{setelem=true}=St}=Vst) ->
+vm_1(_, #vst{current=#st{setelem=true}=St}=Vst) ->
Vst#vst{current=St#st{setelem=false}};
-val_dsetel(_, Vst) -> Vst.
+vm_1(_, Vst) -> Vst.
kill_state(Vst) ->
Vst#vst{current=none}.
@@ -1272,12 +1485,13 @@ kill_state(Vst) ->
call(Name, Live, #vst{current=St0}=Vst0) ->
verify_call_args(Name, Live, Vst0),
verify_y_init(Vst0),
- case call_return_type(Name, Vst0) of
- Type when Type =/= exception ->
- %% Type is never 'exception' because it has been handled earlier.
+ case call_types(Name, Live, Vst0) of
+ {none, _, _} ->
+ kill_state(Vst0);
+ {RetType, _, _} ->
St = St0#st{f=init_fregs()},
Vst = prune_x_regs(0, Vst0#vst{current=St}),
- create_term(Type, call, [], {x,0}, Vst)
+ create_term(RetType, call, [], {x,0}, Vst)
end.
%% Tail call.
@@ -1287,16 +1501,19 @@ tail_call(Name, Live, Vst0) ->
verify_y_init(Vst0),
Vst = deallocate(Vst0),
verify_call_args(Name, Live, Vst),
- case call_return_type(Name, Vst0) of
- exception -> verify_no_ct(Vst);
- _ -> verify_return(Vst)
- end,
- kill_state(Vst).
+ verify_return(Vst).
verify_call_args(_, 0, #vst{}) ->
ok;
-verify_call_args({f,Lbl}, Live, Vst) when is_integer(Live)->
- verify_local_args(Live - 1, Lbl, #{}, Vst);
+verify_call_args({f,Lbl}, Live, #vst{ft=Ft}=Vst) when is_integer(Live) ->
+ case gb_trees:lookup(Lbl, Ft) of
+ {value, FuncInfo} ->
+ #{ arity := Live,
+ parameter_info := ParamInfo } = FuncInfo,
+ verify_local_args(Live - 1, ParamInfo, #{}, Vst);
+ none ->
+ error(local_call_to_unknown_function)
+ end;
verify_call_args(_, Live, Vst) when is_integer(Live)->
verify_remote_args_1(Live - 1, Vst);
verify_call_args(_, Live, _) ->
@@ -1308,86 +1525,50 @@ verify_remote_args_1(X, Vst) ->
assert_durable_term({x, X}, Vst),
verify_remote_args_1(X - 1, Vst).
-verify_local_args(-1, _Lbl, _CtxIds, _Vst) ->
+verify_local_args(-1, _ParamInfo, _CtxIds, _Vst) ->
ok;
-verify_local_args(X, Lbl, CtxIds, Vst) ->
+verify_local_args(X, ParamInfo, CtxRefs, Vst) ->
Reg = {x, X},
assert_not_fragile(Reg, Vst),
case get_movable_term_type(Reg, Vst) of
- #ms{id=Id}=Type ->
- case CtxIds of
- #{ Id := Other } ->
+ #t_bs_context{}=Type ->
+ VRef = get_reg_vref(Reg, Vst),
+ case CtxRefs of
+ #{ VRef := Other } ->
error({multiple_match_contexts, [Reg, Other]});
#{} ->
- verify_arg_type(Lbl, Reg, Type, Vst),
- verify_local_args(X - 1, Lbl, CtxIds#{ Id => Reg }, Vst)
+ verify_arg_type(Reg, Type, ParamInfo),
+ verify_local_args(X - 1, ParamInfo,
+ CtxRefs#{ VRef => Reg }, Vst)
end;
Type ->
- verify_arg_type(Lbl, Reg, Type, Vst),
- verify_local_args(X - 1, Lbl, CtxIds, Vst)
+ verify_arg_type(Reg, Type, ParamInfo),
+ verify_local_args(X - 1, ParamInfo, CtxRefs, Vst)
end.
-%% Verifies that the given argument narrows to what the function expects.
-verify_arg_type(Lbl, Reg, #ms{}, #vst{ft=Ft}) ->
- %% Match contexts require explicit support, and may not be passed to a
- %% function that accepts arbitrary terms.
- case gb_trees:lookup({Lbl, Reg}, Ft) of
- {value, #ms{}} -> ok;
- _ -> error(no_bs_start_match2)
- end;
-verify_arg_type(Lbl, Reg, GivenType, #vst{ft=Ft}) ->
- case gb_trees:lookup({Lbl, Reg}, Ft) of
- {value, #ms{}} ->
- %% Functions that accept match contexts also accept all other
- %% terms. This will change once we support union types.
- ok;
- {value, RequiredType} ->
- case vat_1(GivenType, RequiredType) of
- true -> ok;
- false -> error({bad_arg_type, Reg, GivenType, RequiredType})
+verify_arg_type(Reg, GivenType, ParamTypes) ->
+ case {ParamTypes, GivenType} of
+ {#{ Reg := Info }, #t_bs_context{}} ->
+ %% Match contexts require explicit support, and may not be passed
+ %% to a function that accepts arbitrary terms.
+ case member(accepts_match_context, Info) of
+ true -> verify_arg_type_1(Reg, GivenType, Info);
+ false -> error(no_bs_start_match2)
end;
- none ->
+ {_, #t_bs_context{}} ->
+ error(no_bs_start_match2);
+ {#{ Reg := Info }, _} ->
+ verify_arg_type_1(Reg, GivenType, Info);
+ {#{}, _} ->
ok
end.
-%% Checks whether the Given argument is compatible with the Required one. This
-%% is essentially a relaxed version of 'meet(Given, Req) =:= Given', where we
-%% accept that the Given value has the right type but not necessarily the exact
-%% same value; if {atom,gurka} is required, we'll consider {atom,[]} valid.
-%%
-%% This will catch all problems that could crash the emulator, like passing a
-%% 1-tuple when the callee expects a 3-tuple, but some value errors might slip
-%% through.
-vat_1(Same, Same) -> true;
-vat_1({atom,A}, {atom,B}) -> A =:= B orelse is_list(A) orelse is_list(B);
-vat_1({atom,A}, bool) -> is_boolean(A) orelse is_list(A);
-vat_1(bool, {atom,B}) -> is_boolean(B) orelse is_list(B);
-vat_1(cons, list) -> true;
-vat_1({float,A}, {float,B}) -> A =:= B orelse is_list(A) orelse is_list(B);
-vat_1({float,_}, number) -> true;
-vat_1({integer,A}, {integer,B}) -> A =:= B orelse is_list(A) orelse is_list(B);
-vat_1({integer,_}, number) -> true;
-vat_1(_, {literal,_}) -> false;
-vat_1({literal,_}=Lit, Required) -> vat_1(get_literal_type(Lit), Required);
-vat_1(nil, list) -> true;
-vat_1({tuple,SzA,EsA}, {tuple,SzB,EsB}) ->
- if
- is_list(SzB) ->
- tuple_sz(SzA) >= tuple_sz(SzB) andalso vat_elements(EsA, EsB);
- SzA =:= SzB ->
- vat_elements(EsA, EsB);
- SzA =/= SzB ->
- false
- end;
-vat_1(_, _) -> false.
-
-vat_elements(EsA, EsB) ->
- maps:fold(fun(Key, Req, Acc) ->
- case EsA of
- #{ Key := Given } -> Acc andalso vat_1(Given, Req);
- #{} -> false
- end
- end, true, EsB).
+verify_arg_type_1(Reg, GivenType, Info) ->
+ RequiredType = proplists:get_value(type, Info, any),
+ case meet(GivenType, RequiredType) of
+ GivenType -> ok;
+ _ -> error({bad_arg_type, Reg, GivenType, RequiredType})
+ end.
allocate(Tag, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst0) ->
verify_live(Live, Vst0),
@@ -1560,7 +1741,7 @@ assert_unique_map_keys([_,_|_]=Ls) ->
assert_literal(L),
L
end || L <- Ls],
- case length(Vs) =:= sets:size(sets:from_list(Vs)) of
+ case length(Vs) =:= cerl_sets:size(cerl_sets:from_list(Vs)) of
true -> ok;
false -> error(keys_not_unique)
end.
@@ -1569,49 +1750,35 @@ assert_unique_map_keys([_,_|_]=Ls) ->
%%% New binary matching instructions.
%%%
-bsm_match_state() ->
- #ms{}.
-bsm_match_state(Slots) ->
- #ms{slots=Slots}.
-
-bsm_validate_context(Reg, Vst) ->
- _ = bsm_get_context(Reg, Vst),
- ok.
-
-bsm_get_context({Kind,_}=Reg, Vst) when Kind =:= x; Kind =:= y->
- case get_movable_term_type(Reg, Vst) of
- #ms{}=Ctx -> Ctx;
- _ -> error({no_bsm_context,Reg})
- end;
-bsm_get_context(Reg, _) ->
- error({bad_source,Reg}).
-
bsm_save(Reg, {atom,start}, Vst) ->
%% Save point refering to where the match started.
%% It is always valid. But don't forget to validate the context register.
- bsm_validate_context(Reg, Vst),
+ assert_type(#t_bs_context{}, Reg, Vst),
Vst;
bsm_save(Reg, SavePoint, Vst) ->
- case bsm_get_context(Reg, Vst) of
- #ms{valid=Bits,slots=Slots}=Ctxt0 when SavePoint < Slots ->
- Ctx = Ctxt0#ms{valid=Bits bor (1 bsl SavePoint),slots=Slots},
- override_type(Ctx, Reg, Vst);
- _ -> error({illegal_save,SavePoint})
+ case get_movable_term_type(Reg, Vst) of
+ #t_bs_context{valid=Bits,slots=Slots}=Ctxt0 when SavePoint < Slots ->
+ Ctx = Ctxt0#t_bs_context{valid=Bits bor (1 bsl SavePoint),
+ slots=Slots},
+ override_type(Ctx, Reg, Vst);
+ _ ->
+ error({illegal_save, SavePoint})
end.
bsm_restore(Reg, {atom,start}, Vst) ->
%% (Mostly) automatic save point refering to where the match started.
%% It is always valid. But don't forget to validate the context register.
- bsm_validate_context(Reg, Vst),
+ assert_type(#t_bs_context{}, Reg, Vst),
Vst;
bsm_restore(Reg, SavePoint, Vst) ->
- case bsm_get_context(Reg, Vst) of
- #ms{valid=Bits,slots=Slots} when SavePoint < Slots ->
- case Bits band (1 bsl SavePoint) of
- 0 -> error({illegal_restore,SavePoint,not_set});
- _ -> Vst
- end;
- _ -> error({illegal_restore,SavePoint,range})
+ case get_movable_term_type(Reg, Vst) of
+ #t_bs_context{valid=Bits,slots=Slots} when SavePoint < Slots ->
+ case Bits band (1 bsl SavePoint) of
+ 0 -> error({illegal_restore, SavePoint, not_set});
+ _ -> Vst
+ end;
+ _ ->
+ error({illegal_restore, SavePoint, range})
end.
validate_select_val(_Fail, _Choices, _Src, #vst{current=none}=Vst) ->
@@ -1627,7 +1794,7 @@ validate_select_val(Fail, [Val,{f,L}|T], Src, Vst0) ->
update_ne_types(Src, Val, FailVst)
end),
validate_select_val(Fail, T, Src, Vst);
-validate_select_val(Fail, [], _, Vst) ->
+validate_select_val(Fail, [], _Src, Vst) ->
branch(Fail, Vst,
fun(SuccVst) ->
%% The next instruction is never executed.
@@ -1639,7 +1806,7 @@ validate_select_tuple_arity(_Fail, _Choices, _Src, #vst{current=none}=Vst) ->
%% can't reach the fail label or any of the remaining choices.
Vst;
validate_select_tuple_arity(Fail, [Arity,{f,L}|T], Tuple, Vst0) ->
- Type = {tuple, Arity, #{}},
+ Type = #t_tuple{exact=true,size=Arity},
Vst = branch(L, Vst0,
fun(BranchVst) ->
update_type(fun meet/2, Type, Tuple, BranchVst)
@@ -1655,63 +1822,103 @@ validate_select_tuple_arity(Fail, [], _, #vst{}=Vst) ->
kill_state(SuccVst)
end).
-infer_types({Kind,_}=Reg, Vst) when Kind =:= x; Kind =:= y ->
- infer_types(get_reg_vref(Reg, Vst), Vst);
-infer_types(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
+%%
+%% Infers types from comparisons, looking at the expressions that produced the
+%% compared values and updates their types if we've learned something new from
+%% the comparison.
+%%
+
+infer_types(CompareOp, {Kind,_}=LHS, RHS, Vst) when Kind =:= x; Kind =:= y ->
+ infer_types(CompareOp, get_reg_vref(LHS, Vst), RHS, Vst);
+infer_types(CompareOp, LHS, {Kind,_}=RHS, Vst) when Kind =:= x; Kind =:= y ->
+ infer_types(CompareOp, LHS, get_reg_vref(RHS, Vst), Vst);
+infer_types(CompareOp, LHS, RHS, #vst{current=#st{vs=Vs}}=Vst0) ->
case Vs of
- #{ Ref := Entry } -> infer_types_1(Entry);
- #{} -> fun(_, S) -> S end
+ #{ LHS := LEntry, RHS := REntry } ->
+ Vst = infer_types_1(LEntry, RHS, CompareOp, Vst0),
+ infer_types_1(REntry, LHS, CompareOp, Vst);
+ #{ LHS := LEntry } ->
+ infer_types_1(LEntry, RHS, CompareOp, Vst0);
+ #{ RHS := REntry } ->
+ infer_types_1(REntry, LHS, CompareOp, Vst0);
+ #{} ->
+ Vst0
+ end.
+
+infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}, Val, Op, Vst) ->
+ case Val of
+ {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
+ update_eq_types(LHS, RHS, Vst);
+ {atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
+ update_ne_types(LHS, RHS, Vst);
+ _ ->
+ Vst
end;
-infer_types(_, #vst{}) ->
- fun(_, S) -> S end.
-
-infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}) ->
- fun({atom,true}, S) ->
- %% Either side might contain something worth inferring, so we need
- %% to check them both.
- Infer_L = infer_types(RHS, S),
- Infer_R = infer_types(LHS, S),
- Infer_R(RHS, Infer_L(LHS, S));
- (_, S) -> S
+infer_types_1(#value{op={bif,'=/='},args=[LHS,RHS]}, Val, Op, Vst) ->
+ case Val of
+ {atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
+ update_ne_types(LHS, RHS, Vst);
+ {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
+ update_eq_types(LHS, RHS, Vst);
+ _ ->
+ Vst
end;
-infer_types_1(#value{op={bif,element},args=[{integer,Index}=Key,Tuple]}) ->
- fun(Val, S) ->
- Type = {tuple,[Index], #{ Key => get_term_type(Val, S) }},
- update_type(fun meet/2, Type, Tuple, S)
+infer_types_1(#value{op={bif,element},args=[{integer,Index},Tuple]},
+ Val, Op, Vst) when Index >= 1 ->
+ ElementType = get_term_type(Val, Vst),
+ Es = beam_types:set_tuple_element(Index, ElementType, #{}),
+ TupleType = #t_tuple{size=Index,elements=Es},
+ case Op of
+ eq_exact ->
+ update_type(fun meet/2, TupleType, Tuple, Vst);
+ ne_exact ->
+ %% Subtraction is only safe when ElementType is single-valued and
+ %% the index is below the tuple element limit.
+ case beam_types:is_singleton_type(ElementType) of
+ true when Es =/= #{} ->
+ update_type(fun subtract/2, TupleType, Tuple, Vst);
+ _ ->
+ Vst
+ end
end;
-infer_types_1(#value{op={bif,is_atom},args=[Src]}) ->
- infer_type_test_bif({atom,[]}, Src);
-infer_types_1(#value{op={bif,is_boolean},args=[Src]}) ->
- infer_type_test_bif(bool, Src);
-infer_types_1(#value{op={bif,is_binary},args=[Src]}) ->
- infer_type_test_bif(binary, Src);
-infer_types_1(#value{op={bif,is_bitstring},args=[Src]}) ->
- infer_type_test_bif(binary, Src);
-infer_types_1(#value{op={bif,is_float},args=[Src]}) ->
- infer_type_test_bif(float, Src);
-infer_types_1(#value{op={bif,is_integer},args=[Src]}) ->
- infer_type_test_bif({integer,{}}, Src);
-infer_types_1(#value{op={bif,is_list},args=[Src]}) ->
- infer_type_test_bif(list, Src);
-infer_types_1(#value{op={bif,is_map},args=[Src]}) ->
- infer_type_test_bif(map, Src);
-infer_types_1(#value{op={bif,is_number},args=[Src]}) ->
- infer_type_test_bif(number, Src);
-infer_types_1(#value{op={bif,is_tuple},args=[Src]}) ->
- infer_type_test_bif({tuple,[0],#{}}, Src);
-infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]}) ->
- fun({integer,Arity}, S) ->
- update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
- (_, S) -> S
+infer_types_1(#value{op={bif,is_atom},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_atom{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_boolean},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(beam_types:make_boolean(), Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_binary},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_bitstring{size_unit=8}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_bitstring},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_bitstring{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_float},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_float{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_integer},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_integer{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_list},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_list{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_map},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_map{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_number},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(number, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,is_tuple},args=[Src]}, Val, Op, Vst) ->
+ infer_type_test_bif(#t_tuple{}, Src, Val, Op, Vst);
+infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]},
+ {integer,Arity}, Op, Vst) ->
+ Type = #t_tuple{exact=true,size=Arity},
+ case Op of
+ eq_exact -> update_type(fun meet/2, Type, Tuple, Vst);
+ ne_exact -> update_type(fun subtract/2, Type, Tuple, Vst)
end;
-infer_types_1(_) ->
- fun(_, S) -> S end.
-
-infer_type_test_bif(Type, Src) ->
- fun({atom,true}, S) ->
- update_type(fun meet/2, Type, Src, S);
- (_, S) ->
- S
+infer_types_1(_, _, _, Vst) ->
+ Vst.
+
+infer_type_test_bif(Type, Src, Val, Op, Vst) ->
+ case Val of
+ {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
+ update_type(fun meet/2, Type, Src, Vst);
+ {atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
+ update_type(fun subtract/2, Type, Src, Vst);
+ _ ->
+ Vst
end.
%%%
@@ -1822,43 +2029,58 @@ update_type(Merge, With, #value_ref{}=Ref, Vst) ->
update_type(Merge, With, {Kind,_}=Reg, Vst) when Kind =:= x; Kind =:= y ->
update_type(Merge, With, get_reg_vref(Reg, Vst), Vst);
update_type(Merge, With, Literal, Vst) ->
- assert_literal(Literal),
%% Literals always retain their type, but we still need to bail on type
%% conflicts.
- case Merge(Literal, With) of
- none -> throw({type_conflict, Literal, With});
+ Type = get_literal_type(Literal),
+ case Merge(Type, With) of
+ none -> throw({type_conflict, Type, With});
_Type -> Vst
end.
-update_ne_types(LHS, RHS, Vst) ->
+update_eq_types(LHS, RHS, Vst0) ->
+ LType = get_term_type(LHS, Vst0),
+ RType = get_term_type(RHS, Vst0),
+
+ Vst1 = update_type(fun meet/2, RType, LHS, Vst0),
+ Vst = update_type(fun meet/2, LType, RHS, Vst1),
+
+ infer_types(eq_exact, LHS, RHS, Vst).
+
+update_ne_types(LHS, RHS, Vst0) ->
+ Vst1 = update_ne_types_1(LHS, RHS, Vst0),
+ Vst = update_ne_types_1(RHS, LHS, Vst1),
+
+ infer_types(ne_exact, LHS, RHS, Vst).
+
+update_ne_types_1(LHS, RHS, Vst0) ->
%% While updating types on equality is fairly straightforward, inequality
%% is a bit trickier since all we know is that the *value* of LHS differs
%% from RHS, so we can't blindly subtract their types.
%%
- %% Consider `number =/= {integer,[]}`; all we know is that LHS isn't equal
+ %% Consider `number =/= #t_integer{}`; all we know is that LHS isn't equal
%% to some *specific integer* of unknown value, and if we were to subtract
- %% {integer,[]} we would erroneously infer that the new type is {float,[]}.
+ %% #t_integer{} we would erroneously infer that the new type is float.
%%
%% Therefore, we only subtract when we know that RHS has a specific value.
- RType = get_term_type(RHS, Vst),
- case is_literal(RType) of
- true -> update_type(fun subtract/2, RType, LHS, Vst);
- false -> Vst
+ RType = get_term_type(RHS, Vst0),
+ case beam_types:is_singleton_type(RType) of
+ true ->
+ Vst = update_type(fun subtract/2, RType, LHS, Vst0),
+
+ %% If LHS has a specific value after subtraction we can infer types
+ %% as if we've made an exact match, which is much stronger than
+ %% ne_exact.
+ LType = get_term_type(LHS, Vst),
+ case beam_types:get_singleton_value(LType) of
+ {ok, Value} ->
+ infer_types(eq_exact, LHS, value_to_literal(Value), Vst);
+ error ->
+ Vst
+ end;
+ false ->
+ Vst0
end.
-update_eq_types(LHS, RHS, Vst0) ->
- %% Either side might contain something worth inferring, so we need
- %% to check them both.
- Infer_L = infer_types(RHS, Vst0),
- Infer_R = infer_types(LHS, Vst0),
- Vst1 = Infer_R(RHS, Infer_L(LHS, Vst0)),
-
- T1 = get_term_type(LHS, Vst1),
- T2 = get_term_type(RHS, Vst1),
-
- Vst = update_type(fun meet/2, T2, LHS, Vst1),
- update_type(fun meet/2, T1, RHS, Vst).
-
%% Helper functions for the above.
assign_1(Src, Dst, Vst0) ->
@@ -1909,16 +2131,9 @@ get_reg_vref({y,_}=Src, #vst{current=#st{ys=Ys}}) ->
end.
set_type(Type, #value_ref{}=Ref, #vst{current=#st{vs=Vs0}=St}=Vst) ->
- case Vs0 of
- #{ Ref := #value{}=Entry } ->
- Vs = Vs0#{ Ref => Entry#value{type=Type} },
- Vst#vst{current=St#st{vs=Vs}};
- #{} ->
- %% Dead references may happen during type inference and are not an
- %% error in and of themselves. If a problem were to arise from this
- %% it'll explode elsewhere.
- Vst
- end.
+ #{ Ref := #value{}=Entry } = Vs0,
+ Vs = Vs0#{ Ref => Entry#value{type=Type} },
+ Vst#vst{current=St#st{vs=Vs}}.
new_value(Type, Op, Ss, #vst{current=#st{vs=Vs0}=St,ref_ctr=Counter}=Vst) ->
Ref = #value_ref{id=Counter},
@@ -1926,9 +2141,9 @@ new_value(Type, Op, Ss, #vst{current=#st{vs=Vs0}=St,ref_ctr=Counter}=Vst) ->
{Ref, Vst#vst{current=St#st{vs=Vs},ref_ctr=Counter+1}}.
-kill_catch_tag(Reg, #vst{current=#st{ct=[Fail|Fails]}=St}=Vst0) ->
- Vst = Vst0#vst{current=St#st{ct=Fails,fls=undefined}},
- {_, Fail} = get_tag_type(Reg, Vst), %Assertion.
+kill_catch_tag(Reg, #vst{current=#st{ct=[Tag|Tags]}=St}=Vst0) ->
+ Vst = Vst0#vst{current=St#st{ct=Tags,fls=undefined}},
+ Tag = get_tag_type(Reg, Vst), %Assertion.
kill_tag(Reg, Vst).
check_try_catch_tags(Type, {y,N}=Reg, Vst) ->
@@ -1973,308 +2188,44 @@ is_literal({integer,I}) when is_integer(I) -> true;
is_literal({literal,_L}) -> true;
is_literal(_) -> false.
-%% The possible types.
-%%
-%% First non-term types:
-%%
-%% initialized Only for Y registers. Means that the Y register
-%% has been initialized with some valid term so that
-%% it is safe to pass to the garbage collector.
-%% NOT safe to use in any other way (will not crash the
-%% emulator, but clearly points to a bug in the compiler).
-%%
-%% {catchtag,[Lbl]} A special term used within a catch. Must only be used
-%% by the catch instructions; NOT safe to use in other
-%% instructions.
-%%
-%% {trytag,[Lbl]} A special term used within a try block. Must only be
-%% used by the catch instructions; NOT safe to use in other
-%% instructions.
-%%
-%% exception Can only be used as a type returned by
-%% call_return_type/2 (which gives the type of the value
-%% returned by a call). Thus 'exception' is never stored
-%% as type descriptor for a register.
-%%
-%% #ms{} A match context for bit syntax matching. We do allow
-%% it to moved/to from stack, but otherwise it must only
-%% be accessed by bit syntax matching instructions.
-%%
-%%
-%% Normal terms:
-%%
-%% term Any valid Erlang (but not of the special types above).
-%%
-%% binary Binary or bitstring.
-%%
-%% bool The atom 'true' or the atom 'false'.
-%%
-%% cons Cons cell: [_|_]
-%%
-%% nil Empty list: []
-%%
-%% list List: [] or [_|_]
-%%
-%% {tuple,[Sz],Es} Tuple. An element has been accessed using
-%% element/2 or setelement/3 so that it is known that
-%% the type is a tuple of size at least Sz. Es is a map
-%% containing known types by tuple index.
-%%
-%% {tuple,Sz,Es} Tuple. A test_arity instruction has been seen
-%% so that it is known that the size is exactly Sz.
-%%
-%% {atom,[]} Atom.
-%% {atom,Atom}
-%%
-%% {integer,[]} Integer.
-%% {integer,Integer}
-%%
-%% {float,[]} Float.
-%% {float,Float}
-%%
-%% number Integer or Float of unknown value
-%%
-%% map Map.
-%%
-%% none A conflict in types. There will be an exception at runtime.
-%%
-
-%% join(Type1, Type2) -> Type
-%% Return the most specific type possible.
-join(Same, Same) ->
- Same;
-join(none, Other) ->
- Other;
-join(Other, none) ->
- Other;
-join({literal,_}=T1, T2) ->
- join_literal(T1, T2);
-join(T1, {literal,_}=T2) ->
- join_literal(T2, T1);
-join({tuple,Size,EsA}, {tuple,Size,EsB}) ->
- Es = join_tuple_elements(tuple_sz(Size), EsA, EsB),
- {tuple, Size, Es};
-join({tuple,A,EsA}, {tuple,B,EsB}) ->
- Size = min(tuple_sz(A), tuple_sz(B)),
- Es = join_tuple_elements(Size, EsA, EsB),
- {tuple, [Size], Es};
-join({Type,A}, {Type,B})
- when Type =:= atom; Type =:= integer; Type =:= float ->
- if A =:= B -> {Type,A};
- true -> {Type,[]}
- end;
-join({Type,_}, number)
- when Type =:= integer; Type =:= float ->
- number;
-join(number, {Type,_})
- when Type =:= integer; Type =:= float ->
- number;
-join({integer,_}, {float,_}) ->
- number;
-join({float,_}, {integer,_}) ->
- number;
-join(bool, {atom,A}) ->
- join_bool(A);
-join({atom,A}, bool) ->
- join_bool(A);
-join({atom,A}, {atom,B}) when is_boolean(A), is_boolean(B) ->
- bool;
-join({atom,_}, {atom,_}) ->
- {atom,[]};
-join(#ms{id=Id1,valid=B1,slots=Slots1},
- #ms{id=Id2,valid=B2,slots=Slots2}) ->
- Id = if
- Id1 =:= Id2 -> Id1;
- true -> make_ref()
- end,
- #ms{id=Id,valid=B1 band B2,slots=min(Slots1, Slots2)};
-join(T1, T2) when T1 =/= T2 ->
- %% We've exhaused all other options, so the type must either be a list or
- %% a 'term'.
- join_list(T1, T2).
+value_to_literal([]) -> nil;
+value_to_literal(A) when is_atom(A) -> {atom,A};
+value_to_literal(F) when is_float(F) -> {float,F};
+value_to_literal(I) when is_integer(I) -> {integer,I};
+value_to_literal(Other) -> {literal,Other}.
-join_tuple_elements(Limit, EsA, EsB) ->
- Es0 = join_elements(EsA, EsB),
- maps:filter(fun({integer,Index}, _Type) -> Index =< Limit end, Es0).
-
-join_elements(Es1, Es2) ->
- Keys = if
- map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
- map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
- end,
- join_elements_1(Keys, Es1, Es2, #{}).
-
-join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
- Type = case {Es1, Es2} of
- {#{ Key := Same }, #{ Key := Same }} -> Same;
- {#{ Key := Type1 }, #{ Key := Type2 }} -> join(Type1, Type2);
- {#{}, #{}} -> term
- end,
- Acc = set_element_type(Key, Type, Acc0),
- join_elements_1(Keys, Es1, Es2, Acc);
-join_elements_1([], _Es1, _Es2, Acc) ->
- Acc.
-
-%% Joins types of literals; note that the left argument must either be a
-%% literal or exactly equal to the second argument.
-join_literal(Same, Same) ->
- Same;
-join_literal({literal,_}=Lit, T) ->
- join_literal(T, get_literal_type(Lit));
-join_literal(T1, T2) ->
- %% We're done extracting the types, try merging them again.
- join(T1, T2).
-
-join_list(nil, cons) -> list;
-join_list(nil, list) -> list;
-join_list(cons, list) -> list;
-join_list(T, nil) -> join_list(nil, T);
-join_list(T, cons) -> join_list(cons, T);
-join_list(_, _) ->
- %% Not a list, so it must be a term.
- term.
-
-join_bool([]) -> {atom,[]};
-join_bool(true) -> bool;
-join_bool(false) -> bool;
-join_bool(_) -> {atom,[]}.
-
-%% meet(Type1, Type2) -> Type
-%% Return the meet of two types. The meet is a more specific type.
-%% It will be 'none' if the types are in conflict.
-
-meet(Same, Same) ->
- Same;
-meet(term, Other) ->
- Other;
-meet(Other, term) ->
- Other;
-meet(#ms{}, binary) ->
- #ms{};
-meet(binary, #ms{}) ->
- #ms{};
-meet({literal,_}, {literal,_}) ->
- none;
-meet(T1, {literal,_}=T2) ->
- meet(T2, T1);
-meet({literal,_}=T1, T2) ->
- case meet(get_literal_type(T1), T2) of
- none -> none;
- _ -> T1
- end;
-meet(T1, T2) ->
- case {erlang:min(T1, T2),erlang:max(T1, T2)} of
- {{atom,_}=A,{atom,[]}} -> A;
- {bool,{atom,B}=Atom} when is_boolean(B) -> Atom;
- {bool,{atom,[]}} -> bool;
- {cons,list} -> cons;
- {{float,_}=T,{float,[]}} -> T;
- {{integer,_}=T,{integer,[]}} -> T;
- {list,nil} -> nil;
- {number,{integer,_}=T} -> T;
- {number,{float,_}=T} -> T;
- {{tuple,Size1,Es1},{tuple,Size2,Es2}} ->
- Es = meet_elements(Es1, Es2),
- case {Size1,Size2,Es} of
- {_, _, none} ->
- none;
- {[Sz1],[Sz2],_} ->
- Sz = erlang:max(Sz1, Sz2),
- assert_tuple_elements(Sz, Es),
- {tuple,[Sz],Es};
- {Sz1,[Sz2],_} when Sz2 =< Sz1 ->
- assert_tuple_elements(Sz1, Es),
- {tuple,Sz1,Es};
- {Sz,Sz,_} ->
- assert_tuple_elements(Sz, Es),
- {tuple,Sz,Es};
- {_,_,_} ->
- none
- end;
- {_,_} -> none
+%% These are just wrappers around their equivalents in beam_types, which
+%% handle the validator-specific #t_abstract{} type.
+%%
+%% The funny-looking abstract types produced here are intended to provoke
+%% errors on actual use; they do no harm just lying around.
+
+normalize(#t_abstract{}=A) -> error({abstract_type, A});
+normalize(T) -> beam_types:normalize(T).
+
+join(Same, Same) -> Same;
+join(#t_abstract{}=A, B) -> #t_abstract{kind={join, A, B}};
+join(A, #t_abstract{}=B) -> #t_abstract{kind={join, A, B}};
+join(A, B) -> beam_types:join(A, B).
+
+meet(Same, Same) -> Same;
+meet(#t_abstract{}=A, B) -> #t_abstract{kind={meet, A, B}};
+meet(A, #t_abstract{}=B) -> #t_abstract{kind={meet, A, B}};
+meet(A, B) -> beam_types:meet(A, B).
+
+subtract(#t_abstract{}=A, B) -> #t_abstract{kind={subtract, A, B}};
+subtract(A, #t_abstract{}=B) -> #t_abstract{kind={subtract, A, B}};
+subtract(A, B) -> beam_types:subtract(A, B).
+
+assert_type(RequiredType, Term, Vst) ->
+ GivenType = get_movable_term_type(Term, Vst),
+ case meet(RequiredType, GivenType) of
+ GivenType ->
+ ok;
+ _RequiredType ->
+ error({bad_type,{needed,RequiredType},{actual,GivenType}})
end.
-meet_elements(Es1, Es2) ->
- Keys = maps:keys(Es1) ++ maps:keys(Es2),
- meet_elements_1(Keys, Es1, Es2, #{}).
-
-meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
- case {Es1, Es2} of
- {#{ Key := Type1 }, #{ Key := Type2 }} ->
- case meet(Type1, Type2) of
- none -> none;
- Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
- end;
- {#{ Key := Type1 }, _} ->
- meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
- {_, #{ Key := Type2 }} ->
- meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
- end;
-meet_elements_1([], _Es1, _Es2, Acc) ->
- Acc.
-
-%% No tuple elements may have an index above the known size.
-assert_tuple_elements(Limit, Es) ->
- true = maps:fold(fun({integer,Index}, _T, true) ->
- Index =< Limit
- end, true, Es). %Assertion.
-
-%% subtract(Type1, Type2) -> Type
-%% Subtract Type2 from Type2. Example:
-%% subtract(list, nil) -> cons
-
-subtract(Same, Same) -> none;
-subtract(list, nil) -> cons;
-subtract(list, cons) -> nil;
-subtract(number, {integer,[]}) -> {float,[]};
-subtract(number, {float,[]}) -> {integer,[]};
-subtract(bool, {atom,false}) -> {atom, true};
-subtract(bool, {atom,true}) -> {atom, false};
-subtract(Type, _) -> Type.
-
-assert_type(WantedType, Term, Vst) ->
- Type = get_term_type(Term, Vst),
- assert_type(WantedType, Type).
-
-assert_type(Correct, Correct) -> ok;
-assert_type(float, {float,_}) -> ok;
-assert_type(tuple, {tuple,_,_}) -> ok;
-assert_type(tuple, {literal,Tuple}) when is_tuple(Tuple) -> ok;
-assert_type({tuple_element,I}, {tuple,[Sz],_})
- when 1 =< I, I =< Sz ->
- ok;
-assert_type({tuple_element,I}, {tuple,Sz,_})
- when is_integer(Sz), 1 =< I, I =< Sz ->
- ok;
-assert_type({tuple_element,I}, {literal,Lit}) when I =< tuple_size(Lit) ->
- ok;
-assert_type(cons, {literal,[_|_]}) ->
- ok;
-assert_type(Needed, Actual) ->
- error({bad_type,{needed,Needed},{actual,Actual}}).
-
-get_element_type(Key, Src, Vst) ->
- get_element_type_1(Key, get_term_type(Src, Vst)).
-
-get_element_type_1({integer,_}=Key, {tuple,_Sz,Es}) ->
- case Es of
- #{ Key := Type } -> Type;
- #{} -> term
- end;
-get_element_type_1(_Index, _Type) ->
- term.
-
-set_element_type(_Key, none, Es) ->
- Es;
-set_element_type(Key, term, Es) ->
- maps:remove(Key, Es);
-set_element_type(Key, Type, Es) ->
- Es#{ Key => Type }.
-
-get_tuple_size({integer,[]}) -> 0;
-get_tuple_size({integer,Sz}) -> Sz;
-get_tuple_size(_) -> 0.
-
validate_src(Ss, Vst) when is_list(Ss) ->
_ = [assert_term(S, Vst) || S <- Ss],
ok.
@@ -2285,7 +2236,8 @@ validate_src(Ss, Vst) when is_list(Ss) ->
get_term_type(Src, Vst) ->
case get_movable_term_type(Src, Vst) of
- #ms{} -> error({match_context,Src});
+ #t_bs_context{} -> error({match_context,Src});
+ #t_abstract{} -> error({abstract_term,Src});
Type -> Type
end.
@@ -2295,12 +2247,11 @@ get_term_type(Src, Vst) ->
get_movable_term_type(Src, Vst) ->
case get_raw_type(Src, Vst) of
+ #t_abstract{kind=unfinished_tuple=Kind} -> error({Kind,Src});
initialized -> error({unassigned,Src});
uninitialized -> error({uninitialized_reg,Src});
{catchtag,_} -> error({catchtag,Src});
{trytag,_} -> error({trytag,Src});
- tuple_in_progress -> error({tuple_in_progress,Src});
- {literal,_}=Lit -> get_literal_type(Lit);
Type -> Type
end.
@@ -2339,33 +2290,21 @@ get_raw_type(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
#{ Ref := #value{type=Type} } -> Type;
#{} -> none
end;
-get_raw_type(Src, #vst{}) ->
+get_raw_type(Src, #vst{current=#st{}}) ->
get_literal_type(Src).
-get_literal_type(nil=T) -> T;
-get_literal_type({atom,A}=T) when is_atom(A) -> T;
-get_literal_type({float,F}=T) when is_float(F) -> T;
-get_literal_type({integer,I}=T) when is_integer(I) -> T;
-get_literal_type({literal,[_|_]}) -> cons;
-get_literal_type({literal,Bitstring}) when is_bitstring(Bitstring) -> binary;
-get_literal_type({literal,Map}) when is_map(Map) -> map;
-get_literal_type({literal,Tuple}) when is_tuple(Tuple) -> glt_1(Tuple);
-get_literal_type({literal,_}) -> term;
-get_literal_type(T) -> error({not_literal,T}).
-
-glt_1([]) -> nil;
-glt_1(A) when is_atom(A) -> {atom, A};
-glt_1(F) when is_float(F) -> {float, F};
-glt_1(I) when is_integer(I) -> {integer, I};
-glt_1(T) when is_tuple(T) ->
- {Es,_} = foldl(fun(Val, {Es0, Index}) ->
- Type = glt_1(Val),
- Es = set_element_type({integer,Index}, Type, Es0),
- {Es, Index + 1}
- end, {#{}, 1}, tuple_to_list(T)),
- {tuple, tuple_size(T), Es};
-glt_1(L) ->
- {literal, L}.
+get_literal_type(nil) ->
+ beam_types:make_type_from_value([]);
+get_literal_type({atom,A}) when is_atom(A) ->
+ beam_types:make_type_from_value(A);
+get_literal_type({float,F}) when is_float(F) ->
+ beam_types:make_type_from_value(F);
+get_literal_type({integer,I}) when is_integer(I) ->
+ beam_types:make_type_from_value(I);
+get_literal_type({literal,L}) ->
+ beam_types:make_type_from_value(L);
+get_literal_type(T) ->
+ error({not_literal,T}).
%%%
%%% Branch tracking
@@ -2383,10 +2322,12 @@ glt_1(L) ->
SuccFun :: BranchFun) -> #vst{} when
BranchFun :: fun((#vst{}) -> #vst{}).
branch(Lbl, Vst0, FailFun, SuccFun) ->
+ validate_branch(Lbl, Vst0),
#vst{current=St0} = Vst0,
+
try FailFun(Vst0) of
Vst1 ->
- Vst2 = branch_state(Lbl, Vst1),
+ Vst2 = fork_state(Lbl, Vst1),
Vst = Vst2#vst{current=St0},
try SuccFun(Vst) of
V -> V
@@ -2404,6 +2345,24 @@ branch(Lbl, Vst0, FailFun, SuccFun) ->
SuccFun(Vst0)
end.
+validate_branch(Lbl, #vst{current=#st{ct=Tags}}) ->
+ validate_branch_1(Lbl, Tags).
+
+validate_branch_1(Lbl, [{trytag, FailLbls} | Tags]) ->
+ %% 'try_case' assumes that an exception has been thrown, so a direct branch
+ %% will crash the emulator.
+ %%
+ %% (Jumping to a 'catch_end' is fine however as it will simply nop in the
+ %% absence of an exception.)
+ case ordsets:is_element(Lbl, FailLbls) of
+ true -> error({illegal_branch, try_handler, Lbl});
+ false -> validate_branch_1(Lbl, Tags)
+ end;
+validate_branch_1(Lbl, [_ | Tags]) ->
+ validate_branch_1(Lbl, Tags);
+validate_branch_1(_Lbl, []) ->
+ ok.
+
%% A shorthand version of branch/4 for when the state is only altered on
%% success.
branch(Fail, Vst, SuccFun) ->
@@ -2411,12 +2370,12 @@ branch(Fail, Vst, SuccFun) ->
%% Directly branches off the state. This is an "internal" operation that should
%% be used sparingly.
-branch_state(0, #vst{}=Vst) ->
+fork_state(0, #vst{}=Vst) ->
%% If the instruction fails, the stack may be scanned looking for a catch
%% tag. Therefore the Y registers must be initialized at this point.
verify_y_init(Vst),
Vst;
-branch_state(L, #vst{current=St,branched=B,ref_ctr=Counter0}=Vst) ->
+fork_state(L, #vst{current=St,branched=B,ref_ctr=Counter0}=Vst) ->
case gb_trees:is_defined(L, B) of
true ->
{MergedSt, Counter} = merge_states(L, St, B, Counter0),
@@ -2432,14 +2391,14 @@ branch_state(L, #vst{current=St,branched=B,ref_ctr=Counter0}=Vst) ->
merge_states(L, St, Branched, Counter) when L =/= 0 ->
case gb_trees:lookup(L, Branched) of
- none ->
- {St, Counter};
- {value,OtherSt} when St =:= none ->
- {OtherSt, Counter};
- {value,OtherSt} ->
- merge_states_1(St, OtherSt, Counter)
+ {value, OtherSt} -> merge_states_1(St, OtherSt, Counter);
+ none -> {St, Counter}
end.
+merge_states_1(St, none, Counter) ->
+ {St, Counter};
+merge_states_1(none, St, Counter) ->
+ {St, Counter};
merge_states_1(StA, StB, Counter0) ->
#st{xs=XsA,ys=YsA,vs=VsA,fragile=FragA,numy=NumYA,
h=HA,ct=CtA,recv_marker=MarkerA} = StA,
@@ -2501,10 +2460,10 @@ merge_tags(uninitialized, _) ->
uninitialized;
merge_tags(_, uninitialized) ->
uninitialized;
-merge_tags({catchtag,T0}, {catchtag,T1}) ->
- {catchtag, ordsets:from_list(T0 ++ T1)};
-merge_tags({trytag,T0}, {trytag,T1}) ->
- {trytag, ordsets:from_list(T0 ++ T1)};
+merge_tags({trytag, LblsA}, {trytag, LblsB}) ->
+ {trytag, ordsets:union(LblsA, LblsB)};
+merge_tags({catchtag, LblsA}, {catchtag, LblsB}) ->
+ {catchtag, ordsets:union(LblsA, LblsB)};
merge_tags(_A, _B) ->
%% All other combinations leave the register initialized. Errors arising
%% from this will be caught later on.
@@ -2587,13 +2546,14 @@ merge_stk(_, _) -> undecided.
merge_ct(S, S) -> S;
merge_ct(Ct0, Ct1) -> merge_ct_1(Ct0, Ct1).
-merge_ct_1([C0|Ct0], [C1|Ct1]) ->
- [ordsets:from_list(C0++C1)|merge_ct_1(Ct0, Ct1)];
-merge_ct_1([], []) -> [];
-merge_ct_1(_, _) -> undecided.
-
-tuple_sz([Sz]) -> Sz;
-tuple_sz(Sz) -> Sz.
+merge_ct_1([], []) ->
+ [];
+merge_ct_1([{trytag, LblsA} | CtA], [{trytag, LblsB} | CtB]) ->
+ [{trytag, ordsets:union(LblsA, LblsB)} | merge_ct_1(CtA, CtB)];
+merge_ct_1([{catchtag, LblsA} | CtA], [{catchtag, LblsB} | CtB]) ->
+ [{catchtag, ordsets:union(LblsA, LblsB)} | merge_ct_1(CtA, CtB)];
+merge_ct_1(_, _) ->
+ undecided.
verify_y_init(#vst{current=#st{numy=NumY,ys=Ys}}=Vst) when is_integer(NumY) ->
HighestY = maps:fold(fun({y,Y}, _, Acc) -> max(Y, Acc) end, -1, Ys),
@@ -2770,320 +2730,46 @@ assert_not_fragile(Lit, #vst{}) ->
ok.
%%%
-%%% Return/argument types of BIFs
-%%%
-
-bif_return_type('-', Src, Vst) ->
- arith_return_type(Src, Vst);
-bif_return_type('+', Src, Vst) ->
- arith_return_type(Src, Vst);
-bif_return_type('*', Src, Vst) ->
- arith_return_type(Src, Vst);
-bif_return_type(abs, [Num], Vst) ->
- case get_term_type(Num, Vst) of
- {float,_}=T -> T;
- {integer,_}=T -> T;
- _ -> number
- end;
-bif_return_type(float, _, _) -> {float,[]};
-bif_return_type('/', _, _) -> {float,[]};
-%% Binary operations
-bif_return_type('binary_part', [_,_], _) -> binary;
-bif_return_type('binary_part', [_,_,_], _) -> binary;
-bif_return_type('bit_size', [_], _) -> {integer,[]};
-bif_return_type('byte_size', [_], _) -> {integer,[]};
-%% Integer operations.
-bif_return_type(ceil, [_], _) -> {integer,[]};
-bif_return_type('div', [_,_], _) -> {integer,[]};
-bif_return_type(floor, [_], _) -> {integer,[]};
-bif_return_type('rem', [_,_], _) -> {integer,[]};
-bif_return_type(length, [_], _) -> {integer,[]};
-bif_return_type(size, [_], _) -> {integer,[]};
-bif_return_type(trunc, [_], _) -> {integer,[]};
-bif_return_type(round, [_], _) -> {integer,[]};
-bif_return_type('band', [_,_], _) -> {integer,[]};
-bif_return_type('bor', [_,_], _) -> {integer,[]};
-bif_return_type('bxor', [_,_], _) -> {integer,[]};
-bif_return_type('bnot', [_], _) -> {integer,[]};
-bif_return_type('bsl', [_,_], _) -> {integer,[]};
-bif_return_type('bsr', [_,_], _) -> {integer,[]};
-%% Booleans.
-bif_return_type('==', [_,_], _) -> bool;
-bif_return_type('/=', [_,_], _) -> bool;
-bif_return_type('=<', [_,_], _) -> bool;
-bif_return_type('<', [_,_], _) -> bool;
-bif_return_type('>=', [_,_], _) -> bool;
-bif_return_type('>', [_,_], _) -> bool;
-bif_return_type('=:=', [_,_], _) -> bool;
-bif_return_type('=/=', [_,_], _) -> bool;
-bif_return_type('not', [_], _) -> bool;
-bif_return_type('and', [_,_], _) -> bool;
-bif_return_type('or', [_,_], _) -> bool;
-bif_return_type('xor', [_,_], _) -> bool;
-bif_return_type(is_atom, [_], _) -> bool;
-bif_return_type(is_boolean, [_], _) -> bool;
-bif_return_type(is_binary, [_], _) -> bool;
-bif_return_type(is_float, [_], _) -> bool;
-bif_return_type(is_function, [_], _) -> bool;
-bif_return_type(is_function, [_,_], _) -> bool;
-bif_return_type(is_integer, [_], _) -> bool;
-bif_return_type(is_list, [_], _) -> bool;
-bif_return_type(is_map, [_], _) -> bool;
-bif_return_type(is_map_key, [_, _], _) -> bool;
-bif_return_type(is_number, [_], _) -> bool;
-bif_return_type(is_pid, [_], _) -> bool;
-bif_return_type(is_port, [_], _) -> bool;
-bif_return_type(is_reference, [_], _) -> bool;
-bif_return_type(is_tuple, [_], _) -> bool;
-%% Misc.
-bif_return_type(tuple_size, [_], _) -> {integer,[]};
-bif_return_type(map_size, [_], _) -> {integer,[]};
-bif_return_type(node, [], _) -> {atom,[]};
-bif_return_type(node, [_], _) -> {atom,[]};
-bif_return_type(hd, [_], _) -> term;
-bif_return_type(tl, [_], _) -> term;
-bif_return_type(get, [_], _) -> term;
-bif_return_type(Bif, _, _) when is_atom(Bif) -> term.
-
-%% Generic
-bif_arg_types(tuple_size, [_]) -> [{tuple,[0],#{}}];
-bif_arg_types(map_size, [_]) -> [map];
-bif_arg_types(is_map_key, [_,_]) -> [term, map];
-bif_arg_types(map_get, [_,_]) -> [term, map];
-bif_arg_types(length, [_]) -> [list];
-bif_arg_types(hd, [_]) -> [cons];
-bif_arg_types(tl, [_]) -> [cons];
-%% Boolean
-bif_arg_types('not', [_]) -> [bool];
-bif_arg_types('and', [_,_]) -> [bool, bool];
-bif_arg_types('or', [_,_]) -> [bool, bool];
-bif_arg_types('xor', [_,_]) -> [bool, bool];
-%% Binary
-bif_arg_types('binary_part', [_,_]) ->
- PosLen = {tuple, 2, #{ {integer,1} => {integer,[]},
- {integer,2} => {integer,[]} }},
- [binary, PosLen];
-bif_arg_types('binary_part', [_,_,_]) ->
- [binary, {integer,[]}, {integer,[]}];
-bif_arg_types('bit_size', [_]) -> [binary];
-bif_arg_types('byte_size', [_]) -> [binary];
-%% Numerical
-bif_arg_types('-', [_]) -> [number];
-bif_arg_types('-', [_,_]) -> [number,number];
-bif_arg_types('+', [_]) -> [number];
-bif_arg_types('+', [_,_]) -> [number,number];
-bif_arg_types('*', [_,_]) -> [number, number];
-bif_arg_types('/', [_,_]) -> [number, number];
-bif_arg_types(abs, [_]) -> [number];
-bif_arg_types(ceil, [_]) -> [number];
-bif_arg_types(float, [_]) -> [number];
-bif_arg_types(floor, [_]) -> [number];
-bif_arg_types(trunc, [_]) -> [number];
-bif_arg_types(round, [_]) -> [number];
-%% Integer-specific
-bif_arg_types('div', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('rem', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('band', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('bor', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('bxor', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('bnot', [_]) -> [{integer,[]}];
-bif_arg_types('bsl', [_,_]) -> [{integer,[]}, {integer,[]}];
-bif_arg_types('bsr', [_,_]) -> [{integer,[]}, {integer,[]}];
-%% Unsafe type tests that may fail if an argument doesn't have the right type.
-bif_arg_types(is_function, [_,_]) -> [term, {integer,[]}];
-bif_arg_types(_, Args) -> [term || _Arg <- Args].
-
-is_bif_safe('/=', 2) -> true;
-is_bif_safe('<', 2) -> true;
-is_bif_safe('=/=', 2) -> true;
-is_bif_safe('=:=', 2) -> true;
-is_bif_safe('=<', 2) -> true;
-is_bif_safe('==', 2) -> true;
-is_bif_safe('>', 2) -> true;
-is_bif_safe('>=', 2) -> true;
-is_bif_safe(is_atom, 1) -> true;
-is_bif_safe(is_boolean, 1) -> true;
-is_bif_safe(is_binary, 1) -> true;
-is_bif_safe(is_bitstring, 1) -> true;
-is_bif_safe(is_float, 1) -> true;
-is_bif_safe(is_function, 1) -> true;
-is_bif_safe(is_integer, 1) -> true;
-is_bif_safe(is_list, 1) -> true;
-is_bif_safe(is_map, 1) -> true;
-is_bif_safe(is_number, 1) -> true;
-is_bif_safe(is_pid, 1) -> true;
-is_bif_safe(is_port, 1) -> true;
-is_bif_safe(is_reference, 1) -> true;
-is_bif_safe(is_tuple, 1) -> true;
-is_bif_safe(get, 1) -> true;
-is_bif_safe(self, 0) -> true;
-is_bif_safe(node, 0) -> true;
-is_bif_safe(_, _) -> false.
-
-arith_return_type([A], Vst) ->
- %% Unary '+' or '-'.
- case get_term_type(A, Vst) of
- {integer,_} -> {integer,[]};
- {float,_} -> {float,[]};
- _ -> number
- end;
-arith_return_type([A,B], Vst) ->
- TypeA = get_term_type(A, Vst),
- TypeB = get_term_type(B, Vst),
- case {TypeA, TypeB} of
- {{integer,_},{integer,_}} -> {integer,[]};
- {{float,_},_} -> {float,[]};
- {_,{float,_}} -> {float,[]};
- {_,_} -> number
- end;
-arith_return_type(_, _) -> number.
-
-%%%
-%%% Return/argument types of calls
+%%% Return/argument types of calls and BIFs
%%%
-call_return_type({extfunc,M,F,A}, Vst) -> call_return_type_1(M, F, A, Vst);
-call_return_type(_, _) -> term.
-
-call_return_type_1(erlang, setelement, 3, Vst) ->
- IndexType = get_term_type({x,0}, Vst),
- TupleType =
- case get_term_type({x,1}, Vst) of
- {literal,Tuple}=Lit when is_tuple(Tuple) -> get_literal_type(Lit);
- {tuple,_,_}=TT -> TT;
- _ -> {tuple,[0],#{}}
- end,
- case IndexType of
- {integer,I} when is_integer(I) ->
- case meet({tuple,[I],#{}}, TupleType) of
- {tuple, Sz, Es0} ->
- ValueType = get_term_type({x,2}, Vst),
- Es = set_element_type({integer,I}, ValueType, Es0),
- {tuple, Sz, Es};
- none ->
- TupleType
- end;
- _ ->
- %% The index could point anywhere, so we must discard all element
- %% information.
- setelement(3, TupleType, #{})
- end;
-call_return_type_1(erlang, '++', 2, Vst) ->
- LType = get_term_type({x,0}, Vst),
- RType = get_term_type({x,1}, Vst),
- case LType =:= cons orelse RType =:= cons of
- true ->
- cons;
- false ->
- %% `[] ++ RHS` yields RHS, even if RHS is not a list
- join(list, RType)
- end;
-call_return_type_1(erlang, '--', 2, _Vst) ->
- list;
-call_return_type_1(erlang, F, A, _) ->
- erlang_mod_return_type(F, A);
-call_return_type_1(lists, F, A, Vst) ->
- lists_mod_return_type(F, A, Vst);
-call_return_type_1(math, F, A, _) ->
- math_mod_return_type(F, A);
-call_return_type_1(M, F, A, _) when is_atom(M), is_atom(F), is_integer(A), A >= 0 ->
- term.
-
-erlang_mod_return_type(exit, 1) -> exception;
-erlang_mod_return_type(throw, 1) -> exception;
-erlang_mod_return_type(error, 1) -> exception;
-erlang_mod_return_type(error, 2) -> exception;
-erlang_mod_return_type(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
-
-math_mod_return_type(cos, 1) -> {float,[]};
-math_mod_return_type(cosh, 1) -> {float,[]};
-math_mod_return_type(sin, 1) -> {float,[]};
-math_mod_return_type(sinh, 1) -> {float,[]};
-math_mod_return_type(tan, 1) -> {float,[]};
-math_mod_return_type(tanh, 1) -> {float,[]};
-math_mod_return_type(acos, 1) -> {float,[]};
-math_mod_return_type(acosh, 1) -> {float,[]};
-math_mod_return_type(asin, 1) -> {float,[]};
-math_mod_return_type(asinh, 1) -> {float,[]};
-math_mod_return_type(atan, 1) -> {float,[]};
-math_mod_return_type(atanh, 1) -> {float,[]};
-math_mod_return_type(erf, 1) -> {float,[]};
-math_mod_return_type(erfc, 1) -> {float,[]};
-math_mod_return_type(exp, 1) -> {float,[]};
-math_mod_return_type(log, 1) -> {float,[]};
-math_mod_return_type(log2, 1) -> {float,[]};
-math_mod_return_type(log10, 1) -> {float,[]};
-math_mod_return_type(sqrt, 1) -> {float,[]};
-math_mod_return_type(atan2, 2) -> {float,[]};
-math_mod_return_type(pow, 2) -> {float,[]};
-math_mod_return_type(ceil, 1) -> {float,[]};
-math_mod_return_type(floor, 1) -> {float,[]};
-math_mod_return_type(fmod, 2) -> {float,[]};
-math_mod_return_type(pi, 0) -> {float,[]};
-math_mod_return_type(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
-
-lists_mod_return_type(all, 2, _Vst) ->
- bool;
-lists_mod_return_type(any, 2, _Vst) ->
- bool;
-lists_mod_return_type(keymember, 3, _Vst) ->
- bool;
-lists_mod_return_type(member, 2, _Vst) ->
- bool;
-lists_mod_return_type(prefix, 2, _Vst) ->
- bool;
-lists_mod_return_type(suffix, 2, _Vst) ->
- bool;
-lists_mod_return_type(dropwhile, 2, _Vst) ->
- list;
-lists_mod_return_type(duplicate, 2, _Vst) ->
- list;
-lists_mod_return_type(filter, 2, _Vst) ->
- list;
-lists_mod_return_type(flatten, 1, _Vst) ->
- list;
-lists_mod_return_type(map, 2, Vst) ->
- same_length_type({x,1}, Vst);
-lists_mod_return_type(MF, 3, Vst) when MF =:= mapfoldl; MF =:= mapfoldr ->
- ListType = same_length_type({x,2}, Vst),
- {tuple,2,#{ {integer,1} => ListType} };
-lists_mod_return_type(partition, 2, _Vst) ->
- two_tuple(list, list);
-lists_mod_return_type(reverse, 1, Vst) ->
- same_length_type({x,0}, Vst);
-lists_mod_return_type(seq, 2, _Vst) ->
- list;
-lists_mod_return_type(sort, 1, Vst) ->
- same_length_type({x,0}, Vst);
-lists_mod_return_type(sort, 2, Vst) ->
- same_length_type({x,1}, Vst);
-lists_mod_return_type(splitwith, 2, _Vst) ->
- two_tuple(list, list);
-lists_mod_return_type(takewhile, 2, _Vst) ->
- list;
-lists_mod_return_type(unzip, 1, Vst) ->
- ListType = same_length_type({x,0}, Vst),
- two_tuple(ListType, ListType);
-lists_mod_return_type(usort, 1, Vst) ->
- same_length_type({x,0}, Vst);
-lists_mod_return_type(zip, 2, _Vst) ->
- list;
-lists_mod_return_type(zipwith, 3, _Vst) ->
- list;
-lists_mod_return_type(_, _, _) ->
- term.
-
-two_tuple(Type1, Type2) ->
- {tuple,2,#{ {integer,1} => Type1,
- {integer,2} => Type2 }}.
-
-same_length_type(Reg, Vst) ->
- case get_term_type(Reg, Vst) of
- {literal,[_|_]} -> cons;
- cons -> cons;
- nil -> nil;
- _ -> list
- end.
+bif_types(Op, Ss, Vst) ->
+ Args = [normalize(get_term_type(Arg, Vst)) || Arg <- Ss],
+ beam_call_types:types(erlang, Op, Args).
+
+call_types({extfunc,M,F,A}, A, Vst) ->
+ Args = get_call_args(A, Vst),
+ beam_call_types:types(M, F, Args);
+call_types(_, A, Vst) ->
+ {any, get_call_args(A, Vst), false}.
+
+will_bif_succeed(fadd, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fdiv, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fmul, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fnegate, [_], _Vst) ->
+ maybe;
+will_bif_succeed(fsub, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(Op, Ss, Vst) ->
+ Args = [normalize(get_term_type(Arg, Vst)) || Arg <- Ss],
+ beam_call_types:will_succeed(erlang, Op, Args).
+
+will_call_succeed({extfunc,M,F,A}, Vst) ->
+ beam_call_types:will_succeed(M, F, get_call_args(A, Vst));
+will_call_succeed(_Call, _Vst) ->
+ maybe.
+
+get_call_args(Arity, Vst) ->
+ get_call_args_1(0, Arity, Vst).
+
+get_call_args_1(Arity, Arity, _) ->
+ [];
+get_call_args_1(N, Arity, Vst) when N < Arity ->
+ ArgType = normalize(get_movable_term_type({x,N}, Vst)),
+ [ArgType | get_call_args_1(N + 1, Arity, Vst)].
check_limit({x,X}=Src) when is_integer(X) ->
if
@@ -3108,6 +2794,12 @@ check_limit({fr,Fr}=Src) when is_integer(Fr) ->
min(A, B) when is_integer(A), is_integer(B), A < B -> A;
min(A, B) when is_integer(A), is_integer(B) -> B.
+gcd(A, B) ->
+ case A rem B of
+ 0 -> B;
+ X -> gcd(B, X)
+ end.
+
gb_trees_from_list(L) -> gb_trees:from_orddict(sort(L)).
error(Error) -> throw(Error).
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 415b579240..b5b2dde22c 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -47,16 +47,31 @@ function({function,Name,Arity,CLabel,Is0}, NoGetHdTl) ->
undo_renames([{call_ext,2,send}|Is]) ->
[send|undo_renames(Is)];
+
undo_renames([{apply,A},{deallocate,N},return|Is]) ->
[{apply_last,A,N}|undo_renames(Is)];
+
+undo_renames([{call,A,F},{'%',{var_info,{x,0},_}},{deallocate,N},return|Is]) ->
+ %% We've removed a redundant move of a literal to {x,0}.
+ [{call_last,A,F,N} | undo_renames(Is)];
undo_renames([{call,A,F},{deallocate,N},return|Is]) ->
- [{call_last,A,F,N}|undo_renames(Is)];
+ [{call_last,A,F,N} | undo_renames(Is)];
+
+undo_renames([{call_ext,A,F},{'%',{var_info,{x,0},_}},{deallocate,N},return|Is]) ->
+ [{call_ext_last,A,F,N} | undo_renames(Is)];
undo_renames([{call_ext,A,F},{deallocate,N},return|Is]) ->
- [{call_ext_last,A,F,N}|undo_renames(Is)];
+ [{call_ext_last,A,F,N} | undo_renames(Is)];
+
+undo_renames([{call,A,F},{'%',{var_info,{x,0},_}},return|Is]) ->
+ [{call_only,A,F} | undo_renames(Is)];
undo_renames([{call,A,F},return|Is]) ->
[{call_only,A,F}|undo_renames(Is)];
+
+undo_renames([{call_ext,A,F},{'%',{var_info,{x,0},_}},return|Is]) ->
+ [{call_ext_only,A,F} | undo_renames(Is)];
undo_renames([{call_ext,A,F},return|Is]) ->
[{call_ext_only,A,F}|undo_renames(Is)];
+
undo_renames([{bif,raise,_,_,_}=I|Is0]) ->
%% A minor optimization. Done here because:
%% (1) beam_jump may move or share 'raise' instructions, and that
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index caff47dbcb..8a2ea77b99 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -65,7 +65,7 @@
map_pair_op/1, map_pair_key/1, map_pair_val/1
]).
--import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
+-import(lists, [foldl/3, foldr/3, member/2, mapfoldl/3, reverse/1]).
%%
%% Constants
@@ -142,7 +142,7 @@ weight(module) -> 1. % Like a letrec with a constant body
%% environment, the state location, and the effort counter at the call
%% site (cf. `visit').
--record(opnd, {expr, ren, env, loc, effort}).
+-record(opnd, {expr, ren, env, loc, effort, no_inline}).
%% Since expressions are only visited in `effect' context when they are
%% not bound to a referenced variable, only expressions visited in
@@ -903,10 +903,14 @@ i_fun(E, Ctxt, Ren, Env, S) ->
%% side of each definition.
i_letrec(E, Ctxt, Ren, Env, S) ->
+ %% We must turn off inlining if this `letrec' is specially
+ %% implemented.
+ NoInline = member(letrec_goto, get_ann(E)),
+
%% Note that we pass an empty list for the auto-referenced
%% (exported) functions here.
{Es, B, _, S1} = i_letrec(letrec_defs(E), letrec_body(E), [], Ctxt,
- Ren, Env, S),
+ Ren, Env, NoInline, S),
%% If no bindings remain, only the body is returned.
case Es of
@@ -920,12 +924,13 @@ i_letrec(E, Ctxt, Ren, Env, S) ->
%% The major part of this is shared by letrec-expressions and module
%% definitions alike.
-i_letrec(Es, B, Xs, Ctxt, Ren, Env, S) ->
+i_letrec(Es, B, Xs, Ctxt, Ren, Env, NoInline, S) ->
%% First, we create operands with dummy renamings and environments,
%% and with fresh store locations for cached expressions and operand
%% info.
{Opnds, S1} = mapfoldl(fun ({_, E}, S) ->
- make_opnd(E, undefined, undefined, S)
+ make_opnd(E, undefined, undefined,
+ NoInline, S)
end,
S, Es),
@@ -1277,7 +1282,7 @@ i_module(E, Ctxt, Ren, Env, S) ->
%% "body" parameter.
Exps = i_module_exports(E),
{Es, _, Xs1, S1} = i_letrec(module_defs(E), void(),
- Exps, Ctxt, Ren, Env, S),
+ Exps, Ctxt, Ren, Env, false, S),
%% Sanity check:
case Es of
[] ->
@@ -1500,23 +1505,15 @@ inline(E, #app{opnds = Opnds, ctxt = Ctxt, loc = L}, Ren, Env, S) ->
%% respective operand structures from the app-structure.
{Rs, Ren1, Env1, S1} = bind_locals(Vs, Opnds, Ren, Env, S),
- %% function_clause exceptions that have been inlined
- %% into another function (or even into the same function)
- %% will not work properly. The v3_kernel pass will
- %% take care of it, but we will need to help it by
- %% removing any function_name annotations on match_fail
- %% primops that we inline.
- E1 = kill_function_name_anns(fun_body(E)),
-
%% Visit the body in the context saved in the structure.
- {E2, S2} = i(E1, Ctxt, Ren1, Env1, S1),
+ {E1, S2} = i(fun_body(E), Ctxt, Ren1, Env1, S1),
%% Create necessary bindings and/or set flags.
- {E3, S3} = make_let_bindings(Rs, E2, S2),
+ {E2, S3} = make_let_bindings(Rs, E1, S2),
%% Lastly, flag the application as inlined, since the inlining
%% attempt was not aborted before we reached this point.
- {E3, st__set_app_inlined(L, S3)}
+ {E2, st__set_app_inlined(L, S3)}
end.
%% For the (possibly renamed) argument variables to an inlined call,
@@ -1674,6 +1671,8 @@ copy_var(R, Ctxt, Env, S) ->
end
end.
+copy_1(R, #opnd{no_inline = true}, _E, _Ctxt, _Env, S) ->
+ residualize_var(R, S);
copy_1(R, Opnd, E, Ctxt, Env, S) ->
case type(E) of
'fun' ->
@@ -2075,9 +2074,13 @@ ref_to_var(#ref{name = Name}) ->
%% passive, the operands will also be processed with a passive counter.
make_opnd(E, Ren, Env, S) ->
+ make_opnd(E, Ren, Env, false, S).
+
+make_opnd(E, Ren, Env, NoInline, S) ->
{L, S1} = st__new_opnd_loc(S),
C = st__get_effort(S1),
- Opnd = #opnd{expr = E, ren = Ren, env = Env, loc = L, effort = C},
+ Opnd = #opnd{expr = E, ren = Ren, env = Env, loc = L,
+ effort = C, no_inline = NoInline},
{Opnd, S1}.
keep_referenced(Rs, S) ->
@@ -2469,19 +2472,6 @@ kill_id_anns([A | As]) ->
kill_id_anns([]) ->
[].
-kill_function_name_anns(Body) ->
- F = fun(P) ->
- case type(P) of
- primop ->
- Ann = get_ann(P),
- Ann1 = lists:keydelete(function_name, 1, Ann),
- set_ann(P, Ann1);
- _ ->
- P
- end
- end,
- cerl_trees:map(F, Body).
-
%% =====================================================================
%% General utilities
@@ -2526,21 +2516,19 @@ set_clause_bodies([], _) ->
%% Abstract datatype: renaming()
ren__identity() ->
- dict:new().
+ #{}.
ren__add(X, Y, Ren) ->
- dict:store(X, Y, Ren).
+ Ren#{X=>Y}.
ren__map(X, Ren) ->
- case dict:find(X, Ren) of
- {ok, Y} ->
- Y;
- error ->
- X
+ case Ren of
+ #{X:=Y} -> Y;
+ #{} -> X
end.
ren__add_identity(X, Ren) ->
- dict:erase(X, Ren).
+ maps:remove(X, Ren).
%% =====================================================================
@@ -2633,7 +2621,7 @@ st__new(Effort, Size, Unroll) ->
size = counter__new_passive(Size),
effort = counter__new_passive(Effort),
unroll = Unroll,
- cache = dict:new(),
+ cache = maps:new(),
var_flags = ets:new(var, EtsOpts),
opnd_flags = ets:new(opnd, EtsOpts),
app_flags = ets:new(app, EtsOpts)}.
@@ -2664,12 +2652,12 @@ st__get_var_referenced(L, S) ->
ets:lookup_element(S#state.var_flags, L, #var_flags.referenced).
st__lookup_opnd_cache(L, S) ->
- dict:find(L, S#state.cache).
+ maps:find(L, S#state.cache).
%% Note that setting the cache should only be done once.
st__set_opnd_cache(L, C, S) ->
- S#state{cache = dict:store(L, C, S#state.cache)}.
+ S#state{cache = maps:put(L, C, S#state.cache)}.
st__set_opnd_effect(L, S) ->
T = S#state.opnd_flags,
diff --git a/lib/compiler/src/cerl_sets.erl b/lib/compiler/src/cerl_sets.erl
index f489baf238..0564779f39 100644
--- a/lib/compiler/src/cerl_sets.erl
+++ b/lib/compiler/src/cerl_sets.erl
@@ -130,8 +130,10 @@ union1(S1, []) -> S1.
Set2 :: set(Element),
Set3 :: set(Element).
+intersection(S1, S2) when map_size(S1) >= map_size(S2) ->
+ filter(fun (E) -> is_element(E, S1) end, S2);
intersection(S1, S2) ->
- filter(fun (E) -> is_element(E, S1) end, S2).
+ intersection(S2, S1).
%% intersection([Set]) -> Set.
%% Return the intersection of the list of sets.
@@ -153,14 +155,21 @@ intersection1(S1, []) -> S1.
Set1 :: set(Element),
Set2 :: set(Element).
-is_disjoint(S1, S2) when map_size(S1) < map_size(S2) ->
- fold(fun (_, false) -> false;
- (E, true) -> not is_element(E, S2)
- end, true, S1);
+is_disjoint(S1, S2) when map_size(S1) > map_size(S2) ->
+ is_disjoint_1(S1, maps:iterator(S2));
is_disjoint(S1, S2) ->
- fold(fun (_, false) -> false;
- (E, true) -> not is_element(E, S1)
- end, true, S2).
+ is_disjoint_1(S2, maps:iterator(S1)).
+
+is_disjoint_1(Set, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Set of
+ #{K := _} -> false;
+ #{} -> is_disjoint_1(Set, NextIter)
+ end;
+ none ->
+ true
+ end.
%% subtract(Set1, Set2) -> Set.
%% Return all and only the elements of Set1 which are not also in
@@ -180,8 +189,21 @@ subtract(S1, S2) ->
Set1 :: set(Element),
Set2 :: set(Element).
+is_subset(S1, S2) when map_size(S1) > map_size(S2) ->
+ false;
is_subset(S1, S2) ->
- fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1).
+ is_subset_1(S2, maps:iterator(S1)).
+
+is_subset_1(Set, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Set of
+ #{K := _} -> is_subset_1(Set, NextIter);
+ #{} -> false
+ end;
+ none ->
+ true
+ end.
%% fold(Fun, Accumulator, Set) -> Accumulator.
%% Fold function Fun over all elements in Set and return Accumulator.
@@ -193,8 +215,16 @@ is_subset(S1, S2) ->
AccIn :: Acc,
AccOut :: Acc.
-fold(F, Init, D) ->
- lists:foldl(fun(E,Acc) -> F(E,Acc) end,Init,maps:keys(D)).
+fold(Fun, Init, Set) ->
+ fold_1(Fun, Init, maps:iterator(Set)).
+
+fold_1(Fun, Acc, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ fold_1(Fun, Fun(K,Acc), NextIter);
+ none ->
+ Acc
+ end.
%% filter(Fun, Set) -> Set.
%% Filter Set with Fun.
@@ -203,5 +233,18 @@ fold(F, Init, D) ->
Set1 :: set(Element),
Set2 :: set(Element).
-filter(F, D) ->
- maps:filter(fun(K,_) -> F(K) end, D).
+filter(Fun, Set) ->
+ maps:from_list(filter_1(Fun, maps:iterator(Set))).
+
+filter_1(Fun, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Fun(K) of
+ true ->
+ [{K,ok} | filter_1(Fun, NextIter)];
+ false ->
+ filter_1(Fun, NextIter)
+ end;
+ none ->
+ []
+ end.
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 533c984221..a2089b5c1b 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -823,7 +823,7 @@ label(T) ->
-spec label(cerl:cerl(), integer()) -> {cerl:cerl(), integer()}.
label(T, N) ->
- label(T, N, dict:new()).
+ label(T, N, #{}).
label(T, N, Env) ->
case type(T) of
@@ -831,12 +831,13 @@ label(T, N, Env) ->
%% Constant literals are not labeled.
{T, N};
var ->
+ VarName = var_name(T),
{As, N1} =
- case dict:find(var_name(T), Env) of
- {ok, L} ->
+ case Env of
+ #{VarName := L} ->
{A, _} = label_ann(T, L),
{A, N};
- error ->
+ #{} ->
label_ann(T, N)
end,
{set_ann(T, As), N1};
@@ -974,7 +975,7 @@ label_list([], N, _Env) ->
{[], N}.
label_vars([T | Ts], N, Env) ->
- Env1 = dict:store(var_name(T), N, Env),
+ Env1 = Env#{var_name(T) => N},
{As, N1} = label_ann(T, N),
T1 = set_ann(T, As),
{Ts1, N2, Env2} = label_vars(Ts, N1, Env1),
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index fd5233d379..e7f58b3783 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -20,6 +20,7 @@
%% Purpose: Run the Erlang compiler.
-module(compile).
+-compile([{nowarn_deprecated_function,{crypto,block_encrypt,4}}]).
%% High-level interface.
-export([file/1,file/2,noenv_file/2,format_error/1,iofile/1]).
@@ -253,11 +254,11 @@ expand_opt(return, Os) ->
[return_errors,return_warnings|Os];
expand_opt(no_bsm3, Os) ->
%% The new bsm pass requires bsm3 instructions.
- [no_bsm3,no_bsm_opt|Os];
-expand_opt(r16, Os) ->
- expand_opt_before_21(Os);
-expand_opt(r17, Os) ->
- expand_opt_before_21(Os);
+ [no_bsm3,no_bsm_opt|expand_opt(no_bsm4, Os)];
+expand_opt(no_bsm4, Os) ->
+ %% bsm4 instructions are only used when type optimization has determined
+ %% that a match instruction won't fail.
+ expand_opt(no_type_opt, Os);
expand_opt(r18, Os) ->
expand_opt_before_21(Os);
expand_opt(r19, Os) ->
@@ -265,7 +266,11 @@ expand_opt(r19, Os) ->
expand_opt(r20, Os) ->
expand_opt_before_21(Os);
expand_opt(r21, Os) ->
- [no_put_tuple2 | expand_opt(no_bsm3, Os)];
+ [no_shared_fun_wrappers,
+ no_swap, no_put_tuple2 | expand_opt(no_bsm3, Os)];
+expand_opt(r22, Os) ->
+ [no_shared_fun_wrappers,
+ no_swap | expand_opt(no_bsm4, Os)];
expand_opt({debug_info_key,_}=O, Os) ->
[encrypt_debug_info,O|Os];
expand_opt(no_type_opt=O, Os) ->
@@ -278,7 +283,8 @@ expand_opt(no_type_opt=O, Os) ->
expand_opt(O, Os) -> [O|Os].
expand_opt_before_21(Os) ->
- [no_put_tuple2, no_get_hd_tl, no_ssa_opt_record,
+ [no_shared_fun_wrappers, no_swap,
+ no_put_tuple2, no_get_hd_tl, no_ssa_opt_record,
no_utf8_atoms | expand_opt(no_bsm3, Os)].
%% format_error(ErrorDescriptor) -> string()
@@ -597,7 +603,7 @@ passes_1([]) ->
{".erl",[?pass(parse_module)|standard_passes()]}.
pass(from_core) ->
- {".core",[?pass(parse_core)|core_passes(mandatory_core_lint)]};
+ {".core",[?pass(parse_core)|core_passes(non_verified_core)]};
pass(from_asm) ->
{".S",[?pass(beam_consult_asm)|asm_passes()]};
pass(from_beam) ->
@@ -795,33 +801,35 @@ standard_passes() ->
?pass(core),
{iff,'dcore',{listing,"core"}},
{iff,'to_core0',{done,"core"}}
- | core_passes(optional_core_lint)].
+ | core_passes(verified_core)].
-core_passes(LintOpt) ->
+core_passes(CoreStatus) ->
%% Optimization and transforms of Core Erlang code.
- CoreLint = case LintOpt of
- mandatory_core_lint ->
- ?pass(core_lint_module);
- optional_core_lint ->
- {iff,clint0,?pass(core_lint_module)}
- end,
- [CoreLint,
- {delay,
- [{unless,no_copt,
- [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/2},
- {iff,doldinline,{listing,"oldinline"}},
- {unless,no_fold,{pass,sys_core_fold}},
- {iff,dcorefold,{listing,"corefold"}},
- {core_inline_module,fun test_core_inliner/1,fun core_inline_module/2},
- {iff,dinline,{listing,"inline"}},
- {core_fold_after_inlining,fun test_any_inliner/1,
- fun core_fold_module_after_inlining/2},
- {iff,dcopt,{listing,"copt"}},
- {unless,no_alias,{pass,sys_core_alias}},
- {iff,dalias,{listing,"core_alias"}},
- ?pass(core_transforms)]},
- {iff,'to_core',{done,"core"}}]}
- | kernel_passes()].
+ case CoreStatus of
+ non_verified_core ->
+ [?pass(core_lint_module),
+ {pass,sys_core_prepare},
+ {iff,dprep,{listing,"prepare"}}];
+ verified_core ->
+ [{iff,clint0,?pass(core_lint_module)}]
+ end ++
+ [
+ {delay,
+ [{unless,no_copt,
+ [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/2},
+ {iff,doldinline,{listing,"oldinline"}},
+ {unless,no_fold,{pass,sys_core_fold}},
+ {iff,dcorefold,{listing,"corefold"}},
+ {core_inline_module,fun test_core_inliner/1,fun core_inline_module/2},
+ {iff,dinline,{listing,"inline"}},
+ {core_fold_after_inlining,fun test_any_inliner/1,
+ fun core_fold_module_after_inlining/2},
+ {iff,dcopt,{listing,"copt"}},
+ {unless,no_alias,{pass,sys_core_alias}},
+ {iff,dalias,{listing,"core_alias"}},
+ ?pass(core_transforms)]},
+ {iff,'to_core',{done,"core"}}]}
+ | kernel_passes()].
kernel_passes() ->
%% Optimizations that must be done after all other optimizations.
@@ -839,7 +847,9 @@ kernel_passes() ->
{iff,dssa,{listing,"ssa"}},
{iff,ssalint,{pass,beam_ssa_lint}},
{delay,
- [{unless,no_share_opt,{pass,beam_ssa_share}},
+ [{unless,no_bool_opt,{pass,beam_ssa_bool}},
+ {iff,dbool,{listing,"bool"}},
+ {unless,no_share_opt,{pass,beam_ssa_share}},
{iff,dssashare,{listing,"ssashare"}},
{iff,ssalint,{pass,beam_ssa_lint}},
{unless,no_bsm_opt,{pass,beam_ssa_bsm}},
@@ -869,8 +879,6 @@ asm_passes() ->
{unless,no_postopt,
[{pass,beam_block},
{iff,dblk,{listing,"block"}},
- {unless,no_except,{pass,beam_except}},
- {iff,dexcept,{listing,"except"}},
{unless,no_jopt,{pass,beam_jump}},
{iff,djmp,{listing,"jump"}},
{unless,no_peep_opt,{pass,beam_peep}},
@@ -915,8 +923,6 @@ remove_file(Code, St) ->
exports,
labels,
functions=[],
- cfun,
- code,
attributes=[]}).
preprocess_asm_forms(Forms) ->
@@ -926,36 +932,30 @@ preprocess_asm_forms(Forms) ->
{R1#asm_module.module,
R1#asm_module.exports,
R1#asm_module.attributes,
- R1#asm_module.functions,
+ reverse(R1#asm_module.functions),
R1#asm_module.labels}}.
-collect_asm([], R) ->
- case R#asm_module.cfun of
- undefined ->
- R;
- {A,B,C} ->
- R#asm_module{functions=R#asm_module.functions++
- [{function,A,B,C,R#asm_module.code}]}
- end;
collect_asm([{module,M} | Rest], R) ->
collect_asm(Rest, R#asm_module{module=M});
collect_asm([{exports,M} | Rest], R) ->
collect_asm(Rest, R#asm_module{exports=M});
collect_asm([{labels,M} | Rest], R) ->
collect_asm(Rest, R#asm_module{labels=M});
-collect_asm([{function,A,B,C} | Rest], R) ->
- R1 = case R#asm_module.cfun of
- undefined ->
- R;
- {A0,B0,C0} ->
- R#asm_module{functions=R#asm_module.functions++
- [{function,A0,B0,C0,R#asm_module.code}]}
- end,
- collect_asm(Rest, R1#asm_module{cfun={A,B,C}, code=[]});
+collect_asm([{function,A,B,C} | Rest0], R0) ->
+ {Code,Rest} = collect_asm_function(Rest0, []),
+ Func = {function,A,B,C,Code},
+ R = R0#asm_module{functions=[Func | R0#asm_module.functions]},
+ collect_asm(Rest, R);
collect_asm([{attributes, Attr} | Rest], R) ->
collect_asm(Rest, R#asm_module{attributes=Attr});
-collect_asm([X | Rest], R) ->
- collect_asm(Rest, R#asm_module{code=R#asm_module.code++[X]}).
+collect_asm([], R) -> R.
+
+collect_asm_function([{function,_,_,_}|_]=Is, Acc) ->
+ {reverse(Acc),Is};
+collect_asm_function([I|Is], Acc) ->
+ collect_asm_function(Is, [I|Acc]);
+collect_asm_function([], Acc) ->
+ {reverse(Acc),[]}.
beam_consult_asm(_Code, St) ->
case file:consult(St#compile.ifile) of
@@ -2102,15 +2102,17 @@ pre_load() ->
L = [beam_a,
beam_asm,
beam_block,
+ beam_call_types,
beam_clean,
beam_dict,
- beam_except,
+ beam_digraph,
beam_flatten,
beam_jump,
beam_kernel_to_ssa,
beam_opcodes,
beam_peep,
beam_ssa,
+ beam_ssa_bool,
beam_ssa_bsm,
beam_ssa_codegen,
beam_ssa_dead,
@@ -2121,6 +2123,7 @@ pre_load() ->
beam_ssa_share,
beam_ssa_type,
beam_trim,
+ beam_types,
beam_utils,
beam_validator,
beam_z,
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index a086a3a8d3..e6f5604d59 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -24,10 +24,11 @@
beam_a,
beam_asm,
beam_block,
+ beam_call_types,
beam_clean,
beam_dict,
+ beam_digraph,
beam_disasm,
- beam_except,
beam_flatten,
beam_jump,
beam_kernel_to_ssa,
@@ -35,6 +36,7 @@
beam_opcodes,
beam_peep,
beam_ssa,
+ beam_ssa_bool,
beam_ssa_bsm,
beam_ssa_codegen,
beam_ssa_dead,
@@ -47,6 +49,7 @@
beam_ssa_share,
beam_ssa_type,
beam_trim,
+ beam_types,
beam_utils,
beam_validator,
beam_z,
@@ -68,6 +71,7 @@
sys_core_fold,
sys_core_fold_lists,
sys_core_inline,
+ sys_core_prepare,
sys_pre_attributes,
v3_core,
v3_kernel,
@@ -76,5 +80,5 @@
{registered, []},
{applications, [kernel, stdlib]},
{env, []},
- {runtime_dependencies, ["stdlib-2.5","kernel-4.0","hipe-3.12","erts-9.0",
+ {runtime_dependencies, ["stdlib-@OTP-15251@","kernel-@OTP-15251@","hipe-3.12","erts-@OTP-15251@",
"crypto-3.6"]}]}.
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index c1806272bd..c16881cb0d 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -26,6 +26,15 @@
-include("core_parse.hrl").
+%% Removed functions
+
+-removed([{get_anno,1,"use cerl:get_ann/1 instead"},
+ {set_anno,2,"use cerl:set_ann/2 instead"}]).
+
+-removed([{is_literal,1,"use cerl:is_literal/1 instead"},
+ {is_literal_list,1,"use cerl:is_literal_list/1 instead"},
+ {literal_value,1,"use cerl:concrete/1 instead"}]).
+
%% Make a suitable values structure, expr or values, depending on Expr.
-spec make_values([cerl:cerl()] | cerl:cerl()) -> cerl:cerl().
@@ -79,8 +88,6 @@ vu_expr(V, #c_seq{arg=Arg,body=B}) ->
vu_expr(V, Arg) orelse vu_expr(V, B);
vu_expr(V, #c_case{arg=Arg,clauses=Cs}) ->
vu_expr(V, Arg) orelse vu_clauses(V, Cs);
-vu_expr(V, #c_receive{clauses=Cs,timeout=T,action=A}) ->
- vu_clauses(V, Cs) orelse vu_expr(V, T) orelse vu_expr(V, A);
vu_expr(V, #c_apply{op=Op,args=As}) ->
vu_expr_list(V, [Op|As]);
vu_expr(V, #c_call{module=M,name=N,args=As}) ->
@@ -115,77 +122,47 @@ vu_seg_list(V, Ss) ->
vu_expr(V, Val) orelse vu_expr(V, Size)
end, Ss).
-%% Have to get the pattern results right.
-
-spec vu_clause(cerl:var_name(), cerl:c_clause()) -> boolean().
vu_clause(V, #c_clause{pats=Ps,guard=G,body=B}) ->
- case vu_pattern_list(V, Ps) of
- {true,_Shad} -> true; %It is used
- {false,true} -> false; %Shadowed
- {false,false} -> %Not affected
- %% Neither used nor shadowed. Check guard and body.
- vu_expr(V, G) orelse vu_expr(V, B)
- end.
+ vu_pattern_list(V, Ps) orelse vu_expr(V, G) orelse vu_expr(V, B).
-spec vu_clauses(cerl:var_name(), [cerl:c_clause()]) -> boolean().
vu_clauses(V, Cs) ->
lists:any(fun(C) -> vu_clause(V, C) end, Cs).
-%% vu_pattern(VarName, Pattern) -> {Used,Shadow}.
-%% vu_pattern_list(VarName, [Pattern]) -> {Used,Shadow}.
-%% Binaries complicate patterns as a variable can both be properly
-%% used, in a bit segment size, and shadow. They can also do both.
-
-%% vu_pattern(V, Pat) -> vu_pattern(V, Pat, {false,false}).
-
-vu_pattern(V, #c_var{name=V2}, {Used,_}) ->
- {Used,V =:= V2};
-vu_pattern(V, #c_cons{hd=H,tl=T}, St0) ->
- case vu_pattern(V, H, St0) of
- {true,_}=St1 -> St1; %Nothing more to know
- St1 -> vu_pattern(V, T, St1)
- end;
-vu_pattern(V, #c_tuple{es=Es}, St) ->
- vu_pattern_list(V, Es, St);
-vu_pattern(V, #c_binary{segments=Ss}, St) ->
- vu_pat_seg_list(V, Ss, St);
-vu_pattern(V, #c_map{es=Es}, St) ->
- vu_map_pairs(V, Es, St);
-vu_pattern(V, #c_alias{var=Var,pat=P}, St0) ->
- case vu_pattern(V, Var, St0) of
- {true,_}=St1 -> St1;
- St1 -> vu_pattern(V, P, St1)
- end;
-vu_pattern(_, _, St) -> St.
-
-vu_pattern_list(V, Ps) -> vu_pattern_list(V, Ps, {false,false}).
-
-vu_pattern_list(V, Ps, St0) ->
- lists:foldl(fun(P, St) -> vu_pattern(V, P, St) end, St0, Ps).
-
-vu_pat_seg_list(V, Ss, St) ->
- lists:foldl(fun(_, {true,_}=St0) -> St0;
- (#c_bitstr{val=Val,size=Size}, St0) ->
- case vu_pattern(V, Val, St0) of
- {true,_}=St1 -> St1;
- {false,Shad} ->
- {vu_expr(V, Size),Shad}
- end
- end, St, Ss).
-
-vu_map_pairs(V, [#c_map_pair{key=Key,val=Pat}|T], St0) ->
- case vu_expr(V, Key) of
- true ->
- {true,false};
- false ->
- case vu_pattern(V, Pat, St0) of
- {true,_}=St -> St;
- St -> vu_map_pairs(V, T, St)
- end
- end;
-vu_map_pairs(_, [], St) -> St.
+%% vu_pattern(VarName, Pattern) -> Used.
+%% vu_pattern_list(VarName, [Pattern]) -> Used.
+%% Binary and map patterns can use variables.
+
+vu_pattern(V, #c_var{name=V2}) ->
+ V =:= V2;
+vu_pattern(V, #c_cons{hd=H,tl=T}) ->
+ vu_pattern(V, H) orelse vu_pattern(V, T);
+vu_pattern(V, #c_tuple{es=Es}) ->
+ vu_pattern_list(V, Es);
+vu_pattern(V, #c_binary{segments=Ss}) ->
+ vu_pat_seg_list(V, Ss);
+vu_pattern(V, #c_map{es=Es}) ->
+ vu_map_pairs(V, Es);
+vu_pattern(V, #c_alias{var=Var,pat=P}) ->
+ vu_pattern(V, Var) orelse vu_pattern(V, P);
+vu_pattern(_V, #c_literal{}) -> false.
+
+vu_pattern_list(V, Ps) ->
+ lists:any(fun(P) -> vu_pattern(V, P) end, Ps).
+
+vu_pat_seg_list(V, Ss) ->
+ lists:any(fun(#c_bitstr{size=Size}) ->
+ vu_pattern(V, Size)
+ end, Ss).
+
+vu_map_pairs(V, [#c_map_pair{key=Key,val=Pat}|T]) ->
+ vu_expr(V, Key) orelse
+ vu_pattern(V, Pat) orelse
+ vu_map_pairs(V, T);
+vu_map_pairs(_, []) -> false.
-spec vu_var_list(cerl:var_name(), [cerl:c_var()]) -> boolean().
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 3f69cb03a9..579fa59487 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -55,9 +55,9 @@
-type fa() :: {atom(), arity()}.
-type err_desc() :: 'invalid_attributes' | 'invalid_exports'
- | {'arg_mismatch', fa()} | {'bittype_unit', fa()}
+ | {'arg_mismatch', fa()}
| {'illegal_expr', fa()} | {'illegal_guard', fa()}
- | {'illegal_pattern', fa()} | {'illegal_try', fa()}
+ | {'illegal_try', fa()}
| {'not_bs_pattern', fa()} | {'not_pattern', fa()}
| {'not_var', fa()} | {'pattern_mismatch', fa()}
| {'return_mismatch', fa()} | {'undefined_function', fa()}
@@ -88,14 +88,10 @@ format_error(invalid_attributes) -> "invalid attributes";
format_error(invalid_exports) -> "invalid exports";
format_error({arg_mismatch,{F,A}}) ->
io_lib:format("argument count mismatch in ~w/~w", [F,A]);
-format_error({bittype_unit,{F,A}}) ->
- io_lib:format("unit without size in bit syntax pattern/expression in ~w/~w", [F,A]);
format_error({illegal_expr,{F,A}}) ->
io_lib:format("illegal expression in ~w/~w", [F,A]);
format_error({illegal_guard,{F,A}}) ->
io_lib:format("illegal guard expression in ~w/~w", [F,A]);
-format_error({illegal_pattern,{F,A}}) ->
- io_lib:format("illegal pattern in ~w/~w", [F,A]);
format_error({illegal_try,{F,A}}) ->
io_lib:format("illegal try expression in ~w/~w", [F,A]);
format_error({not_bs_pattern,{F,A}}) ->
@@ -111,9 +107,9 @@ format_error({return_mismatch,{F,A}}) ->
format_error({undefined_function,{F,A}}) ->
io_lib:format("function ~w/~w undefined", [F,A]);
format_error({duplicate_var,N,{F,A}}) ->
- io_lib:format("duplicate variable ~s in ~w/~w", [N,F,A]);
+ io_lib:format("duplicate variable ~p in ~w/~w", [N,F,A]);
format_error({unbound_var,N,{F,A}}) ->
- io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]);
+ io_lib:format("unbound variable ~p in ~w/~w", [N,F,A]);
format_error({undefined_function,{F1,A1},{F2,A2}}) ->
io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]);
format_error({tail_segment_not_at_end,{F,A}}) ->
@@ -201,8 +197,13 @@ module_defs(B, Def, St) ->
%% functions([Fdef], Defined, State) -> State.
-functions(Fs, Def, St0) ->
- foldl(fun (F, St) -> function(F, Def, St) end, St0, Fs).
+functions(Fs, Def, Rt, St0) ->
+ foldl(fun ({_Name,#c_fun{vars=Vs,body=B}}, Sti0) ->
+ {Vvs,St} = variable_list(Vs, Sti0),
+ body(B, union(Vvs, Def), Rt, St);
+ (_, St) ->
+ add_error({illegal_expr,St#lint.func}, St)
+ end, St0, Fs).
%% function(CoreFunc, Defined, State) -> State.
@@ -347,7 +348,7 @@ expr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
body(B, union(Lvs, Def), Rt, St2);
expr(#c_letrec{defs=Fs,body=B}, Def0, Rt, St0) ->
Def1 = union(defined_funcs(Fs), Def0), %All defined stuff
- St1 = functions(Fs, Def1, St0),
+ St1 = functions(Fs, Def1, Rt, St0),
body(B, Def1, Rt, St1#lint{func=St0#lint.func});
expr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) ->
Pc = case_patcount(Cs),
@@ -357,9 +358,9 @@ expr(#c_receive{clauses=Cs,timeout=T,action=A}, Def, Rt, St0) ->
St1 = expr(T, Def, 1, St0),
St2 = body(A, Def, Rt, St1),
clauses(Cs, Def, 1, Rt, St2);
-expr(#c_apply{op=Op,args=As}, Def, Rt, St0) ->
+expr(#c_apply{op=Op,args=As}, Def, _Rt, St0) ->
St1 = apply_op(Op, Def, length(As), St0),
- return_match(Rt, 1, expr_list(As, Def, St1));
+ return_match(any, 1, expr_list(As, Def, St1));
expr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As},
Def, Rt, St0) when is_atom(Name) ->
St1 = expr_list(As, Def, St0),
@@ -375,6 +376,7 @@ expr(#c_primop{name=#c_literal{val=A},args=As}, Def, Rt, St0) when is_atom(A) ->
St1 = expr_list(As, Def, St0),
case A of
match_fail -> St1;
+ recv_peek_message -> return_match(Rt, 2, St1);
_ -> return_match(Rt, 1, St1)
end;
expr(#c_catch{body=B}, Def, Rt, St) ->
@@ -513,22 +515,16 @@ pat_var(N, _Def, Ps, St) ->
%% pat_bin_list([Elem], Defined, [PatVar], State) -> {[PatVar],State}.
-pat_bin(Es, Def0, Ps0, St0) ->
- {Ps,_,St} = foldl(fun (E, {Ps,Def,St}) ->
- pat_segment(E, Def, Ps, St)
- end, {Ps0,Def0,St0}, Es),
- {Ps,St}.
-
-pat_segment(#c_bitstr{val=V,size=S,type=T}, Def0, Ps0, St0) ->
- St1 = pat_bit_expr(S, T, Def0, St0),
- {Ps,St2} = pattern(V, Def0, Ps0, St1),
- Def = case V of
- #c_var{name=Name} -> add_element(Name, Def0);
- _ -> Def0
- end,
- {Ps,Def,St2};
-pat_segment(_, Def, Ps, St) ->
- {Ps,Def,add_error({not_bs_pattern,St#lint.func}, St)}.
+pat_bin(Es, Def, Ps0, St0) ->
+ foldl(fun (E, {Ps,St}) ->
+ pat_segment(E, Def, Ps, St)
+ end, {Ps0,St0}, Es).
+
+pat_segment(#c_bitstr{val=V,size=S,type=T}, Def, Ps0, St0) ->
+ St1 = pat_bit_expr(S, T, Def, St0),
+ pattern(V, Def, Ps0, St1);
+pat_segment(_, _, Ps, St) ->
+ {Ps,add_error({not_bs_pattern,St#lint.func}, St)}.
%% pat_bin_tail_check([Elem], State) -> State.
%% There must be at most one tail segment (a size-less segment of
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index cb3f24fd08..19fa11235c 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -214,7 +214,7 @@ format_1(#c_let{anno=Anno0,vars=Vs0,arg=A0,body=B}, #ctxt{clean=Clean}=Ctxt) ->
{Vs0,A0,Anno0};
true ->
{[cerl:set_ann(V, []) || V <- Vs0],
- cerl:set_ann(A0, []),
+ clean_anno_carefully(A0),
[]}
end,
case is_simple_term(A) andalso Anno =:= [] of
@@ -546,3 +546,13 @@ segs_from_bitstring(Bitstring) ->
unit=#c_literal{val=1},
type=#c_literal{val=integer},
flags=#c_literal{val=[unsigned,big]}}].
+
+clean_anno_carefully(Node) ->
+ Anno = clean_anno_carefully_1(cerl:get_ann(Node)),
+ cerl:set_ann(Node, Anno).
+
+clean_anno_carefully_1([letrec_goto=Keep|Annos]) ->
+ [Keep|clean_anno_carefully_1(Annos)];
+clean_anno_carefully_1([_|Annos]) ->
+ clean_anno_carefully_1(Annos);
+clean_anno_carefully_1([]) -> [].
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index 94a5dfe012..caf067fde7 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -80,10 +80,12 @@ is_pure(erlang, 'or', 2) -> true;
is_pure(erlang, 'rem', 2) -> true;
is_pure(erlang, 'xor', 2) -> true;
is_pure(erlang, abs, 1) -> true;
+is_pure(erlang, atom_to_binary, 1) -> true;
is_pure(erlang, atom_to_binary, 2) -> true;
is_pure(erlang, atom_to_list, 1) -> true;
is_pure(erlang, binary_part, 2) -> true;
is_pure(erlang, binary_part, 3) -> true;
+is_pure(erlang, binary_to_atom, 1) -> true;
is_pure(erlang, binary_to_atom, 2) -> true;
is_pure(erlang, binary_to_float, 1) -> true;
is_pure(erlang, binary_to_integer, 1) -> true;
@@ -144,6 +146,9 @@ is_pure(erlang, tuple_size, 1) -> true;
is_pure(erlang, tuple_to_list, 1) -> true;
is_pure(lists, append, 2) -> true;
is_pure(lists, subtract, 2) -> true;
+is_pure(maps, get, 2) -> true;
+is_pure(maps, is_key, 2) -> true;
+is_pure(maps, new, 0) -> true;
is_pure(math, acos, 1) -> true;
is_pure(math, acosh, 1) -> true;
is_pure(math, asin, 1) -> true;
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 86590fad87..64680ca1ed 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -596,3 +596,15 @@ BEAM_FORMAT_NUMBER=0
## @spec bs_set_positon Ctx Pos
## @doc Sets the current position of Ctx to Pos
168: bs_set_position/2
+
+# OTP 23
+
+## @spec swap Register1 Register2
+## @doc Swaps the contents of two registers.
+169: swap/2
+
+## @spec bs_start_match4 Fail Bin Live Dst
+## @doc As bs_start_match3, but the fail label can be 'no_fail' when we know
+## it will never fail at runtime, or 'resume' when we know the input is
+## a match context.
+170: bs_start_match4/4
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index eb1f69269c..b8cf4b42ff 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -99,10 +99,6 @@
t=#{} :: map(), %Types
in_guard=false}). %In guard or not.
--type type_info() :: cerl:cerl() | 'bool' | 'integer' | {'fun', pos_integer()}.
--type yes_no_maybe() :: 'yes' | 'no' | 'maybe'.
--type sub() :: #sub{}.
-
-spec module(cerl:c_module(), [compile:option()]) ->
{'ok', cerl:c_module(), [_]}.
@@ -315,10 +311,10 @@ expr(#c_seq{arg=Arg0,body=B0}=Seq0, Ctxt, Sub) ->
false ->
%% Arg cannot be "values" here - only a single value
%% make sense here.
- case {Ctxt,is_safe_simple(Arg, Sub)} of
+ case {Ctxt,is_safe_simple(Arg)} of
{effect,true} -> B1;
{effect,false} ->
- case is_safe_simple(B1, Sub) of
+ case is_safe_simple(B1) of
true -> Arg;
false -> Seq0#c_seq{arg=Arg,body=B1}
end;
@@ -384,11 +380,11 @@ expr(#c_case{}=Case0, Ctxt, Sub) ->
%% according to the rules above).
%%
case opt_bool_case(Case0, Sub) of
- #c_case{arg=Arg0,clauses=Cs0}=Case1 ->
+ #c_case{anno=Anno,arg=Arg0,clauses=Cs0}=Case1 ->
Arg1 = body(Arg0, value, Sub),
LitExpr = cerl:is_literal(Arg1),
{Arg2,Cs1} = case_opt(Arg1, Cs0, Sub),
- Cs2 = clauses(Arg2, Cs1, Ctxt, Sub, LitExpr),
+ Cs2 = clauses(Arg2, Cs1, Ctxt, Sub, LitExpr, Anno),
Case = Case1#c_case{arg=Arg2,clauses=Cs2},
warn_no_clause_match(Case1, Case),
Expr = eval_case(Case, Sub),
@@ -396,11 +392,6 @@ expr(#c_case{}=Case0, Ctxt, Sub) ->
Other ->
expr(Other, Ctxt, Sub)
end;
-expr(#c_receive{clauses=Cs0,timeout=T0,action=A0}=Recv, Ctxt, Sub) ->
- Cs1 = clauses(#c_var{name='_'}, Cs0, Ctxt, Sub, false),
- T1 = expr(T0, value, Sub),
- A1 = body(A0, Ctxt, Sub),
- Recv#c_receive{clauses=Cs1,timeout=T1,action=A1};
expr(#c_apply{anno=Anno,op=Op0,args=As0}=Apply0, _, Sub) ->
Op1 = expr(Op0, value, Sub),
As1 = expr_list(As0, value, Sub),
@@ -442,7 +433,7 @@ expr(#c_catch{anno=Anno,body=B}, effect, Sub) ->
expr(#c_catch{body=B0}=Catch, _, Sub) ->
%% We can remove catch if the value is simple
B1 = body(B0, value, Sub),
- case is_safe_simple(B1, Sub) of
+ case is_safe_simple(B1) of
true -> B1;
false -> Catch#c_catch{body=B1}
end;
@@ -458,7 +449,7 @@ expr(#c_try{arg=E0,vars=[#c_var{name=X}],body=#c_var{name=X},
%% We can remove try/catch if the expression is an
%% expression that cannot fail.
- case is_safe_bool_expr(E2, Sub) orelse is_safe_simple(E2, Sub) of
+ case is_safe_bool_expr(E2) orelse is_safe_simple(E2) of
true -> E2;
false -> Try#c_try{arg=E2}
end;
@@ -472,27 +463,15 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
E1 = body(E0, value, Sub0),
{Vs1,Sub1} = var_list(Vs0, Sub0),
B1 = body(B0, value, Sub1),
- case is_safe_simple(E1, Sub0) of
+ case is_safe_simple(E1) of
true ->
expr(#c_let{anno=A,vars=Vs1,arg=E1,body=B1}, value, Sub0);
false ->
{Evs1,Sub2} = var_list(Evs0, Sub0),
H1 = body(H0, value, Sub2),
- H2 = opt_try_handler(H1, lists:last(Evs1)),
- Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H2}
+ Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}
end.
-%% Attempts to convert old erlang:get_stacktrace/0 calls into the new
-%% three-argument catch, with possibility of further optimisations.
-opt_try_handler(#c_call{anno=A,module=#c_literal{val=erlang},name=#c_literal{val=get_stacktrace},args=[]}, Var) ->
- #c_primop{anno=A,name=#c_literal{val=build_stacktrace},args=[Var]};
-opt_try_handler(#c_case{clauses=Cs0} = Case, Var) ->
- Cs = [C#c_clause{body=opt_try_handler(B, Var)} || #c_clause{body=B} = C <- Cs0],
- Case#c_case{clauses=Cs};
-opt_try_handler(#c_let{arg=Arg} = Let, Var) ->
- Let#c_let{arg=opt_try_handler(Arg, Var)};
-opt_try_handler(X, _) -> X.
-
%% If a fun or its application is used as an argument, then it's unsafe to
%% handle it in effect context as the side-effects may rely on its return
%% value. The following is a minimal example of where it can go wrong:
@@ -545,10 +524,6 @@ ifes_1(FVar, #c_map_pair{key=Key,val=Val}, _Safe) ->
ifes_1(FVar, Key, false) andalso ifes_1(FVar, Val, false);
ifes_1(FVar, #c_primop{args=Args}, _Safe) ->
ifes_list(FVar, Args, false);
-ifes_1(FVar, #c_receive{timeout=Timeout,action=Action,clauses=Clauses}, Safe) ->
- ifes_1(FVar, Timeout, false) andalso
- ifes_1(FVar, Action, Safe) andalso
- ifes_list(FVar, Clauses, Safe);
ifes_1(FVar, #c_seq{arg=Arg,body=Body}, Safe) ->
%% Arg of a #c_seq{} has no effect so it's okay to use FVar there even if
%% Safe=false.
@@ -602,20 +577,20 @@ is_literal_fun(_) -> false.
%% Currently, we don't attempt to check binaries because they
%% are difficult to check.
-is_safe_simple(#c_var{}=Var, _) ->
+is_safe_simple(#c_var{}=Var) ->
not cerl:is_c_fname(Var);
-is_safe_simple(#c_cons{hd=H,tl=T}, Sub) ->
- is_safe_simple(H, Sub) andalso is_safe_simple(T, Sub);
-is_safe_simple(#c_tuple{es=Es}, Sub) -> is_safe_simple_list(Es, Sub);
-is_safe_simple(#c_literal{}, _) -> true;
+is_safe_simple(#c_cons{hd=H,tl=T}) ->
+ is_safe_simple(H) andalso is_safe_simple(T);
+is_safe_simple(#c_tuple{es=Es}) -> is_safe_simple_list(Es);
+is_safe_simple(#c_literal{}) -> true;
is_safe_simple(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name},
- args=Args}, Sub) when is_atom(Name) ->
+ args=Args}) when is_atom(Name) ->
NumArgs = length(Args),
case erl_internal:bool_op(Name, NumArgs) of
true ->
%% Boolean operators are safe if the arguments are boolean.
- all(fun(C) -> is_boolean_type(C, Sub) =:= yes end, Args);
+ all(fun is_bool_expr/1, Args);
false ->
%% We need a rather complicated test to ensure that
%% we only allow safe calls that are allowed in a guard.
@@ -624,9 +599,9 @@ is_safe_simple(#c_call{module=#c_literal{val=erlang},
(erl_internal:comp_op(Name, NumArgs) orelse
erl_internal:new_type_test(Name, NumArgs))
end;
-is_safe_simple(_, _) -> false.
+is_safe_simple(_) -> false.
-is_safe_simple_list(Es, Sub) -> all(fun(E) -> is_safe_simple(E, Sub) end, Es).
+is_safe_simple_list(Es) -> all(fun(E) -> is_safe_simple(E) end, Es).
%% will_fail(Expr) -> true|false.
%% Determine whether the expression will fail with an exception.
@@ -696,15 +671,7 @@ eval_binary(#c_binary{anno=Anno,segments=Ss}=Bin) ->
eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},
unit=#c_literal{val=Unit},type=#c_literal{val=Type},
flags=#c_literal{val=Flags}}|Ss], Acc0) ->
- Endian = case member(big, Flags) of
- true ->
- big;
- false ->
- case member(little, Flags) of
- true -> little;
- false -> throw(impossible) %Native endian.
- end
- end,
+ Endian = bs_endian(Flags),
%% Make sure that the size is reasonable.
case Type of
@@ -738,10 +705,14 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},
end;
float when is_float(Val) ->
%% Bad float size.
- case Sz*Unit of
+ try Sz*Unit of
32 -> ok;
64 -> ok;
- _ -> throw(impossible)
+ _ ->
+ throw({badarg,bad_float_size})
+ catch
+ error:_ ->
+ throw({badarg,bad_float_size})
end;
utf8 -> ok;
utf16 -> ok;
@@ -750,6 +721,11 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},
throw(impossible)
end,
+ case Endian =:= native andalso Type =/= binary of
+ true -> throw(impossible);
+ false -> ok
+ end,
+
%% Evaluate the field.
try eval_binary_2(Acc0, Val, Sz, Unit, Type, Endian) of
Acc -> eval_binary_1(Ss, Acc)
@@ -813,6 +789,11 @@ eval_binary_2(Acc, Val, all, Unit, binary, _) ->
eval_binary_2(Acc, Val, Size, Unit, binary, _) ->
<<Acc/bitstring,Val:(Size*Unit)/bitstring>>.
+bs_endian([big=E|_]) -> E;
+bs_endian([little=E|_]) -> E;
+bs_endian([native=E|_]) -> E;
+bs_endian([_|Fs]) -> bs_endian(Fs).
+
%% Count the number of bits approximately needed to store Int.
%% (We don't need an exact result for this purpose.)
@@ -853,7 +834,7 @@ useless_call(_, _) -> no.
%% Anything that will not have any effect will be thrown away.
make_effect_seq([H|T], Sub) ->
- case is_safe_simple(H, Sub) of
+ case is_safe_simple(H) of
true -> make_effect_seq(T, Sub);
false -> #c_seq{arg=H,body=make_effect_seq(T, Sub)}
end;
@@ -880,25 +861,45 @@ fold_apply(Apply, _, _) -> Apply.
%% Handling remote calls. The module/name fields have been processed.
-call(#c_call{args=As}=Call, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) ->
- case get(no_inline_list_funcs) of
- true ->
- call_1(Call, M0, N0, As, Sub);
- false ->
- case sys_core_fold_lists:call(Call, M, N, As) of
- none ->
- call_1(Call, M0, N0, As, Sub);
- Core ->
- expr(Core, Sub)
- end
-
- end;
-call(#c_call{args=As}=Call, M, N, Sub) ->
- call_1(Call, M, N, As, Sub).
-
-call_1(Call, M, N, As0, Sub) ->
+call(#c_call{args=As0}=Call0, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) ->
As1 = expr_list(As0, value, Sub),
- fold_call(Call#c_call{args=As1}, M, N, As1, Sub).
+ case simplify_call(Call0, M, N, As1) of
+ #c_literal{}=Lit ->
+ Lit;
+ #c_call{args=As}=Call ->
+ case get(no_inline_list_funcs) of
+ true ->
+ fold_call(Call, M0, N0, As, Sub);
+ false ->
+ case sys_core_fold_lists:call(Call, M, N, As) of
+ none -> fold_call(Call, M0, N0, As, Sub);
+ Core -> expr(Core, Sub)
+ end
+ end
+ end;
+call(#c_call{args=As0}=Call, M, N, Sub) ->
+ As = expr_list(As0, value, Sub),
+ fold_call(Call#c_call{args=As}, M, N, As, Sub).
+
+%% Rewrite certain known functions to BIFs, improving performance
+%% slightly at the cost of making tracing and stack traces incorrect.
+simplify_call(Call, maps, get, [Key, Map]) ->
+ rewrite_call(Call, erlang, map_get, [Key, Map]);
+simplify_call(Call, maps, is_key, [Key, Map]) ->
+ rewrite_call(Call, erlang, is_map_key, [Key, Map]);
+simplify_call(_Call, maps, new, []) ->
+ #c_literal{val=#{}};
+simplify_call(Call, maps, size, [Map]) ->
+ rewrite_call(Call, erlang, map_size, [Map]);
+simplify_call(Call, _, _, Args) ->
+ Call#c_call{args=Args}.
+
+%% rewrite_call(Call0, Mod, Func, Args, Sub) -> Call
+%% Rewrites a call to the given MFA.
+rewrite_call(Call, Mod, Func, Args) ->
+ ModLit = #c_literal{val=Mod},
+ FuncLit = #c_literal{val=Func},
+ Call#c_call{module=ModLit,name=FuncLit,args=Args}.
%% fold_call(Call, Mod, Name, Args, Sub) -> Expr.
%% Try to safely evaluate the call. Just try to evaluate arguments,
@@ -959,138 +960,14 @@ fold_lit_args(Call, Module, Name, Args0) ->
%% Attempt to evaluate some pure BIF calls with one or more
%% non-literals arguments.
%%
-fold_non_lit_args(Call, erlang, is_boolean, [Arg], Sub) ->
- eval_is_boolean(Call, Arg, Sub);
fold_non_lit_args(Call, erlang, length, [Arg], _) ->
eval_length(Call, Arg);
fold_non_lit_args(Call, erlang, '++', [Arg1,Arg2], _) ->
eval_append(Call, Arg1, Arg2);
fold_non_lit_args(Call, lists, append, [Arg1,Arg2], _) ->
eval_append(Call, Arg1, Arg2);
-fold_non_lit_args(Call, erlang, is_function, [Arg1], Sub) ->
- eval_is_function_1(Call, Arg1, Sub);
-fold_non_lit_args(Call, erlang, is_function, [Arg1,Arg2], Sub) ->
- eval_is_function_2(Call, Arg1, Arg2, Sub);
-fold_non_lit_args(Call, erlang, N, Args, Sub) ->
- NumArgs = length(Args),
- case erl_internal:comp_op(N, NumArgs) of
- true ->
- eval_rel_op(Call, N, Args, Sub);
- false ->
- case erl_internal:bool_op(N, NumArgs) of
- true ->
- eval_bool_op(Call, N, Args, Sub);
- false ->
- Call
- end
- end;
fold_non_lit_args(Call, _, _, _, _) -> Call.
-eval_is_function_1(Call, Arg1, Sub) ->
- case get_type(Arg1, Sub) of
- none -> Call;
- {'fun',_} -> #c_literal{anno=cerl:get_ann(Call),val=true};
- _ -> #c_literal{anno=cerl:get_ann(Call),val=false}
- end.
-
-eval_is_function_2(Call, Arg1, #c_literal{val=Arity}, Sub)
- when is_integer(Arity), Arity > 0 ->
- case get_type(Arg1, Sub) of
- none -> Call;
- {'fun',Arity} -> #c_literal{anno=cerl:get_ann(Call),val=true};
- _ -> #c_literal{anno=cerl:get_ann(Call),val=false}
- end;
-eval_is_function_2(Call, _Arg1, _Arg2, _Sub) -> Call.
-
-%% Evaluate a relational operation using type information.
-eval_rel_op(Call, Op, [#c_var{name=V},#c_var{name=V}], _) ->
- Bool = erlang:Op(same, same),
- #c_literal{anno=cerl:get_ann(Call),val=Bool};
-eval_rel_op(Call, '=:=', [Term,#c_literal{val=true}], Sub) ->
- %% BoolVar =:= true ==> BoolVar
- case is_boolean_type(Term, Sub) of
- yes -> Term;
- maybe -> Call;
- no -> #c_literal{val=false}
- end;
-eval_rel_op(Call, '==', Ops, Sub) ->
- case is_exact_eq_ok(Ops, Sub) of
- true ->
- Name = #c_literal{anno=cerl:get_ann(Call),val='=:='},
- Call#c_call{name=Name};
- false ->
- Call
- end;
-eval_rel_op(Call, '/=', Ops, Sub) ->
- case is_exact_eq_ok(Ops, Sub) of
- true ->
- Name = #c_literal{anno=cerl:get_ann(Call),val='=/='},
- Call#c_call{name=Name};
- false ->
- Call
- end;
-eval_rel_op(Call, _, _, _) -> Call.
-
-is_exact_eq_ok([A,B]=L, Sub) ->
- case is_int_type(A, Sub) =:= yes andalso is_int_type(B, Sub) =:= yes of
- true -> true;
- false -> is_exact_eq_ok_1(L)
- end.
-
-is_exact_eq_ok_1([#c_literal{val=Lit}|_]) ->
- is_non_numeric(Lit);
-is_exact_eq_ok_1([_|T]) ->
- is_exact_eq_ok_1(T);
-is_exact_eq_ok_1([]) -> false.
-
-is_non_numeric([H|T]) ->
- is_non_numeric(H) andalso is_non_numeric(T);
-is_non_numeric(Tuple) when is_tuple(Tuple) ->
- is_non_numeric_tuple(Tuple, tuple_size(Tuple));
-is_non_numeric(Map) when is_map(Map) ->
- %% Note that 17.x and 18.x compare keys in different ways.
- %% Be very conservative -- require that both keys and values
- %% are non-numeric.
- is_non_numeric(maps:to_list(Map));
-is_non_numeric(Num) when is_number(Num) ->
- false;
-is_non_numeric(_) -> true.
-
-is_non_numeric_tuple(Tuple, El) when El >= 1 ->
- is_non_numeric(element(El, Tuple)) andalso
- is_non_numeric_tuple(Tuple, El-1);
-is_non_numeric_tuple(_Tuple, 0) -> true.
-
-%% Evaluate a bool op using type information. We KNOW that
-%% there must be at least one non-literal argument (i.e.
-%% there is no need to handle the case that all argments
-%% are literal).
-
-eval_bool_op(Call, 'and', [#c_literal{val=true},Term], Sub) ->
- eval_bool_op_1(Call, Term, Term, Sub);
-eval_bool_op(Call, 'and', [Term,#c_literal{val=true}], Sub) ->
- eval_bool_op_1(Call, Term, Term, Sub);
-eval_bool_op(Call, 'and', [#c_literal{val=false}=Res,Term], Sub) ->
- eval_bool_op_1(Call, Res, Term, Sub);
-eval_bool_op(Call, 'and', [Term,#c_literal{val=false}=Res], Sub) ->
- eval_bool_op_1(Call, Res, Term, Sub);
-eval_bool_op(Call, _, _, _) -> Call.
-
-eval_bool_op_1(Call, Res, Term, Sub) ->
- case is_boolean_type(Term, Sub) of
- yes -> Res;
- no -> eval_failure(Call, badarg);
- maybe -> Call
- end.
-
-%% Evaluate is_boolean/1 using type information.
-eval_is_boolean(Call, Term, Sub) ->
- case is_boolean_type(Term, Sub) of
- no -> #c_literal{val=false};
- yes -> #c_literal{val=true};
- maybe -> Call
- end.
-
%% eval_length(Call, List) -> Val.
%% Evaluates the length for the prefix of List which has a known
%% shape.
@@ -1199,10 +1076,6 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) ->
%% No need for substitution tricks when the guard
%% does not contain any variables.
Sub1;
- {#c_var{name='_'},_,_} ->
- %% In a 'receive', Cexpr is the variable '_', which represents the
- %% message being matched. We must NOT do any extra substiutions.
- Sub1;
{#c_var{},[#c_var{}=Var],_} ->
%% The idea here is to optimize expressions such as
%%
@@ -1329,20 +1202,27 @@ map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,
{V,Osub} = pattern(V0,Isub,Osub0),
{Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
-bin_pattern_list(Ps0, Isub, Osub0) ->
- {Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0),
- {Ps,Osub}.
-
-bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat0, {Isub0,Osub0}) ->
- Size1 = expr(Size0, Isub0),
- {E1,Osub} = pattern(E0, Isub0, Osub0),
- Isub = case E0 of
- #c_var{} -> sub_set_var(E0, E1, Isub0);
- _ -> Isub0
- end,
- Pat = Pat0#c_bitstr{val=E1,size=Size1},
+bin_pattern_list(Ps, Isub, Osub0) ->
+ mapfoldl(fun(P, Osub) ->
+ bin_pattern(P, Isub, Osub)
+ end, Osub0, Ps).
+
+bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat0, Isub, Osub0) ->
+ Size2 = case {Size0,expr(Size0, Isub)} of
+ {#c_var{},#c_literal{val=all}} ->
+ %% The size `all` is used for the size of the final binary
+ %% segment in a pattern. Using `all` explicitly is not allowed,
+ %% so we convert it to an obvious invalid size. We also need
+ %% to add an annotation to get the correct wording of the warning
+ %% that will soon be issued.
+ #c_literal{anno=[size_was_all],val=bad_size};
+ {_,Size1} ->
+ Size1
+ end,
+ {E1,Osub} = pattern(E0, Isub, Osub0),
+ Pat = Pat0#c_bitstr{val=E1,size=Size2},
bin_pat_warn(Pat),
- {Pat,{Isub,Osub}}.
+ {Pat,Osub}.
pattern_list(Ps, Sub) -> pattern_list(Ps, Sub, Sub).
@@ -1365,7 +1245,7 @@ var_list(Vs, Sub0) ->
bin_pat_warn(#c_bitstr{type=#c_literal{val=Type},
val=Val0,
- size=#c_literal{val=Sz},
+ size=#c_literal{anno=SizeAnno,val=Sz},
unit=#c_literal{val=Unit},
flags=Fl}=Pat) ->
case {Type,Sz} of
@@ -1375,7 +1255,12 @@ bin_pat_warn(#c_bitstr{type=#c_literal{val=Type},
{utf16,undefined} -> ok;
{utf32,undefined} -> ok;
{_,_} ->
- add_warning(Pat, {nomatch_bit_syntax_size,Sz}),
+ case member(size_was_all, SizeAnno) of
+ true ->
+ add_warning(Pat, {nomatch_bit_syntax_size,all});
+ false ->
+ add_warning(Pat, {nomatch_bit_syntax_size,Sz})
+ end,
throw(nomatch)
end,
case {Type,Val0} of
@@ -1562,11 +1447,11 @@ warn_no_clause_match(CaseOrig, CaseOpt) ->
ok
end.
-%% clauses(E, [Clause], TopLevel, Context, Sub) -> [Clause].
+%% clauses(E, [Clause], TopLevel, Context, Sub, Anno) -> [Clause].
%% Trim the clauses by removing all clauses AFTER the first one which
%% is guaranteed to match. Also remove all trivially false clauses.
-clauses(E, [C0|Cs], Ctxt, Sub, LitExpr) ->
+clauses(E, [C0|Cs], Ctxt, Sub, LitExpr, Anno) ->
#c_clause{pats=Ps,guard=G} = C1 = clause(C0, E, Ctxt, Sub),
%%ok = io:fwrite("~w: ~p~n", [?LINE,{E,Ps}]),
case {will_match(E, Ps),will_succeed(G)} of
@@ -1574,7 +1459,7 @@ clauses(E, [C0|Cs], Ctxt, Sub, LitExpr) ->
case LitExpr of
false ->
Line = get_line(cerl:get_ann(C1)),
- shadow_warning(Cs, Line);
+ shadow_warning(Cs, Line, Anno);
true ->
%% If the case expression is a literal,
%% it is probably OK that some clauses don't match.
@@ -1584,19 +1469,24 @@ clauses(E, [C0|Cs], Ctxt, Sub, LitExpr) ->
[C1]; %Skip the rest
{_Mat,no} -> %Guard fails.
add_warning(C1, nomatch_guard),
- clauses(E, Cs, Ctxt, Sub, LitExpr); %Skip this clause
+ clauses(E, Cs, Ctxt, Sub, LitExpr, Anno); %Skip this clause
{_Mat,_Suc} ->
- [C1|clauses(E, Cs, Ctxt, Sub, LitExpr)]
+ [C1|clauses(E, Cs, Ctxt, Sub, LitExpr, Anno)]
end;
-clauses(_, [], _, _, _) -> [].
+clauses(_, [], _, _, _, _) -> [].
-shadow_warning([C|Cs], none) ->
+shadow_warning([C|Cs], none, Anno) ->
add_warning(C, nomatch_shadow),
- shadow_warning(Cs, none);
-shadow_warning([C|Cs], Line) ->
- add_warning(C, {nomatch_shadow, Line}),
- shadow_warning(Cs, Line);
-shadow_warning([], _) -> ok.
+ shadow_warning(Cs, none, Anno);
+shadow_warning([C|Cs], Line, Anno) ->
+ case keyfind(function, 1, Anno) of
+ {function, {Name, Arity}} ->
+ add_warning(C, {nomatch_shadow, Line, {Name, Arity}});
+ _ ->
+ add_warning(C, {nomatch_shadow, Line})
+ end,
+ shadow_warning(Cs, Line, Anno);
+shadow_warning([], _, _) -> ok.
%% will_succeed(Guard) -> yes | maybe | no.
%% Test if we know whether a guard will succeed/fail or just don't
@@ -1669,7 +1559,7 @@ opt_bool_clauses(Cs, true, true) ->
%% Any remaining clauses cannot possibly match.
case Cs of
[_|_] ->
- shadow_warning(Cs, none),
+ shadow_warning(Cs, none, []),
[];
[] ->
[]
@@ -1771,7 +1661,7 @@ opt_bool_not_invert(#c_clause{pats=[#c_literal{val=Bool}]}=C) ->
opt_bool_case_redundant(#c_case{arg=Arg,clauses=Cs}=Case) ->
case all(fun opt_bool_case_redundant_1/1, Cs) of
true -> Arg;
- false -> opt_bool_case_guard(Case)
+ false -> Case
end.
opt_bool_case_redundant_1(#c_clause{pats=[#c_literal{val=B}],
@@ -1779,45 +1669,6 @@ opt_bool_case_redundant_1(#c_clause{pats=[#c_literal{val=B}],
true;
opt_bool_case_redundant_1(_) -> false.
-%% opt_bool_case_guard(Case) -> Case'.
-%% Move a boolean case expression into the guard if we are sure that
-%% it cannot fail.
-%%
-%% case SafeBoolExpr of case <> of
-%% true -> TrueClause; ==> <> when SafeBoolExpr -> TrueClause;
-%% false -> FalseClause <> when true -> FalseClause
-%% end. end.
-%%
-%% Generally, evaluting a boolean expression in a guard should
-%% be faster than evaulating it in the body.
-%%
-opt_bool_case_guard(#c_case{arg=#c_literal{}}=Case) ->
- %% It is not necessary to move a literal case expression into the
- %% guard, because it will be handled quite well in other
- %% optimizations, and moving the literal into the guard will
- %% cause some extra warnings, for instance for this code
- %%
- %% case true of
- %% true -> ...;
- %% false -> ...
- %% end.
- %%
- Case;
-opt_bool_case_guard(#c_case{arg=Arg,clauses=Cs0}=Case) ->
- case is_safe_bool_expr(Arg, sub_new()) of
- false ->
- Case;
- true ->
- Cs = opt_bool_case_guard(Arg, Cs0),
- Case#c_case{arg=#c_values{anno=cerl:get_ann(Arg),es=[]},
- clauses=Cs}
- end.
-
-opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=true}]}=Tc,Fc]) ->
- [Tc#c_clause{pats=[],guard=Arg},Fc#c_clause{pats=[]}];
-opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=false}]}=Fc,Tc]) ->
- [Tc#c_clause{pats=[],guard=Arg},Fc#c_clause{pats=[]}].
-
%% eval_case(Case) -> #c_case{} | #c_let{}.
%% If possible, evaluate a case at compile time. We know that the
%% last clause is guaranteed to match so if there is only one clause
@@ -1945,7 +1796,7 @@ case_opt_arg(E0, Sub, Cs, LitExpr) ->
{error,Cs};
false ->
%% If possible, expand this variable to a previously
- %% matched term.
+ %% constructed tuple
E = case_expand_var(E0, Sub),
case_opt_arg_1(E, Cs, LitExpr)
end
@@ -2004,13 +1855,8 @@ case_opt_compiler_generated(Core) ->
case_expand_var(E, #sub{t=Tdb}) ->
Key = cerl:var_name(E),
case Tdb of
- #{Key:=T} ->
- case cerl:is_c_tuple(T) of
- false -> E;
- true -> T
- end;
- _ ->
- E
+ #{Key:=T} -> T;
+ _ -> E
end.
%% case_opt_nomatch(E, Clauses, LitExpr) -> Clauses'
@@ -2302,115 +2148,84 @@ is_simple_case_arg(_) -> false.
%% Check whether the Core expression is guaranteed to return
%% a boolean IF IT RETURNS AT ALL.
%%
-is_bool_expr(Core) ->
- is_bool_expr(Core, sub_new()).
-%% is_bool_expr(Core, Sub) -> true|false
-%% Check whether the Core expression is guaranteed to return
-%% a boolean IF IT RETURNS AT ALL. Uses type information
-%% to be able to identify more expressions as booleans.
-%%
is_bool_expr(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=Name},args=Args}=Call, _) ->
+ name=#c_literal{val=Name},args=Args}=Call) ->
NumArgs = length(Args),
erl_internal:comp_op(Name, NumArgs) orelse
erl_internal:new_type_test(Name, NumArgs) orelse
erl_internal:bool_op(Name, NumArgs) orelse
will_fail(Call);
is_bool_expr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
- handler=#c_literal{val=false}}, Sub) ->
- is_bool_expr(E, Sub);
-is_bool_expr(#c_case{clauses=Cs}, Sub) ->
- is_bool_expr_list(Cs, Sub);
-is_bool_expr(#c_clause{body=B}, Sub) ->
- is_bool_expr(B, Sub);
-is_bool_expr(#c_let{vars=[V],arg=Arg,body=B}, Sub0) ->
- Sub = case is_bool_expr(Arg, Sub0) of
- true -> update_types(V, [bool], Sub0);
- false -> Sub0
- end,
- is_bool_expr(B, Sub);
-is_bool_expr(#c_let{body=B}, Sub) ->
- %% Binding of multiple variables.
- is_bool_expr(B, Sub);
-is_bool_expr(C, Sub) ->
- is_boolean_type(C, Sub) =:= yes.
-
-is_bool_expr_list([C|Cs], Sub) ->
- is_bool_expr(C, Sub) andalso is_bool_expr_list(Cs, Sub);
-is_bool_expr_list([], _) -> true.
+ handler=#c_literal{val=false}}) ->
+ is_bool_expr(E);
+is_bool_expr(#c_case{clauses=Cs}) ->
+ is_bool_expr_list(Cs);
+is_bool_expr(#c_clause{body=B}) ->
+ is_bool_expr(B);
+is_bool_expr(#c_let{body=B}) ->
+ is_bool_expr(B);
+is_bool_expr(#c_literal{val=Val}) ->
+ is_boolean(Val);
+is_bool_expr(_) -> false.
+
+is_bool_expr_list([C|Cs]) ->
+ is_bool_expr(C) andalso is_bool_expr_list(Cs);
+is_bool_expr_list([]) -> true.
%% is_safe_bool_expr(Core) -> true|false
%% Check whether the Core expression ALWAYS returns a boolean
-%% (i.e. it cannot fail). Also make sure that the expression
-%% is suitable for a guard (no calls to non-guard BIFs, local
-%% functions, or is_record/2).
+%% (i.e. it cannot fail).
%%
-is_safe_bool_expr(Core, Sub) ->
- is_safe_bool_expr_1(Core, Sub, cerl_sets:new()).
+is_safe_bool_expr(Core) ->
+ is_safe_bool_expr_1(Core, cerl_sets:new()).
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
- 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);
+ _BoolVars) when is_integer(Arity), Arity >= 0 ->
+ is_safe_simple(A);
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function}},
- _Sub, _BoolVars) ->
+ _BoolVars) ->
false;
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name},args=Args},
- Sub, BoolVars) ->
+ BoolVars) ->
NumArgs = length(Args),
case (erl_internal:comp_op(Name, NumArgs) orelse
erl_internal:new_type_test(Name, NumArgs)) andalso
- is_safe_simple_list(Args, Sub) of
+ is_safe_simple_list(Args) of
true ->
true;
false ->
%% Boolean operators are safe if all arguments are boolean.
erl_internal:bool_op(Name, NumArgs) andalso
- is_safe_bool_expr_list(Args, Sub, BoolVars)
+ is_safe_bool_expr_list(Args, BoolVars)
end;
-is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) ->
- case is_safe_simple(Arg, Sub) of
+is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, BoolVars) ->
+ case is_safe_simple(Arg) of
true ->
- case {is_safe_bool_expr_1(Arg, Sub, BoolVars),Vars} of
+ case {is_safe_bool_expr_1(Arg, BoolVars),Vars} of
{true,[#c_var{name=V}]} ->
- is_safe_bool_expr_1(B, Sub, cerl_sets:add_element(V, BoolVars));
+ is_safe_bool_expr_1(B, cerl_sets:add_element(V, BoolVars));
{false,_} ->
- is_safe_bool_expr_1(B, Sub, BoolVars)
+ is_safe_bool_expr_1(B, BoolVars)
end;
false -> false
end;
-is_safe_bool_expr_1(#c_literal{val=Val}, _Sub, _) ->
+is_safe_bool_expr_1(#c_literal{val=Val}, _BoolVars) ->
is_boolean(Val);
-is_safe_bool_expr_1(#c_var{name=V}, _Sub, BoolVars) ->
+is_safe_bool_expr_1(#c_var{name=V}, BoolVars) ->
cerl_sets:is_element(V, BoolVars);
-is_safe_bool_expr_1(_, _, _) -> false.
+is_safe_bool_expr_1(_, _) -> false.
-is_safe_bool_expr_list([C|Cs], Sub, BoolVars) ->
- case is_safe_bool_expr_1(C, Sub, BoolVars) of
- true -> is_safe_bool_expr_list(Cs, Sub, BoolVars);
+is_safe_bool_expr_list([C|Cs], BoolVars) ->
+ case is_safe_bool_expr_1(C, BoolVars) of
+ true -> is_safe_bool_expr_list(Cs, BoolVars);
false -> false
end;
-is_safe_bool_expr_list([], _, _) -> true.
+is_safe_bool_expr_list([], _) -> true.
%% simplify_let(Let, Sub) -> Expr | impossible
%% If the argument part of an let contains a complex expression, such
@@ -2705,19 +2520,6 @@ delay_build_expr_1(#c_case{clauses=Cs0}=Case, TypeSig) ->
delay_build_expr_1(#c_let{body=B0}=Let, TypeSig) ->
B = delay_build_expr(B0, TypeSig),
Let#c_let{body=B};
-delay_build_expr_1(#c_receive{clauses=Cs0,
- timeout=Timeout,
- action=A0}=Rec, TypeSig) ->
- Cs = delay_build_cs(Cs0, TypeSig),
- A = case {Timeout,A0} of
- {#c_literal{val=infinity},#c_literal{}} ->
- {_Type,Arity} = TypeSig,
- Es = lists:duplicate(Arity, A0),
- core_lib:make_values(Es);
- _ ->
- delay_build_expr(A0, TypeSig)
- end,
- Rec#c_receive{clauses=Cs,action=A};
delay_build_expr_1(#c_seq{body=B0}=Seq, TypeSig) ->
B = delay_build_expr(B0, TypeSig),
Seq#c_seq{body=B};
@@ -2770,32 +2572,24 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Sub) ->
{[],#c_values{es=[]},_} ->
%% No variables left.
Body;
- {[#c_var{name=V}=Var|Vars]=Vars0,Arg1,Body} ->
+ {[#c_var{name=V}=Var]=Vars0,Arg1,Body} ->
case core_lib:is_var_used(V, Body) of
- false when Vars =:= [] ->
+ false ->
%% If the variable is not used in the body, we can
%% rewrite the let to a sequence:
%% let <Var> = Arg in BodyWithoutVar ==>
%% seq Arg BodyWithoutVar
Arg = maybe_suppress_warnings(Arg1, Var, PrevBody),
#c_seq{arg=Arg,body=Body};
- false ->
- %% There are multiple values returned by the argument
- %% and the first value is not used (this is a 'case'
- %% with exported variables, but the return value is
- %% ignored). We can remove the first variable and the
- %% the first value returned from the 'let' argument.
- Arg2 = remove_first_value(Arg1, Sub),
- Let1 = Let0#c_let{vars=Vars,arg=Arg2,body=Body},
- post_opt_let(Let1, Sub);
true ->
Let1 = Let0#c_let{vars=Vars0,arg=Arg1,body=Body},
post_opt_let(Let1, Sub)
end;
- {[],Arg,Body} ->
+ {_,_,_} ->
%% The argument for a sequence must be a single value (not
%% #c_values{}). Therefore, we must keep the let.
- post_opt_let(#c_let{vars=[],arg=Arg,body=Body}, Sub)
+ Let1 = Let0#c_let{vars=Vs0,arg=Arg0,body=Body},
+ post_opt_let(Let1, Sub)
end.
%% post_opt_let(Let, Sub)
@@ -2808,39 +2602,6 @@ post_opt_let(Let0, Sub) ->
Let1 = opt_bool_case_in_let(Let0, Sub),
opt_build_stacktrace(Let1).
-
-%% remove_first_value(Core0, Sub) -> Core.
-%% Core0 is an expression that returns at least two values.
-%% Remove the first value returned from Core0.
-
-remove_first_value(#c_values{es=[V|Vs]}, Sub) ->
- Values = core_lib:make_values(Vs),
- case is_safe_simple(V, Sub) of
- false ->
- #c_seq{arg=V,body=Values};
- true ->
- Values
- end;
-remove_first_value(#c_case{clauses=Cs0}=Core, Sub) ->
- Cs = remove_first_value_cs(Cs0, Sub),
- Core#c_case{clauses=Cs};
-remove_first_value(#c_receive{clauses=Cs0,action=Act0}=Core, Sub) ->
- Cs = remove_first_value_cs(Cs0, Sub),
- Act = remove_first_value(Act0, Sub),
- Core#c_receive{clauses=Cs,action=Act};
-remove_first_value(#c_let{body=B}=Core, Sub) ->
- Core#c_let{body=remove_first_value(B, Sub)};
-remove_first_value(#c_seq{body=B}=Core, Sub) ->
- Core#c_seq{body=remove_first_value(B, Sub)};
-remove_first_value(#c_primop{}=Core, _Sub) ->
- Core;
-remove_first_value(#c_call{}=Core, _Sub) ->
- Core.
-
-remove_first_value_cs(Cs, Sub) ->
- [C#c_clause{body=remove_first_value(B, Sub)} ||
- #c_clause{body=B}=C <- Cs].
-
%% maybe_suppress_warnings(Arg, #c_var{}, PreviousBody) -> Arg'
%% Try to suppress false warnings when a variable is not used.
%% For instance, we don't expect a warning for useless building in:
@@ -2966,54 +2727,6 @@ move_case_into_arg(Expr, _) ->
Expr.
%%%
-%%% Retrieving information about types.
-%%%
-
--spec get_type(cerl:cerl(), #sub{}) -> type_info() | 'none'.
-
-get_type(#c_var{name=V}, #sub{t=Tdb}) ->
- case Tdb of
- #{V:=Type} -> Type;
- _ -> none
- end;
-get_type(C, _) ->
- case cerl:type(C) of
- binary -> C;
- map -> C;
- _ ->
- case cerl:is_data(C) of
- true -> C;
- false -> none
- end
- end.
-
--spec is_boolean_type(cerl:cerl(), sub()) -> yes_no_maybe().
-
-is_boolean_type(Var, Sub) ->
- case get_type(Var, Sub) of
- none ->
- maybe;
- bool ->
- yes;
- C ->
- B = cerl:is_c_atom(C) andalso
- is_boolean(cerl:atom_val(C)),
- yes_no(B)
- end.
-
--spec is_int_type(cerl:cerl(), sub()) -> yes_no_maybe().
-
-is_int_type(Var, Sub) ->
- case get_type(Var, Sub) of
- none -> maybe;
- integer -> yes;
- C -> yes_no(cerl:is_c_int(C))
- end.
-
-yes_no(true) -> yes;
-yes_no(false) -> no.
-
-%%%
%%% Update type information.
%%%
@@ -3024,70 +2737,14 @@ update_let_types(_Vs, _Arg, Sub) ->
%% that returns multiple values.
Sub.
-update_let_types_1([#c_var{}=V|Vs], [A|As], Sub0) ->
- Sub = update_types_from_expr(V, A, Sub0),
+update_let_types_1([#c_var{name=V}|Vs], [A|As], Sub0) ->
+ Sub = update_types(V, A, Sub0),
update_let_types_1(Vs, As, Sub);
update_let_types_1([], [], Sub) -> Sub.
-update_types_from_expr(V, Expr, Sub) ->
- Type = extract_type(Expr, Sub),
- update_types(V, [Type], Sub).
-
-extract_type(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=Name},
- args=Args}=Call, Sub) ->
- case returns_integer(Name, Args) of
- true -> integer;
- false -> extract_type_1(Call, Sub)
- end;
-extract_type(Expr, Sub) ->
- extract_type_1(Expr, Sub).
-
-extract_type_1(Expr, Sub) ->
- case is_bool_expr(Expr, Sub) of
- false -> Expr;
- true -> bool
- end.
-
-returns_integer('band', [_,_]) -> true;
-returns_integer('bnot', [_]) -> true;
-returns_integer('bor', [_,_]) -> true;
-returns_integer('bxor', [_,_]) -> true;
-returns_integer(bit_size, [_]) -> true;
-returns_integer('bsl', [_,_]) -> true;
-returns_integer('bsr', [_,_]) -> true;
-returns_integer(byte_size, [_]) -> true;
-returns_integer(ceil, [_]) -> true;
-returns_integer('div', [_,_]) -> true;
-returns_integer(floor, [_]) -> true;
-returns_integer(length, [_]) -> true;
-returns_integer('rem', [_,_]) -> true;
-returns_integer('round', [_]) -> true;
-returns_integer(size, [_]) -> true;
-returns_integer(tuple_size, [_]) -> true;
-returns_integer(trunc, [_]) -> true;
-returns_integer(_, _) -> false.
-
-%% update_types(Expr, Pattern, Sub) -> Sub'
-%% Update the type database.
-
--spec update_types(cerl:c_var(), [type_info()], sub()) -> sub().
-
-update_types(#c_var{name=V}, Pat, #sub{t=Tdb0}=Sub) ->
- Tdb = update_types_1(V, Pat, Tdb0),
- Sub#sub{t=Tdb}.
-
-update_types_1(V, [#c_tuple{}=P], Types) ->
- Types#{V=>P};
-update_types_1(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) ->
- Types#{V=>bool};
-update_types_1(V, [#c_fun{vars=Vars}], Types) ->
- Types#{V=>{'fun',length(Vars)}};
-update_types_1(V, [#c_var{name={_,Arity}}], Types) ->
- Types#{V=>{'fun',Arity}};
-update_types_1(V, [Type], Types) when is_atom(Type) ->
- Types#{V=>Type};
-update_types_1(_, _, Types) -> Types.
+update_types(V, #c_tuple{}=P, #sub{t=Tdb}=Sub) ->
+ Sub#sub{t=Tdb#{V=>P}};
+update_types(_, _, Sub) -> Sub.
%% kill_types(V, Tdb) -> Tdb'
%% Kill any entries that references the variable,
@@ -3103,10 +2760,6 @@ kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) ->
false -> [Entry|kill_types2(V, Tdb)];
true -> kill_types2(V, Tdb)
end;
-kill_types2(V, [{_, {'fun',_}}=Entry|Tdb]) ->
- [Entry|kill_types2(V, Tdb)];
-kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) ->
- [Entry|kill_types2(V, Tdb)];
kill_types2(_, []) -> [].
%% copy_type(DestVar, SrcVar, Tdb) -> Tdb'
@@ -3198,6 +2851,13 @@ format_error({embedded_unit,Unit,Size}) ->
format_error(bad_unicode) ->
"binary construction will fail with a 'badarg' exception "
"(invalid Unicode code point in a utf8/utf16/utf32 segment)";
+format_error(bad_float_size) ->
+ "binary construction will fail with a 'badarg' exception "
+ "(invalid size for a float segment)";
+format_error({nomatch_shadow,Line,{Name, Arity}}) ->
+ M = io_lib:format("this clause for ~ts/~B cannot match because a previous "
+ "clause at line ~p always matches", [Name, Arity, Line]),
+ flatten(M);
format_error({nomatch_shadow,Line}) ->
M = io_lib:format("this clause cannot match because a previous clause at line ~p "
"always matches", [Line]),
diff --git a/lib/compiler/src/sys_core_fold_lists.erl b/lib/compiler/src/sys_core_fold_lists.erl
index e93b435011..ab5fcb3da4 100644
--- a/lib/compiler/src/sys_core_fold_lists.erl
+++ b/lib/compiler/src/sys_core_fold_lists.erl
@@ -56,9 +56,8 @@ call(#c_call{anno=Anno}, lists, all, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=true}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -90,9 +89,8 @@ call(#c_call{anno=Anno}, lists, any, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=false}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^any',1}}|Anno], Err2)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -113,9 +111,8 @@ call(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=ok}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -143,9 +140,8 @@ call(#c_call{anno=Anno}, lists, map, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^map',1}}|Anno], Err)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -174,9 +170,8 @@ call(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -213,10 +208,9 @@ call(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{anno=Anno,
pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)},
+ body=function_clause(Anno, [F, Xs])},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -241,9 +235,8 @@ call(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
body=A},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)},
+ body=function_clause(Anno, [F, A, Xs])},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -268,9 +261,8 @@ call(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3]) ->
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
body=A},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)},
+ body=function_clause(Anno, [F, A, Xs])},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -321,9 +313,8 @@ call(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3]) ->
es=[#c_literal{val=[]}, Avar]}},
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)},
+ body=function_clause(Anno, [F, Avar, Xs])},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -382,9 +373,8 @@ call(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3]) ->
es=[#c_literal{val=[]}, Avar]}},
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
- body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)},
+ body=function_clause(Anno, [F, Avar, Xs])},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -406,3 +396,9 @@ match_fail(Ann, Arg) ->
Name = cerl:abstract(match_fail),
Args = [Arg],
cerl:ann_c_primop(Ann, Name, Args).
+
+function_clause(Anno, Args) ->
+ #c_call{anno=Anno,
+ module=#c_literal{val=erlang},
+ name=#c_literal{val=error},
+ args=[#c_literal{val=function_clause},cerl:ann_make_list(Anno, Args)]}.
diff --git a/lib/compiler/src/sys_core_inline.erl b/lib/compiler/src/sys_core_inline.erl
index 3380e3f1bd..f8d26d1c5d 100644
--- a/lib/compiler/src/sys_core_inline.erl
+++ b/lib/compiler/src/sys_core_inline.erl
@@ -44,7 +44,7 @@
-export([module/2]).
--import(lists, [member/2,map/2,foldl/3,mapfoldl/3,keydelete/3]).
+-import(lists, [member/2,map/2,foldl/3,mapfoldl/3]).
-include("core_parse.hrl").
@@ -116,14 +116,11 @@ inline(Fs0, St0) ->
false -> {Fst,Ifs}
end
end, [], Fs1),
- Is1 = map(fun (#ifun{body=B}=If) ->
- If#ifun{body=cerl_trees:map(match_fail_fun(), B)}
- end, Is0),
- Is2 = [inline_inline(If, Is1) || If <- Is1],
+ Is1 = [inline_inline(If, Is0) || If <- Is0],
%% We would like to remove inlined, non-exported functions here,
%% but this can be difficult as they may be recursive.
%% Use fixed inline functions on all functions.
- Fs = [inline_func(F, Is2) || F <- Fs2],
+ Fs = [inline_func(F, Is1) || F <- Fs2],
%% Regenerate module body.
[Def || #fstat{def=Def} <- Fs].
@@ -172,17 +169,6 @@ inline_func(#fstat{def={Name,F0}}=Fstat, Is) ->
weight_func(_Core, Acc) -> Acc + 1.
-%% match_fail_fun() -> fun/1.
-%% Return a function to use with map to fix inlineable functions
-%% function_clause match_fail (if they have one).
-
-match_fail_fun() ->
- fun (#c_primop{anno=Anno0,name=#c_literal{val=match_fail}}=P) ->
- Anno = keydelete(function_name, 1, Anno0),
- P#c_primop{anno=Anno};
- (Other) -> Other
- end.
-
%% find_inl(Func, Arity, [Inline]) -> #ifun{} | no.
find_inl(F, A, [#ifun{func=F,arity=A}=If|_]) -> If;
diff --git a/lib/compiler/src/sys_core_prepare.erl b/lib/compiler/src/sys_core_prepare.erl
new file mode 100644
index 0000000000..5d9954e04f
--- /dev/null
+++ b/lib/compiler/src/sys_core_prepare.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Purpose: Prepare Core Erlang not generated by v3_core.
+
+-module(sys_core_prepare).
+-export([module/2]).
+
+-include("core_parse.hrl").
+
+-spec module(cerl:c_module(), [compile:option()]) ->
+ {'ok',cerl:c_module(),[]}.
+
+module(Mod0, _Opts) ->
+ Count = cerl_trees:next_free_variable_name(Mod0),
+ {Mod,_} = cerl_trees:mapfold(fun rewrite_recv/2, Count, Mod0),
+ {ok,Mod,[]}.
+
+rewrite_recv(#c_receive{clauses=[],timeout=Timeout0,action=Action}, Count0) ->
+ %% Lower a receive with only an after blcok to its primitive operations.
+ False = #c_literal{val=false},
+ True = #c_literal{val=true},
+
+ {TimeoutVal,Count1} = new_var(Count0),
+ {LoopName,Count2} = new_func_varname(Count1),
+ LoopFun = #c_var{name={LoopName,0}},
+ ApplyLoop = #c_apply{op=LoopFun,args=[]},
+
+ TimeoutCs = [#c_clause{pats=[True],guard=True,
+ body=#c_seq{arg=primop(timeout),
+ body=Action}},
+ #c_clause{pats=[False],guard=True,
+ body=ApplyLoop}],
+ {TimeoutBool,Count4} = new_var(Count2),
+ TimeoutCase = #c_case{arg=TimeoutBool,clauses=TimeoutCs},
+ TimeoutLet = #c_let{vars=[TimeoutBool],
+ arg=primop(recv_wait_timeout, [TimeoutVal]),
+ body=TimeoutCase},
+
+ Fun = #c_fun{vars=[],body=TimeoutLet},
+
+ Letrec = #c_letrec{anno=[letrec_goto],
+ defs=[{LoopFun,Fun}],
+ body=ApplyLoop},
+
+ OuterLet = #c_let{vars=[TimeoutVal],arg=Timeout0,body=Letrec},
+ {OuterLet,Count4};
+rewrite_recv(#c_receive{clauses=Cs0,timeout=Timeout0,action=Action}, Count0) ->
+ %% Lower receive to its primitive operations.
+ False = #c_literal{val=false},
+ True = #c_literal{val=true},
+
+ {TimeoutVal,Count1} = new_var(Count0),
+ {LoopName,Count2} = new_func_varname(Count1),
+ LoopFun = #c_var{name={LoopName,0}},
+ ApplyLoop = #c_apply{op=LoopFun,args=[]},
+
+ Cs1 = rewrite_cs(Cs0),
+ RecvNext = #c_seq{arg=primop(recv_next),
+ body=ApplyLoop},
+ RecvNextC = #c_clause{anno=[compiler_generated],
+ pats=[#c_var{name='Other'}],guard=True,body=RecvNext},
+ Cs = Cs1 ++ [RecvNextC],
+ {Msg,Count3} = new_var(Count2),
+ MsgCase = #c_case{arg=Msg,clauses=Cs},
+
+ TimeoutCs = [#c_clause{pats=[True],guard=True,
+ body=#c_seq{arg=primop(timeout),
+ body=Action}},
+ #c_clause{pats=[False],guard=True,
+ body=ApplyLoop}],
+ {TimeoutBool,Count4} = new_var(Count3),
+ TimeoutCase = #c_case{arg=TimeoutBool,clauses=TimeoutCs},
+ TimeoutLet = #c_let{vars=[TimeoutBool],
+ arg=primop(recv_wait_timeout, [TimeoutVal]),
+ body=TimeoutCase},
+
+ {PeekSucceeded,Count5} = new_var(Count4),
+ PeekCs = [#c_clause{pats=[True],guard=True,
+ body=MsgCase},
+ #c_clause{pats=[False],guard=True,
+ body=TimeoutLet}],
+ PeekCase = #c_case{arg=PeekSucceeded,clauses=PeekCs},
+ PeekLet = #c_let{vars=[PeekSucceeded,Msg],
+ arg=primop(recv_peek_message),
+ body=PeekCase},
+ Fun = #c_fun{vars=[],body=PeekLet},
+
+ Letrec = #c_letrec{anno=[letrec_goto],
+ defs=[{LoopFun,Fun}],
+ body=ApplyLoop},
+
+ OuterLet = #c_let{vars=[TimeoutVal],arg=Timeout0,body=Letrec},
+ {OuterLet,Count5};
+rewrite_recv(Tree, Count) ->
+ {Tree,Count}.
+
+rewrite_cs([#c_clause{body=B0}=C|Cs]) ->
+ B = #c_seq{arg=primop(remove_message),body=B0},
+ [C#c_clause{body=B}|rewrite_cs(Cs)];
+rewrite_cs([]) -> [].
+
+primop(Name) ->
+ primop(Name, []).
+
+primop(Name, Args) ->
+ #c_primop{name=#c_literal{val=Name},args=Args}.
+
+new_var(Count) ->
+ {#c_var{name=Count},Count+1}.
+
+new_func_varname(Count) ->
+ Name = list_to_atom("@pre" ++ integer_to_list(Count)),
+ {Name,Count+1}.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 007a0247f4..0efc8f7821 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -22,7 +22,7 @@
%% At this stage all preprocessing has been done. All that is left are
%% "pure" Erlang functions.
%%
-%% Core transformation is done in three stages:
+%% Core transformation is done in four stages:
%%
%% 1. Flatten expressions into an internal core form without doing
%% matching.
@@ -37,6 +37,12 @@
%% annotations to change implicit exported variables to explicit
%% returns.
%%
+%% 4. Lower receives to more primitive operations. Split binary
+%% patterns where a value is matched out and then used used as
+%% a size in the same pattern. That simplifies the subsequent
+%% passes as all variables are within a single pattern are either
+%% new or used, but never both at the same time.
+%%
%% To ensure the evaluation order we ensure that all arguments are
%% safe. A "safe" is basically a core_lib simple with VERY restricted
%% binaries.
@@ -91,13 +97,16 @@
-record(iapply, {anno=#a{},op,args}).
-record(ibinary, {anno=#a{},segments}). %Not used in patterns.
+-record(ibitstr, {anno=#a{},val,size,unit,type,flags}).
-record(icall, {anno=#a{},module,name,args}).
-record(icase, {anno=#a{},args,clauses,fc}).
-record(icatch, {anno=#a{},body}).
--record(iclause, {anno=#a{},pats,pguard=[],guard,body}).
+-record(iclause, {anno=#a{},pats,guard,body}).
-record(ifun, {anno=#a{},id,vars,clauses,fc,name=unnamed}).
-record(iletrec, {anno=#a{},defs,body}).
-record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
+-record(imap, {anno=#a{},arg=#c_literal{val=#{}},es,is_pat=false}).
+-record(imappair, {anno=#a{},op,key,val}).
-record(iprimop, {anno=#a{},name,args}).
-record(iprotect, {anno=#a{},body}).
-record(ireceive1, {anno=#a{},clauses}).
@@ -105,7 +114,7 @@
-record(iset, {anno=#a{},var,arg}).
-record(itry, {anno=#a{},args,vars,body,evars,handler}).
-record(ifilter, {anno=#a{},arg}).
--record(igen, {anno=#a{},ceps=[],acc_pat,acc_guard,
+-record(igen, {anno=#a{},acc_pat,acc_guard,
skip_pat,tail,tail_pat,arg}).
-record(isimple, {anno=#a{},term :: cerl:cerl()}).
@@ -118,6 +127,7 @@
-type ifun() :: #ifun{}.
-type iletrec() :: #iletrec{}.
-type imatch() :: #imatch{}.
+-type imap() :: #imap{}.
-type iprimop() :: #iprimop{}.
-type iprotect() :: #iprotect{}.
-type ireceive1() :: #ireceive1{}.
@@ -128,19 +138,22 @@
-type igen() :: #igen{}.
-type isimple() :: #isimple{}.
--type i() :: iapply() | ibinary() | icall() | icase() | icatch()
- | iclause() | ifun() | iletrec() | imatch() | iprimop()
- | iprotect() | ireceive1() | ireceive2() | iset() | itry()
- | ifilter() | igen() | isimple().
+-type i() :: iapply() | ibinary() | icall() | icase() | icatch()
+ | iclause() | ifun() | iletrec() | imatch() | imap()
+ | iprimop() | iprotect() | ireceive1() | ireceive2()
+ | iset() | itry() | ifilter()
+ | igen() | isimple().
-type warning() :: {file:filename(), [{integer(), module(), term()}]}.
-record(core, {vcount=0 :: non_neg_integer(), %Variable counter
fcount=0 :: non_neg_integer(), %Function counter
+ gcount=0 :: non_neg_integer(), %Goto counter
function={none,0} :: fa(), %Current function.
in_guard=false :: boolean(), %In guard or not.
wanted=true :: boolean(), %Result wanted or not.
- opts :: [compile:option()], %Options.
+ opts=[] :: [compile:option()], %Options.
+ dialyzer=false :: boolean(), %Help dialyzer or not.
ws=[] :: [warning()], %Warnings.
file=[{file,""}] %File.
}).
@@ -216,69 +229,61 @@ defined_functions(Forms) ->
%% ok.
function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) ->
- St0 = #core{vcount=0,function={Name,Arity},opts=Opts,
- ws=Ws0,file=[{file,File}]},
- {B0,St1} = body(Cs0, Name, Arity, St0),
- %% ok = function_dump(Name,Arity,"body:~n~p~n",[B0]),
- {B1,St2} = ubody(B0, St1),
- %% ok = function_dump(Name,Arity,"ubody:~n~p~n",[B1]),
- {B2,#core{ws=Ws}} = cbody(B1, St2),
- %% ok = function_dump(Name,Arity,"cbody:~n~p~n",[B2]),
- {{#c_var{name={Name,Arity}},B2},Ws}.
+ try
+ St0 = #core{vcount=0,function={Name,Arity},opts=Opts,
+ dialyzer=member(dialyzer, Opts),
+ ws=Ws0,file=[{file,File}]},
+ {B0,St1} = body(Cs0, Name, Arity, St0),
+ %% ok = function_dump(Name, Arity, "body:~n~p~n",[B0]),
+ {B1,St2} = ubody(B0, St1),
+ %% ok = function_dump(Name, Arity, "ubody:~n~p~n",[B1]),
+ {B2,St3} = cbody(B1, St2),
+ %% ok = function_dump(Name, Arity, "cbody:~n~p~n",[B2]),
+ {B3,#core{ws=Ws}} = lbody(B2, St3),
+ %% ok = function_dump(Name, Arity, "lbody:~n~p~n",[B3]),
+ {{#c_var{name={Name,Arity}},B3},Ws}
+ catch
+ Class:Error:Stack ->
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
body(Cs0, Name, Arity, St0) ->
Anno = lineno_anno(element(2, hd(Cs0)), St0),
+ FunAnno = [{function,{Name,Arity}} | Anno],
{Args0,St1} = new_vars(Anno, Arity, St0),
Args = reverse(Args0), %Nicer order
- case clauses(Cs0, St1) of
- {Cs1,[],St2} ->
- {Ps,St3} = new_vars(Arity, St2), %Need new variables here
- Fc = function_clause(Ps, Anno, {Name,Arity}),
- {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3};
- {Cs1,Eps,St2} ->
- %% We have pre-expressions from patterns and
- %% these needs to be letified before matching
- %% since only bound variables are allowed
- AnnoGen = #a{anno=[compiler_generated]},
- {Ps1,St3} = new_vars(Arity, St2), %Need new variables here
- Fc1 = function_clause(Ps1, Anno, {Name,Arity}),
- {Ps2,St4} = new_vars(Arity, St3), %Need new variables here
- Fc2 = function_clause(Ps2, Anno, {Name,Arity}),
- Case = #icase{anno=AnnoGen,args=Args,
- clauses=Cs1,
- fc=Fc2},
- {#ifun{anno=#a{anno=Anno},id=[],vars=Args,
- clauses=[#iclause{anno=AnnoGen,pats=Ps1,
- guard=[#c_literal{val=true}],
- body=Eps ++ [Case]}],
- fc=Fc1},St4}
- end.
+ {Cs1,St2} = clauses(Cs0, St1),
+ {Ps,St3} = new_vars(Arity, St2), %Need new variables here
+ Fc = function_clause(Ps, Anno),
+ {#ifun{anno=#a{anno=FunAnno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.
%% clause(Clause, State) -> {Cclause,State} | noclause.
%% clauses([Clause], State) -> {[Cclause],State}.
%% Convert clauses. Trap bad pattern aliases and remove clause from
%% clause list.
-clauses([C0|Cs0],St0) ->
+clauses([C0|Cs0], St0) ->
case clause(C0, St0) of
- {noclause,_,St} -> clauses(Cs0,St);
- {C,Eps1,St1} ->
- {Cs,Eps2,St2} = clauses(Cs0, St1),
- {[C|Cs],Eps1++Eps2,St2}
+ {noclause,St} ->
+ clauses(Cs0, St);
+ {C,St1} ->
+ {Cs,St2} = clauses(Cs0, St1),
+ {[C|Cs],St2}
end;
-clauses([],St) -> {[],[],St}.
+clauses([], St) -> {[],St}.
clause({clause,Lc,H0,G0,B0}, St0) ->
try head(H0, St0) of
- {H1,Eps,St1} ->
+ {H1,St1} ->
{G1,St2} = guard(G0, St1),
{B1,St3} = exprs(B0, St2),
Anno = lineno_anno(Lc, St3),
- {#iclause{anno=#a{anno=Anno},pats=H1,guard=G1,body=B1},Eps,St3}
+ {#iclause{anno=#a{anno=Anno},pats=H1,guard=G1,body=B1},St3}
catch
throw:nomatch ->
St = add_warning(Lc, nomatch, St0),
- {noclause,[],St} %Bad pattern
+ {noclause,St} %Bad pattern
end.
clause_arity({clause,_,H0,_,_}) -> length(H0).
@@ -419,9 +424,21 @@ gexpr_test(E0, Bools0, St0) ->
{E1,Eps0,St1} = expr(E0, St0),
%% Generate "top-level" test and argument calls.
case E1 of
+ #icall{anno=Anno,module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[_,_]} ->
+ %% is_function/2 is not a safe type test. We must force
+ %% it to be protected.
+ Lanno = Anno#a.anno,
+ {New,St2} = new_var(Lanno, St1),
+ {icall_eq_true(New),
+ Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools0,St2};
#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
+ %% Note that erl_expand_records has renamed type
+ %% tests to the new names; thus, float/1 as a type
+ %% test will now be named is_float/1.
Ar = length(As),
- case erl_internal:type_test(N, Ar) orelse
+ case erl_internal:new_type_test(N, Ar) orelse
erl_internal:comp_op(N, Ar) orelse
erl_internal:bool_op(N, Ar) of
true -> {E1,Eps0,Bools0,St1};
@@ -586,14 +603,14 @@ expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, full_anno(L, St0), St0) of
{_,_,_}=Res -> Res
catch
- throw:bad_binary ->
- St = add_warning(L, bad_binary, St0),
+ throw:{bad_binary,Eps,St1} ->
+ St = add_warning(L, bad_binary, St1),
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}
+ args=As},Eps,St}
end;
expr({block,_,Es0}, St0) ->
%% Inline the block directly.
@@ -601,26 +618,26 @@ expr({block,_,Es0}, St0) ->
{E1,Eps,St2} = expr(last(Es0), St1),
{E1,Es1 ++ Eps,St2};
expr({'if',L,Cs0}, St0) ->
- {Cs1,Ceps,St1} = clauses(Cs0, St0),
+ {Cs1,St1} = clauses(Cs0, St0),
Lanno = lineno_anno(L, St1),
Fc = fail_clause([], Lanno, #c_literal{val=if_clause}),
- {#icase{anno=#a{anno=Lanno},args=[],clauses=Cs1,fc=Fc},Ceps,St1};
+ {#icase{anno=#a{anno=Lanno},args=[],clauses=Cs1,fc=Fc},[],St1};
expr({'case',L,E0,Cs0}, St0) ->
{E1,Eps,St1} = novars(E0, St0),
- {Cs1,Ceps,St2} = clauses(Cs0, St1),
+ {Cs1,St2} = clauses(Cs0, St1),
{Fpat,St3} = new_var(St2),
Lanno = lineno_anno(L, St2),
Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=case_clause},Fpat])),
- {#icase{anno=#a{anno=Lanno},args=[E1],clauses=Cs1,fc=Fc},Eps++Ceps,St3};
+ {#icase{anno=#a{anno=Lanno},args=[E1],clauses=Cs1,fc=Fc},Eps,St3};
expr({'receive',L,Cs0}, St0) ->
- {Cs1,Ceps,St1} = clauses(Cs0, St0),
- {#ireceive1{anno=#a{anno=lineno_anno(L, St1)},clauses=Cs1},Ceps, St1};
+ {Cs1,St1} = clauses(Cs0, St0),
+ {#ireceive1{anno=#a{anno=lineno_anno(L, St1)},clauses=Cs1},[],St1};
expr({'receive',L,Cs0,Te0,Tes0}, St0) ->
{Te1,Teps,St1} = novars(Te0, St0),
{Tes1,St2} = exprs(Tes0, St1),
- {Cs1,Ceps,St3} = clauses(Cs0, St2),
+ {Cs1,St3} = clauses(Cs0, St2),
{#ireceive2{anno=#a{anno=lineno_anno(L, St3)},
- clauses=Cs1,timeout=Te1,action=Tes1},Teps++Ceps,St3};
+ clauses=Cs1,timeout=Te1,action=Tes1},Teps,St3};
expr({'try',L,Es0,[],Ecs,[]}, St0) ->
%% 'try ... catch ... end'
{Es1,St1} = exprs(Es0, St0),
@@ -634,7 +651,7 @@ expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->
%% 'try ... of ... catch ... end'
{Es1,St1} = exprs(Es0, St0),
{V,St2} = new_var(St1), %This name should be arbitrary
- {Cs1,Ceps,St3} = clauses(Cs0, St2),
+ {Cs1,St3} = clauses(Cs0, St2),
{Fpat,St4} = new_var(St3),
Lanno = lineno_anno(L, St4),
Fc = fail_clause([Fpat], Lanno,
@@ -643,7 +660,7 @@ expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->
{#itry{anno=#a{anno=lineno_anno(L, St5)},args=Es1,
vars=[V],body=[#icase{anno=#a{anno=Lanno},args=[V],clauses=Cs1,fc=Fc}],
evars=Evs,handler=Hs},
- Ceps,St5};
+ [],St5};
expr({'try',L,Es0,[],[],As0}, St0) ->
%% 'try ... after ... end'
{Es1,St1} = exprs(Es0, St0),
@@ -652,7 +669,7 @@ expr({'try',L,Es0,[],[],As0}, St0) ->
{V,St4} = new_var(St3), % (must not exist in As1)
LA = lineno_anno(L, St4),
Lanno = #a{anno=LA},
- Fc = function_clause([], LA, {Name,0}),
+ Fc = function_clause([], LA),
Fun = #ifun{anno=Lanno,id=[],vars=[],
clauses=[#iclause{anno=Lanno,pats=[],
guard=[#c_literal{val=true}],
@@ -699,7 +716,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_fun(length(As0), FunExp, St0),
+ {Fun,Fps,St1} = safe(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};
@@ -712,12 +729,12 @@ expr({match,L,P0,E0}, St0) ->
end,
{E2,Eps1,St2} = novars(E1, St1),
St3 = St2#core{wanted=St0#core.wanted},
- {P2,Eps2,St4} = try
- pattern(P1, St3)
- catch
- throw:Thrown ->
- {Thrown,[],St3}
- end,
+ {P2,St4} = try
+ pattern(P1, St3)
+ catch
+ throw:Thrown ->
+ {Thrown,St3}
+ end,
{Fpat,St5} = new_var(St4),
Lanno = lineno_anno(L, St5),
Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),
@@ -746,15 +763,15 @@ expr({match,L,P0,E0}, St0) ->
St6 = add_warning(L, nomatch, St5),
{Expr,Eps3,St7} = safe(E1, St6),
SanPat0 = sanitize(P1),
- {SanPat,Eps4,St} = pattern(SanPat0, St7),
+ {SanPat,St} = pattern(SanPat0, St7),
Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),
Fail = #iprimop{anno=#a{anno=Lanno},
name=#c_literal{val=match_fail},
args=[Badmatch]},
- Eps = Eps3 ++ Eps4 ++ [Fail],
+ Eps = Eps3 ++ [Fail],
{#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St};
Other when not is_atom(Other) ->
- {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5}
+ {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1,St5}
end;
expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
%% Optimise '++' here because of the list comprehension algorithm.
@@ -820,6 +837,8 @@ sanitize({tuple,L,Ps0}) ->
sanitize({map,L,Ps0}) ->
Ps = [sanitize(V) || {map_field_exact,_,_,V} <- Ps0],
{tuple,L,Ps};
+sanitize({op,L,_Name,P1,P2}) ->
+ {tuple,L,[sanitize(P1),sanitize(P2)]};
sanitize(P) -> P.
make_bool_switch(L, E, V, T, F, #core{in_guard=true}) ->
@@ -924,7 +943,7 @@ is_valid_map_src(_) -> false.
try_exception(Ecs0, St0) ->
%% Note that Tag is not needed for rethrow - it is already in Info.
{Evs,St1} = new_vars(3, St0), % Tag, Value, Info
- {Ecs1,Ceps,St2} = clauses(Ecs0, St1),
+ {Ecs1,St2} = clauses(Ecs0, St1),
Ecs2 = try_build_stacktrace(Ecs1, hd(Evs)),
[_,Value,Info] = Evs,
LA = case Ecs2 of
@@ -937,7 +956,7 @@ try_exception(Ecs0, St0) ->
name=#c_literal{val=raise},
args=[Info,Value]}]},
Hs = [#icase{anno=#a{anno=LA},args=[c_tuple(Evs)],clauses=Ecs2,fc=Ec}],
- {Evs,Ceps++Hs,St2}.
+ {Evs,Hs,St2}.
try_after(As, St0) ->
%% See above.
@@ -992,14 +1011,30 @@ bin_element({bin_element,Line,Expr,Size0,Type0}) ->
make_bit_type(Line, default, Type0) ->
case erl_bits:set_bit_type(default, Type0) of
- {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)};
+ {ok,all,Bt} -> {make_all_size(Line),erl_bits:as_list(Bt)};
{ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)};
{ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)}
end;
+make_bit_type(_Line, {atom,Anno,all}=Size, Type0) ->
+ case erl_anno:generated(Anno) of
+ true ->
+ %% This `all` was created by the compiler from a binary
+ %% segment without a size.
+ {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
+ {Size,erl_bits:as_list(Bt)};
+ false ->
+ %% This `all` was present in the source code. It is not
+ %% a valid size.
+ throw(nomatch)
+ end;
make_bit_type(_Line, Size, Type0) -> %Integer or 'all'
{ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
{Size,erl_bits:as_list(Bt)}.
+make_all_size(Line) ->
+ Anno = erl_anno:set_generated(true, Line),
+ {atom,Anno,all}.
+
%% constant_bin([{bin_element,_,_,_,_}]) -> binary() | error
%% If the binary construction is truly constant (no variables,
%% no native fields), and does not contain fields whose expansion
@@ -1112,8 +1147,9 @@ expr_bin_1(Es, St) ->
end, {[],[],St}, Es).
bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
- {E1,Eps,St1} = safe(E0, St0),
- {Size1,Eps2,St2} = safe(Size0, St1),
+ {E1,Eps0,St1} = safe(E0, St0),
+ {Size1,Eps1,St2} = safe(Size0, St1),
+ Eps = Eps0 ++ Eps1,
case {Type,E1} of
{_,#c_var{}} -> ok;
{integer,#c_literal{val=I}} when is_integer(I) -> ok;
@@ -1123,41 +1159,43 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
{float,#c_literal{val=V}} when is_number(V) -> ok;
{binary,#c_literal{val=V}} when is_bitstring(V) -> ok;
{_,_} ->
- throw(bad_binary)
+ %% Note that the pre expressions may bind variables that
+ %% are used later or have side effects.
+ throw({bad_binary,Eps,St2})
end,
case Size1 of
#c_var{} -> ok;
#c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok;
#c_literal{val=undefined} -> ok;
#c_literal{val=all} -> ok;
- _ -> throw(bad_binary)
+ _ -> throw({bad_binary,Eps,St2})
end,
{#c_bitstr{val=E1,size=Size1,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
flags=#c_literal{val=Flags}},
- Eps ++ Eps2,St2}.
+ Eps,St2}.
%% fun_tq(Id, [Clauses], Line, State, NameInfo) -> {Fun,[PreExp],State}.
fun_tq(Cs0, L, St0, NameInfo) ->
Arity = clause_arity(hd(Cs0)),
- {Cs1,Ceps,St1} = clauses(Cs0, St0),
+ {Cs1,St1} = clauses(Cs0, St0),
{Args,St2} = new_vars(Arity, St1),
{Ps,St3} = new_vars(Arity, St2), %Need new variables here
Anno = full_anno(L, St3),
{Name,St4} = new_fun_name(St3),
- Fc = function_clause(Ps, Anno, {Name,Arity}),
+ Fc = function_clause(Ps, Anno),
Id = {0,0,Name},
Fun = #ifun{anno=#a{anno=Anno},
id=[{id,Id}], %We KNOW!
vars=Args,clauses=Cs1,fc=Fc,name=NameInfo},
- {Fun,Ceps,St4}.
+ {Fun,[],St4}.
%% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
%% This TQ from Simon PJ pp 127-138.
-lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,ceps=Ceps,
+lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
acc_pat=AccPat,acc_guard=AccGuard,
skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
arg={Pre,Arg}}|Qs], Mc, St0) ->
@@ -1167,7 +1205,7 @@ lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,ceps=Ceps,
F = #c_var{anno=LA,name={Name,1}},
Nc = #iapply{anno=GAnno,op=F,args=[Tail]},
{Var,St2} = new_var(St1),
- Fc = function_clause([Var], GA, {Name,1}),
+ Fc = function_clause([Var], GA),
TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]},
Cs0 = case {AccPat,AccGuard} of
{SkipPat,[]} ->
@@ -1193,7 +1231,7 @@ lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,ceps=Ceps,
Fun = #ifun{anno=GAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
{#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},defs=[{{Name,1},Fun}],
body=Pre ++ [#iapply{anno=GAnno,op=F,args=[Arg]}]},
- Ceps,St4};
+ [],St4};
lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
lc_tq(Line, E0, [], Mc0, St0) ->
@@ -1218,7 +1256,7 @@ bc_tq(Line, Exp, Qs0, St0) ->
args=[Sz]}}] ++ BcPre,
{E,Pre,St}.
-bc_tq1(Line, E, [#igen{anno=GAnno,ceps=Ceps,
+bc_tq1(Line, E, [#igen{anno=GAnno,
acc_pat=AccPat,acc_guard=AccGuard,
skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
arg={Pre,Arg}}|Qs], Mc, St0) ->
@@ -1228,7 +1266,7 @@ bc_tq1(Line, E, [#igen{anno=GAnno,ceps=Ceps,
{Vars=[_,AccVar],St2} = new_vars(LA, 2, St1),
F = #c_var{anno=LA,name={Name,2}},
Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]},
- Fc = function_clause(Vars, LA, {Name,2}),
+ Fc = function_clause(Vars, LA),
TailClause = #iclause{anno=LAnno,pats=[TailPat,AccVar],guard=[],
body=[AccVar]},
Cs0 = case {AccPat,AccGuard} of
@@ -1257,14 +1295,14 @@ bc_tq1(Line, E, [#igen{anno=GAnno,ceps=Ceps,
Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc},
{#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}],
body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]},
- Ceps,St4};
+ [],St4};
bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5);
bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
bc_tq_build(Bl, [], AccVar, Elements, St0);
bc_tq1(Line, E0, [], AccVar, St0) ->
BsFlags = [binary,{unit,1}],
- BsSize = {atom,Line,all},
+ BsSize = make_all_size(Line),
{E1,Pre0,St1} = safe(E0, St0),
case E1 of
#c_var{name=VarName} ->
@@ -1287,7 +1325,7 @@ bc_tq1(Line, E0, [], AccVar, St0) ->
end.
bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) ->
- Elements = [{bin_element,Line,{var,Line,AccVar},{atom,Line,all},
+ Elements = [{bin_element,Line,{var,Line,AccVar},make_all_size(Line),
[binary,{unit,1}]}|Elements0],
{E,Pre,St} = expr({bin,Line,Elements}, St0),
#a{anno=A} = Anno0 = get_anno(E),
@@ -1399,7 +1437,7 @@ get_qual_anno(Abstract) -> element(2, Abstract).
generator(Line, {generate,Lg,P0,E}, Gs, St0) ->
LA = lineno_anno(Line, St0),
GA = lineno_anno(Lg, St0),
- {Head,Ceps,St1} = list_gen_pattern(P0, Line, St0),
+ {Head,St1} = list_gen_pattern(P0, Line, St0),
{[Tail,Skip],St2} = new_vars(2, St1),
{Cg,St3} = lc_guard_tests(Gs, St2),
{AccPat,SkipPat} = case Head of
@@ -1419,44 +1457,55 @@ generator(Line, {generate,Lg,P0,E}, Gs, St0) ->
ann_c_cons(LA, Skip, Tail)}
end,
{Ce,Pre,St4} = safe(E, St3),
- Gen = #igen{anno=#a{anno=GA},ceps=Ceps,
+ Gen = #igen{anno=#a{anno=GA},
acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}},
{Gen,St4};
generator(Line, {b_generate,Lg,P,E}, Gs, St0) ->
LA = lineno_anno(Line, St0),
GA = lineno_anno(Lg, St0),
- {Cp = #c_binary{segments=Segs},[],St1} = pattern(P, St0),
-
- %% The function append_tail_segment/2 keeps variable patterns as-is, making
- %% it possible to have the same skip clause removal as with list generators.
- {AccSegs,Tail,TailSeg,St2} = append_tail_segment(Segs, St1),
- AccPat = Cp#c_binary{segments=AccSegs},
- {Cg,St3} = lc_guard_tests(Gs, St2),
- {SkipSegs,St4} = emasculate_segments(AccSegs, St3),
- SkipPat = Cp#c_binary{segments=SkipSegs},
- {Ce,Pre,St5} = safe(E, St4),
- Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
- tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]},
- arg={Pre,Ce}},
- {Gen,St5}.
+ try pattern(P, St0) of
+ {#ibinary{segments=Segs}=Cp,St1} ->
+ %% The function append_tail_segment/2 keeps variable
+ %% patterns as-is, making it possible to have the same
+ %% skip clause removal as with list generators.
+ {AccSegs,Tail,TailSeg,St2} = append_tail_segment(Segs, St1),
+ AccPat = Cp#ibinary{segments=AccSegs},
+ {Cg,St3} = lc_guard_tests(Gs, St2),
+ {SkipSegs,St4} = emasculate_segments(AccSegs, St3),
+ SkipPat = Cp#ibinary{segments=SkipSegs},
+ {Ce,Pre,St5} = safe(E, St4),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,
+ skip_pat=SkipPat,tail=Tail,
+ tail_pat=#ibinary{anno=#a{anno=LA},segments=[TailSeg]},
+ arg={Pre,Ce}},
+ {Gen,St5}
+ catch
+ throw:nomatch ->
+ {Ce,Pre,St1} = safe(E, St0),
+ Gen = #igen{anno=#a{anno=GA},acc_pat=nomatch,acc_guard=[],
+ skip_pat=nomatch,
+ tail_pat=#c_var{name='_'},
+ arg={Pre,Ce}},
+ {Gen,St1}
+ end.
append_tail_segment(Segs, St0) ->
{Var,St} = new_var(St0),
- Tail = #c_bitstr{val=Var,size=#c_literal{val=all},
- unit=#c_literal{val=1},
- type=#c_literal{val=binary},
- flags=#c_literal{val=[unsigned,big]}},
+ Tail = #ibitstr{val=Var,size=[#c_literal{val=all}],
+ unit=#c_literal{val=1},
+ type=#c_literal{val=binary},
+ flags=#c_literal{val=[unsigned,big]}},
{Segs++[Tail],Var,Tail,St}.
emasculate_segments(Segs, St) ->
emasculate_segments(Segs, St, []).
-emasculate_segments([#c_bitstr{val=#c_var{}}=B|Rest], St, Acc) ->
+emasculate_segments([#ibitstr{val=#c_var{}}=B|Rest], St, Acc) ->
emasculate_segments(Rest, St, [B|Acc]);
emasculate_segments([B|Rest], St0, Acc) ->
{Var,St1} = new_var(St0),
- emasculate_segments(Rest, St1, [B#c_bitstr{val=Var}|Acc]);
+ emasculate_segments(Rest, St1, [B#ibitstr{val=Var}|Acc]);
emasculate_segments([], St, Acc) ->
{reverse(Acc),St}.
@@ -1468,9 +1517,9 @@ lc_guard_tests(Gs0, St0) ->
list_gen_pattern(P0, Line, St) ->
try
- pattern(P0,St)
- catch
- nomatch -> {nomatch,[],add_warning(Line, nomatch, St)}
+ pattern(P0, St)
+ catch
+ nomatch -> {nomatch,add_warning(Line, nomatch, St)}
end.
%%%
@@ -1502,7 +1551,9 @@ bc_initial_size(E0, Q, St0) ->
end
catch
throw:impossible ->
- {#c_literal{val=256},[],St0}
+ {#c_literal{val=256},[],St0};
+ throw:nomatch ->
+ {#c_literal{val=1},[],St0}
end.
bc_elem_size({bin,_,El}, St0) ->
@@ -1737,18 +1788,6 @@ force_novars(#c_map{}=Bin, St) -> {Bin,[],St};
force_novars(Ce, St) ->
force_safe(Ce, St).
-
-%% safe_pattern_expr(Expr, State) -> {Cexpr,[PreExpr],State}.
-%% only literals and variables are safe expressions in patterns
-safe_pattern_expr(E,St0) ->
- case safe(E,St0) of
- {#c_var{},_,_}=Safe -> Safe;
- {#c_literal{},_,_}=Safe -> Safe;
- {Ce,Eps,St1} ->
- {V,St2} = new_var(St1),
- {V,Eps++[#iset{var=V,arg=Ce}],St2}
- end.
-
%% safe(Expr, State) -> {Safe,[PreExpr],State}.
%% Generate an internal safe expression. These are simples without
%% binaries which can fail. At this level we do not need to do a
@@ -1759,15 +1798,6 @@ 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),
@@ -1825,44 +1855,33 @@ fold_match({match,L,P0,E0}, P) ->
fold_match(E, P) -> {P,E}.
%% pattern(Pattern, State) -> {CorePat,[PreExp],State}.
-%% Transform a pattern by removing line numbers. We also normalise
-%% aliases in patterns to standard form, {alias,Pat,[Var]}.
-%%
-%% In patterns we may have expressions
-%% 1) Binaries -> #c_bitstr{size=Expr}
-%% 2) Maps -> #c_map_pair{key=Expr}
-%%
-%% Both of these may generate pre-expressions since only bound variables
-%% or literals are allowed for these in core patterns.
-%%
-%% Therefor, we need to drag both the state and the collection of pre-expression
-%% around in the whole pattern transformation tree.
-
-pattern({var,L,V}, St) -> {#c_var{anno=lineno_anno(L, St),name=V},[],St};
-pattern({char,L,C}, St) -> {#c_literal{anno=lineno_anno(L, St),val=C},[],St};
-pattern({integer,L,I}, St) -> {#c_literal{anno=lineno_anno(L, St),val=I},[],St};
-pattern({float,L,F}, St) -> {#c_literal{anno=lineno_anno(L, St),val=F},[],St};
-pattern({atom,L,A}, St) -> {#c_literal{anno=lineno_anno(L, St),val=A},[],St};
-pattern({string,L,S}, St) -> {#c_literal{anno=lineno_anno(L, St),val=S},[],St};
-pattern({nil,L}, St) -> {#c_literal{anno=lineno_anno(L, St),val=[]},[],St};
+%% Transform a pattern by removing line numbers. We also normalise
+%% aliases in patterns to standard form: {alias,Pat,[Var]}.
+
+pattern({var,L,V}, St) -> {#c_var{anno=lineno_anno(L, St),name=V},St};
+pattern({char,L,C}, St) -> {#c_literal{anno=lineno_anno(L, St),val=C},St};
+pattern({integer,L,I}, St) -> {#c_literal{anno=lineno_anno(L, St),val=I},St};
+pattern({float,L,F}, St) -> {#c_literal{anno=lineno_anno(L, St),val=F},St};
+pattern({atom,L,A}, St) -> {#c_literal{anno=lineno_anno(L, St),val=A},St};
+pattern({string,L,S}, St) -> {#c_literal{anno=lineno_anno(L, St),val=S},St};
+pattern({nil,L}, St) -> {#c_literal{anno=lineno_anno(L, St),val=[]},St};
pattern({cons,L,H,T}, St) ->
- {Ph,Eps1,St1} = pattern(H, St),
- {Pt,Eps2,St2} = pattern(T, St1),
- {annotate_cons(lineno_anno(L, St), Ph, Pt, St2),Eps1++Eps2,St2};
+ {Ph,St1} = pattern(H, St),
+ {Pt,St2} = pattern(T, St1),
+ {annotate_cons(lineno_anno(L, St), Ph, Pt, St2),St2};
pattern({tuple,L,Ps}, St) ->
- {Ps1,Eps,St1} = pattern_list(Ps,St),
- {annotate_tuple(record_anno(L, St), Ps1, St),Eps,St1};
+ {Ps1,St1} = pattern_list(Ps, St),
+ {annotate_tuple(record_anno(L, St), Ps1, St),St1};
pattern({map,L,Pairs}, St0) ->
- {Ps,Eps,St1} = pattern_map_pairs(Pairs, St0),
- {#c_map{anno=lineno_anno(L, St1),es=Ps,is_pat=true},Eps,St1};
-pattern({bin,L,Ps}, St) ->
- %% We don't create a #ibinary record here, since there is
- %% no need to hold any used/new annotations in a pattern.
- {#c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)},[],St};
+ {Ps,St1} = pattern_map_pairs(Pairs, St0),
+ {#imap{anno=#a{anno=lineno_anno(L, St1)},es=Ps},St1};
+pattern({bin,L,Ps}, St0) ->
+ {Segments,St} = pat_bin(Ps, St0),
+ {#ibinary{anno=#a{anno=lineno_anno(L, St)},segments=Segments},St};
pattern({match,_,P1,P2}, St) ->
- {Cp1,Eps1,St1} = pattern(P1,St),
- {Cp2,Eps2,St2} = pattern(P2,St1),
- {pat_alias(Cp1,Cp2),Eps1++Eps2,St2};
+ {Cp1,St1} = pattern(P1, St),
+ {Cp2,St2} = pattern(P2, St1),
+ {pat_alias(Cp1, Cp2),St2};
%% Evaluate compile-time expressions.
pattern({op,_,'++',{nil,_},R}, St) ->
pattern(R, St);
@@ -1876,57 +1895,76 @@ pattern({op,_Line,_Op,_L,_R}=Op, St) ->
pattern(erl_eval:partial_eval(Op), St).
%% pattern_map_pairs([MapFieldExact],State) -> [#c_map_pairs{}]
-pattern_map_pairs(Ps, St) ->
- %% check literal key uniqueness
- %% - guaranteed via aliasing map pairs
- %% pattern all pairs in two steps
- %% 1) Construct Core Pattern
- %% 2) Alias Keys in Core Pattern
- {CMapPairs, {Eps,St1}} = lists:mapfoldl(fun
- (P,{EpsM,Sti0}) ->
- {CMapPair,EpsP,Sti1} = pattern_map_pair(P,Sti0),
- {CMapPair, {EpsM++EpsP,Sti1}}
- end, {[],St}, Ps),
- {pat_alias_map_pairs(CMapPairs),Eps,St1}.
+pattern_map_pairs(Ps, St0) ->
+ {CMapPairs,St1} = mapfoldl(fun pattern_map_pair/2, St0, Ps),
+ {pat_alias_map_pairs(CMapPairs),St1}.
pattern_map_pair({map_field_exact,L,K,V}, St0) ->
- {Ck,EpsK,St1} = safe_pattern_expr(K, St0),
- {Cv,EpsV,St2} = pattern(V, St1),
- {#c_map_pair{anno=lineno_anno(L, St2),
- op=#c_literal{val=exact},
- key=Ck,
- val=Cv},EpsK++EpsV,St2}.
+ Ck0 = erl_eval:partial_eval(K),
+ {Ck,St1} = exprs([Ck0], St0),
+ {Cv,St2} = pattern(V, St1),
+ {#imappair{anno=#a{anno=lineno_anno(L, St2)},
+ op=#c_literal{val=exact},
+ key=Ck,
+ val=Cv},St2}.
pat_alias_map_pairs(Ps) ->
- D = foldl(fun(#c_map_pair{key=K0}=Pair, D0) ->
- K = cerl:set_ann(K0, []),
- dict:append(K, Pair, D0)
- end, dict:new(), Ps),
- pat_alias_map_pairs_1(dict:to_list(D)).
-
-pat_alias_map_pairs_1([{_,[#c_map_pair{val=V0}=Pair|Vs]}|T]) ->
- V = foldl(fun(#c_map_pair{val=V}, Pat) ->
+ D0 = foldl(fun(#imappair{key=K0}=Pair, A) ->
+ K = map_sort_key(K0, A),
+ case A of
+ #{K:=Aliases} ->
+ A#{K:=[Pair|Aliases]};
+ #{} ->
+ A#{K=>[Pair]}
+ end
+ end, #{}, Ps),
+ %% We must sort to ensure that the order remains consistent
+ %% between compilations.
+ D = sort(maps:to_list(D0)),
+ pat_alias_map_pairs_1(D).
+
+pat_alias_map_pairs_1([{_,[#imappair{val=V0}=Pair|Vs]}|T]) ->
+ V = foldl(fun(#imappair{val=V}, Pat) ->
pat_alias(V, Pat)
end, V0, Vs),
- [Pair#c_map_pair{val=V}|pat_alias_map_pairs_1(T)];
+ [Pair#imappair{val=V}|pat_alias_map_pairs_1(T)];
pat_alias_map_pairs_1([]) -> [].
+map_sort_key(Key, KeyMap) ->
+ case Key of
+ [#c_literal{}=Lit] ->
+ {atomic,cerl:set_ann(Lit, [])};
+ [#c_var{}=Var] ->
+ {atomic,cerl:set_ann(Var, [])};
+ _ ->
+ {expr,map_size(KeyMap)}
+ end.
+
%% pat_bin([BinElement], State) -> [BinSeg].
-pat_bin(Ps, St) -> [pat_segment(P, St) || P <- bin_expand_strings(Ps)].
+pat_bin(Ps0, St) ->
+ Ps = bin_expand_strings(Ps0),
+ pat_segments(Ps, St).
+
+pat_segments([P0|Ps0], St0) ->
+ {P,St1} = pat_segment(P0, St0),
+ {Ps,St2} = pat_segments(Ps0, St1),
+ {[P|Ps],St2};
+pat_segments([], St) -> {[],St}.
pat_segment({bin_element,L,Val,Size0,Type0}, St) ->
- {Size,Type1} = make_bit_type(L, Size0, Type0),
+ {Size1,Type1} = make_bit_type(L, Size0, Type0),
[Type,{unit,Unit}|Flags] = Type1,
Anno = lineno_anno(L, St),
- {Pval0,[],St1} = pattern(Val, St),
+ {Pval0,St1} = pattern(Val, St),
Pval = coerce_to_float(Pval0, Type0),
- {Psize,[],_St2} = pattern(Size, St1),
- #c_bitstr{anno=Anno,
- val=Pval,size=Psize,
- unit=#c_literal{val=Unit},
- type=#c_literal{val=Type},
- flags=#c_literal{val=Flags}}.
+ Size = erl_eval:partial_eval(Size1),
+ {Psize,St2} = exprs([Size], St1),
+ {#ibitstr{anno=#a{anno=Anno},
+ val=Pval,size=Psize,
+ unit=#c_literal{val=Unit},
+ type=#c_literal{val=Type},
+ flags=#c_literal{val=Flags}},St2}.
coerce_to_float(#c_literal{val=Int}=E, [float|_]) when is_integer(Int) ->
try
@@ -1964,8 +2002,8 @@ pat_alias(#c_alias{var=#c_var{name=V1}=Var1,pat=P1},
pat_alias(#c_alias{var=#c_var{}=Var,pat=P1}, P2) ->
#c_alias{var=Var,pat=pat_alias(P1, P2)};
-pat_alias(#c_map{es=Es1}=M, #c_map{es=Es2}) ->
- M#c_map{es=pat_alias_map_pairs(Es1 ++ Es2)};
+pat_alias(#imap{es=Es1}=M, #imap{es=Es2}) ->
+ M#imap{es=pat_alias_map_pairs(Es1 ++ Es2)};
pat_alias(P1, #c_var{}=Var) ->
#c_alias{var=Var,pat=P1};
@@ -1999,11 +2037,11 @@ pat_alias_list(_, _) -> throw(nomatch).
%% pattern_list([P], State) -> {[P],Exprs,St}
pattern_list([P0|Ps0], St0) ->
- {P1,Eps,St1} = pattern(P0, St0),
- {Ps1,Epsl,St2} = pattern_list(Ps0, St1),
- {[P1|Ps1], Eps ++ Epsl, St2};
+ {P1,St1} = pattern(P0, St0),
+ {Ps1,St2} = pattern_list(Ps0, St1),
+ {[P1|Ps1],St2};
pattern_list([], St) ->
- {[],[],St}.
+ {[],St}.
string_to_conses(Line, Cs, Tail) ->
foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs).
@@ -2049,9 +2087,8 @@ new_vars_1(N, Anno, St0, Vs) when N > 0 ->
new_vars_1(N-1, Anno, St1, [V|Vs]);
new_vars_1(0, _, St, Vs) -> {Vs,St}.
-function_clause(Ps, LineAnno, Name) ->
- FcAnno = [{function_name,Name}|LineAnno],
- fail_clause(Ps, FcAnno,
+function_clause(Ps, LineAnno) ->
+ fail_clause(Ps, LineAnno,
ann_c_tuple(LineAnno, [#c_literal{val=function_clause}|Ps])).
fail_clause(Pats, Anno, Arg) ->
@@ -2065,8 +2102,8 @@ right_assoc({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
right_assoc({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
right_assoc(E, _Op) -> E.
-annotate_tuple(A, Es, St) ->
- case member(dialyzer, St#core.opts) of
+annotate_tuple(A, Es, #core{dialyzer=Dialyzer}) ->
+ case Dialyzer of
true ->
%% Do not coalesce constant tuple elements. A Hack.
Node = cerl:ann_c_tuple(A, [cerl:c_var(any)]),
@@ -2075,8 +2112,8 @@ annotate_tuple(A, Es, St) ->
ann_c_tuple(A, Es)
end.
-annotate_cons(A, H, T, St) ->
- case member(dialyzer, St#core.opts) of
+annotate_cons(A, H, T, #core{dialyzer=Dialyzer}) ->
+ case Dialyzer of
true ->
%% Do not coalesce constant conses. A Hack.
Node= cerl:ann_c_cons(A, cerl:c_var(any), cerl:c_var(any)),
@@ -2157,6 +2194,20 @@ uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
uexprs([#icase{anno=A,args=[Arg],
clauses=[Mc],fc=Fc}], Ks, St0)
end;
+uexprs([#ireceive1{clauses=[]}=Le0|_], Ks, St0) ->
+ %% All clauses have been optimized away because they had impossible patterns.
+ %% For example:
+ %%
+ %% receive
+ %% a = b ->
+ %% V = whatever
+ %% end,
+ %% V
+ %%
+ %% Discard the unreachable code following the receive to ensure
+ %% that there are no references to unbound variables.
+ {Le1,St1} = uexpr(Le0, Ks, St0),
+ {[Le1],St1};
uexprs([Le0|Les0], Ks, St0) ->
{Le1,St1} = uexpr(Le0, Ks, St0),
{Les1,St2} = uexprs(Les0, union((get_anno(Le1))#a.ns, Ks), St1),
@@ -2210,18 +2261,26 @@ uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
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) ->
+uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}=Fun0, Ks0, St0) ->
+ {Fun1,St2} = case Ks0 of
+ [] ->
+ {Fun0,St0};
+ [_|_] ->
+ {Cs1,St1} = rename_shadowing_clauses(Cs0, Ks0, St0),
+ {Fun0#ifun{clauses=Cs1},St1}
+ end,
+ #ifun{clauses=Cs2} = Fun1,
Avs = lit_list_vars(As),
Ks1 = case Name of
unnamed -> Ks0;
{named,FName} -> union(subtract([FName], Avs), Ks0)
end,
Ks2 = union(Avs, Ks1),
- {Cs1,St1} = ufun_clauses(Cs0, Ks2, St0),
- {Fc1,St2} = ufun_clause(Fc0, Ks2, St1),
- Used = subtract(intersection(used_in_any(Cs1), Ks1), Avs),
+ {Cs3,St3} = ufun_clauses(Cs2, Ks2, St2),
+ {Fc1,St4} = ufun_clause(Fc0, Ks2, St3),
+ Used = subtract(intersection(used_in_any(Cs3), Ks1), Avs),
A1 = A0#a{us=Used,ns=[]},
- {#ifun{anno=A1,id=Id,vars=As,clauses=Cs1,fc=Fc1,name=Name},St2};
+ {#ifun{anno=A1,id=Id,vars=As,clauses=Cs3,fc=Fc1,name=Name},St4};
uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
Used = union(lit_vars(Op), lit_list_vars(As)),
{#iapply{anno=A#a{us=Used},op=Op,args=As},St};
@@ -2318,20 +2377,17 @@ upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
{Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1};
-upattern(#c_map{es=Es0}=Map, Ks, St0) ->
+upattern(#imap{es=Es0}=Map, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
- {Map#c_map{es=Es1},Esg,Esv,Eus,St1};
-upattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,Ks,St0) ->
+ {Map#imap{es=Es1},Esg,Esv,Eus,St1};
+upattern(#imappair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,Ks,St0) ->
{V,Vg,Vn,Vu,St1} = upattern(V0, Ks, St0),
- % A variable key must be considered used here
- Ku = case K0 of
- #c_var{name=Name} -> [Name];
- _ -> []
- end,
- {Pair#c_map_pair{val=V},Vg,Vn,union(Ku,Vu),St1};
-upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
+ {K,St2} = uexprs(K0, Ks, St1),
+ Ku = used_in_expr(K),
+ {Pair#imappair{key=K,val=V},Vg,Vn,union(Ku, Vu),St2};
+upattern(#ibinary{segments=Es0}=Bin, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
- {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
+ {Bin#ibinary{segments=Es1},Esg,Esv,Eus,St1};
upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
{V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
{P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
@@ -2377,7 +2433,7 @@ upat_bin([], _, _, St) -> {[],[],[],[],St}.
%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
-upat_element(#c_bitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
+upat_element(#ibitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
{H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
Bs1 = case H0 of
#c_var{name=Hname} ->
@@ -2390,13 +2446,22 @@ upat_element(#c_bitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
_ ->
Bs0
end,
- {Sz1,Us} = case Sz0 of
- #c_var{name=Vname} ->
- rename_bitstr_size(Vname, Bs0);
- _Other ->
- {Sz0,[]}
- end,
- {Seg#c_bitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St1}.
+ case Sz0 of
+ [#c_var{name=Vname}] ->
+ {Sz1,Us} = rename_bitstr_size(Vname, Bs0),
+ {Sz2,St2} = uexprs([Sz1], Ks, St1),
+ {Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2};
+ [#c_literal{}] ->
+ {Sz1,St2} = uexprs(Sz0, Ks, St1),
+ Us = [],
+ {Seg#ibitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St2};
+ Expr when is_list(Expr) ->
+ Sz1 = [#iset{var=#c_var{name=Old},arg=#c_var{name=New}} ||
+ {Old,New} <- Bs0] ++ Expr,
+ {Sz2,St2} = uexprs(Sz1, Ks, St1),
+ Us = used_in_expr(Sz2),
+ {Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2}
+ end.
rename_bitstr_size(V, [{V,N}|_]) ->
New = #c_var{name=N},
@@ -2406,7 +2471,13 @@ rename_bitstr_size(V, [_|Rest]) ->
rename_bitstr_size(V, []) ->
Old = #c_var{name=V},
{Old,[V]}.
-
+
+used_in_expr([Le|Les]) ->
+ #a{us=Us,ns=Ns} = get_anno(Le),
+ Used = used_in_expr(Les),
+ union(Us, subtract(Used, Ns));
+used_in_expr([]) -> [].
+
used_in_any(Les) ->
foldl(fun (Le, Ns) -> union((get_anno(Le))#a.us, Ns) end,
[], Les).
@@ -2420,6 +2491,114 @@ new_in_all([Le|Les]) ->
(get_anno(Le))#a.ns, Les);
new_in_all([]) -> [].
+%%%
+%%% Rename shadowing variables in fun heads.
+%%%
+%%% Pattern variables in fun heads always shadow variables bound in
+%%% the enclosing environment. Because that is the way that variables
+%%% behave in Core Erlang, there was previously no need to rename
+%%% the variables.
+%%%
+%%% However, to support splitting of patterns and/or pattern matching
+%%% compilation in Core Erlang, there is a need to rename all
+%%% shadowing variables to avoid changing the semantics of the Erlang
+%%% program.
+%%%
+
+rename_shadowing_clauses([C0|Cs0], Ks, St0) ->
+ {C,St1} = rename_shadowing_clause(C0, Ks, St0),
+ {Cs,St} = rename_shadowing_clauses(Cs0, Ks, St1),
+ {[C|Cs],St};
+rename_shadowing_clauses([], _Ks, St) ->
+ {[],St}.
+
+rename_shadowing_clause(#iclause{pats=Ps0,guard=G0,body=B0}=C, Ks, St0) ->
+ Subs = {[],[]},
+ {Ps,{_Isub,Osub},St} = ren_pats(Ps0, Ks, Subs, St0),
+ G = case G0 of
+ [] -> G0;
+ [_|_] -> Osub ++ G0
+ end,
+ B = Osub ++ B0,
+ {C#iclause{pats=Ps,guard=G,body=B},St}.
+
+ren_pats([P0|Ps0], Ks, {_,_}=Subs0, St0) ->
+ {P,Subs1,St1} = ren_pat(P0, Ks, Subs0, St0),
+ {Ps,Subs,St} = ren_pats(Ps0, Ks, Subs1, St1),
+ {[P|Ps],Subs,St};
+ren_pats([], _Ks, {_,_}=Subs, St) ->
+ {[],Subs,St}.
+
+ren_pat(#c_var{name='_'}=P, _Ks, Subs, St) ->
+ {P,Subs,St};
+ren_pat(#c_var{name=V}=Old, Ks, {Isub0,Osub0}=Subs, St0) ->
+ case member(V, Ks) of
+ true ->
+ case ren_is_subst(V, Osub0) of
+ {yes,New} ->
+ {New,Subs,St0};
+ no ->
+ {New,St} = new_var(St0),
+ Osub = [#iset{var=Old,arg=New}|Osub0],
+ {New,{Isub0,Osub},St}
+ end;
+ false ->
+ {Old,Subs,St0}
+ end;
+ren_pat(#c_literal{}=P, _Ks, {_,_}=Subs, St) ->
+ {P,Subs,St};
+ren_pat(#c_alias{var=Var0,pat=Pat0}=Alias, Ks, {_,_}=Subs0, St0) ->
+ {Var,Subs1,St1} = ren_pat(Var0, Ks, Subs0, St0),
+ {Pat,Subs,St} = ren_pat(Pat0, Ks, Subs1, St1),
+ {Alias#c_alias{var=Var,pat=Pat},Subs,St};
+ren_pat(#imap{es=Es0}=Map, Ks, {_,_}=Subs0, St0) ->
+ {Es,Subs,St} = ren_pat_map(Es0, Ks, Subs0, St0),
+ {Map#imap{es=Es},Subs,St};
+ren_pat(#ibinary{segments=Es0}=P, Ks, {Isub,Osub0}, St0) ->
+ {Es,_Isub,Osub,St} = ren_pat_bin(Es0, Ks, Isub, Osub0, St0),
+ {P#ibinary{segments=Es},{Isub,Osub},St};
+ren_pat(P, Ks0, {_,_}=Subs0, St0) ->
+ Es0 = cerl:data_es(P),
+ {Es,Subs,St} = ren_pats(Es0, Ks0, Subs0, St0),
+ {cerl:make_data(cerl:data_type(P), Es),Subs,St}.
+
+ren_pat_bin([#ibitstr{val=Val0,size=Sz0}=E|Es0], Ks, Isub0, Osub0, St0) ->
+ Sz = ren_get_subst(Sz0, Isub0),
+ {Val,{_,Osub1},St1} = ren_pat(Val0, Ks, {Isub0,Osub0}, St0),
+ Isub1 = case Val0 of
+ #c_var{} ->
+ [#iset{var=Val0,arg=Val}|Isub0];
+ _ ->
+ Isub0
+ end,
+ {Es,Isub,Osub,St} = ren_pat_bin(Es0, Ks, Isub1, Osub1, St1),
+ {[E#ibitstr{val=Val,size=Sz}|Es],Isub,Osub,St};
+ren_pat_bin([], _Ks, Isub, Osub, St) ->
+ {[],Isub,Osub,St}.
+
+ren_pat_map([#imappair{val=Val0}=MapPair|Es0], Ks, Subs0, St0) ->
+ {Val,Subs1,St1} = ren_pat(Val0, Ks, Subs0, St0),
+ {Es,Subs,St} = ren_pat_map(Es0, Ks, Subs1, St1),
+ {[MapPair#imappair{val=Val}|Es],Subs,St};
+ren_pat_map([], _Ks, Subs, St) ->
+ {[],Subs,St}.
+
+ren_get_subst([#c_var{name=V}]=Old, Sub) ->
+ case ren_is_subst(V, Sub) of
+ no -> Old;
+ {yes,New} -> [New]
+ end;
+ren_get_subst([#c_literal{}]=Old, _Sub) ->
+ Old;
+ren_get_subst(Expr, Sub) when is_list(Expr) ->
+ Sub ++ Expr.
+
+ren_is_subst(V, [#iset{var=#c_var{name=V},arg=Arg}|_]) ->
+ {yes,Arg};
+ren_is_subst(V, [_|Sub]) ->
+ ren_is_subst(V, Sub);
+ren_is_subst(_V, []) -> no.
+
%% The AfterVars are the variables which are used afterwards. We need
%% this to work out which variables are actually exported and used
%% from case/receive. In subblocks/clauses the AfterVars of the block
@@ -2432,7 +2611,8 @@ cbody(B0, St0) ->
%% cclause(Lclause, [AfterVar], State) -> {Cclause,State}.
%% The AfterVars are the exported variables.
-cclause(#iclause{anno=#a{anno=Anno},pats=Ps,guard=G0,body=B0}, Exp, St0) ->
+cclause(#iclause{anno=#a{anno=Anno},pats=Ps0,guard=G0,body=B0}, Exp, St0) ->
+ Ps = cpattern_list(Ps0),
{B1,_Us1,St1} = cexprs(B0, Exp, St0),
{G1,St2} = cguard(G0, St1),
{#c_clause{anno=Anno,pats=Ps,guard=G1,body=B1},St2}.
@@ -2444,7 +2624,36 @@ cguard([], St) -> {#c_literal{val=true},St};
cguard(Gs, St0) ->
{G,_,St1} = cexprs(Gs, [], St0),
{G,St1}.
-
+
+cpattern_list([P|Ps]) ->
+ [cpattern(P)|cpattern_list(Ps)];
+cpattern_list([]) -> [].
+
+cpattern(#c_alias{pat=Pat}=Alias) ->
+ Alias#c_alias{pat=cpattern(Pat)};
+cpattern(#c_cons{hd=Hd,tl=Tl}=Cons) ->
+ Cons#c_cons{hd=cpattern(Hd),tl=cpattern(Tl)};
+cpattern(#c_tuple{es=Es}=Tup) ->
+ Tup#c_tuple{es=cpattern_list(Es)};
+cpattern(#imap{anno=#a{anno=Anno},es=Es}) ->
+ #c_map{anno=Anno,es=cpat_map_pairs(Es),is_pat=true};
+cpattern(#ibinary{anno=#a{anno=Anno},segments=Segs0}) ->
+ Segs = [cpat_bin_seg(S) || S <- Segs0],
+ #c_binary{anno=Anno,segments=Segs};
+cpattern(Other) -> Other.
+
+cpat_map_pairs([#imappair{anno=#a{anno=Anno},op=Op,key=Key0,val=Val0}|T]) ->
+ {Key,_,_} = cexprs(Key0, [], #core{}),
+ Val = cpattern(Val0),
+ Pair = #c_map_pair{anno=Anno,op=Op,key=Key,val=Val},
+ [Pair|cpat_map_pairs(T)];
+cpat_map_pairs([]) -> [].
+
+cpat_bin_seg(#ibitstr{anno=#a{anno=Anno},val=E,size=Sz0,unit=Unit,
+ type=Type,flags=Flags}) ->
+ {Sz,_,_} = cexprs(Sz0, [], #core{}),
+ #c_bitstr{anno=Anno,val=E,size=Sz,unit=Unit,type=Type,flags=Flags}.
+
%% cexprs([Lexpr], [AfterVar], State) -> {Cexpr,[AfterVar],State}.
%% Must be sneaky here at the last expr when combining exports for the
%% whole sequence and exports for that expr.
@@ -2625,6 +2834,508 @@ c_call_erl(Fun, Args) ->
As = [compiler_generated],
cerl:ann_c_call(As, cerl:c_atom(erlang), cerl:c_atom(Fun), Args).
+%%%
+%%% Lower a `receive` to more primitive operations. Rewrite patterns
+%%% that use and bind the same variable as nested cases.
+%%%
+%%% Here follows an example of how a receive in this Erlang code:
+%%%
+%%% foo(Timeout) ->
+%%% receive
+%%% {tag,Msg} -> Msg
+%%% after
+%%% Timeout ->
+%%% no_message
+%%% end.
+%%%
+%%% is translated into Core Erlang:
+%%%
+%%% 'foo'/1 =
+%%% fun (Timeout) ->
+%%% ( letrec
+%%% 'recv$^0'/0 =
+%%% fun () ->
+%%% let <PeekSucceeded,Message> =
+%%% primop 'recv_peek_message'()
+%%% in case PeekSucceeded of
+%%% <'true'> when 'true' ->
+%%% case Message of
+%%% <{'tag',Msg}> when 'true' ->
+%%% do primop 'remove_message'()
+%%% Msg
+%%% ( <Other> when 'true' ->
+%%% do primop 'recv_next'()
+%%% apply 'recv$^0'/0()
+%%% -| ['compiler_generated'] )
+%%% end
+%%% <'false'> when 'true' ->
+%%% let <TimedOut> =
+%%% primop 'recv_wait_timeout'(Timeout)
+%%% in case TimedOut of
+%%% <'true'> when 'true' ->
+%%% do primop 'timeout'()
+%%% 'no_message'
+%%% <'false'> when 'true' ->
+%%% apply 'recv$^0'/0()
+%%% end
+%%% end
+%%% in apply 'recv$^0'/0()
+%%% -| ['letrec_goto'] )
+
+lbody(B, St) ->
+ cerl_trees:mapfold(fun lexpr/2, St, B).
+
+lexpr(#c_case{}=Case, St) ->
+ %% Split patterns that bind and use the same variable.
+ split_case(Case, St);
+lexpr(#c_receive{clauses=[],timeout=Timeout0,action=Action}, St0) ->
+ %% Lower a receive with only an after to its primitive operations.
+ False = #c_literal{val=false},
+ True = #c_literal{val=true},
+
+ {Timeout,Outer0,St1} =
+ case is_safe(Timeout0) of
+ true ->
+ {Timeout0,False,St0};
+ false ->
+ {TimeoutVar,Sti0} = new_var(St0),
+ OuterLet = #c_let{vars=[TimeoutVar],arg=Timeout0,body=False},
+ {TimeoutVar,OuterLet,Sti0}
+ end,
+
+ MaybeIgnore = case Timeout of
+ #c_literal{val=infinity} -> [dialyzer_ignore];
+ _ -> []
+ end,
+
+ {LoopName,St2} = new_fun_name("recv", St1),
+ LoopFun = #c_var{name={LoopName,0}},
+ ApplyLoop = #c_apply{anno=[dialyzer_ignore],op=LoopFun,args=[]},
+
+ TimeoutCs = [#c_clause{anno=MaybeIgnore,pats=[True],guard=True,
+ body=#c_seq{arg=primop(timeout),
+ body=Action}},
+ #c_clause{anno=[compiler_generated,dialyzer_ignore],
+ pats=[False],guard=True,
+ body=ApplyLoop}],
+ {TimeoutBool,St3} = new_var(St2),
+ TimeoutCase = #c_case{anno=[receive_timeout],arg=TimeoutBool,
+ clauses=TimeoutCs},
+ TimeoutLet = #c_let{vars=[TimeoutBool],
+ arg=primop(recv_wait_timeout, [Timeout]),
+ body=TimeoutCase},
+
+ Fun = #c_fun{vars=[],body=TimeoutLet},
+
+ Letrec = #c_letrec{anno=[letrec_goto],
+ defs=[{LoopFun,Fun}],
+ body=ApplyLoop},
+
+ %% If the 'after' expression is unsafe we evaluate it in an outer 'let'.
+ Outer = case Outer0 of
+ #c_let{} -> Outer0#c_let{body=Letrec};
+ _ -> Letrec
+ end,
+ {Outer,St3};
+lexpr(#c_receive{anno=RecvAnno,clauses=Cs0,timeout=Timeout0,action=Action}, St0) ->
+ %% Lower receive to its primitive operations.
+ False = #c_literal{val=false},
+ True = #c_literal{val=true},
+
+ {Timeout,Outer0,St1} =
+ case is_safe(Timeout0) of
+ true ->
+ {Timeout0,False,St0};
+ false ->
+ {TimeoutVar,Sti0} = new_var(St0),
+ OuterLet = #c_let{vars=[TimeoutVar],arg=Timeout0,body=False},
+ {TimeoutVar,OuterLet,Sti0}
+ end,
+
+ MaybeIgnore = case Timeout of
+ #c_literal{val=infinity} -> [dialyzer_ignore];
+ _ -> []
+ end,
+
+ {LoopName,St2} = new_fun_name("recv", St1),
+ LoopFun = #c_var{name={LoopName,0}},
+ ApplyLoop = #c_apply{anno=[dialyzer_ignore],op=LoopFun,args=[]},
+
+ Cs1 = rewrite_cs(Cs0),
+ RecvNext = #c_seq{arg=primop(recv_next),
+ body=ApplyLoop},
+ RecvNextC = #c_clause{anno=[compiler_generated,dialyzer_ignore],
+ pats=[#c_var{name='Other'}],guard=True,body=RecvNext},
+ Cs = Cs1 ++ [RecvNextC],
+ {Msg,St3} = new_var(St2),
+ {MsgCase,St4} = split_case(#c_case{anno=RecvAnno,arg=Msg,clauses=Cs}, St3),
+
+ TimeoutCs = [#c_clause{pats=[True],guard=True,
+ body=#c_seq{arg=primop(timeout),
+ body=Action}},
+ #c_clause{anno=[dialyzer_ignore],pats=[False],guard=True,
+ body=ApplyLoop}],
+ {TimeoutBool,St5} = new_var(St4),
+ TimeoutCase = #c_case{arg=TimeoutBool,clauses=TimeoutCs},
+ TimeoutLet = #c_let{vars=[TimeoutBool],
+ arg=primop(recv_wait_timeout, [Timeout]),
+ body=TimeoutCase},
+
+ {PeekSucceeded,St6} = new_var(St5),
+ PeekCs = [#c_clause{pats=[True],guard=True,
+ body=MsgCase},
+ #c_clause{anno=MaybeIgnore,
+ pats=[False],guard=True,
+ body=TimeoutLet}],
+ PeekCase = #c_case{arg=PeekSucceeded,clauses=PeekCs},
+ PeekLet = #c_let{vars=[PeekSucceeded,Msg],
+ arg=primop(recv_peek_message),
+ body=PeekCase},
+ Fun = #c_fun{vars=[],body=PeekLet},
+
+ Letrec = #c_letrec{anno=[letrec_goto],
+ defs=[{LoopFun,Fun}],
+ body=ApplyLoop},
+
+ %% If the 'after' expression is unsafe we evaluate it in an outer 'let'.
+ Outer = case Outer0 of
+ #c_let{} -> Outer0#c_let{body=Letrec};
+ _ -> Letrec
+ end,
+ {Outer,St6};
+lexpr(Tree, St) ->
+ {Tree,St}.
+
+rewrite_cs([#c_clause{body=B0}=C|Cs]) ->
+ B = #c_seq{arg=primop(remove_message),body=B0},
+ [C#c_clause{body=B}|rewrite_cs(Cs)];
+rewrite_cs([]) -> [].
+
+primop(Name) ->
+ primop(Name, []).
+
+primop(Name, Args) ->
+ #c_primop{name=#c_literal{val=Name},args=Args}.
+
+%%%
+%%% Split patterns such as <<Size:32,Tail:Size>> that bind
+%%% and use a variable in the same pattern. Rewrite to a
+%%% nested case in a letrec.
+%%%
+
+split_case(#c_case{anno=CaseAnno,arg=Arg,clauses=Cs0}=Case0, St0) ->
+ Args = case Arg of
+ #c_values{es=Es} -> Es;
+ _ -> [Arg]
+ end,
+ {VarArgs,St1} = split_var_args(Args, St0),
+ case split_clauses(Cs0, VarArgs, CaseAnno, St1) of
+ none ->
+ {Case0,St0};
+ {PreCase,AftCs,St2} ->
+ AftCase = Case0#c_case{arg=core_lib:make_values(VarArgs),
+ clauses=AftCs},
+ AftFun = #c_fun{vars=[],body=AftCase},
+ {Letrec,St3} = split_case_letrec(AftFun, PreCase, St2),
+ Body = split_letify(VarArgs, Args, Letrec, [], []),
+ {Body,St3}
+ end.
+
+split_var_args(Args, St) ->
+ mapfoldl(fun(#c_var{}=Var, S0) ->
+ {Var,S0};
+ (#c_literal{}=Lit, S0) ->
+ {Lit,S0};
+ (_, S0) ->
+ new_var(S0)
+ end, St, Args).
+
+split_letify([Same|Vs], [Same|Args], Body, VsAcc, ArgAcc) ->
+ split_letify(Vs, Args, Body, VsAcc, ArgAcc);
+split_letify([V|Vs], [Arg|Args], Body, VsAcc, ArgAcc) ->
+ split_letify(Vs, Args, Body, [V|VsAcc], [Arg|ArgAcc]);
+split_letify([], [], Body, [], []) ->
+ Body;
+split_letify([], [], Body, [_|_]=VsAcc, [_|_]=ArgAcc) ->
+ #c_let{vars=reverse(VsAcc),
+ arg=core_lib:make_values(reverse(ArgAcc)),
+ body=Body}.
+
+split_case_letrec(#c_fun{anno=FunAnno0}=Fun0, Body, #core{gcount=C}=St0) ->
+ FunAnno = [compiler_generated|FunAnno0],
+ Fun = Fun0#c_fun{anno=FunAnno},
+ Anno = [letrec_goto],
+ DefFunName = goto_func(C),
+ Letrec = #c_letrec{anno=Anno,defs=[{#c_var{name=DefFunName},Fun}],body=Body},
+ St = St0#core{gcount=C+1},
+ lbody(Letrec, St).
+
+split_clauses([C0|Cs0], Args, CaseAnno, St0) ->
+ case split_clauses(Cs0, Args, CaseAnno, St0) of
+ none ->
+ case split_clause(C0, St0) of
+ none ->
+ none;
+ {Ps,Nested,St1} ->
+ {Case,St2} = split_reconstruct(Args, Ps, Nested,
+ C0, CaseAnno, St1),
+ {Case,Cs0,St2}
+ end;
+ {Case0,Cs,St} ->
+ #c_case{clauses=NewClauses} = Case0,
+ Case = Case0#c_case{clauses=[C0|NewClauses]},
+ {Case,Cs,St}
+ end;
+split_clauses([], _, _, _) ->
+ none.
+
+goto_func(Count) ->
+ {list_to_atom("label^" ++ integer_to_list(Count)),0}.
+
+split_reconstruct(Args, Ps, nil, #c_clause{anno=Anno}=C0, CaseAnno, St0) ->
+ C = C0#c_clause{pats=Ps},
+ {Fc,St1} = split_fc_clause(Ps, Anno, St0),
+ {#c_case{anno=CaseAnno,arg=core_lib:make_values(Args),clauses=[C,Fc]},St1};
+split_reconstruct(Args, Ps, {split,SplitArgs,Pat,Nested}, C, CaseAnno, St) ->
+ Split = {split,SplitArgs,fun(Body) -> Body end,Pat,Nested},
+ split_reconstruct(Args, Ps, Split, C, CaseAnno, St);
+split_reconstruct(Args, Ps, {split,SplitArgs,Wrap,Pat,Nested},
+ #c_clause{anno=Anno}=C0, CaseAnno, St0) ->
+ {InnerCase,St1} = split_reconstruct(SplitArgs, [Pat], Nested, C0,
+ CaseAnno, St0),
+ {Fc,St2} = split_fc_clause(Args, Anno, St1),
+ Wrapped = Wrap(InnerCase),
+ C = C0#c_clause{pats=Ps,guard=#c_literal{val=true},body=Wrapped},
+ {#c_case{anno=CaseAnno,arg=core_lib:make_values(Args),clauses=[C,Fc]},St2}.
+
+split_fc_clause(Args, Anno0, #core{gcount=Count}=St0) ->
+ Anno = [compiler_generated|Anno0],
+ Arity = length(Args),
+ {Vars,St1} = new_vars(Arity, St0),
+ Op = #c_var{name=goto_func(Count)},
+ Apply = #c_apply{anno=Anno,op=Op,args=[]},
+ {#c_clause{anno=[dialyzer_ignore|Anno],pats=Vars,
+ guard=#c_literal{val=true},body=Apply},St1}.
+
+split_clause(#c_clause{pats=Ps0}, St0) ->
+ case split_pats(Ps0, St0) of
+ none ->
+ none;
+ {Ps,Case,St} ->
+ {Ps,Case,St}
+ end.
+
+split_pats([P0|Ps0], St0) ->
+ case split_pats(Ps0, St0) of
+ none ->
+ case split_pat(P0, St0) of
+ none ->
+ none;
+ {P,Case,St} ->
+ {[P|Ps0],Case,St}
+ end;
+ {Ps,Case,St} ->
+ {[P0|Ps],Case,St}
+ end;
+split_pats([], _) ->
+ none.
+
+split_pat(#c_binary{segments=Segs0}=Bin, St0) ->
+ Vars = gb_sets:empty(),
+ case split_bin_segments(Segs0, Vars, St0, []) of
+ none ->
+ none;
+ {TailVar,Wrap,Bef,Aft,St} ->
+ BefBin = Bin#c_binary{segments=Bef},
+ {BefBin,{split,[TailVar],Wrap,Bin#c_binary{segments=Aft},nil},St}
+ end;
+split_pat(#c_map{es=Es}=Map, St) ->
+ split_map_pat(Es, Map, St, []);
+split_pat(#c_var{}, _) ->
+ none;
+split_pat(#c_alias{pat=Pat}=Alias0, St0) ->
+ case split_pat(Pat, St0) of
+ none ->
+ none;
+ {Ps,Split,St1} ->
+ {Var,St} = new_var(St1),
+ Alias = Alias0#c_alias{pat=Var},
+ {Alias,{split,[Var],Ps,Split},St}
+ end;
+split_pat(Data, St0) ->
+ Type = cerl:data_type(Data),
+ Es = cerl:data_es(Data),
+ split_data(Es, Type, St0, []).
+
+split_map_pat([#c_map_pair{key=Key,val=Val}=E0|Es], Map0, St0, Acc) ->
+ case eval_map_key(Key, E0, Es, Map0, St0) of
+ none ->
+ case split_pat(Val, St0) of
+ none ->
+ split_map_pat(Es, Map0, St0, [E0|Acc]);
+ {Ps,Split,St1} ->
+ {Var,St} = new_var(St1),
+ E = E0#c_map_pair{val=Var},
+ Map = Map0#c_map{es=reverse(Acc, [E|Es])},
+ {Map,{split,[Var],Ps,Split},St}
+ end;
+ {MapVar,Split,St1} ->
+ BefMap0 = Map0#c_map{es=reverse(Acc)},
+ BefMap = #c_alias{var=MapVar,pat=BefMap0},
+ {BefMap,Split,St1}
+ end;
+split_map_pat([], _, _, _) -> none.
+
+eval_map_key(#c_var{}, _E, _Es, _Map, _St) ->
+ none;
+eval_map_key(#c_literal{}, _E, _Es, _Map, _St) ->
+ none;
+eval_map_key(Key, E0, Es, Map, St0) ->
+ {[KeyVar,MapVar],St1} = new_vars(2, St0),
+ E = E0#c_map_pair{key=KeyVar},
+ AftMap0 = Map#c_map{es=[E|Es]},
+ {Wrap,CaseArg,AftMap,St2} = wrap_map_key_fun(Key, KeyVar, MapVar, AftMap0, St1),
+ {MapVar,{split,[CaseArg],Wrap,AftMap,nil},St2}.
+
+wrap_map_key_fun(Key, KeyVar, MapVar, AftMap, St0) ->
+ case is_safe(Key) of
+ true ->
+ {fun(Body) ->
+ #c_let{vars=[KeyVar],arg=Key,body=Body}
+ end,MapVar,AftMap,St0};
+ false ->
+ {[SuccVar|Evars],St} = new_vars(4, St0),
+ {fun(Body) ->
+ Try = #c_try{arg=Key,vars=[KeyVar],
+ body=#c_values{es=[#c_literal{val=true},KeyVar]},
+ evars=Evars,
+ handler=#c_values{es=[#c_literal{val=false},
+ #c_literal{val=false}]}},
+ #c_let{vars=[SuccVar,KeyVar],arg=Try,body=Body}
+ end,
+ #c_tuple{es=[SuccVar,MapVar]},
+ #c_tuple{es=[#c_literal{val=true},AftMap]},
+ St}
+ end.
+
+split_data([E|Es0], Type, St0, Acc) ->
+ case split_pat(E, St0) of
+ none ->
+ split_data(Es0, Type, St0, [E|Acc]);
+ {Ps,Split,St1} ->
+ {Var,St} = new_var(St1),
+ Data = cerl:make_data(Type, reverse(Acc, [Var|Es0])),
+ {Data,{split,[Var],Ps,Split},St}
+ end;
+split_data([], _, _, _) -> none.
+
+split_bin_segments([#c_bitstr{val=Val,size=Size}=S0|Segs], Vars0, St0, Acc) ->
+ Vars = case Val of
+ #c_var{name=V} -> gb_sets:add(V, Vars0);
+ _ -> Vars0
+ end,
+ case Size of
+ #c_literal{} ->
+ split_bin_segments(Segs, Vars, St0, [S0|Acc]);
+ #c_var{name=SizeVar} ->
+ case gb_sets:is_member(SizeVar, Vars0) of
+ true ->
+ %% The size variable is variable previously bound
+ %% in this same segment. Split the clause here to
+ %% avoid a variable that is both defined and used
+ %% in the same pattern.
+ {TailVar,Tail,St} = split_tail_seg(S0, Segs, St0),
+ Wrap = fun(Body) -> Body end,
+ {TailVar,Wrap,reverse(Acc, [Tail]),[S0|Segs],St};
+ false ->
+ split_bin_segments(Segs, Vars, St0, [S0|Acc])
+ end;
+ _ ->
+ %% The size is an expression. Split the clause here,
+ %% calculate the expression in a try/catch, and finally
+ %% continue the match in an inner case.
+ {TailVar,Tail,St1} = split_tail_seg(S0, Segs, St0),
+ {SizeVar,St2} = new_var(St1),
+ S = S0#c_bitstr{size=SizeVar},
+ {Wrap,St3} = split_wrap(SizeVar, Size, St2),
+ {TailVar,Wrap,reverse(Acc, [Tail]),[S|Segs],St3}
+ end;
+split_bin_segments(_, _, _, _) ->
+ none.
+
+split_tail_seg(#c_bitstr{anno=A}=S, Segs, St0) ->
+ {TailVar,St} = new_var(St0),
+ Unit = split_bin_unit([S|Segs], St0),
+ {TailVar,
+ #c_bitstr{anno=A,val=TailVar,
+ size=#c_literal{val=all},
+ unit=#c_literal{val=Unit},
+ type=#c_literal{val=binary},
+ flags=#c_literal{val=[unsigned,big]}},
+ St}.
+
+split_wrap(SizeVar, SizeExpr, St0) ->
+ {Evars,St1} = new_vars(3, St0),
+ {fun(Body) ->
+ Try = #c_try{arg=SizeExpr,vars=[SizeVar],body=SizeVar,
+ evars=Evars,handler=#c_literal{val=bad_size}},
+ #c_let{vars=[SizeVar],arg=Try,body=Body}
+ end,St1}.
+
+split_bin_unit(Ss, #core{dialyzer=Dialyzer}) ->
+ case Dialyzer of
+ true ->
+ %% When a binary match has been rewritten to a nested
+ %% case like this:
+ %%
+ %% case Bin of
+ %% <<Size:32,Tail:Size/bitstring-unit:1>> ->
+ %% case Tail of
+ %% <<Result/binary-unit:8>> -> Result;
+ %% ...
+ %% end
+ %%
+ %% dialyzer will determine the type of Bin based solely on
+ %% the binary pattern in the outer case. It will not
+ %% back-propagate any type information for Tail to Bin. For
+ %% this example, dialyzer would infer the type of Bin to
+ %% be <<_:8,_:_*1>>.
+ %%
+ %% Help dialyzer to infer a better type by calculating the
+ %% greatest common unit for the segments in the inner case
+ %% expression. For this example, the greatest common unit
+ %% for the pattern in the inner case is 8; it will allow
+ %% dialyzer to infer the type for Bin to be
+ %% <<_:32,_:_*8>>.
+
+ split_bin_unit_1(Ss, 0);
+ false ->
+ %% Return the unit for pattern in the outer case that
+ %% results in the best code.
+
+ 1
+ end.
+
+split_bin_unit_1([#c_bitstr{type=#c_literal{val=Type},size=Size,
+ unit=#c_literal{val=U}}|Ss],
+ GCU) ->
+ Bits = case {Type,Size} of
+ {utf8,_} -> 8;
+ {utf16,_} -> 16;
+ {utf32,_} -> 32;
+ {_,#c_literal{val=0}} -> 1;
+ {_,#c_literal{val=Sz}} when is_integer(Sz) -> Sz * U;
+ {_,_} -> U
+ end,
+ split_bin_unit_1(Ss, gcd(GCU, Bits));
+split_bin_unit_1([], GCU) -> GCU.
+
+gcd(A, B) ->
+ case A rem B of
+ 0 -> B;
+ X -> gcd(B, X)
+ end.
+
%% lit_vars(Literal) -> [Var].
lit_vars(Lit) -> lit_vars(Lit, []).
@@ -2649,10 +3360,8 @@ bitstr_vars(Segs, Vs) ->
lit_vars(V, lit_vars(S, Vs0))
end, Vs, Segs).
-record_anno(L, St) ->
- case
- erl_anno:record(L) andalso member(dialyzer, St#core.opts)
- of
+record_anno(L, #core{dialyzer=Dialyzer}=St) ->
+ case erl_anno:record(L) andalso Dialyzer of
true ->
[record | lineno_anno(L, St)];
false ->
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index bcdc59699b..733acf7a46 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -29,11 +29,7 @@
%%
%% 3. Pattern matching (in cases and receives) has been compiled.
%%
-%% 4. The annotations contain variable usages. Seeing we have to work
-%% this out anyway for funs we might as well pass it on for free to
-%% later passes.
-%%
-%% 5. All remote-calls are to statically named m:f/a. Meta-calls are
+%% 4. All remote-calls are to statically named m:f/a. Meta-calls are
%% passed via erlang:apply/3.
%%
%% The translation is done in two passes:
@@ -81,13 +77,17 @@
-export([module/2,format_error/1]).
--import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2,
- keyfind/3,partition/2,droplast/1,last/1,sort/1,reverse/1]).
--import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
--import(cerl, [c_tuple/1]).
+-import(lists, [all/2,droplast/1,flatten/1,foldl/3,foldr/3,
+ map/2,mapfoldl/3,member/2,
+ keyfind/3,keyreplace/4,
+ last/1,partition/2,reverse/1,
+ sort/1,sort/2,splitwith/2]).
+-import(ordsets, [add_element/2,intersection/2,
+ subtract/2,union/2,union/1]).
-include("core_parse.hrl").
-include("v3_kernel.hrl").
+-define(EXPAND_MAX_SIZE_SEGMENT, 1024).
%% These are not defined in v3_kernel.hrl.
get_kanno(Kthing) -> element(2, Kthing).
@@ -105,33 +105,34 @@ copy_anno(Kdst, Ksrc) ->
-record(iletrec, {anno=[],defs}).
-record(ialias, {anno=[],vars,pat}).
-record(iclause, {anno=[],isub,osub,pats,guard,body}).
--record(ireceive_accept, {anno=[],arg}).
--record(ireceive_next, {anno=[],arg}).
--record(ignored, {anno=[]}).
-type warning() :: term(). % XXX: REFINE
%% State record for kernel translator.
-record(kern, {func, %Current host function
- ff, %Current function
+ fargs=[] :: [#k_var{}], %Arguments for current function
vcount=0, %Variable counter
fcount=0, %Fun counter
ds=cerl_sets:new() :: cerl_sets:set(), %Defined variables
funs=[], %Fun functions
free=#{}, %Free variables
ws=[] :: [warning()], %Warnings.
- guard_refc=0}). %> 0 means in guard
+ no_shared_fun_wrappers=false :: boolean(),
+ labels=cerl_sets:new()
+ }).
-spec module(cerl:c_module(), [compile:option()]) ->
{'ok', #k_mdef{}, [warning()]}.
-module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, _Options) ->
+module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, Options) ->
Kas = attributes(As),
Kes = map(fun (#c_var{name={_,_}=Fname}) -> Fname end, Es),
- St0 = #kern{},
+ NoSharedFunWrappers = proplists:get_bool(no_shared_fun_wrappers,
+ Options),
+ St0 = #kern{no_shared_fun_wrappers=NoSharedFunWrappers},
{Kfs,St} = mapfoldl(fun function/2, St0, Fs),
{ok,#k_mdef{anno=A,name=M#c_literal.val,exports=Kes,attributes=Kas,
- body=Kfs ++ St#kern.funs},lists:sort(St#kern.ws)}.
+ body=Kfs ++ St#kern.funs},sort(St#kern.ws)}.
attributes([{#c_literal{val=Name},#c_literal{val=Val}}|As]) ->
case include_attribute(Name) of
@@ -162,11 +163,11 @@ function({#c_var{name={F,Arity}=FA},Body}, St0) ->
%% the function. We use integers as variable names to avoid
%% filling up the atom table when compiling huge functions.
Count = cerl_trees:next_free_variable_name(Body),
- St1 = St0#kern{func=FA,ff=undefined,vcount=Count,fcount=0,ds=cerl_sets:new()},
+ St1 = St0#kern{func=FA,vcount=Count,fcount=0,ds=cerl_sets:new()},
{#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1),
{B1,_,St3} = ubody(B0, return, St2),
%%B1 = B0, St3 = St2, %Null second pass
- {make_fdef(#k{us=[],ns=[],a=Ab}, F, Arity, Kvs, B1),St3}
+ {make_fdef(Ab, F, Arity, Kvs, B1),St3}
catch
Class:Error:Stack ->
io:fwrite("Function: ~w/~w\n", [F,Arity]),
@@ -180,512 +181,27 @@ function({#c_var{name={F,Arity}=FA},Body}, St0) ->
body(#c_values{anno=A,es=Ces}, Sub, St0) ->
%% Do this here even if only in bodies.
{Kes,Pe,St1} = atomic_list(Ces, Sub, St0),
- %%{Kes,Pe,St1} = expr_list(Ces, Sub, St0),
{#ivalues{anno=A,args=Kes},Pe,St1};
-body(#ireceive_next{anno=A}, _, St) ->
- {#k_receive_next{anno=A},[],St};
body(Ce, Sub, St0) ->
expr(Ce, Sub, St0).
%% guard(Cexpr, Sub, State) -> {Kexpr,State}.
%% We handle guards almost as bodies. The only special thing we
%% must do is to make the final Kexpr a #k_test{}.
-%% Also, we wrap the entire guard in a try/catch which is
-%% not strictly needed, but makes sure that every 'bif' instruction
-%% will get a proper failure label.
guard(G0, Sub, St0) ->
- {G1,St1} = wrap_guard(G0, St0),
- {Ge0,Pre,St2} = expr(G1, Sub, St1),
- {Ge1,St3} = gexpr_test(Ge0, St2),
- {Ge,St} = guard_opt(Ge1, St3),
+ {Ge0,Pre,St1} = expr(G0, Sub, St0),
+ {Ge,St} = gexpr_test(Ge0, St1),
{pre_seq(Pre, Ge),St}.
-%% guard_opt(Kexpr, State) -> {Kexpr,State}.
-%% Optimize the Kexpr for the guard. Instead of evaluating a boolean
-%% expression comparing it to 'true' in a final #k_test{},
-%% replace BIF calls with #k_test{} in the expression.
-%%
-%% As an example, take the guard:
-%%
-%% when is_integer(V0), is_atom(V1) ->
-%%
-%% The unoptimized Kexpr translated to pseudo BEAM assembly
-%% code would look like:
-%%
-%% bif is_integer V0 => Bool0
-%% bif is_atom V1 => Bool1
-%% bif and Bool0 Bool1 => Bool
-%% test Bool =:= true else goto Fail
-%% ...
-%% Fail:
-%% ...
-%%
-%% The optimized code would look like:
-%%
-%% test is_integer V0 else goto Fail
-%% test is_atom V1 else goto Fail
-%% ...
-%% Fail:
-%% ...
-%%
-%% An 'or' operation is only slightly more complicated:
-%%
-%% test is_integer V0 else goto NotFailedYet
-%% goto Success
-%%
-%% NotFailedYet:
-%% test is_atom V1 else goto Fail
-%%
-%% Success:
-%% ...
-%% Fail:
-%% ...
-
-guard_opt(G, St0) ->
- {Root,Forest0,St1} = make_forest(G, St0),
- {Exprs,Forest,St} = rewrite_bool(Root, Forest0, false, St1),
- E = forest_pre_seq(Exprs, Forest),
- {G#k_try{arg=E},St}.
-
-%% rewrite_bool(Kexpr, Forest, Inv, St) -> {[Kexpr],Forest,St}.
-%% Rewrite Kexpr to use #k_test{} operations instead of comparison
-%% and type test BIFs.
-%%
-%% If Kexpr is a #k_test{} operation, the call will always
-%% succeed. Otherwise, a 'not_possible' exception will be
-%% thrown if Kexpr cannot be rewritten.
-
-rewrite_bool(#k_test{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val='=:='}},
- args=[#k_var{}=V,#k_atom{val=true}]}=Test, Forest0, Inv, St0) ->
- try rewrite_bool_var(V, Forest0, Inv, St0) of
- {_,_,_}=Res ->
- Res
- catch
- throw:not_possible ->
- {[Test],Forest0,St0}
- end;
-rewrite_bool(#k_test{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val='=:='}},
- args=[#k_var{}=V,#k_atom{val=false}]}=Test, Forest0, Inv, St0) ->
- try rewrite_bool_var(V, Forest0, not Inv, St0) of
- {_,_,_}=Res ->
- Res
- catch
- throw:not_possible ->
- {[Test],Forest0,St0}
- end;
-rewrite_bool(#k_test{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val='=:='}},
- args=[#k_atom{val=V1},#k_atom{val=V2}]}, Forest0, false, St0) ->
- case V1 =:= V2 of
- true ->
- {[make_test(is_boolean, [#k_atom{val=true}])],Forest0,St0};
- false ->
- {[make_failing_test()],Forest0,St0}
- end;
-rewrite_bool(#k_test{}=Test, Forest, false, St) ->
- {[Test],Forest,St};
-rewrite_bool(#k_try{vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false},ret=[]}=Prot,
- Forest0, Inv, St0) ->
- {Root,Forest1,St1} = make_forest(Prot, Forest0, St0),
- {Exprs,Forest2,St} = rewrite_bool(Root, Forest1, Inv, St1),
- InnerForest = maps:without(maps:keys(Forest0), Forest2),
- Forest = maps:without(maps:keys(InnerForest), Forest2),
- E = forest_pre_seq(Exprs, InnerForest),
- {[Prot#k_try{arg=E}],Forest,St};
-rewrite_bool(#k_match{body=Body,ret=[]}, Forest, Inv, St) ->
- rewrite_match(Body, Forest, Inv, St);
-rewrite_bool(Other, Forest, Inv, St) ->
- case extract_bif(Other) of
- {Name,Args} ->
- rewrite_bif(Name, Args, Forest, Inv, St);
- error ->
- throw(not_possible)
- end.
-
-%% rewrite_bool_var(Var, Forest, Inv, St) -> {[Kexpr],Forest,St}.
-%% Rewrite the boolean expression whose key in Forest is
-%% given by Var. Throw a 'not_possible' expression if something
-%% prevents the rewriting.
-
-rewrite_bool_var(Arg, Forest0, Inv, St) ->
- {Expr,Forest} = forest_take_expr(Arg, Forest0),
- rewrite_bool(Expr, Forest, Inv, St).
-
-%% rewrite_bool_args([Kexpr], Forest, Inv, St) -> {[[Kexpr]],Forest,St}.
-%% Rewrite each Kexpr in the list. The input Kexpr should be variables
-%% or boolean values. Throw a 'not_possible' expression if something
-%% prevents the rewriting.
-%%
-%% This function is suitable for handling the arguments for both
-%% 'and' and 'or'.
-
-rewrite_bool_args([#k_atom{val=B}=A|Vs], Forest0, false=Inv, St0) when is_boolean(B) ->
- {Tail,Forest1,St1} = rewrite_bool_args(Vs, Forest0, Inv, St0),
- Bif = make_bif('=:=', [A,#k_atom{val=true}]),
- {Exprs,Forest,St} = rewrite_bool(Bif, Forest1, Inv, St1),
- {[Exprs|Tail],Forest,St};
-rewrite_bool_args([#k_var{}=Var|Vs], Forest0, false=Inv, St0) ->
- {Tail,Forest1,St1} = rewrite_bool_args(Vs, Forest0, Inv, St0),
- {Exprs,Forest,St} =
- case is_bool_expr(Var, Forest0) of
- true ->
- rewrite_bool_var(Var, Forest1, Inv, St1);
- false ->
- Bif = make_bif('=:=', [Var,#k_atom{val=true}]),
- rewrite_bool(Bif, Forest1, Inv, St1)
- end,
- {[Exprs|Tail],Forest,St};
-rewrite_bool_args([_|_], _Forest, _Inv, _St) ->
- throw(not_possible);
-rewrite_bool_args([], Forest, _Inv, St) ->
- {[],Forest,St}.
-
-%% rewrite_bif(Name, [Kexpr], Forest, Inv, St) -> {[Kexpr],Forest,St}.
-%% Rewrite a BIF. Throw a 'not_possible' expression if something
-%% prevents the rewriting.
-
-rewrite_bif('or', Args, Forest, true, St) ->
- rewrite_not_args('and', Args, Forest, St);
-rewrite_bif('and', Args, Forest, true, St) ->
- rewrite_not_args('or', Args, Forest, St);
-rewrite_bif('and', [#k_atom{val=Val},Arg], Forest0, Inv, St0) ->
- false = Inv, %Assertion.
- case Val of
- true ->
- %% The result only depends on Arg.
- rewrite_bool_var(Arg, Forest0, Inv, St0);
- _ ->
- %% Will fail. There is no need to evalute the expression
- %% represented by Arg. Take it out from the forest and
- %% discard the expression.
- Failing = make_failing_test(),
- try rewrite_bool_var(Arg, Forest0, Inv, St0) of
- {_,Forest,St} ->
- {[Failing],Forest,St}
- catch
- throw:not_possible ->
- try forest_take_expr(Arg, Forest0) of
- {_,Forest} ->
- {[Failing],Forest,St0}
- catch
- throw:not_possible ->
- %% Arg is probably a variable bound in an
- %% outer scope.
- {[Failing],Forest0,St0}
- end
- end
- end;
-rewrite_bif('and', [Arg,#k_atom{}=Atom], Forest, Inv, St) ->
- false = Inv, %Assertion.
- rewrite_bif('and', [Atom,Arg], Forest, Inv, St);
-rewrite_bif('and', Args, Forest0, Inv, St0) ->
- false = Inv, %Assertion.
- {[Es1,Es2],Forest,St} = rewrite_bool_args(Args, Forest0, Inv, St0),
- {Es1 ++ Es2,Forest,St};
-rewrite_bif('or', Args, Forest0, Inv, St0) ->
- false = Inv, %Assertion.
- {[First,Then],Forest,St} = rewrite_bool_args(Args, Forest0, Inv, St0),
- Alt = make_alt(First, Then),
- {[Alt],Forest,St};
-rewrite_bif('xor', [_,_], _Forest, _Inv, _St) ->
- %% Rewriting 'xor' is not practical. Fortunately, 'xor' is
- %% almost never used in practice.
- throw(not_possible);
-rewrite_bif('not', [Arg], Forest0, Inv, St) ->
- {Expr,Forest} = forest_take_expr(Arg, Forest0),
- rewrite_bool(Expr, Forest, not Inv, St);
-rewrite_bif(Op, Args, Forest, Inv, St) ->
- case is_test(Op, Args) of
- true ->
- rewrite_bool(make_test(Op, Args, Inv), Forest, false, St);
- false ->
- throw(not_possible)
- end.
-
-rewrite_not_args(Op, [A0,B0], Forest0, St0) ->
- {A,Forest1,St1} = rewrite_not_args_1(A0, Forest0, St0),
- {B,Forest2,St2} = rewrite_not_args_1(B0, Forest1, St1),
- rewrite_bif(Op, [A,B], Forest2, false, St2).
-
-rewrite_not_args_1(Arg, Forest, St) ->
- Not = make_bif('not', [Arg]),
- forest_add_expr(Not, Forest, St).
-
-%% rewrite_match(Kvar, TypeClause, Forest, Inv, St) ->
-%% {[Kexpr],Forest,St}.
-%% Try to rewrite a #k_match{} originating from an 'andalso' or an 'orelse'.
-
-rewrite_match(#k_alt{first=First,then=Then}, Forest, Inv, St) ->
- case {First,Then} of
- {#k_select{var=#k_var{name=V}=Var,types=[TypeClause]},#k_var{name=V}} ->
- rewrite_match_1(Var, TypeClause, Forest, Inv, St);
- {_,_} ->
- throw(not_possible)
- end.
-
-rewrite_match_1(Var, #k_type_clause{values=Cs0}, Forest0, Inv, St0) ->
- Cs = sort([{Val,B} || #k_val_clause{val=#k_atom{val=Val},body=B} <- Cs0]),
- case Cs of
- [{false,False},{true,True}] ->
- rewrite_match_2(Var, False, True, Forest0, Inv, St0);
- _ ->
- throw(not_possible)
- end.
-
-rewrite_match_2(Var, False, #k_atom{val=true}, Forest0, Inv, St0) ->
- %% Originates from an 'orelse'.
- case False of
- #k_atom{val=NotBool} when not is_boolean(NotBool) ->
- rewrite_bool(Var, Forest0, Inv, St0);
- _ ->
- {CodeVar,Forest1,St1} = add_protected_expr(False, Forest0, St0),
- rewrite_bif('or', [Var,CodeVar], Forest1, Inv, St1)
- end;
-rewrite_match_2(Var, #k_atom{val=false}, True, Forest0, Inv, St0) ->
- %% Originates from an 'andalso'.
- {CodeVar,Forest1,St1} = add_protected_expr(True, Forest0, St0),
- rewrite_bif('and', [Var,CodeVar], Forest1, Inv, St1);
-rewrite_match_2(_V, _, _, _Forest, _Inv, _St) ->
- throw(not_possible).
-
-%% is_bool_expr(#k_var{}, Forest) -> true|false.
-%% Return true if the variable refers to a boolean expression
-%% that does not need an explicit '=:= true' test.
-
-is_bool_expr(V, Forest) ->
- case forest_peek_expr(V, Forest) of
- error ->
- %% Defined outside of the guard. We can't know.
- false;
- Expr ->
- case extract_bif(Expr) of
- {Name,Args} ->
- is_test(Name, Args) orelse
- erl_internal:bool_op(Name, length(Args));
- error ->
- %% Not a BIF. Should be possible to rewrite
- %% to a boolean. Definitely does not need
- %% a '=:= true' test.
- true
- end
- end.
-
-make_bif(Op, Args) ->
- #k_bif{op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=Op},
- arity=length(Args)},
- args=Args}.
-
-extract_bif(#k_bif{op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=Name}},
- args=Args}) ->
- {Name,Args};
-extract_bif(_) ->
- error.
-
-%% make_alt(First, Then) -> KMatch.
-%% Make a #k_alt{} within a #k_match{} to implement
-%% 'or' or 'orelse'.
-
-make_alt(First0, Then0) ->
- First1 = pre_seq(droplast(First0), last(First0)),
- Then1 = pre_seq(droplast(Then0), last(Then0)),
- First2 = make_protected(First1),
- Then2 = make_protected(Then1),
- Body = #ignored{},
- First3 = #k_guard_clause{guard=First2,body=Body},
- Then3 = #k_guard_clause{guard=Then2,body=Body},
- First = #k_guard{clauses=[First3]},
- Then = #k_guard{clauses=[Then3]},
- Alt = #k_alt{first=First,then=Then},
- #k_match{vars=[],body=Alt}.
-
-add_protected_expr(#k_atom{}=Atom, Forest, St) ->
- {Atom,Forest,St};
-add_protected_expr(#k_var{}=Var, Forest, St) ->
- {Var,Forest,St};
-add_protected_expr(E0, Forest, St) ->
- E = make_protected(E0),
- forest_add_expr(E, Forest, St).
-
-make_protected(#k_try{}=Try) ->
- Try;
-make_protected(B) ->
- #k_try{arg=B,vars=[#k_var{name=''}],body=#k_var{name=''},
- handler=#k_atom{val=false}}.
-
-make_failing_test() ->
- make_test(is_boolean, [#k_atom{val=fail}]).
-
-make_test(Op, Args) ->
- make_test(Op, Args, false).
-
-make_test(Op, Args, Inv) ->
- Remote = #k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=Op},
- arity=length(Args)},
- #k_test{op=Remote,args=Args,inverted=Inv}.
-
-is_test(Op, Args) ->
- A = length(Args),
- erl_internal:new_type_test(Op, A) orelse erl_internal:comp_op(Op, A).
-
-%% make_forest(Kexpr, St) -> {RootKexpr,Forest,St}.
-%% Build a forest out of Kexpr. RootKexpr is the final expression
-%% nested inside Kexpr.
-
-make_forest(G, St) ->
- make_forest_1(G, #{}, 0, St).
-
-%% make_forest(Kexpr, St) -> {RootKexpr,Forest,St}.
-%% Add to Forest from Kexpr. RootKexpr is the final expression
-%% nested inside Kexpr.
-
-make_forest(G, Forest0, St) ->
- N = forest_next_index(Forest0),
- make_forest_1(G, Forest0, N, St).
-
-make_forest_1(#k_try{arg=B}, Forest, I, St) ->
- make_forest_1(B, Forest, I, St);
-make_forest_1(#iset{vars=[]}=Iset0, Forest, I, St0) ->
- {UnrefVar,St} = new_var(St0),
- Iset = Iset0#iset{vars=[UnrefVar]},
- make_forest_1(Iset, Forest, I, St);
-make_forest_1(#iset{vars=[#k_var{name=V}],arg=Arg,body=B}, Forest0, I, St) ->
- Forest = Forest0#{V => {I,Arg}, {untaken,V} => true},
- make_forest_1(B, Forest, I+1, St);
-make_forest_1(Innermost, Forest, _I, St) ->
- {Innermost,Forest,St}.
-
-%% forest_take_expr(Kexpr, Forest) -> {Expr,Forest}.
-%% If Kexpr is a variable, take out the expression corresponding
-%% to variable in Forest. Expressions that have been taken out
-%% of the forest will not be included the Kexpr returned
-%% by forest_pre_seq/2.
-%%
-%% Throw a 'not_possible' exception if Kexpr is not a variable or
-%% if the name of the variable is not a key in Forest.
-
-forest_take_expr(#k_var{name=V}, Forest0) ->
- %% v3_core currently always generates guard expressions that can
- %% be represented as a tree. Other code generators (such as LFE)
- %% could generate guard expressions that can only be represented
- %% as a DAG (i.e. some nodes are referenced more than once). To
- %% handle DAGs, we must never remove a node from the forest, but
- %% just remove the {untaken,V} marker. That will effectively convert
- %% the DAG to a tree by duplicating the shared nodes and their
- %% descendants.
-
- case maps:find(V, Forest0) of
- {ok,{_,Expr}} ->
- Forest = maps:remove({untaken,V}, Forest0),
- {Expr,Forest};
- error ->
- throw(not_possible)
- end;
-forest_take_expr(_, _) ->
- throw(not_possible).
-
-%% forest_peek_expr(Kvar, Forest) -> Kexpr | error.
-%% Return the expression corresponding to Kvar in Forest or
-%% return 'error' if there is a corresponding expression.
-
-forest_peek_expr(#k_var{name=V}, Forest0) ->
- case maps:find(V, Forest0) of
- {ok,{_,Expr}} -> Expr;
- error -> error
- end.
-
-%% forest_add_expr(Kexpr, Forest, St) -> {Kvar,Forest,St}.
-%% Add a new expression to Forest.
-
-forest_add_expr(Expr, Forest0, St0) ->
- {#k_var{name=V}=Var,St} = new_var(St0),
- N = forest_next_index(Forest0),
- Forest = Forest0#{V => {N,Expr}},
- {Var,Forest,St}.
-
-forest_next_index(Forest) ->
- 1 + lists:max([N || {N,_} <- maps:values(Forest),
- is_integer(N)] ++ [0]).
-
-%% forest_pre_seq([Kexpr], Forest) -> Kexpr.
-%% Package the list of Kexprs into a nested Kexpr, prepending all
-%% expressions in Forest that have not been taken out using
-%% forest_take_expr/2.
-
-forest_pre_seq(Exprs, Forest) ->
- Es0 = [#k_var{name=V} || {untaken,V} <- maps:keys(Forest)],
- Es = Es0 ++ Exprs,
- Vs = extract_all_vars(Es, Forest, []),
- Pre0 = sort([{maps:get(V, Forest),V} || V <- Vs]),
- Pre = [#iset{vars=[#k_var{name=V}],arg=A} ||
- {{_,A},V} <- Pre0],
- pre_seq(Pre++droplast(Exprs), last(Exprs)).
-
-extract_all_vars(Es, Forest, Acc0) ->
- case extract_var_list(Es) of
- [] ->
- Acc0;
- [_|_]=Vs0 ->
- Vs = [V || V <- Vs0, maps:is_key(V, Forest)],
- NewVs = ordsets:subtract(Vs, Acc0),
- NewEs = [begin
- {_,E} = maps:get(V, Forest),
- E
- end || V <- NewVs],
- Acc = union(NewVs, Acc0),
- extract_all_vars(NewEs, Forest, Acc)
- end.
-
-extract_vars(#iset{arg=A,body=B}) ->
- union(extract_vars(A), extract_vars(B));
-extract_vars(#k_bif{args=Args}) ->
- ordsets:from_list(lit_list_vars(Args));
-extract_vars(#k_call{}) ->
- [];
-extract_vars(#k_test{args=Args}) ->
- ordsets:from_list(lit_list_vars(Args));
-extract_vars(#k_match{body=Body}) ->
- extract_vars(Body);
-extract_vars(#k_alt{first=First,then=Then}) ->
- union(extract_vars(First), extract_vars(Then));
-extract_vars(#k_guard{clauses=Cs}) ->
- extract_var_list(Cs);
-extract_vars(#k_guard_clause{guard=G}) ->
- extract_vars(G);
-extract_vars(#k_select{var=Var,types=Types}) ->
- union(ordsets:from_list(lit_vars(Var)),
- extract_var_list(Types));
-extract_vars(#k_type_clause{values=Values}) ->
- extract_var_list(Values);
-extract_vars(#k_val_clause{body=Body}) ->
- extract_vars(Body);
-extract_vars(#k_try{arg=Arg}) ->
- extract_vars(Arg);
-extract_vars(Lit) ->
- ordsets:from_list(lit_vars(Lit)).
-
-extract_var_list(L) ->
- union([extract_vars(E) || E <- L]).
-
-%% Wrap the entire guard in a try/catch if needed.
-
-wrap_guard(#c_try{}=Try, St) -> {Try,St};
-wrap_guard(Core, St0) ->
- {VarName,St} = new_var_name(St0),
- Var = #c_var{name=VarName},
- Try = #c_try{arg=Core,vars=[Var],body=Var,evars=[],handler=#c_literal{val=false}},
- {Try,St}.
-
%% gexpr_test(Kexpr, State) -> {Kexpr,State}.
%% Builds the final boolean test from the last Kexpr in a guard test.
%% Must enter try blocks and isets and find the last Kexpr in them.
%% This must end in a recognised BEAM test!
-gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=F},arity=Ar}=Op,
+gexpr_test(#k_bif{anno=A,
+ op=#k_remote{mod=#k_literal{val=erlang},
+ name=#k_literal{val=F},arity=Ar}=Op,
args=Kargs}=Ke, St) ->
%% Either convert to test if ok, or add test.
%% At this stage, erlang:float/1 is not a type test. (It should
@@ -696,7 +212,7 @@ gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
false -> gexpr_test_add(Ke, St) %Add equality test
end;
gexpr_test(#k_try{arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, St0) ->
+ handler=#k_literal{val=false}}=Try, St0) ->
{B,St} = gexpr_test(B0, St0),
%%ok = io:fwrite("~w: ~p~n", [?LINE,{B0,B}]),
{Try#k_try{arg=B},St};
@@ -706,42 +222,41 @@ gexpr_test(#iset{body=B0}=Iset, St0) ->
gexpr_test(Ke, St) -> gexpr_test_add(Ke, St). %Add equality test
gexpr_test_add(Ke, St0) ->
- Test = #k_remote{mod=#k_atom{val='erlang'},
- name=#k_atom{val='=:='},
+ Test = #k_remote{mod=#k_literal{val='erlang'},
+ name=#k_literal{val='=:='},
arity=2},
{Ae,Ap,St1} = force_atomic(Ke, St0),
{pre_seq(Ap, #k_test{anno=get_kanno(Ke),
- op=Test,args=[Ae,#k_atom{val='true'}]}),St1}.
+ op=Test,args=[Ae,#k_literal{val='true'}]}),St1}.
%% expr(Cexpr, Sub, State) -> {Kexpr,[PreKexpr],State}.
%% Convert a Core expression, flattening it at the same time.
-expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) ->
- %% A local in an expression.
- %% For now, these are wrapped into a fun by reverse
- %% eta-conversion, but really, there should be exactly one
- %% such "lambda function" for each escaping local name,
- %% instead of one for each occurrence as done now.
+expr(#c_var{anno=A0,name={Name,Arity}}=Fname, Sub, St) ->
Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
- V <- integers(1, Arity)],
- Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{anno=A,op=Fname,args=Vs}},
- expr(Fun, Sub, St);
+ V <- integers(1, Arity)],
+ case St#kern.no_shared_fun_wrappers of
+ false ->
+ %% Generate a (possibly shared) wrapper function for calling
+ %% this function.
+ Wrapper0 = ["-fun.",atom_to_list(Name),"/",integer_to_list(Arity),"-"],
+ Wrapper = list_to_atom(flatten(Wrapper0)),
+ Id = {id,{0,0,Wrapper}},
+ A = keyreplace(id, 1, A0, Id),
+ Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{anno=A,op=Fname,args=Vs}},
+ expr(Fun, Sub, St);
+ true ->
+ %% For backward compatibility with OTP 22 and earlier,
+ %% use the pre-generated name for the fun wrapper.
+ %% There will be one wrapper function for each occurrence
+ %% of `fun F/A`.
+ Fun = #c_fun{anno=A0,vars=Vs,body=#c_apply{anno=A0,op=Fname,args=Vs}},
+ expr(Fun, Sub, St)
+ end;
expr(#c_var{anno=A,name=V}, Sub, St) ->
{#k_var{anno=A,name=get_vsub(V, Sub)},[],St};
expr(#c_literal{anno=A,val=V}, _Sub, St) ->
- Klit = case V of
- [] ->
- #k_nil{anno=A};
- V when is_integer(V) ->
- #k_int{anno=A,val=V};
- V when is_float(V) ->
- #k_float{anno=A,val=V};
- V when is_atom(V) ->
- #k_atom{anno=A,val=V};
- _ ->
- #k_literal{anno=A,val=V}
- end,
- {Klit,[],St};
+ {#k_literal{anno=A,val=V},[],St};
expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
%% Do cons in two steps, first the expressions left to right, then
%% any remaining literals right to left.
@@ -768,24 +283,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
expr(Error, Sub, St1)
end;
-expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) ->
- FA = case OldFF of
- undefined ->
- Func;
- _ ->
- case lists:keyfind(id, 1, A) of
- {id,{_,_,Name}} -> Name;
- _ ->
- case lists:keyfind(letrec_name, 1, A) of
- {letrec_name,Name} -> Name;
- _ -> unknown_fun
- end
- end
- end,
- {Kvs,Sub1,St1} = pattern_list(Cvs, Sub0, St0#kern{ff=FA}),
+expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0,
+ #kern{fargs=OldFargs}=St0) ->
+ {Kvs,Sub1,St1} = pattern_list(Cvs, Sub0, St0),
%%ok = io:fwrite("~w: ~p~n", [?LINE,{{Cvs,Sub0,St0},{Kvs,Sub1,St1}}]),
- {Kb,Pb,St2} = body(Cb, Sub1, St1#kern{ff=FA}),
- {#ifun{anno=A,vars=Kvs,body=pre_seq(Pb, Kb)},[],St2#kern{ff=OldFF}};
+ {Kb,Pb,St2} = body(Cb, Sub1, St1#kern{fargs=Kvs}),
+ {#ifun{anno=A,vars=Kvs,body=pre_seq(Pb, Kb)},[],St2#kern{fargs=OldFargs}};
expr(#c_seq{arg=Ca,body=Cb}, Sub, St0) ->
{Ka,Pa,St1} = body(Ca, Sub, St0),
{Kb,Pb,St2} = body(Cb, Sub, St1),
@@ -806,104 +309,42 @@ expr(#c_let{anno=A,vars=Cvs,arg=Ca,body=Cb}, Sub0, St0) ->
end,
{Kb,Pb,St3} = body(Cb, Sub1, St2),
{Kb,Pa ++ Sets ++ Pb,St3};
-expr(#c_letrec{anno=A,defs=Cfs,body=Cb}, Sub0, St0) ->
- %% Make new function names and store substitution.
- {Fs0,{Sub1,St1}} =
- mapfoldl(fun ({#c_var{name={F,Ar}},B0}, {Sub,S0}) ->
- {N,St1} = new_fun_name(atom_to_list(F)
- ++ "/" ++
- integer_to_list(Ar),
- S0),
- B = set_kanno(B0, [{letrec_name,N}]),
- {{N,B},{set_fsub(F, Ar, N, Sub),St1}}
- end, {Sub0,St0}, Cfs),
- %% Run translation on functions and body.
- {Fs1,St2} = mapfoldl(fun ({N,Fd0}, S1) ->
- {Fd1,[],St2} = expr(Fd0, Sub1, S1#kern{ff=N}),
- Fd = set_kanno(Fd1, A),
- {{N,Fd},St2}
- end, St1, Fs0),
- {Kb,Pb,St3} = body(Cb, Sub1, St2#kern{ff=St1#kern.ff}),
- {Kb,[#iletrec{anno=A,defs=Fs1}|Pb],St3};
+expr(#c_letrec{anno=A,defs=Cfs,body=Cb}, Sub, St) ->
+ case member(letrec_goto, A) of
+ true ->
+ letrec_goto(Cfs, Cb, Sub, St);
+ false ->
+ letrec_local_function(A, Cfs, Cb, Sub, St)
+ end;
expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
{Ka,Pa,St1} = body(Ca, Sub, St0), %This is a body!
{Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
{Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
- Match = flatten_seq(build_match(Kvs, Km)),
+ Match = flatten_seq(build_match(Km)),
{last(Match),Pa ++ Pv ++ droplast(Match),St3};
-expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
- {Ke,Pe,St1} = atomic(Ce, Sub, St0), %Force this to be atomic!
- {Rvar,St2} = new_var(St1),
- %% Need to massage accept clauses and add reject clause before matching.
- Ccs1 = map(fun (#c_clause{anno=Banno,body=B0}=C) ->
- B1 = #c_seq{arg=#ireceive_accept{anno=A},body=B0},
- C#c_clause{anno=Banno,body=B1}
- end, Ccs0),
- {Mpat,St3} = new_var_name(St2),
- Rc = #c_clause{anno=[compiler_generated|A],
- pats=[#c_var{name=Mpat}],guard=#c_literal{anno=A,val=true},
- body=#ireceive_next{anno=A}},
- {Km,St4} = kmatch([Rvar], Ccs1 ++ [Rc], Sub, add_var_def(Rvar, St3)),
- {Ka,Pa,St5} = body(Ca, Sub, St4),
- {#k_receive{anno=A,var=Rvar,body=Km,timeout=Ke,action=pre_seq(Pa, Ka)},
- Pe,St5};
expr(#c_apply{anno=A,op=Cop,args=Cargs}, Sub, St) ->
c_apply(A, Cop, Cargs, Sub, St);
-expr(#c_call{anno=A,module=#c_literal{val=erlang},name=#c_literal{val=is_record},
- args=[_,Tag,Sz]=Args0}, Sub, St0) ->
- {Args,Ap,St} = atomic_list(Args0, Sub, St0),
- Remote = #k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=is_record},arity=3},
- case {Tag,Sz} of
- {#c_literal{val=Atom},#c_literal{val=Int}}
- when is_atom(Atom), is_integer(Int) ->
- %% Tag and size are literals. Make it a BIF, which will actually
- %% be expanded out in a later pass.
- {#k_bif{anno=A,op=Remote,args=Args},Ap,St};
- {_,_} ->
- %% (Only in bodies.) Make it into an actual call to the BIF.
- {#k_call{anno=A,op=Remote,args=Args},Ap,St}
- end;
expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
Ar = length(Cargs),
- {Type,St1} = case call_type(M0, F0, Ar) of
- error ->
- %% Invalid call (e.g. M:42/3). Issue a warning,
- %% and let the generated code use the old explict apply.
- {old_apply,add_warning(get_line(A), bad_call, A, St0)};
- Type0 ->
- {Type0,St0}
- end,
-
- case Type of
- old_apply ->
+ {[M,F|Kargs],Ap,St1} = atomic_list([M0,F0|Cargs], Sub, St0),
+ Remote = #k_remote{mod=M,name=F,arity=Ar},
+ case call_type(M0, F0, Cargs) of
+ bif ->
+ {#k_bif{anno=A,op=Remote,args=Kargs},Ap,St1};
+ call ->
+ {#k_call{anno=A,op=Remote,args=Kargs},Ap,St1};
+ error ->
+ %% Invalid call (e.g. M:42/3). Issue a warning, and let
+ %% the generated code use the old explict apply.
+ St = add_warning(get_line(A), bad_call, A, St0),
Call = #c_call{anno=A,
module=#c_literal{val=erlang},
name=#c_literal{val=apply},
args=[M0,F0,cerl:make_list(Cargs)]},
- expr(Call, Sub, St1);
- _ ->
- {[M1,F1|Kargs],Ap,St} = atomic_list([M0,F0|Cargs], Sub, St1),
- Call = case Type of
- bif ->
- #k_bif{anno=A,op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs};
- call ->
- #k_call{anno=A,op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs};
- apply ->
- #k_call{anno=A,op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs}
- end,
- {Call,Ap,St}
+ expr(Call, Sub, St)
end;
-expr(#c_primop{anno=A,name=#c_literal{val=match_fail},args=Cargs0}, Sub, St0) ->
- Cargs = translate_match_fail(Cargs0, Sub, A, St0),
- {Kargs,Ap,St} = atomic_list(Cargs, Sub, St0),
- Ar = length(Cargs),
- Call = #k_call{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=error},
- arity=Ar},args=Kargs},
- {Call,Ap,St};
+expr(#c_primop{anno=A,name=#c_literal{val=match_fail},args=[Arg]}, Sub, St) ->
+ translate_match_fail(Arg, Sub, A, St);
expr(#c_primop{anno=A,name=#c_literal{val=N},args=Cargs}, Sub, St0) ->
{Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
Ar = length(Cargs),
@@ -921,60 +362,90 @@ expr(#c_try{anno=A,arg=Ca,vars=Cvs,body=Cb,evars=Evs,handler=Ch}, Sub0, St0) ->
evars=Kevs,handler=pre_seq(Ph, Kh)},[],St5};
expr(#c_catch{anno=A,body=Cb}, Sub, St0) ->
{Kb,Pb,St1} = body(Cb, Sub, St0),
- {#k_catch{anno=A,body=pre_seq(Pb, Kb)},[],St1};
-%% Handle internal expressions.
-expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}.
-
-%% Translate a function_clause exception to a case_clause exception if
-%% it has been moved into another function. (A function_clause exception
-%% will not work correctly if it is moved into another function, or
-%% even if it is invoked not from the top level in the correct function.)
-translate_match_fail(Args, Sub, Anno, St) ->
- case Args of
- [#c_tuple{es=[#c_literal{val=function_clause}|As]}] ->
- translate_match_fail_1(Anno, As, Sub, St);
- [#c_literal{val=Tuple}] when is_tuple(Tuple) ->
- %% The inliner may have created a literal out of
- %% the original #c_tuple{}.
- case tuple_to_list(Tuple) of
- [function_clause|As0] ->
- As = [#c_literal{val=E} || E <- As0],
- translate_match_fail_1(Anno, As, Sub, St);
- _ ->
- Args
- end;
- _ ->
- %% Not a function_clause exception.
- Args
+ {#k_catch{anno=A,body=pre_seq(Pb, Kb)},[],St1}.
+
+%% Implement letrec in the traditional way as a local
+%% function for each definition in the letrec.
+
+letrec_local_function(A, Cfs, Cb, Sub0, St0) ->
+ %% Make new function names and store substitution.
+ {Fs0,{Sub1,St1}} =
+ mapfoldl(fun ({#c_var{name={F,Ar}},B0}, {Sub,S0}) ->
+ {N,St1} = new_fun_name(atom_to_list(F)
+ ++ "/" ++
+ integer_to_list(Ar),
+ S0),
+ B = set_kanno(B0, [{letrec_name,N}]),
+ {{N,B},{set_fsub(F, Ar, N, Sub),St1}}
+ end, {Sub0,St0}, Cfs),
+ %% Run translation on functions and body.
+ {Fs1,St2} = mapfoldl(fun ({N,Fd0}, S1) ->
+ {Fd1,[],St2} = expr(Fd0, Sub1, S1),
+ Fd = set_kanno(Fd1, A),
+ {{N,Fd},St2}
+ end, St1, Fs0),
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kb,[#iletrec{anno=A,defs=Fs1}|Pb],St3}.
+
+%% Implement letrec with the single definition as a label and each
+%% apply of it as a goto.
+
+letrec_goto([{#c_var{name={Label,0}},Cfail}], Cb, Sub0,
+ #kern{labels=Labels0}=St0) ->
+ Labels = cerl_sets:add_element(Label, Labels0),
+ {Kb,Pb,St1} = body(Cb, Sub0, St0#kern{labels=Labels}),
+ #c_fun{body=FailBody} = Cfail,
+ {Kfail,Fb,St2} = body(FailBody, Sub0, St1),
+ case {Kb,Kfail,Fb} of
+ {#k_goto{label=Label},#k_goto{}=InnerGoto,[]} ->
+ {InnerGoto,Pb,St2};
+ {_,_,_} ->
+ St3 = St2#kern{labels=Labels0},
+ Alt = #k_letrec_goto{label=Label,first=Kb,then=pre_seq(Fb, Kfail)},
+ {Alt,Pb,St3}
end.
-translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
- AnnoFunc = case keyfind(function_name, 1, Anno) of
- false ->
- none; %Force rewrite.
- {function_name,{Name,Arity}} ->
- {get_fsub(Name, Arity, Sub),Arity}
- end,
- case {AnnoFunc,FF} of
- {Same,Same} ->
- %% Still in the correct function.
- translate_fc(As);
- {{F,_},F} ->
- %% Still in the correct function.
- translate_fc(As);
- _ ->
- %% Wrong function or no function_name annotation.
- %%
- %% The inliner has copied the match_fail(function_clause)
- %% primop from another function (or from another instance of
- %% the current function). match_fail(function_clause) will
- %% only work at the top level of the function it was originally
- %% defined in, so we will need to rewrite it to a case_clause.
- [c_tuple([#c_literal{val=case_clause},c_tuple(As)])]
+%% translate_match_fail(Arg, Sub, Anno, St) -> {Kexpr,[PreKexpr],State}.
+%% Translate a match_fail primop to a call erlang:error/1 or
+%% erlang:error/2.
+
+translate_match_fail(Arg, Sub, Anno, St0) ->
+ Cargs = case {cerl:data_type(Arg),cerl:data_es(Arg)} of
+ {tuple,[#c_literal{val=function_clause}|As]} ->
+ translate_fc_args(As, Sub, St0);
+ {_,_} ->
+ [Arg]
+ end,
+ {Kargs,Ap,St} = atomic_list(Cargs, Sub, St0),
+ Ar = length(Cargs),
+ Call = #k_call{anno=Anno,
+ op=#k_remote{mod=#k_literal{val=erlang},
+ name=#k_literal{val=error},
+ arity=Ar},args=Kargs},
+ {Call,Ap,St}.
+
+translate_fc_args(As, Sub, #kern{fargs=Fargs}) ->
+ case same_args(As, Fargs, Sub) of
+ true ->
+ %% The arguments for the `function_clause` exception are
+ %% the arguments for the current function in the correct
+ %% order.
+ [#c_literal{val=function_clause},cerl:make_list(As)];
+ false ->
+ %% The arguments in the `function_clause` exception don't
+ %% match the arguments for the current function because
+ %% of inlining. Keeping the `function_clause`
+ %% exception reason would be confusing. Rewrite it to
+ %% a `case_clause` exception with the arguments in a
+ %% tuple.
+ [cerl:c_tuple([#c_literal{val=case_clause},
+ cerl:c_tuple(As)])]
end.
-translate_fc(Args) ->
- [#c_literal{val=function_clause},cerl:make_list(Args)].
+same_args([#c_var{name=Cv}|Vs], [#k_var{name=Kv}|As], Sub) ->
+ get_vsub(Cv, Sub) =:= Kv andalso same_args(Vs, As, Sub);
+same_args([], [], _Sub) -> true;
+same_args(_, _, _) -> false.
expr_map(A,Var0,Ces,Sub,St0) ->
{Var,Mps,St1} = expr(Var0, Sub, St0),
@@ -1031,45 +502,43 @@ map_group_pairs(A, Var, Pairs0, Esp, St0) ->
end.
map_remove_dup_keys(Es) ->
- dict:to_list(map_remove_dup_keys(Es, dict:new())).
+ map_remove_dup_keys(Es, #{}).
-map_remove_dup_keys([{assoc,K0,V}|Es0],Used0) ->
+map_remove_dup_keys([{assoc,K0,V}|Es0], Used0) ->
K = map_key_clean(K0),
- Op = case dict:find(K, Used0) of
- {ok,{exact,_,_}} -> exact;
- _ -> assoc
- end,
- Used1 = dict:store(K, {Op,K0,V}, Used0),
+ Op = case Used0 of
+ #{K:={exact,_,_}} -> exact;
+ #{} -> assoc
+ end,
+ Used1 = Used0#{K=>{Op,K0,V}},
map_remove_dup_keys(Es0, Used1);
-map_remove_dup_keys([{exact,K0,V}|Es0],Used0) ->
+map_remove_dup_keys([{exact,K0,V}|Es0], Used0) ->
K = map_key_clean(K0),
- Op = case dict:find(K, Used0) of
- {ok,{assoc,_,_}} -> assoc;
- _ -> exact
- end,
- Used1 = dict:store(K, {Op,K0,V}, Used0),
+ Op = case Used0 of
+ #{K:={assoc,_,_}} -> assoc;
+ #{} -> exact
+ end,
+ Used1 = Used0#{K=>{Op,K0,V}},
map_remove_dup_keys(Es0, Used1);
-map_remove_dup_keys([], Used) -> Used.
+map_remove_dup_keys([], Used) ->
+ %% We must sort the map entries to ensure consistent
+ %% order from compilation to compilation.
+ sort(maps:to_list(Used)).
-%% Be explicit instead of using set_kanno(K, []).
+%% Clean a map key from annotations.
map_key_clean(#k_var{name=V}) -> {var,V};
-map_key_clean(#k_literal{val=V}) -> {lit,V};
-map_key_clean(#k_int{val=V}) -> {lit,V};
-map_key_clean(#k_float{val=V}) -> {lit,V};
-map_key_clean(#k_atom{val=V}) -> {lit,V};
-map_key_clean(#k_nil{}) -> {lit,[]}.
-
+map_key_clean(#k_literal{val=V}) -> {lit,V}.
-%% call_type(Module, Function, Arity) -> call | bif | apply | error.
+%% call_type(Module, Function, Arity) -> call | bif | error.
%% Classify the call.
-call_type(#c_literal{val=M}, #c_literal{val=F}, Ar) when is_atom(M), is_atom(F) ->
- case is_remote_bif(M, F, Ar) of
+call_type(#c_literal{val=M}, #c_literal{val=F}, As) when is_atom(M), is_atom(F) ->
+ case is_remote_bif(M, F, As) of
false -> call;
true -> bif
end;
-call_type(#c_var{}, #c_literal{val=A}, _) when is_atom(A) -> apply;
-call_type(#c_literal{val=A}, #c_var{}, _) when is_atom(A) -> apply;
-call_type(#c_var{}, #c_var{}, _) -> apply;
+call_type(#c_var{}, #c_literal{val=A}, _) when is_atom(A) -> call;
+call_type(#c_literal{val=A}, #c_var{}, _) when is_atom(A) -> call;
+call_type(#c_var{}, #c_var{}, _) -> call;
call_type(_, _, _) -> error.
%% match_vars(Kexpr, State) -> {[Kvar],[PreKexpr],State}.
@@ -1085,13 +554,19 @@ match_vars(Ka, St0) ->
{[V],Vp,St1}.
%% c_apply(A, Op, [Carg], Sub, State) -> {Kexpr,[PreKexpr],State}.
-%% Transform application, detect which are guaranteed to be bifs.
+%% Transform application.
-c_apply(A, #c_var{anno=Ra,name={F0,Ar}}, Cargs, Sub, St0) ->
- {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
- F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
- {#k_call{anno=A,op=#k_local{anno=Ra,name=F1,arity=Ar},args=Kargs},
- Ap,St1};
+c_apply(A, #c_var{anno=Ra,name={F0,Ar}}, Cargs, Sub, #kern{labels=Labels}=St0) ->
+ case Ar =:= 0 andalso cerl_sets:is_element(F0, Labels) of
+ true ->
+ %% This is a goto to a label in a letrec_goto construct.
+ {#k_goto{label=F0},[],St0};
+ false ->
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
+ {#k_call{anno=A,op=#k_local{anno=Ra,name=F1,arity=Ar},args=Kargs},
+ Ap,St1}
+ end;
c_apply(A, Cop, Cargs, Sub, St0) ->
{Kop,Op,St1} = variable(Cop, Sub, St0),
{Kargs,Ap,St2} = atomic_list(Cargs, Sub, St1),
@@ -1125,12 +600,6 @@ force_atomic(Ke, St0) ->
{V,[#iset{vars=[V],arg=Ke}],St1}
end.
-% force_atomic_list(Kes, St) ->
-% foldr(fun (Ka, {As,Asp,St0}) ->
-% {A,Ap,St1} = force_atomic(Ka, St0),
-% {[A|As],Ap ++ Asp,St1}
-% end, {[],[],St}, Kes).
-
atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U0,type=T,flags=Fs0}|Es0],
Sub, St0) ->
{E,Ap1,St1} = atomic(E0, Sub, St0),
@@ -1148,11 +617,14 @@ atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U0,type=T,flags=Fs0}|Es0],
atomic_bin([], _Sub, St) -> {#k_bin_end{},[],St}.
validate_bin_element_size(#k_var{}) -> ok;
-validate_bin_element_size(#k_int{val=V}) when V >= 0 -> ok;
-validate_bin_element_size(#k_atom{val=all}) -> ok;
-validate_bin_element_size(#k_atom{val=undefined}) -> ok;
-validate_bin_element_size(_) -> throw(bad_element_size).
-
+validate_bin_element_size(#k_literal{val=Val}) ->
+ case Val of
+ all -> ok;
+ undefined -> ok;
+ _ when is_integer(Val), Val >= 0 -> ok;
+ _ -> throw(bad_element_size)
+ end.
+
%% atomic_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
atomic_list(Ces, Sub, St) ->
@@ -1162,15 +634,11 @@ atomic_list(Ces, Sub, St) ->
end, {[],[],St}, Ces).
%% is_atomic(Kexpr) -> boolean().
-%% Is a Kexpr atomic? Strings are NOT considered atomic!
+%% Is a Kexpr atomic?
is_atomic(#k_literal{}) -> true;
-is_atomic(#k_int{}) -> true;
-is_atomic(#k_float{}) -> true;
-is_atomic(#k_atom{}) -> true;
-%%is_atomic(#k_char{}) -> true; %No characters
-is_atomic(#k_nil{}) -> true;
is_atomic(#k_var{}) -> true;
+%%is_atomic(#k_char{}) -> true; %No characters
is_atomic(_) -> false.
%% variable(Cexpr, Sub, State) -> {Kvar,[PreKexpr],State}.
@@ -1234,7 +702,7 @@ flatten_alias(Pat) -> {[],Pat}.
pattern_map_pairs(Ces0, Isub, Osub0, St0) ->
%% pattern the pair keys and values as normal
- {Kes,{Osub1,St1}} = lists:mapfoldl(fun
+ {Kes,{Osub1,St1}} = mapfoldl(fun
(#c_map_pair{anno=A,key=Ck,val=Cv},{Osubi0,Sti0}) ->
{Kk,[],Sti1} = expr(Ck, Isub, Sti0),
{Kv,Osubi2,Sti2} = pattern(Cv, Isub, Osubi0, Sti1),
@@ -1242,7 +710,7 @@ pattern_map_pairs(Ces0, Isub, Osub0, St0) ->
end, {Osub0, St0}, Ces0),
%% It is later assumed that these keys are term sorted
%% so we need to sort them here
- Kes1 = lists:sort(fun
+ Kes1 = sort(fun
(#k_map_pair{key=KkA},#k_map_pair{key=KkB}) ->
A = map_key_clean(KkA),
B = map_key_clean(KkB),
@@ -1250,41 +718,84 @@ pattern_map_pairs(Ces0, Isub, Osub0, St0) ->
end, Kes),
{Kes1,Osub1,St1}.
-pattern_bin(Es, Isub, Osub0, St0) ->
- {Kbin,{_,Osub},St} = pattern_bin_1(Es, Isub, Osub0, St0),
- {Kbin,Osub,St}.
+pattern_bin(Es, Isub, Osub0, St) ->
+ pattern_bin_1(Es, Isub, Osub0, St).
-pattern_bin_1([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
- Isub0, Osub0, St0) ->
- {S1,[],St1} = expr(S0, Isub0, St0),
+pattern_bin_1([#c_bitstr{anno=A,val=E0,size=S0,unit=U0,type=T,flags=Fs0}|Es0],
+ Isub, Osub0, St0) ->
+ {S1,[],St1} = expr(S0, Isub, St0),
S = case S1 of
- #k_int{} -> S1;
#k_var{} -> S1;
- #k_atom{} -> S1;
+ #k_literal{val=Val} when is_integer(Val); is_atom(Val) -> S1;
_ ->
%% Bad size (coming from an optimization or Core Erlang
%% source code) - replace it with a known atom because
%% a literal or bit syntax construction can cause further
%% problems.
- #k_atom{val=bad_size}
+ #k_literal{val=bad_size}
end,
- U0 = cerl:concrete(U),
- Fs0 = cerl:concrete(Fs),
- %%ok= io:fwrite("~w: ~p~n", [?LINE,{B0,S,U0,Fs0}]),
- {E,Osub1,St2} = pattern(E0, Isub0, Osub0, St1),
- Isub1 = case E0 of
- #c_var{name=V} ->
- set_vsub(V, E#k_var.name, Isub0);
- _ -> Isub0
- end,
- {Es,{Isub,Osub},St3} = pattern_bin_1(Es0, Isub1, Osub1, St2),
- {#k_bin_seg{anno=A,size=S,
- unit=U0,
- type=cerl:concrete(T),
- flags=Fs0,
- seg=E,next=Es},
- {Isub,Osub},St3};
-pattern_bin_1([], Isub, Osub, St) -> {#k_bin_end{},{Isub,Osub},St}.
+ U = cerl:concrete(U0),
+ Fs = cerl:concrete(Fs0),
+ {E,Osub1,St2} = pattern(E0, Isub, Osub0, St1),
+ {Es,Osub,St3} = pattern_bin_1(Es0, Isub, Osub1, St2),
+ {build_bin_seg(A, S, U, cerl:concrete(T), Fs, E, Es),Osub,St3};
+pattern_bin_1([], _Isub, Osub, St) ->
+ {#k_bin_end{},Osub,St}.
+
+%% build_bin_seg(Anno, Size, Unit, Type, Flags, Seg, Next) -> #k_bin_seg{}.
+%% This function normalizes literal integers with size > 8 and literal
+%% utf8 segments into integers with size = 8 (and potentially an integer
+%% with size less than 8 at the end). This is so further optimizations
+%% have a normalized view of literal integers, allowing us to generate
+%% more literals and group more clauses. Those integers may be "squeezed"
+%% later into the largest integer possible.
+%%
+build_bin_seg(A, #k_literal{val=Bits} = Sz, U, integer=Type,
+ [unsigned,big]=Flags, #k_literal{val=Int}=Seg, Next) ->
+ Size = Bits * U,
+ case integer_fits_and_is_expandable(Int, Size) of
+ true -> build_bin_seg_integer_recur(A, Size, Int, Next);
+ false -> #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}
+ end;
+build_bin_seg(A, Sz, U, utf8=Type, [unsigned,big]=Flags, #k_literal{val=Utf8} = Seg, Next) ->
+ case utf8_fits(Utf8) of
+ {Int, Bits} -> build_bin_seg_integer_recur(A, Bits, Int, Next);
+ error -> #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}
+ end;
+build_bin_seg(A, Sz, U, Type, Flags, Seg, Next) ->
+ #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}.
+
+build_bin_seg_integer_recur(A, Bits, Val, Next) when Bits > 8 ->
+ NextBits = Bits - 8,
+ NextVal = Val band ((1 bsl NextBits) - 1),
+ Last = build_bin_seg_integer_recur(A, NextBits, NextVal, Next),
+ build_bin_seg_integer(A, 8, Val bsr NextBits, Last);
+
+build_bin_seg_integer_recur(A, Bits, Val, Next) ->
+ build_bin_seg_integer(A, Bits, Val, Next).
+
+build_bin_seg_integer(A, Bits, Val, Next) ->
+ Sz = #k_literal{anno=A,val=Bits},
+ Seg = #k_literal{anno=A,val=Val},
+ #k_bin_seg{anno=A,size=Sz,unit=1,type=integer,flags=[unsigned,big],seg=Seg,next=Next}.
+
+integer_fits_and_is_expandable(Int, Size) when 0 < Size, Size =< ?EXPAND_MAX_SIZE_SEGMENT ->
+ case <<Int:Size>> of
+ <<Int:Size>> -> true;
+ _ -> false
+ end;
+integer_fits_and_is_expandable(_Int, _Size) ->
+ false.
+
+utf8_fits(Utf8) ->
+ try
+ Bin = <<Utf8/utf8>>,
+ Bits = bit_size(Bin),
+ <<Int:Bits>> = Bin,
+ {Int, Bits}
+ catch
+ _:_ -> error
+ end.
%% pattern_list([Cexpr], Sub, State) -> {[Kexpr],Sub,State}.
@@ -1411,23 +922,25 @@ new_vars(0, St, Vs) -> {Vs,St}.
make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ].
-add_var_def(V, St) ->
- St#kern{ds=cerl_sets:add_element(V#k_var.name, St#kern.ds)}.
-
-%%add_vars_def(Vs, St) ->
-%% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end,
-%% St#kern.ds, Vs),
-%% St#kern{ds=Ds}.
-
%% is_remote_bif(Mod, Name, Arity) -> true | false.
%% Test if function is really a BIF.
-is_remote_bif(erlang, get, 1) -> true;
-is_remote_bif(erlang, N, A) ->
- case erl_internal:guard_bif(N, A) of
+is_remote_bif(erlang, get, [_]) -> true;
+is_remote_bif(erlang, is_record, [_,Tag,Sz]) ->
+ case {Tag,Sz} of
+ {#c_literal{val=Atom},#c_literal{val=Int}}
+ when is_atom(Atom), is_integer(Int) ->
+ %% Tag and size are literals. This is a guard BIF.
+ true;
+ {_,_} ->
+ false
+ end;
+is_remote_bif(erlang, N, As) ->
+ Arity = length(As),
+ case erl_internal:guard_bif(N, Arity) of
true -> true;
false ->
- try erl_internal:op_type(N, A) of
+ try erl_internal:op_type(N, Arity) of
arith -> true;
bool -> true;
comp -> true;
@@ -1445,6 +958,7 @@ is_remote_bif(_, _, _) -> false.
%% return multiple values. Only used in bodies where a BIF may be
%% called for effect only.
+bif_vals(recv_peek_message, 0) -> 2;
bif_vals(_, _) -> 1.
bif_vals(_, _, _) -> 1.
@@ -1488,11 +1002,6 @@ foldr2(_, Acc, [], []) -> Acc.
kmatch(Us, Ccs, Sub, St0) ->
{Cs,St1} = match_pre(Ccs, Sub, St0), %Convert clauses
Def = fail,
-%% Def = #k_call{anno=[compiler_generated],
-%% op=#k_remote{mod=#k_atom{val=erlang},
-%% name=#k_atom{val=exit},
-%% arity=1},
-%% args=[#k_atom{val=kernel_match_error}]},
match(Us, Cs, Def, St1). %Do the match.
%% match_pre([Cclause], Sub, State) -> {[Clause],State}.
@@ -1566,7 +1075,7 @@ maybe_add_warning(Ke, MatchAnno, St) ->
get_line([Line|_]) when is_integer(Line) -> Line;
get_line([_|T]) -> get_line(T);
get_line([]) -> none.
-
+
get_file([{file,File}|_]) -> File;
get_file([_|T]) -> get_file(T);
get_file([]) -> "no_file". % should not happen
@@ -1682,31 +1191,27 @@ expand_pat_lit_clause(#iclause{pats=[#k_literal{anno=A,val=Val}|Ps]}=C) ->
expand_pat_lit_clause(C) -> C.
expand_pat_lit([H|T], A) ->
- #k_cons{anno=A,hd=literal(H, A),tl=literal(T, A)};
+ #k_cons{anno=A,hd=#k_literal{anno=A,val=H},tl=#k_literal{anno=A,val=T}};
expand_pat_lit(Tuple, A) when is_tuple(Tuple) ->
- #k_tuple{anno=A,es=[literal(E, A) || E <- tuple_to_list(Tuple)]};
+ #k_tuple{anno=A,es=[#k_literal{anno=A,val=E} || E <- tuple_to_list(Tuple)]};
expand_pat_lit(Lit, A) ->
- literal(Lit, A).
-
-literal([], A) ->
- #k_nil{anno=A};
-literal(Val, A) when is_integer(Val) ->
- #k_int{anno=A,val=Val};
-literal(Val, A) when is_float(Val) ->
- #k_float{anno=A,val=Val};
-literal(Val, A) when is_atom(Val) ->
- #k_atom{anno=A,val=Val};
-literal(Val, A) when is_list(Val); is_tuple(Val) ->
- #k_literal{anno=A,val=Val}.
+ #k_literal{anno=A,val=Lit}.
%% opt_singled_valued([{Type,Clauses}]) -> [{Type,Clauses}].
-%% If a type only has one clause and if the pattern is literal,
-%% the matching can be done more efficiently by directly comparing
-%% with the literal (that is especially true for binaries).
+%% If a type only has one clause and if the pattern is a complex
+%% literal, the matching can be done more efficiently by directly
+%% comparing with the literal (that is especially true for binaries).
+%%
+%% It is important not to do this transformation for atomic literals
+%% (such as `[]`), since that would cause the test for an emtpy list
+%% to be executed before the test for a nonempty list.
opt_single_valued(Ttcs) ->
opt_single_valued(Ttcs, [], []).
+opt_single_valued([{_,[#iclause{pats=[#k_literal{}|_]}]}=Ttc|Ttcs], TtcAcc, LitAcc) ->
+ %% This is an atomic literal.
+ opt_single_valued(Ttcs, [Ttc|TtcAcc], LitAcc);
opt_single_valued([{_,[#iclause{pats=[P0|Ps]}=Tc]}=Ttc|Ttcs], TtcAcc, LitAcc) ->
try combine_lit_pat(P0) of
P ->
@@ -1736,26 +1241,13 @@ opt_single_valued([], TtcAcc, LitAcc) ->
combine_lit_pat(#ialias{pat=Pat0}=Alias) ->
Pat = combine_lit_pat(Pat0),
Alias#ialias{pat=Pat};
+combine_lit_pat(#k_literal{}) ->
+ %% This is an atomic literal. Rewriting would be a pessimization,
+ %% especially for `[]`.
+ throw(not_possible);
combine_lit_pat(Pat) ->
- case do_combine_lit_pat(Pat) of
- #k_literal{val=Val} when is_atom(Val) ->
- throw(not_possible);
- #k_literal{val=Val} when is_number(Val) ->
- throw(not_possible);
- #k_literal{val=[]} ->
- throw(not_possible);
- #k_literal{}=Lit ->
- Lit
- end.
+ do_combine_lit_pat(Pat).
-do_combine_lit_pat(#k_atom{anno=A,val=Val}) ->
- #k_literal{anno=A,val=Val};
-do_combine_lit_pat(#k_float{anno=A,val=Val}) ->
- #k_literal{anno=A,val=Val};
-do_combine_lit_pat(#k_int{anno=A,val=Val}) ->
- #k_literal{anno=A,val=Val};
-do_combine_lit_pat(#k_nil{anno=A}) ->
- #k_literal{anno=A,val=[]};
do_combine_lit_pat(#k_binary{anno=A,segs=Segs}) ->
Bin = combine_bin_segs(Segs),
#k_literal{anno=A,val=Bin};
@@ -1774,27 +1266,10 @@ do_combine_lit_pat(#k_tuple{anno=A,es=Es0}) ->
do_combine_lit_pat(_) ->
throw(not_possible).
-combine_bin_segs(#k_bin_seg{size=Size0,unit=Unit,type=integer,
- flags=[unsigned,big],seg=Seg,next=Next}) ->
- #k_literal{val=Size1} = do_combine_lit_pat(Size0),
- #k_literal{val=Int} = do_combine_lit_pat(Seg),
- Size = Size1 * Unit,
- if
- 0 < Size, Size < 64 ->
- Bin = <<Int:Size>>,
- case Bin of
- <<Int:Size>> ->
- NextBin = combine_bin_segs(Next),
- <<Bin/bits,NextBin/bits>>;
- _ ->
- %% The integer Int does not fit in the segment,
- %% thus it will not match.
- throw(not_possible)
- end;
- true ->
- %% Avoid creating huge binary literals.
- throw(not_possible)
- end;
+combine_bin_segs(#k_bin_seg{size=#k_literal{val=8},unit=1,type=integer,
+ flags=[unsigned,big],seg=#k_literal{val=Int},next=Next})
+ when is_integer(Int), 0 =< Int, Int =< 255 ->
+ <<Int,(combine_bin_segs(Next))/bits>>;
combine_bin_segs(#k_bin_end{}) ->
<<>>;
combine_bin_segs(_) ->
@@ -1862,13 +1337,12 @@ handle_bin_con_not_possible([]) -> [].
%% exception is thrown.
select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
- size=#k_int{val=Bits0}=Sz,unit=U,
- flags=Fl,seg=#k_literal{val=Val},
- next=N}|Ps]}=C|Cs0])
- when is_integer(Val) ->
+ size=#k_literal{val=Bits0}=Sz,unit=U,
+ flags=Fl,seg=#k_literal{val=Val},
+ next=N}|Ps]}=C|Cs0]) when is_integer(Bits0) ->
Bits = U * Bits0,
if
- Bits > 1024 -> throw(not_possible); %Expands the code too much.
+ Bits > ?EXPAND_MAX_SIZE_SEGMENT -> throw(not_possible); %Expands the code too much.
true -> ok
end,
select_assert_match_possible(Bits, Val, Fl),
@@ -1879,20 +1353,10 @@ select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
end,
Cs = select_bin_int_1(Cs0, Bits, Fl, Val),
[{k_bin_int,[C#iclause{pats=[P|Ps]}|Cs]}];
-select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=utf8,
- flags=[unsigned,big]=Fl,
- seg=#k_literal{val=Val0},
- next=N}|Ps]}=C|Cs0])
- when is_integer(Val0) ->
- {Val,Bits} = select_utf8(Val0),
- P = #k_bin_int{anno=A,size=#k_int{val=Bits},unit=1,
- flags=Fl,val=Val,next=N},
- Cs = select_bin_int_1(Cs0, Bits, Fl, Val),
- [{k_bin_int,[C#iclause{pats=[P|Ps]}|Cs]}];
select_bin_int(_) -> throw(not_possible).
select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
- size=#k_int{val=Bits0}=Sz,
+ size=#k_literal{val=Bits0}=Sz,
unit=U,
flags=Fl,seg=#k_literal{val=Val},
next=N}|Ps]}=C|Cs],
@@ -1903,18 +1367,6 @@ select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
end,
P = #k_bin_int{anno=A,size=Sz,unit=U,flags=Fl,val=Val,next=N},
[C#iclause{pats=[P|Ps]}|select_bin_int_1(Cs, Bits, Fl, Val)];
-select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=utf8,
- flags=Fl,
- seg=#k_literal{val=Val0},
- next=N}|Ps]}=C|Cs],
- Bits, Fl, Val) when is_integer(Val0) ->
- case select_utf8(Val0) of
- {Val,Bits} -> ok;
- {_,_} -> throw(not_possible)
- end,
- P = #k_bin_int{anno=A,size=#k_int{val=Bits},unit=1,
- flags=[unsigned,big],val=Val,next=N},
- [C#iclause{pats=[P|Ps]}|select_bin_int_1(Cs, Bits, Fl, Val)];
select_bin_int_1([], _, _, _) -> [];
select_bin_int_1(_, _, _, _) -> throw(not_possible).
@@ -1940,17 +1392,6 @@ match_fun(Val) ->
{match,Bs}
end.
-select_utf8(Val0) ->
- try
- Bin = <<Val0/utf8>>,
- Size = bit_size(Bin),
- <<Val:Size>> = Bin,
- {Val,Size}
- catch
- error:_ ->
- throw(not_possible)
- end.
-
%% match_value([Var], Con, [Clause], Default, State) -> {SelectExpr,State}.
%% At this point all the clauses have the same constructor, we must
%% now separate them according to value.
@@ -1961,104 +1402,108 @@ match_value(Us0, T, Cs0, Def, St0) ->
%%ok = io:format("match_value ~p ~p~n", [T, Css]),
mapfoldl(fun ({Us,Cs}, St) -> match_clause(Us, Cs, Def, St) end, St1, UCss).
-%% partition_intersection
-%% Partitions a map into two maps with the most common keys to the first map.
+%% partition_intersection(Type, Us, [Clause], State) -> {Us,Cs,State}.
+%% Partitions a map into two maps with the most common keys to the
+%% first map.
+%%
%% case <M> of
-%% <#{a}>
%% <#{a,b}>
%% <#{a,c}>
-%% <#{c}>
+%% <#{a}>
%% end
+%%
%% becomes
+%%
%% case <M,M> of
-%% <#{a}, #{ }>
%% <#{a}, #{b}>
-%% <#{ }, #{c}>
%% <#{a}, #{c}>
+%% <#{a}, #{ }>
%% end
-%% The intention is to group as many keys together as possible and thus
-%% reduce the number of lookups to that key.
-partition_intersection(k_map, [U|_]=Us0, [_,_|_]=Cs0,St0) ->
+%%
+%% The intention is to group as many keys together as possible and
+%% thus reduce the number of lookups to that key.
+
+partition_intersection(k_map, [U|_]=Us, [_,_|_]=Cs0, St0) ->
Ps = [clause_val(C) || C <- Cs0],
- case find_key_partition(Ps) of
- no_partition ->
- {Us0,Cs0,St0};
+ case find_key_intersection(Ps) of
+ none ->
+ {Us,Cs0,St0};
Ks ->
- {Cs1,St1} = mapfoldl(fun(#iclause{pats=[Arg|Args]}=C, Sti) ->
- {{Arg1,Arg2},St} = partition_key_intersection(Arg, Ks, Sti),
- {C#iclause{pats=[Arg1,Arg2|Args]}, St}
- end, St0, Cs0),
- {[U|Us0],Cs1,St1}
+ Cs1 = map(fun(#iclause{pats=[Arg|Args]}=C) ->
+ {Arg1,Arg2} = partition_keys(Arg, Ks),
+ C#iclause{pats=[Arg1,Arg2|Args]}
+ end, Cs0),
+ {[U|Us],Cs1,St0}
end;
partition_intersection(_, Us, Cs, St) ->
{Us,Cs,St}.
-partition_key_intersection(#k_map{es=Pairs}=Map,Ks,St0) ->
- F = fun(#k_map_pair{key=Key}) -> member(map_key_clean(Key), Ks) end,
+partition_keys(#k_map{es=Pairs}=Map, Ks) ->
+ F = fun(#k_map_pair{key=Key}) ->
+ cerl_sets:is_element(map_key_clean(Key), Ks)
+ end,
{Ps1,Ps2} = partition(F, Pairs),
- {{Map#k_map{es=Ps1},Map#k_map{es=Ps2}},St0};
-partition_key_intersection(#ialias{pat=Map}=Alias,Ks,St0) ->
- %% only alias one of them
- {{Map1,Map2},St1} = partition_key_intersection(Map, Ks, St0),
- {{Map1,Alias#ialias{pat=Map2}},St1}.
-
-% Only check for the complete intersection of keys and not commonality
-find_key_partition(Ps) ->
- Sets = [sets:from_list(Ks)||Ks <- Ps],
- Is = sets:intersection(Sets),
- case sets:to_list(Is) of
- [] -> no_partition;
- KeyIntersection ->
- %% Check if the intersection are all keys in all clauses.
- %% Don't split if they are since this will only
- %% infer extra is_map instructions with no gain.
- All = foldl(fun (Kset, Bool) ->
- Bool andalso sets:is_subset(Kset, Is)
- end, true, Sets),
- if All -> no_partition;
- true -> KeyIntersection
+ {Map#k_map{es=Ps1},Map#k_map{es=Ps2}};
+partition_keys(#ialias{pat=Map}=Alias, Ks) ->
+ %% Only alias one of them.
+ {Map1,Map2} = partition_keys(Map, Ks),
+ {Map1,Alias#ialias{pat=Map2}}.
+
+find_key_intersection(Ps) ->
+ Sets = [cerl_sets:from_list(Ks) || Ks <- Ps],
+ Intersection = cerl_sets:intersection(Sets),
+ case cerl_sets:size(Intersection) of
+ 0 ->
+ none;
+ _ ->
+ All = all(fun (Kset) -> Kset =:= Intersection end, Sets),
+ case All of
+ true ->
+ %% All clauses test the same keys. Partitioning
+ %% the keys could only make the code worse.
+ none;
+ false ->
+ Intersection
end
end.
%% group_value([Clause]) -> [[Clause]].
%% Group clauses according to value. Here we know that
%% 1. Some types are singled valued
-%% 2. The clauses in bin_segs cannot be reordered only grouped
+%% 2. The clauses in maps and bin_segs cannot be reordered,
+%% only grouped
%% 3. Other types are disjoint and can be reordered
-group_value(k_cons, Us, Cs) -> [{Us,Cs}]; %These are single valued
+group_value(k_cons, Us, Cs) -> [{Us,Cs}]; %These are single valued
group_value(k_nil, Us, Cs) -> [{Us,Cs}];
group_value(k_binary, Us, Cs) -> [{Us,Cs}];
group_value(k_bin_end, Us, Cs) -> [{Us,Cs}];
-group_value(k_bin_seg, Us, Cs) -> group_bin_seg(Us,Cs);
+group_value(k_bin_seg, Us, Cs) -> group_keeping_order(Us, Cs);
group_value(k_bin_int, Us, Cs) -> [{Us,Cs}];
-group_value(k_map, Us, Cs) -> group_map(Us,Cs);
+group_value(k_map, Us, Cs) -> group_keeping_order(Us, Cs);
group_value(_, Us, Cs) ->
- %% group_value(Cs).
- Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
- dict:new(), Cs),
- dict:fold(fun (_, Vcs, Css) -> [{Us,Vcs}|Css] end, [], Cd).
-
-group_bin_seg(Us, [C1|Cs]) ->
- V1 = clause_val(C1),
- {More,Rest} = splitwith(fun (C) -> clause_val(C) == V1 end, Cs),
- [{Us,[C1|More]}|group_bin_seg(Us,Rest)];
-group_bin_seg(_, []) -> [].
+ Map = group_values(Cs, #{}),
+ %% We must sort the grouped values to ensure consistent
+ %% order from compilation to compilation.
+ sort(maps:fold(fun (_, Vcs, Css) ->
+ [{Us,reverse(Vcs)}|Css]
+ end, [], Map)).
+
+group_values([C|Cs], Acc) ->
+ Val = clause_val(C),
+ case Acc of
+ #{Val:=Gcs} ->
+ group_values(Cs, Acc#{Val:=[C|Gcs]});
+ #{} ->
+ group_values(Cs, Acc#{Val=>[C]})
+ end;
+group_values([], Acc) -> Acc.
-group_map(Us, [C1|Cs]) ->
+group_keeping_order(Us, [C1|Cs]) ->
V1 = clause_val(C1),
{More,Rest} = splitwith(fun (C) -> clause_val(C) =:= V1 end, Cs),
- [{Us,[C1|More]}|group_map(Us,Rest)];
-group_map(_, []) -> [].
-
-%% Profiling shows that this quadratic implementation account for a big amount
-%% of the execution time if there are many values.
-% group_value([C|Cs]) ->
-% V = clause_val(C),
-% Same = [ Cv || Cv <- Cs, clause_val(Cv) == V ], %Same value
-% Rest = [ Cv || Cv <- Cs, clause_val(Cv) /= V ], % and all the rest
-% [[C|Same]|group_value(Rest)];
-% group_value([]) -> [].
+ [{Us,[C1|More]}|group_keeping_order(Us, Rest)];
+group_keeping_order(_, []) -> [].
%% match_clause([Var], [Clause], Default, State) -> {Clause,State}.
%% At this point all the clauses have the same "value". Build one
@@ -2070,7 +1515,8 @@ match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
{Match0,Vs,St1} = get_match(get_con(Cs0), St0),
Match = sub_size_var(Match0, Cs0),
{Cs1,St2} = new_clauses(Cs0, U, St1),
- {B,St3} = match(Vs ++ Us, Cs1, Def, St2),
+ Cs2 = squeeze_clauses_by_bin_integer_count(Cs1, []),
+ {B,St3} = match(Vs ++ Us, Cs2, Def, St2),
{#k_val_clause{anno=Anno,val=Match,body=B},St3}.
sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{isub=Sub}|_]) ->
@@ -2085,17 +1531,14 @@ get_match(#k_cons{}, St0) ->
get_match(#k_binary{}, St0) ->
{[V]=Mes,St1} = new_vars(1, St0),
{#k_binary{segs=V},Mes,St1};
-get_match(#k_bin_seg{size=#k_atom{val=all},next={k_bin_end,[]}}=Seg, St0) ->
- {[S,N0],St1} = new_vars(2, St0),
- N = set_kanno(N0, [no_usage]),
+get_match(#k_bin_seg{size=#k_literal{val=all},next={k_bin_end,[]}}=Seg, St0) ->
+ {[S,N],St1} = new_vars(2, St0),
{Seg#k_bin_seg{seg=S,next=N},[S],St1};
get_match(#k_bin_seg{}=Seg, St0) ->
- {[S,N0],St1} = new_vars(2, St0),
- N = set_kanno(N0, [no_usage]),
+ {[S,N],St1} = new_vars(2, St0),
{Seg#k_bin_seg{seg=S,next=N},[S,N],St1};
get_match(#k_bin_int{}=BinInt, St0) ->
- {N0,St1} = new_var(St0),
- N = set_kanno(N0, [no_usage]),
+ {N,St1} = new_var(St0),
{BinInt#k_bin_int{next=N},[N],St1};
get_match(#k_tuple{es=Es}, St0) ->
{Mes,St1} = new_vars(length(Es), St0),
@@ -2116,7 +1559,7 @@ new_clauses(Cs0, U, St) ->
#k_cons{hd=H,tl=T} -> [H,T|As];
#k_tuple{es=Es} -> Es ++ As;
#k_binary{segs=E} -> [E|As];
- #k_bin_seg{size=#k_atom{val=all},
+ #k_bin_seg{size=#k_literal{val=all},
seg=S,next={k_bin_end,[]}} ->
[S|As];
#k_bin_seg{seg=S,next=N} ->
@@ -2140,6 +1583,104 @@ new_clauses(Cs0, U, St) ->
end, Cs0),
{Cs1,St}.
+%% group and squeeze
+%% The goal of those functions is to group subsequent integer k_bin_seg
+%% literals by count so we can leverage bs_get_integer_16 whenever possible.
+%%
+%% The priority is to create large groups. So if we have three clauses matching
+%% on 16-bits/16-bits/8-bits, we will first have a single 8-bits match for all
+%% three clauses instead of clauses (one with 16 and another with 8). But note
+%% the algorithm is recursive, so the remaining 8-bits for the first two clauses
+%% will be grouped next.
+%%
+%% We also try to not create too large groups. If we have too many clauses,
+%% it is preferrable to match on 8-bits, select a branch, then match on the
+%% next 8-bits, rather than match on 16-bits which would force us to have
+%% to select to many values at the same time, which would not be efficient.
+%%
+%% Another restriction is that we create groups only if the end of the
+%% group is a variadic clause or the end of the binary. That's because
+%% if we have 16-bits/16-bits/catch-all, breaking it into a 16-bits lookup
+%% will make the catch-all more expensive.
+%%
+%% Clauses are grouped in reverse when squeezing and then flattened and
+%% re-reversed at the end.
+squeeze_clauses_by_bin_integer_count([Clause | Clauses], Acc) ->
+ case clause_count_bin_integer_segments(Clause) of
+ {literal, N} -> squeeze_clauses_by_bin_integer_count(Clauses, N, 1, [Clause], Acc);
+ _ -> squeeze_clauses_by_bin_integer_count(Clauses, [[Clause] | Acc])
+ end;
+squeeze_clauses_by_bin_integer_count(_, Acc) ->
+ flat_reverse(Acc, []).
+
+squeeze_clauses_by_bin_integer_count([], N, Count, GroupAcc, Acc) ->
+ Squeezed = squeeze_clauses(GroupAcc, fix_count_without_variadic_segment(N), Count),
+ flat_reverse([Squeezed | Acc], []);
+squeeze_clauses_by_bin_integer_count([#iclause{pats=[#k_bin_end{} | _]} = Clause], N, Count, GroupAcc, Acc) ->
+ Squeezed = squeeze_clauses(GroupAcc, fix_count_without_variadic_segment(N), Count),
+ flat_reverse([[Clause | Squeezed] | Acc], []);
+squeeze_clauses_by_bin_integer_count([Clause | Clauses], N, Count, GroupAcc, Acc) ->
+ case clause_count_bin_integer_segments(Clause) of
+ {literal, NewN} ->
+ squeeze_clauses_by_bin_integer_count(Clauses, min(N, NewN), Count + 1, [Clause | GroupAcc], Acc);
+
+ {variadic, NewN} when NewN =< N ->
+ Squeezed = squeeze_clauses(GroupAcc, NewN, Count),
+ squeeze_clauses_by_bin_integer_count(Clauses, [[Clause | Squeezed] | Acc]);
+
+ _ ->
+ squeeze_clauses_by_bin_integer_count(Clauses, [[Clause | GroupAcc] | Acc])
+ end.
+
+clause_count_bin_integer_segments(#iclause{pats=[#k_bin_seg{seg=#k_literal{}} = BinSeg | _]}) ->
+ count_bin_integer_segments(BinSeg, 0);
+clause_count_bin_integer_segments(#iclause{pats=[#k_bin_seg{size=#k_literal{val=Size},unit=Unit,
+ type=integer,flags=[unsigned,big],
+ seg=#k_var{}} | _]})
+ when ((Size * Unit) rem 8) =:= 0 ->
+ {variadic, (Size * Unit) div 8};
+clause_count_bin_integer_segments(_) ->
+ error.
+
+count_bin_integer_segments(#k_bin_seg{size=#k_literal{val=8},unit=1,type=integer,flags=[unsigned,big],
+ seg=#k_literal{val=Int},next=Next}, Count)
+ when is_integer(Int), 0 =< Int, Int =< 255 ->
+ count_bin_integer_segments(Next, Count + 1);
+count_bin_integer_segments(_, Count) when Count > 0 ->
+ {literal, Count};
+count_bin_integer_segments(_, _Count) ->
+ error.
+
+%% Since 4 bytes in on 32-bits systems are bignums, we convert
+%% anything more than 3 into 2 bytes lookup. The goal is to convert
+%% any multi-clause segment into 2-byte lookups with a potential
+%% 3 byte lookup at the end.
+fix_count_without_variadic_segment(N) when N > 3 -> 2;
+fix_count_without_variadic_segment(N) -> N.
+
+%% If we have more than 16 clauses, then it is better
+%% to branch multiple times than getting a large integer.
+%% We also abort if we have nothing to squeeze.
+squeeze_clauses(Clauses, Size, Count) when Count >= 16; Size == 1 -> Clauses;
+squeeze_clauses(Clauses, Size, _Count) -> squeeze_clauses(Clauses, Size).
+
+squeeze_clauses([#iclause{pats=[#k_bin_seg{seg=#k_literal{}} = BinSeg | Pats]} = Clause | Clauses], Size) ->
+ [Clause#iclause{pats=[squeeze_segments(BinSeg, 0, 0, Size) | Pats]} |
+ squeeze_clauses(Clauses, Size)];
+squeeze_clauses([], _Size) ->
+ [].
+
+squeeze_segments(#k_bin_seg{size=Sz, seg=#k_literal{val=Val}=Lit} = BinSeg, Acc, Size, 1) ->
+ BinSeg#k_bin_seg{size=Sz#k_literal{val=Size + 8}, seg=Lit#k_literal{val=(Acc bsl 8) bor Val}};
+squeeze_segments(#k_bin_seg{seg=#k_literal{val=Val},next=Next}, Acc, Size, Count) ->
+ squeeze_segments(Next, (Acc bsl 8) bor Val, Size + 8, Count - 1).
+
+flat_reverse([Head | Tail], Acc) -> flat_reverse(Tail, flat_reverse_1(Head, Acc));
+flat_reverse([], Acc) -> Acc.
+
+flat_reverse_1([Head | Tail], Acc) -> flat_reverse_1(Tail, [Head | Acc]);
+flat_reverse_1([], Acc) -> Acc.
+
%% build_guard([GuardClause]) -> GuardExpr.
build_guard([]) -> fail;
@@ -2160,13 +1701,13 @@ build_alt_1st_no_fail(First, fail) -> First;
build_alt_1st_no_fail(First, Then) ->
copy_anno(#k_alt{first=First,then=Then}, First).
-%% build_match([MatchVar], MatchExpr) -> Kexpr.
+%% build_match(MatchExpr) -> Kexpr.
%% Build a match expr if there is a match.
-build_match(Us, #k_alt{}=Km) -> copy_anno(#k_match{vars=Us,body=Km}, Km);
-build_match(Us, #k_select{}=Km) -> copy_anno(#k_match{vars=Us,body=Km}, Km);
-build_match(Us, #k_guard{}=Km) -> copy_anno(#k_match{vars=Us,body=Km}, Km);
-build_match(_, Km) -> Km.
+build_match(#k_alt{}=Km) -> copy_anno(#k_match{body=Km}, Km);
+build_match(#k_select{}=Km) -> copy_anno(#k_match{body=Km}, Km);
+build_match(#k_guard{}=Km) -> copy_anno(#k_match{body=Km}, Km);
+build_match(Km) -> Km.
%% clause_arg(Clause) -> FirstArg.
%% clause_con(Clause) -> Constructor.
@@ -2195,26 +1736,26 @@ arg_alias(_Con) -> [].
arg_con(Arg) ->
case arg_arg(Arg) of
- #k_literal{} -> k_literal;
- #k_int{} -> k_int;
- #k_float{} -> k_float;
- #k_atom{} -> k_atom;
- #k_nil{} -> k_nil;
- #k_cons{} -> k_cons;
+ #k_cons{} -> k_cons;
#k_tuple{} -> k_tuple;
#k_map{} -> k_map;
#k_binary{} -> k_binary;
#k_bin_end{} -> k_bin_end;
#k_bin_seg{} -> k_bin_seg;
- #k_var{} -> k_var
+ #k_var{} -> k_var;
+ #k_literal{val=[]} -> k_nil;
+ #k_literal{val=Val} ->
+ if
+ is_atom(Val) -> k_atom;
+ is_integer(Val) -> k_int;
+ is_float(Val) -> k_float;
+ true -> k_literal
+ end
end.
arg_val(Arg, C) ->
case arg_arg(Arg) of
#k_literal{val=Lit} -> Lit;
- #k_int{val=I} -> I;
- #k_float{val=F} -> F;
- #k_atom{val=A} -> A;
#k_tuple{es=Es} -> length(Es);
#k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
case S of
@@ -2225,7 +1766,7 @@ arg_val(Arg, C) ->
{set_kanno(S, []),U,T,Fs}
end;
#k_map{op=exact,es=Es} ->
- lists:sort(fun(A,B) ->
+ sort(fun(A,B) ->
%% on the form K :: {'lit' | 'var', term()}
%% lit < var as intended
erts_internal:cmp_term(A,B) < 0
@@ -2248,23 +1789,22 @@ ubody(#iset{vars=[],arg=#iletrec{}=Let,body=B0}, Br, St0) ->
%% An iletrec{} should never be last.
St = iletrec_funs(Let, St0),
ubody(B0, Br, St);
+ubody(#iset{vars=[],arg=#k_literal{},body=B0}, Br, St0) ->
+ ubody(B0, Br, St0);
ubody(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Br, St0) ->
{E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
{B1,Bu,St2} = ubody(B0, Br, St1),
Ns = lit_list_vars(Vs),
Used = union(Eu, subtract(Bu, Ns)), %Used external vars
- {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+ {#k_seq{anno=A,arg=E1,body=B1},Used,St2};
ubody(#ivalues{anno=A,args=As}, return, St) ->
Au = lit_list_vars(As),
- {#k_return{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
+ {#k_return{anno=A,args=As},Au,St};
ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
Au = lit_list_vars(As),
- case is_in_guard(St) of
- true ->
- {#k_guard_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
- false ->
- {#k_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St}
- end;
+ {#k_break{anno=A,args=As},Au,St};
+ubody(#k_goto{}=Goto, _Br, St) ->
+ {Goto,[],St};
ubody(E, return, St0) ->
%% Enterable expressions need no trailing return.
case is_enter_expr(E) of
@@ -2273,27 +1813,14 @@ ubody(E, return, St0) ->
{Ea,Pa,St1} = force_atomic(E, St0),
ubody(pre_seq(Pa, #ivalues{args=[Ea]}), return, St1)
end;
-ubody(#ignored{}, {break,_} = Break, St) ->
- ubody(#ivalues{args=[]}, Break, St);
ubody(E, {break,[_]} = Break, St0) ->
- %%ok = io:fwrite("ubody ~w:~p~n", [?LINE,{E,Br}]),
- %% Exiting expressions need no trailing break.
- case is_exit_expr(E) of
- true -> uexpr(E, return, St0);
- false ->
- {Ea,Pa,St1} = force_atomic(E, St0),
- ubody(pre_seq(Pa, #ivalues{args=[Ea]}), Break, St1)
- end;
+ {Ea,Pa,St1} = force_atomic(E, St0),
+ ubody(pre_seq(Pa, #ivalues{args=[Ea]}), Break, St1);
ubody(E, {break,Rs}=Break, St0) ->
- case is_exit_expr(E) of
- true ->
- uexpr(E, return, St0);
- false ->
- {Vs,St1} = new_vars(length(Rs), St0),
- Iset = #iset{vars=Vs,arg=E},
- PreSeq = pre_seq([Iset], #ivalues{args=Vs}),
- ubody(PreSeq, Break, St1)
- end.
+ {Vs,St1} = new_vars(length(Rs), St0),
+ Iset = #iset{vars=Vs,arg=E},
+ PreSeq = pre_seq([Iset], #ivalues{args=Vs}),
+ ubody(PreSeq, Break, St1).
iletrec_funs(#iletrec{defs=Fs}, St0) ->
%% Use union of all free variables.
@@ -2319,20 +1846,13 @@ iletrec_funs_gen(_, _, #kern{funs=ignore}=St) ->
iletrec_funs_gen(Fs, FreeVs, St) ->
foldl(fun ({N,#ifun{anno=Fa,vars=Vs,body=Fb0}}, Lst0) ->
Arity0 = length(Vs),
- {Fb1,_,Lst1} = ubody(Fb0, return, Lst0#kern{ff={N,Arity0}}),
+ {Fb1,_,Lst1} = ubody(Fb0, return, Lst0),
Arity = Arity0 + length(FreeVs),
- Fun = make_fdef(#k{us=[],ns=[],a=Fa}, N, Arity,
- Vs++FreeVs, Fb1),
+ Fun = make_fdef(Fa, N, Arity, Vs++FreeVs, Fb1),
Lst1#kern{funs=[Fun|Lst1#kern.funs]}
end, St, Fs).
-%% is_exit_expr(Kexpr) -> boolean().
-%% Test whether Kexpr always exits and never returns.
-
-is_exit_expr(#k_receive_next{}) -> true;
-is_exit_expr(_) -> false.
-
%% is_enter_expr(Kexpr) -> boolean().
%% Test whether Kexpr is "enterable", i.e. can handle return from
%% within itself without extra #k_return{}.
@@ -2340,95 +1860,77 @@ is_exit_expr(_) -> false.
is_enter_expr(#k_try{}) -> true;
is_enter_expr(#k_call{}) -> true;
is_enter_expr(#k_match{}) -> true;
-is_enter_expr(#k_receive{}) -> true;
-is_enter_expr(#k_receive_next{}) -> true;
+is_enter_expr(#k_letrec_goto{}) -> true;
is_enter_expr(_) -> false.
%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
-%% Tag an expression with its used variables.
+%% Calculate the used variables for an expression.
%% Break = return | {break,[RetVar]}.
uexpr(#k_test{anno=A,op=Op,args=As}=Test, {break,Rs}, St) ->
[] = Rs, %Sanity check
Used = union(op_vars(Op), lit_list_vars(As)),
- {Test#k_test{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A}},
- Used,St};
+ {Test#k_test{anno=A},Used,St};
uexpr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, {break,_}=Br, St0) ->
Ns = lit_list_vars(Vs),
{E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
{B1,Bu,St2} = uexpr(B0, Br, St1),
Used = union(Eu, subtract(Bu, Ns)),
- {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+ {#k_seq{anno=A,arg=E1,body=B1},Used,St2};
uexpr(#k_call{anno=A,op=#k_local{name=F,arity=Ar}=Op,args=As0}=Call, Br, St) ->
Free = get_free(F, Ar, St),
As1 = As0 ++ Free, %Add free variables LAST!
Used = lit_list_vars(As1),
{case Br of
{break,Rs} ->
- Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
+ Call#k_call{anno=A,
op=Op#k_local{arity=Ar + length(Free)},
args=As1,ret=Rs};
return ->
- #k_enter{anno=#k{us=Used,ns=[],a=A},
+ #k_enter{anno=A,
op=Op#k_local{arity=Ar + length(Free)},
args=As1}
end,Used,St};
uexpr(#k_call{anno=A,op=Op,args=As}=Call, {break,Rs}, St) ->
Used = union(op_vars(Op), lit_list_vars(As)),
- {Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
- Used,St};
+ {Call#k_call{anno=A,ret=Rs},Used,St};
uexpr(#k_call{anno=A,op=Op,args=As}, return, St) ->
Used = union(op_vars(Op), lit_list_vars(As)),
- {#k_enter{anno=#k{us=Used,ns=[],a=A},op=Op,args=As},
- Used,St};
+ {#k_enter{anno=A,op=Op,args=As},Used,St};
uexpr(#k_bif{anno=A,op=Op,args=As}=Bif, {break,Rs}, St0) ->
Used = union(op_vars(Op), lit_list_vars(As)),
{Brs,St1} = bif_returns(Op, Rs, St0),
- {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Brs),a=A},ret=Brs},
- Used,St1};
-uexpr(#k_match{anno=A,vars=Vs,body=B0}, Br, St0) ->
+ {Bif#k_bif{anno=A,ret=Brs},Used,St1};
+uexpr(#k_match{anno=A,body=B0}, Br, St0) ->
Rs = break_rets(Br),
{B1,Bu,St1} = umatch(B0, Br, St0),
- case is_in_guard(St1) of
- true ->
- {#k_guard_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
- vars=Vs,body=B1,ret=Rs},Bu,St1};
- false ->
- {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
- vars=Vs,body=B1,ret=Rs},Bu,St1}
- end;
-uexpr(#k_receive{anno=A,var=V,body=B0,timeout=T,action=A0}, Br, St0) ->
- Rs = break_rets(Br),
- Tu = lit_vars(T), %Timeout is atomic
- {B1,Bu,St1} = umatch(B0, Br, St0),
- {A1,Au,St2} = ubody(A0, Br, St1),
- Used = del_element(V#k_var.name, union(Bu, union(Tu, Au))),
- {#k_receive{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
- var=V,body=B1,timeout=T,action=A1,ret=Rs},
- Used,St2};
-uexpr(#k_receive_accept{anno=A}, _, St) ->
- {#k_receive_accept{anno=#k{us=[],ns=[],a=A}},[],St};
-uexpr(#k_receive_next{anno=A}, _, St) ->
- {#k_receive_next{anno=#k{us=[],ns=[],a=A}},[],St};
+ {#k_match{anno=A,body=B1,ret=Rs},Bu,St1};
uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
{break,Rs0}=Br, St0) ->
- case is_in_guard(St0) of
- true ->
- {[#k_var{name=X}],#k_var{name=X}} = {Vs,B0}, %Assertion.
- #k_atom{val=false} = H0, %Assertion.
- {Avs,St1} = new_vars(length(Rs0), St0),
- {A1,Bu,St} = uexpr(A0, {break,Avs}, St1),
- {#k_protected{anno=#k{us=Bu,ns=lit_list_vars(Rs0),a=A},
- arg=A1,ret=Rs0,inner=Avs},Bu,St};
- false ->
+ case {Vs,B0,H0,Rs0} of
+ {[#k_var{name=X}],#k_var{name=X},#k_literal{},[]} ->
+ %% This is a simple try/catch whose return value is
+ %% ignored:
+ %%
+ %% try E of V -> V when _:_:_ -> ignored_literal end, ...
+ %%
+ %% This is most probably a try/catch in a guard. To
+ %% correctly handle the #k_test{} that ends the body of
+ %% the guard, we MUST pass an empty list of break
+ %% variables when processing the body.
+ {A1,Bu,St} = ubody(A0, {break,[]}, St0),
+ {#k_try{anno=A,arg=A1,vars=[],body=#k_break{},
+ evars=[],handler=#k_break{},ret=Rs0},
+ Bu,St};
+ {_,_,_,_} ->
+ %% The general try/catch (in a guard or in body).
{Avs,St1} = new_vars(length(Vs), St0),
{A1,Au,St2} = ubody(A0, {break,Avs}, St1),
{B1,Bu,St3} = ubody(B0, Br, St2),
{H1,Hu,St4} = ubody(H0, Br, St3),
Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
subtract(Hu, lit_list_vars(Evs))]),
- {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs0),a=A},
- arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs0},
+ {#k_try{anno=A,arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs0},
Used,St4}
end;
uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
@@ -2439,8 +1941,7 @@ uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
{H1,Hu,St4} = ubody(H0, return, St3),
Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
subtract(Hu, lit_list_vars(Evs))]),
- {#k_try_enter{anno=#k{us=Used,ns=[],a=A},
- arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1},
+ {#k_try_enter{anno=A,arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1},
Used,St4};
uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
{Rb,St1} = new_var(St0),
@@ -2448,7 +1949,7 @@ uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
%% Guarantee ONE return variable.
{Ns,St3} = new_vars(1 - length(Rs0), St2),
Rs1 = Rs0 ++ Ns,
- {#k_catch{anno=#k{us=Bu,ns=lit_list_vars(Rs1),a=A},body=B1,ret=Rs1},Bu,St3};
+ {#k_catch{anno=A,body=B1,ret=Rs1},Bu,St3};
uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
{B1,Bu,St1} = ubody(B0, return, St0), %Return out of new function
Ns = lit_list_vars(Vs),
@@ -2456,37 +1957,55 @@ uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
Fvs = make_vars(Free),
Arity = length(Vs) + length(Free),
{Fname,St} =
- case lists:keyfind(id, 1, A) of
+ case keyfind(id, 1, A) of
{id,{_,_,Fname0}} ->
{Fname0,St1};
false ->
%% No id annotation. Must invent a fun name.
new_fun_name(St1)
end,
- Fun = make_fdef(#k{us=[],ns=[],a=A}, Fname, Arity, Vs++Fvs, B1),
- {#k_bif{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
+ Fun = make_fdef(A, Fname, Arity, Vs++Fvs, B1),
+ Local = #k_local{name=Fname,arity=Arity},
+ {#k_bif{anno=A,
op=#k_internal{name=make_fun,arity=length(Free)+2},
- args=[#k_atom{val=Fname},#k_int{val=Arity}|Fvs],
+ args=[Local|Fvs],
ret=Rs},
Free,add_local_function(Fun, St)};
+uexpr(#k_letrec_goto{anno=A,first=F0,then=T0}=MatchAlt, Br, St0) ->
+ Rs = break_rets(Br),
+ {F1,Fu,St1} = ubody(F0, Br, St0),
+ {T1,Tu,St2} = ubody(T0, Br, St1),
+ Used = union(Fu, Tu),
+ {MatchAlt#k_letrec_goto{anno=A,first=F1,then=T1,ret=Rs},Used,St2};
uexpr(Lit, {break,Rs0}, St0) ->
%% Transform literals to puts here.
%%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
Used = lit_vars(Lit),
{Rs,St1} = ensure_return_vars(Rs0, St0),
- {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
- arg=Lit,ret=Rs},Used,St1}.
+ {#k_put{anno=get_kanno(Lit),arg=Lit,ret=Rs},Used,St1}.
+
+add_local_function(_, #kern{funs=ignore}=St) ->
+ St;
+add_local_function(#k_fdef{func=Name,arity=Arity}=F, #kern{funs=Funs}=St) ->
+ case is_defined(Name, Arity, Funs) of
+ false ->
+ St#kern{funs=[F|Funs]};
+ true ->
+ St
+ end.
-add_local_function(_, #kern{funs=ignore}=St) -> St;
-add_local_function(F, #kern{funs=Funs}=St) -> St#kern{funs=[F|Funs]}.
+is_defined(Name, Arity, [#k_fdef{func=Name,arity=Arity}|_]) ->
+ true;
+is_defined(Name, Arity, [#k_fdef{}|T]) ->
+ is_defined(Name, Arity, T);
+is_defined(_, _, []) -> false.
%% Make a #k_fdef{}, making sure that the body is always a #k_match{}.
make_fdef(Anno, Name, Arity, Vs, #k_match{}=Body) ->
#k_fdef{anno=Anno,func=Name,arity=Arity,vars=Vs,body=Body};
make_fdef(Anno, Name, Arity, Vs, Body) ->
Ka = get_kanno(Body),
- Match = #k_match{anno=#k{us=Ka#k.us,ns=[],a=Ka#k.a},
- vars=Vs,body=Body,ret=[]},
+ Match = #k_match{anno=Ka,body=Body,ret=[]},
#k_fdef{anno=Anno,func=Name,arity=Arity,vars=Vs,body=Match}.
%% get_free(Name, Arity, State) -> [Free].
@@ -2524,42 +2043,34 @@ ensure_return_vars([], St) -> new_vars(1, St);
ensure_return_vars([_]=Rs, St) -> {Rs,St}.
%% umatch(Match, Break, State) -> {Match,[UsedVar],State}.
-%% Tag a match expression with its used variables.
+%% Calculate the used variables for a match expression.
umatch(#k_alt{anno=A,first=F0,then=T0}, Br, St0) ->
{F1,Fu,St1} = umatch(F0, Br, St0),
{T1,Tu,St2} = umatch(T0, Br, St1),
Used = union(Fu, Tu),
- {#k_alt{anno=#k{us=Used,ns=[],a=A},first=F1,then=T1},
- Used,St2};
+ {#k_alt{anno=A,first=F1,then=T1},Used,St2};
umatch(#k_select{anno=A,var=V,types=Ts0}, Br, St0) ->
{Ts1,Tus,St1} = umatch_list(Ts0, Br, St0),
- Used = case member(no_usage, get_kanno(V)) of
- true -> Tus;
- false -> add_element(V#k_var.name, Tus)
- end,
- {#k_select{anno=#k{us=Used,ns=[],a=A},var=V,types=Ts1},Used,St1};
+ Used = add_element(V#k_var.name, Tus),
+ {#k_select{anno=A,var=V,types=Ts1},Used,St1};
umatch(#k_type_clause{anno=A,type=T,values=Vs0}, Br, St0) ->
{Vs1,Vus,St1} = umatch_list(Vs0, Br, St0),
- {#k_type_clause{anno=#k{us=Vus,ns=[],a=A},type=T,values=Vs1},Vus,St1};
+ {#k_type_clause{anno=A,type=T,values=Vs1},Vus,St1};
umatch(#k_val_clause{anno=A,val=P0,body=B0}, Br, St0) ->
{U0,Ps} = pat_vars(P0),
- P = set_kanno(P0, #k{us=U0,ns=Ps,a=get_kanno(P0)}),
{B1,Bu,St1} = umatch(B0, Br, St0),
+ P = pat_anno_unused(P0, Bu, Ps),
Used = union(U0, subtract(Bu, Ps)),
- {#k_val_clause{anno=#k{us=Used,ns=[],a=A},val=P,body=B1},
- Used,St1};
+ {#k_val_clause{anno=A,val=P,body=B1},Used,St1};
umatch(#k_guard{anno=A,clauses=Gs0}, Br, St0) ->
{Gs1,Gus,St1} = umatch_list(Gs0, Br, St0),
- {#k_guard{anno=#k{us=Gus,ns=[],a=A},clauses=Gs1},Gus,St1};
+ {#k_guard{anno=A,clauses=Gs1},Gus,St1};
umatch(#k_guard_clause{anno=A,guard=G0,body=B0}, Br, St0) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,G0]),
- {G1,Gu,St1} = uexpr(G0, {break,[]},
- St0#kern{guard_refc=St0#kern.guard_refc+1}),
- %%ok = io:fwrite("~w: ~p~n", [?LINE,G1]),
- {B1,Bu,St2} = umatch(B0, Br, St1#kern{guard_refc=St1#kern.guard_refc-1}),
+ {G1,Gu,St1} = uexpr(G0, {break,[]}, St0),
+ {B1,Bu,St2} = umatch(B0, Br, St1),
Used = union(Gu, Bu),
- {#k_guard_clause{anno=#k{us=Used,ns=[],a=A},guard=G1,body=B1},Used,St2};
+ {#k_guard_clause{anno=A,guard=G1,body=B1},Used,St2};
umatch(B0, Br, St0) -> ubody(B0, Br, St0).
umatch_list(Ms0, Br, St) ->
@@ -2568,6 +2079,19 @@ umatch_list(Ms0, Br, St) ->
{[M1|Ms1],union(Mu, Us),Stb}
end, {[],[],St}, Ms0).
+pat_anno_unused(#k_tuple{es=Es0}=P, Used0, Ps) ->
+ %% Not extracting unused tuple elements is an optimization for
+ %% compile time and memory use during compilation. It is probably
+ %% worthwhile because it is common to extract only a few elements
+ %% from a huge record.
+ Used = intersection(Used0, Ps),
+ Es = [case member(V, Used) of
+ true -> Var;
+ false -> set_kanno(Var, [unused|get_kanno(Var)])
+ end || #k_var{name=V}=Var <- Es0],
+ P#k_tuple{es=Es};
+pat_anno_unused(P, _Used, _Ps) -> P.
+
%% op_vars(Op) -> [VarName].
op_vars(#k_remote{mod=Mod,name=Name}) ->
@@ -2579,11 +2103,7 @@ op_vars(Atomic) -> lit_vars(Atomic).
%% Return the variables in a literal.
lit_vars(#k_var{name=N}) -> [N];
-lit_vars(#k_int{}) -> [];
-lit_vars(#k_float{}) -> [];
-lit_vars(#k_atom{}) -> [];
%%lit_vars(#k_char{}) -> [];
-lit_vars(#k_nil{}) -> [];
lit_vars(#k_cons{hd=H,tl=T}) ->
union(lit_vars(H), lit_vars(T));
lit_vars(#k_map{var=Var,es=Es}) ->
@@ -2603,27 +2123,24 @@ lit_list_vars(Ps) ->
%% pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
%% Return variables in a pattern. All variables are new variables
-%% except those in the size field of binary segments.
-%% and map_pair keys
+%% except those in the size field of binary segments and the key
+%% field in map_pairs.
pat_vars(#k_var{name=N}) -> {[],[N]};
%%pat_vars(#k_char{}) -> {[],[]};
pat_vars(#k_literal{}) -> {[],[]};
-pat_vars(#k_int{}) -> {[],[]};
-pat_vars(#k_float{}) -> {[],[]};
-pat_vars(#k_atom{}) -> {[],[]};
-pat_vars(#k_nil{}) -> {[],[]};
pat_vars(#k_cons{hd=H,tl=T}) ->
pat_list_vars([H,T]);
pat_vars(#k_binary{segs=V}) ->
pat_vars(V);
-pat_vars(#k_bin_seg{size=Size,seg=S}) ->
- {U1,New} = pat_list_vars([S]),
+pat_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
+ {U1,New} = pat_list_vars([S,N]),
{[],U2} = pat_vars(Size),
{union(U1, U2),New};
-pat_vars(#k_bin_int{size=Size}) ->
+pat_vars(#k_bin_int{size=Size,next=N}) ->
+ {[],New} = pat_vars(N),
{[],U} = pat_vars(Size),
- {U,[]};
+ {U,New};
pat_vars(#k_bin_end{}) -> {[],[]};
pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
@@ -2646,11 +2163,6 @@ integers(N, M) when N =< M ->
[N|integers(N + 1, M)];
integers(_, _) -> [].
-%% is_in_guard(State) -> true|false.
-
-is_in_guard(#kern{guard_refc=Refc}) ->
- Refc > 0.
-
%%%
%%% Handling of errors and warnings.
%%%
@@ -2662,7 +2174,7 @@ is_in_guard(#kern{guard_refc=Refc}) ->
format_error({nomatch_shadow,Line}) ->
M = io_lib:format("this clause cannot match because a previous clause at line ~p "
"always matches", [Line]),
- lists:flatten(M);
+ flatten(M);
format_error(nomatch_shadow) ->
"this clause cannot match because a previous clause always matches";
format_error(bad_call) ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index e26360a6da..582e4f9b12 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -24,19 +24,10 @@
%% this could make including this file difficult.
%% N.B. the annotation field is ALWAYS the first field!
-%% Kernel annotation record.
--record(k, {us, %Used variables
- ns, %New variables
- a}). %Core annotation
-
%% Literals
%% NO CHARACTERS YET.
%%-record(k_char, {anno=[],val}).
--record(k_literal, {anno=[],val}). %Only used for complex literals.
--record(k_int, {anno=[],val}).
--record(k_float, {anno=[],val}).
--record(k_atom, {anno=[],val}).
--record(k_nil, {anno=[]}).
+-record(k_literal, {anno=[],val}).
-record(k_tuple, {anno=[],es}).
-record(k_map, {anno=[],var=#k_literal{val=#{}},op,es}).
@@ -58,19 +49,17 @@
-record(k_seq, {anno=[],arg,body}).
-record(k_put, {anno=[],arg,ret=[]}).
-record(k_bif, {anno=[],op,args,ret=[]}).
--record(k_test, {anno=[],op,args,inverted=false}).
+-record(k_test, {anno=[],op,args}).
-record(k_call, {anno=[],op,args,ret=[]}).
-record(k_enter, {anno=[],op,args}).
--record(k_receive, {anno=[],var,body,timeout,action,ret=[]}).
--record(k_receive_accept, {anno=[]}).
--record(k_receive_next, {anno=[]}).
-record(k_try, {anno=[],arg,vars,body,evars,handler,ret=[]}).
-record(k_try_enter, {anno=[],arg,vars,body,evars,handler}).
--record(k_protected, {anno=[],arg,ret=[],inner}).
-record(k_catch, {anno=[],body,ret=[]}).
--record(k_guard_match, {anno=[],vars,body,ret=[]}).
--record(k_match, {anno=[],vars,body,ret=[]}).
+-record(k_letrec_goto, {anno=[],label,first,then,ret=[]}).
+-record(k_goto, {anno=[],label}).
+
+-record(k_match, {anno=[],body,ret=[]}).
-record(k_alt, {anno=[],first,then}).
-record(k_select, {anno=[],var,types}).
-record(k_type_clause, {anno=[],type,values}).
@@ -79,7 +68,6 @@
-record(k_guard_clause, {anno=[],guard,body}).
-record(k_break, {anno=[],args=[]}).
--record(k_guard_break, {anno=[],args=[]}).
-record(k_return, {anno=[],args=[]}).
%%k_get_anno(Thing) -> element(2, Thing).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index c12c301ee2..f7479e6b15 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -57,8 +57,6 @@ format(Node, Ctxt) ->
format_1(Node, Ctxt);
[L,{file,_}] when is_integer(L) ->
format_1(Node, Ctxt);
- #k{a=Anno}=K when Anno =/= [] ->
- format(setelement(2, Node, K#k{a=[]}), Ctxt);
List ->
format_anno(List, Ctxt, fun (Ctxt1) ->
format_1(Node, Ctxt1)
@@ -83,11 +81,7 @@ format_anno(Anno, Ctxt0, ObjFun) ->
%% format_1(Kexpr, Context) -> string().
-format_1(#k_atom{val=A}, _Ctxt) -> core_atom(A);
%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C);
-format_1(#k_float{val=F}, _Ctxt) -> float_to_list(F);
-format_1(#k_int{val=I}, _Ctxt) -> integer_to_list(I);
-format_1(#k_nil{}, _Ctxt) -> "[]";
format_1(#k_var{name=V}, _Ctxt) ->
if is_atom(V) ->
case atom_to_list(V) of
@@ -135,10 +129,13 @@ format_1(#k_bin_seg{next=Next}=S, Ctxt) ->
[format_bin_seg_1(S, Ctxt),
format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))];
format_1(#k_bin_int{size=Sz,unit=U,flags=Fs,val=Val,next=Next}, Ctxt) ->
- S = #k_bin_seg{size=Sz,unit=U,type=integer,flags=Fs,seg=#k_int{val=Val},next=Next},
+ S = #k_bin_seg{size=Sz,unit=U,type=integer,flags=Fs,
+ seg=#k_literal{val=Val},next=Next},
[format_bin_seg_1(S, Ctxt),
format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))];
format_1(#k_bin_end{}, _Ctxt) -> "#<>#";
+format_1(#k_literal{val=A}, _Ctxt) when is_atom(A) ->
+ core_atom(A);
format_1(#k_literal{val=Term}, _Ctxt) ->
io_lib:format("~p", [Term]);
format_1(#k_local{name=N,arity=A}, Ctxt) ->
@@ -158,20 +155,9 @@ format_1(#k_seq{arg=A,body=B}, Ctxt) ->
nl_indent(Ctxt)
| format(B, Ctxt)
];
-format_1(#k_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) ->
+format_1(#k_match{body=Bs,ret=Rs}, Ctxt) ->
Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["match ",
- format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2),
- nl_indent(Ctxt1),
- format(Bs, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_guard_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["guard_match ",
- format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2),
+ ["match",
nl_indent(Ctxt1),
format(Bs, Ctxt1),
nl_indent(Ctxt),
@@ -185,6 +171,20 @@ format_1(#k_alt{first=O,then=T}, Ctxt) ->
format(O, Ctxt1),
nl_indent(Ctxt1),
format(T, Ctxt1)];
+format_1(#k_letrec_goto{label=Label,first=First,then=Then,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["letrec_goto ",
+ atom_to_list(Label),
+ nl_indent(Ctxt1),
+ format(Then, Ctxt1),
+ nl_indent(Ctxt1),
+ format(First, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_goto{label=Label}, _Ctxt) ->
+ ["goto ",atom_to_list(Label)];
format_1(#k_select{var=V,types=Cs}, Ctxt) ->
Ctxt1 = ctxt_bump_indent(Ctxt, 2),
["select ",
@@ -235,13 +235,8 @@ format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) ->
[Txt,format_args(As, Ctxt1),
format_ret(Rs, Ctxt1)
];
-format_1(#k_test{op=Op,args=As,inverted=Inverted}, Ctxt) ->
- Txt = case Inverted of
- false ->
- ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)];
- true ->
- ["inverted_test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)]
- end,
+format_1(#k_test{op=Op,args=As}, Ctxt) ->
+ Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
Ctxt1 = ctxt_bump_indent(Ctxt, 2),
[Txt,format_args(As, Ctxt1)];
format_1(#k_put{arg=A,ret=Rs}, Ctxt) ->
@@ -285,15 +280,6 @@ format_1(#k_try_enter{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
nl_indent(Ctxt),
"end"
];
-format_1(#k_protected{arg=A,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["protected",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, ctxt_bump_indent(Ctxt, 1))
- ];
format_1(#k_catch{body=B,ret=Rs}, Ctxt) ->
Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
["catch",
@@ -303,34 +289,11 @@ format_1(#k_catch{body=B,ret=Rs}, Ctxt) ->
"end",
format_ret(Rs, Ctxt1)
];
-format_1(#k_receive{var=V,body=B,timeout=T,action=A,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["receive ",
- format(V, Ctxt),
- nl_indent(Ctxt1),
- format(B, Ctxt1),
- nl_indent(Ctxt),
- "after ",
- format(T, ctxt_bump_indent(Ctxt, 6)),
- " ->",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_receive_accept{}, _Ctxt) -> "receive_accept";
-format_1(#k_receive_next{}, _Ctxt) -> "receive_next";
format_1(#k_break{args=As}, Ctxt) ->
["<",
format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
">"
];
-format_1(#k_guard_break{args=As}, Ctxt) ->
- [":<",
- format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- ">:"
- ];
format_1(#k_return{args=As}, Ctxt) ->
["<<",
format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
@@ -347,7 +310,7 @@ format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) ->
];
format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) ->
["module ",
- format(#k_atom{val=N}, ctxt_bump_indent(Ctxt, 7)),
+ format(#k_literal{val=N}, ctxt_bump_indent(Ctxt, 7)),
nl_indent(Ctxt),
"export [",
format_vseq(Es,
@@ -437,17 +400,17 @@ format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)].
%% format_attribute({Name,Val}, Context) -> Txt.
format_attribute({Name,Val}, Ctxt) when is_list(Val) ->
- Txt = format(#k_atom{val=Name}, Ctxt),
+ Txt = format(#k_literal{val=Name}, Ctxt),
Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4),
[Txt," = ",
$[,format_vseq(Val, "", ",", Ctxt1,
fun (A, _C) -> io_lib:write(A) end),$]
];
format_attribute({Name,Val}, Ctxt) ->
- Txt = format(#k_atom{val=Name}, Ctxt),
+ Txt = format(#k_literal{val=Name}, Ctxt),
[Txt," = ",io_lib:write(Val)].
-format_list_tail(#k_nil{anno=[]}, _Ctxt) -> "]";
+format_list_tail(#k_literal{anno=[],val=[]}, _Ctxt) -> "]";
format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) ->
Txt = [$,|format(H, Ctxt)],
Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 7be23fbb93..1a6628fc9f 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -16,12 +16,14 @@ MODULES= \
beam_reorder_SUITE \
beam_ssa_SUITE \
beam_type_SUITE \
+ beam_types_SUITE \
beam_utils_SUITE \
bif_SUITE \
bs_bincomp_SUITE \
bs_bit_binaries_SUITE \
bs_construct_SUITE \
bs_match_SUITE \
+ bs_size_expr_SUITE \
bs_utf_SUITE \
core_alias_SUITE \
core_fold_SUITE \
@@ -59,6 +61,7 @@ NO_OPT= \
bif \
bs_construct \
bs_match \
+ bs_size_expr \
bs_utf \
core_fold \
float \
@@ -84,6 +87,7 @@ INLINE= \
bs_bit_binaries \
bs_construct \
bs_match \
+ bs_size_expr \
bs_utf \
core_fold \
float \
@@ -101,6 +105,8 @@ R21= \
bs_construct \
bs_match
+DIALYZER = bs_match
+
CORE_MODULES = \
lfe_andor_SUITE \
lfe_guard_SUITE
@@ -125,6 +131,8 @@ NO_SSA_OPT_MODULES= $(NO_SSA_OPT:%=%_no_ssa_opt_SUITE)
NO_SSA_OPT_ERL_FILES= $(NO_SSA_OPT_MODULES:%=%.erl)
NO_TYPE_OPT_MODULES= $(NO_TYPE_OPT:%=%_no_type_opt_SUITE)
NO_TYPE_OPT_ERL_FILES= $(NO_TYPE_OPT_MODULES:%=%.erl)
+DIALYZER_MODULES= $(DIALYZER:%=%_dialyzer_SUITE)
+DIALYZER_ERL_FILES= $(DIALYZER_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
CORE_FILES= $(CORE_MODULES:%=%.core)
@@ -154,7 +162,8 @@ EBIN = .
# ----------------------------------------------------
make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES) \
- $(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES) $(NO_TYPE_OPT_ERL_FILES)
+ $(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES) $(NO_TYPE_OPT_ERL_FILES) \
+ $(DIALYZER_ERL_FILES)
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +no_copt +no_postopt \
@@ -175,6 +184,8 @@ make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES
-o$(EBIN) $(CORE_MODULES) >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +no_type_opt $(ERL_COMPILE_FLAGS) \
-o$(EBIN) $(NO_TYPE_OPT_MODULES) >> $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile +dialyzer $(ERL_COMPILE_FLAGS) \
+ -o$(EBIN) $(DIALYZER_MODULES) >> $(EMAKEFILE)
tests debug opt: make_emakefile
erl $(ERL_MAKE_FLAGS) -make
@@ -211,6 +222,8 @@ docs:
%_no_type_opt_SUITE.erl: %_SUITE.erl
sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
+%_dialyzer_SUITE.erl: %_SUITE.erl
+ sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
# ----------------------------------------------------
# Release Target
@@ -227,7 +240,8 @@ release_tests_spec: make_emakefile
$(INLINE_ERL_FILES) $(R21_ERL_FILES) \
$(NO_MOD_OPT_ERL_FILES) \
$(NO_SSA_OPT_ERL_FILES) \
- $(NO_TYPE_OPT_ERL_FILES) "$(RELSYSDIR)"
+ $(NO_TYPE_OPT_ERL_FILES) \
+ $(DIALYZER_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(CORE_FILES) "$(RELSYSDIR)"
for file in $(ERL_DUMMY_FILES); do \
module=`basename $$file .erl`; \
@@ -236,6 +250,6 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(ERL_DUMMY_FILES) "$(RELSYSDIR)"
rm $(ERL_DUMMY_FILES)
chmod -R u+w "$(RELSYSDIR)"
- @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index 5c463063c1..7232eb0ffd 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -66,6 +66,17 @@ t_case(Config) when is_list(Config) ->
true = (catch t_case_e({a,b}, {a,b})),
false = (catch t_case_e({a,b}, 42)),
+ {true,false} = t_case_f1(true, pos),
+ {false,true} = t_case_f1(true, whatever),
+ {false,true} = t_case_f1(false, pos),
+ {false,true} = t_case_f1(false, whatever),
+ {false,false} = t_case_f1(not_boolean, pos),
+ {false,false} = t_case_f1(not_boolean, whatever),
+
+ false = t_case_f2(true),
+ true = t_case_f2(false),
+ false = t_case_f2(whatever),
+
true = t_case_xy(42, 100, 700),
true = t_case_xy(42, 100, whatever),
false = t_case_xy(42, wrong, 700),
@@ -109,6 +120,25 @@ t_case_e(A, B) ->
Bool when is_tuple(A) -> id(Bool)
end.
+t_case_f1(IsInt, Eval) ->
+ B = case IsInt of
+ true -> Eval =:= pos;
+ false -> false;
+ _ -> IsInt
+ end,
+
+ %% The above is the same as `IsInt andalso Eval =:= pos` in a guard.
+ %% In a real guard, variable `B` will only be used once.
+ {B =:= true, B =:= false}.
+
+t_case_f2(IsInt) ->
+ B = case IsInt of
+ true -> false;
+ false -> true;
+ _ -> IsInt
+ end,
+ B =:= true.
+
t_case_xy(X, Y, Z) ->
Res = t_case_x(X, Y, Z),
Res = t_case_y(X, Y, Z).
diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl
index c8dfc81969..7ffaf54609 100644
--- a/lib/compiler/test/beam_except_SUITE.erl
+++ b/lib/compiler/test/beam_except_SUITE.erl
@@ -127,19 +127,26 @@ coverage(_) ->
{'EXIT',{function_clause,[{?MODULE,foobar,[[fail],1,2],
[{file,"fake.erl"},{line,16}]}|_]}} =
(catch foobar([fail], 1, 2)),
- {'EXIT',{function_clause,[{?MODULE,fake_function_clause,[{a,b},42.0],_}|_]}} =
- (catch fake_function_clause({a,b})),
+ {'EXIT',{function_clause,[{?MODULE,fake_function_clause1,[{a,b},42.0],_}|_]}} =
+ (catch fake_function_clause1({a,b})),
+
+ {'EXIT',{function_clause,[{?MODULE,fake_function_clause2,[42|bad_tl],_}|_]}} =
+ (catch fake_function_clause2(42, bad_tl)),
+ {'EXIT',{function_clause,[{?MODULE,fake_function_clause3,[x,y],_}|_]}} =
+ (catch fake_function_clause3(42, id([x,y]))),
{'EXIT',{{badmatch,0.0},_}} = (catch coverage_1(id(42))),
{'EXIT',{badarith,_}} = (catch coverage_1(id(a))),
+
ok.
coverage_1(X) ->
%% ERL-1167: Would crash beam_except.
true = 0 / X.
-fake_function_clause(A) -> error(function_clause, [A,42.0]).
-
+fake_function_clause1(A) -> error(function_clause, [A,42.0]).
+fake_function_clause2(A, Tl) -> error(function_clause, [A|Tl]).
+fake_function_clause3(_, Stk) -> error(function_clause, Stk).
binary_construction_allocation(_Config) ->
ok = do_binary_construction_allocation("PUT"),
diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl
index 26676644b7..d687fcfab7 100644
--- a/lib/compiler/test/beam_ssa_SUITE.erl
+++ b/lib/compiler/test/beam_ssa_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
calls/1,tuple_matching/1,recv/1,maps/1,
cover_ssa_dead/1,combine_sw/1,share_opt/1,
- beam_ssa_dead_crash/1,stack_init/1]).
+ beam_ssa_dead_crash/1,stack_init/1,grab_bag/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -40,7 +40,8 @@ groups() ->
combine_sw,
share_opt,
beam_ssa_dead_crash,
- stack_init
+ stack_init,
+ grab_bag
]}].
init_per_suite(Config) ->
@@ -208,6 +209,8 @@ recv(_Config) ->
%% tricky_recv_6/0 is a compile-time error.
tricky_recv_6(),
+ recv_coverage(),
+
ok.
sync_wait_mon({Pid, Ref}, Timeout) ->
@@ -357,7 +360,7 @@ tricky_recv_5a() ->
%% When fixing tricky_recv_5, we introduced a compiler crash when the common
-%% exit block was ?BADARG_BLOCK and floats were in the picture.
+%% exit block was ?EXCEPTION_BLOCK and floats were in the picture.
tricky_recv_6() ->
RefA = make_ref(),
RefB = make_ref(),
@@ -368,6 +371,69 @@ tricky_recv_6() ->
ok
end.
+recv_coverage() ->
+ self() ! 1,
+ a = recv_coverage_1(),
+ self() ! 2,
+ b = recv_coverage_1(),
+
+ self() ! 1,
+ a = recv_coverage_2(),
+ self() ! 2,
+ b = recv_coverage_2(),
+
+ ok.
+
+%% Similar to tricky_recv_5/0, but provides test coverage for the #b_switch{}
+%% terminator.
+recv_coverage_1() ->
+ receive
+ X=1 ->
+ %% Jump to common exit block through #b_switch{list=L}
+ case id(0) of
+ 0 -> a;
+ 1 -> b;
+ 2 -> c;
+ 3 -> d
+ end;
+ X=2 ->
+ %% Jump to common exit block through #b_switch{fail=F}
+ case id(42) of
+ 0 -> exit(quit);
+ 1 -> exit(quit);
+ 2 -> exit(quit);
+ 3 -> exit(quit);
+ _ -> b
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
+%% Similar to recv_coverage_1/0, providing test coverage for #b_br{}.
+recv_coverage_2() ->
+ receive
+ X=1 ->
+ A = id(1),
+ %% Jump to common exit block through #b_br{succ=S}.
+ if
+ A =:= 1 -> a;
+ true -> exit(quit)
+ end;
+ X=2 ->
+ A = id(2),
+ %% Jump to common exit block through #b_br{fail=F}.
+ if
+ A =:= 1 -> exit(quit);
+ true -> a
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
maps(_Config) ->
{'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)),
ok.
@@ -419,48 +485,8 @@ cover_ssa_dead(_Config) ->
40.0 = percentage(4.0, 10.0),
60.0 = percentage(6, 10),
- %% Cover '=:=', followed by '=/='.
- false = 'cover__=:=__=/='(41),
- true = 'cover__=:=__=/='(42),
- false = 'cover__=:=__=/='(43),
-
- %% Cover '<', followed by '=/='.
- true = 'cover__<__=/='(41),
- false = 'cover__<__=/='(42),
- false = 'cover__<__=/='(43),
-
- %% Cover '=<', followed by '=/='.
- true = 'cover__=<__=/='(41),
- true = 'cover__=<__=/='(42),
- false = 'cover__=<__=/='(43),
-
- %% Cover '>=', followed by '=/='.
- false = 'cover__>=__=/='(41),
- true = 'cover__>=__=/='(42),
- true = 'cover__>=__=/='(43),
-
- %% Cover '>', followed by '=/='.
- false = 'cover__>__=/='(41),
- false = 'cover__>__=/='(42),
- true = 'cover__>__=/='(43),
-
ok.
-'cover__=:=__=/='(X) when X =:= 42 -> X =/= 43;
-'cover__=:=__=/='(_) -> false.
-
-'cover__<__=/='(X) when X < 42 -> X =/= 42;
-'cover__<__=/='(_) -> false.
-
-'cover__=<__=/='(X) when X =< 42 -> X =/= 43;
-'cover__=<__=/='(_) -> false.
-
-'cover__>=__=/='(X) when X >= 42 -> X =/= 41;
-'cover__>=__=/='(_) -> false.
-
-'cover__>__=/='(X) when X > 42 -> X =/= 42;
-'cover__>__=/='(_) -> false.
-
format_str(Str, FormatData, IoList, EscChars) ->
Escapable = FormatData =:= escapable,
case id(Str) of
@@ -668,5 +694,72 @@ stack_init(Key, Map) ->
%% (if the second clause was executed).
id(Res).
+grab_bag(_Config) ->
+ {'EXIT',_} = (catch grab_bag_1()),
+ {'EXIT',_} = (catch grab_bag_2()),
+ {'EXIT',_} = (catch grab_bag_3()),
+ {'EXIT',_} = (catch grab_bag_4()),
+ ok.
+
+grab_bag_1() ->
+ %% beam_kernel_to_ssa would crash when attempting to translate a make_fun
+ %% instruction without a destination variable.
+ (catch fun () -> 15 end)(true#{}).
+
+grab_bag_2() ->
+ %% is_guard_cg_safe/1 will be called with #cg_unreachable{}, which was
+ %% not handled.
+ 27
+ or
+ try
+ try
+ x#{}
+ catch
+ _:_ ->
+ []
+ end
+ after
+ false
+ end.
+
+grab_bag_3() ->
+ case
+ fun (V0)
+ when
+ %% The only thing left after optimizations would be
+ %% a bs_add instruction not followed by succeeded,
+ %% which would crash beam_ssa_codegen because there
+ %% was no failure label available.
+ binary_part(<<>>,
+ <<V0:V0/unit:196>>) ->
+ []
+ end
+ of
+ <<>> ->
+ []
+ end.
+
+grab_bag_4() ->
+ %% beam_kernel_to_ssa would crash because there was a #cg_phi{}
+ %% instruction that was not referenced from any #cg_break{}.
+ case $f of
+ V0 ->
+ try
+ try fy of
+ V0 ->
+ fu
+ catch
+ throw:$s ->
+ fy
+ end
+ catch
+ error:#{#{[] + [] => []} := false} when [] ->
+ fy
+ after
+ ok
+ end
+ end.
+
+
%% The identity function.
id(I) -> I.
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index a99dee48aa..4ec427cdec 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -85,6 +85,8 @@ integers(_Config) ->
two = do_integers_5(0, 2),
three = do_integers_5(0, 3),
+ {'EXIT',{badarith,_}} = (catch do_integers_6()),
+
ok.
do_integers_1(B0) ->
@@ -131,6 +133,9 @@ do_integers_5(X0, Y0) ->
3 -> three
end.
+do_integers_6() ->
+ try b after 1 end band 0.
+
numbers(_Config) ->
Int = id(42),
true = is_integer(Int),
diff --git a/lib/compiler/test/beam_types_SUITE.erl b/lib/compiler/test/beam_types_SUITE.erl
new file mode 100644
index 0000000000..4fbf6a130e
--- /dev/null
+++ b/lib/compiler/test/beam_types_SUITE.erl
@@ -0,0 +1,166 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_types_SUITE).
+
+-define(BEAM_TYPES_INTERNAL, true).
+-include_lib("compiler/src/beam_types.hrl").
+
+-export([all/0, suite/0, groups/0,
+ init_per_suite/1, end_per_suite/1]).
+
+-export([absorption/1,
+ associativity/1,
+ commutativity/1,
+ idempotence/1,
+ identity/1,
+ subtraction/1]).
+
+-export([binary_absorption/1,
+ integer_absorption/1,
+ integer_associativity/1,
+ tuple_absorption/1,
+ tuple_set_limit/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group,property_tests},
+ binary_absorption,
+ integer_absorption,
+ integer_associativity,
+ tuple_absorption,
+ tuple_set_limit].
+
+groups() ->
+ [{property_tests,[parallel],
+ [absorption,
+ associativity,
+ commutativity,
+ idempotence,
+ identity,
+ subtraction]}].
+
+init_per_suite(Config) ->
+ ct_property_test:init_per_suite(Config).
+
+end_per_suite(Config) ->
+ Config.
+
+absorption(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:absorption()).
+ true = ct_property_test:quickcheck(beam_types_prop:absorption(), Config).
+
+associativity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:associativity()).
+ true = ct_property_test:quickcheck(beam_types_prop:associativity(), Config).
+
+commutativity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:commutativity()).
+ true = ct_property_test:quickcheck(beam_types_prop:commutativity(), Config).
+
+idempotence(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:idempotence()).
+ true = ct_property_test:quickcheck(beam_types_prop:idempotence(), Config).
+
+identity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:identity()).
+ true = ct_property_test:quickcheck(beam_types_prop:identity(), Config).
+
+subtraction(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:subtraction()).
+ true = ct_property_test:quickcheck(beam_types_prop:subtraction(), Config).
+
+binary_absorption(Config) when is_list(Config) ->
+ %% These binaries should meet into {binary,12} as that's the best common
+ %% unit for both types.
+ A = #t_bitstring{size_unit=4},
+ B = #t_bitstring{size_unit=6},
+
+ #t_bitstring{size_unit=12} = beam_types:meet(A, B),
+ #t_bitstring{size_unit=2} = beam_types:join(A, B),
+
+ A = beam_types:meet(A, beam_types:join(A, B)),
+ A = beam_types:join(A, beam_types:meet(A, B)),
+
+ ok.
+
+integer_absorption(Config) when is_list(Config) ->
+ %% Integers that don't overlap at all should never meet.
+ A = #t_integer{elements={2,3}},
+ B = #t_integer{elements={4,5}},
+
+ none = beam_types:meet(A, B),
+ #t_integer{elements={2,5}} = beam_types:join(A, B),
+
+ A = beam_types:meet(A, beam_types:join(A, B)),
+ A = beam_types:join(A, beam_types:meet(A, B)),
+
+ ok.
+
+integer_associativity(Config) when is_list(Config) ->
+ A = #t_integer{elements={3,5}},
+ B = #t_integer{elements={4,6}},
+ C = #t_integer{elements={5,5}},
+
+ %% a ∨ (b ∨ c) = (a ∨ b) ∨ c,
+ LHS_Join = beam_types:join(A, beam_types:join(B, C)),
+ RHS_Join = beam_types:join(beam_types:join(A, B), C),
+ #t_integer{elements={3,6}} = LHS_Join = RHS_Join,
+
+ %% a ∧ (b ∧ c) = (a ∧ b) ∧ c.
+ LHS_Meet = beam_types:meet(A, beam_types:meet(B, C)),
+ RHS_Meet = beam_types:meet(beam_types:meet(A, B), C),
+ #t_integer{elements={5,5}} = LHS_Meet = RHS_Meet,
+
+ ok.
+
+tuple_absorption(Config) when is_list(Config) ->
+ %% An inexact tuple can't meet an exact one that's smaller
+
+ A = #t_tuple{size=3,exact=true,
+ elements=#{1 => #t_atom{elements=[gurka]}}},
+ B = #t_tuple{size=5,exact=false,
+ elements=#{3 => #t_atom{elements=[gaffel]}}},
+
+ A = beam_types:meet(A, beam_types:join(A, B)),
+ A = beam_types:join(A, beam_types:meet(A, B)),
+
+ ok.
+
+tuple_set_limit(Config) when is_list(Config) ->
+ %% When joining two tuple sets of differing sizes, the resulting set could
+ %% become larger than ?TUPLE_SET_LIMIT.
+
+ As = [#t_tuple{size=N,exact=true,
+ elements=#{ 1 => #t_integer{elements={N,N}} }} ||
+ N <- lists:seq(1, ?TUPLE_SET_LIMIT)],
+
+ Bs = [#t_tuple{size=1,exact=true,
+ elements=#{ 1 => #t_integer{elements={N,N}} }} ||
+ N <- lists:seq(1, ?TUPLE_SET_LIMIT)],
+
+ A = beam_types:join(As),
+ B = beam_types:join(Bs),
+
+ beam_types:verified_type(beam_types:join(A, B)),
+
+ ok.
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index a3f42213e8..89981f1992 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -35,7 +35,8 @@
map_field_lists/1,cover_bin_opt/1,
val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
receive_stacked/1,aliased_types/1,type_conflict/1,
- infer_on_eq/1,infer_dead_value/1,
+ infer_on_eq/1,infer_dead_value/1,infer_on_ne/1,
+ branch_to_try_handler/1,call_without_stack/1,
receive_marker/1,safe_instructions/1,
missing_return_type/1]).
@@ -67,8 +68,10 @@ groups() ->
map_field_lists,cover_bin_opt,val_dsetel,
bad_tuples,bad_try_catch_nesting,
receive_stacked,aliased_types,type_conflict,
- infer_on_eq,infer_dead_value,receive_marker,
- safe_instructions,missing_return_type]}].
+ infer_on_eq,infer_dead_value,infer_on_ne,
+ branch_to_try_handler,call_without_stack,
+ receive_marker,safe_instructions,
+ missing_return_type]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -151,19 +154,34 @@ stack(Config) when is_list(Config) ->
call_last(Config) when is_list(Config) ->
Errors = do_val(call_last, Config),
- [{{t,a,1},{{call_last,1,{f,8},2},9,{allocated,1}}},
+ [{{t,a,1},
+ {{call_last,1,{f,8},2},9,{allocated,1}}},
{{t,b,1},
- {{call_ext_last,2,{extfunc,lists,seq,2},2},
- 10,
- {allocated,1}}}] = Errors,
+ {{call_ext_last,2,{extfunc,lists,seq,2},2},10,{allocated,1}}},
+ {{t,baz,2},
+ {{call_ext_only,2,{extfunc,erlang,put,2}},5,{allocated,0}}},
+ {{t,biz,2},
+ {{call_only,2,{f,10}},5,{allocated,0}}}] = Errors,
+ ok.
+
+call_without_stack(Config) when is_list(Config) ->
+ Errors = do_val(call_without_stack, Config),
+ [{{t,local,2},
+ {{call,2,{f,2}},4,{allocated,none}}},
+ {{t,remote,2},
+ {{call_ext,2,{extfunc,lists,seq,2}},4,{allocated,none}}}] = Errors,
ok.
merge_undefined(Config) when is_list(Config) ->
Errors = do_val(merge_undefined, Config),
- [{{t,handle_call,2},
+ [{{t,undecided,2},
{{call_ext,2,{extfunc,debug,filter,2}},
22,
- {uninitialized_reg,{y,_}}}}] = Errors,
+ {allocated,undecided}}},
+ {{t,uninitialized,2},
+ {{call_ext,2,{extfunc,io,format,2}},
+ 17,
+ {uninitialized_reg,{y,1}}}}] = Errors,
ok.
uninit(Config) when is_list(Config) ->
@@ -220,11 +238,11 @@ bad_catch_try(Config) when is_list(Config) ->
{{catch_end,{x,9}},
8,{invalid_tag_register,{x,9}}}},
{{bad_catch_try,bad_3,1},
- {{catch_end,{y,1}},9,{invalid_tag,{y,1},{atom,kalle}}}},
+ {{catch_end,{y,1}},9,{invalid_tag,{y,1},{t_atom,[kalle]}}}},
{{bad_catch_try,bad_4,1},
{{'try',{x,0},{f,15}},5,{invalid_tag_register,{x,0}}}},
{{bad_catch_try,bad_5,1},
- {{try_case,{y,1}},12,{invalid_tag,{y,1},term}}},
+ {{try_case,{y,1}},12,{invalid_tag,{y,1},any}}},
{{bad_catch_try,bad_6,1},
{{move,{integer,1},{y,1}},7,
{invalid_store,{y,1}}}}] = Errors,
@@ -235,7 +253,7 @@ cons_guard(Config) when is_list(Config) ->
[{{cons,foo,1},
{{get_list,{x,0},{x,1},{x,2}},
5,
- {bad_type,{needed,cons},{actual,term}}}}] = Errors,
+ {bad_type,{needed,{t_cons,any,any}},{actual,any}}}}] = Errors,
ok.
freg_range(Config) when is_list(Config) ->
@@ -266,7 +284,7 @@ freg_uninit(Config) when is_list(Config) ->
{uninitialized_reg,{fr,1}}}},
{{t,sum_2,2},
{{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}},
- 9,
+ 10,
{uninitialized_reg,{fr,0}}}}] = Errors,
ok.
@@ -523,9 +541,9 @@ bad_tuples(Config) ->
{{bad_tuples,long,2},
{{put,{atom,too_long}},8,not_building_a_tuple}},
{{bad_tuples,self_referential,1},
- {{put,{x,1}},7,{tuple_in_progress,{x,1}}}},
+ {{put,{x,1}},7,{unfinished_tuple,{x,1}}}},
{{bad_tuples,short,1},
- {{move,{x,1},{x,0}},7,{tuple_in_progress,{x,1}}}}] = Errors,
+ {{move,{x,1},{x,0}},7,{unfinished_tuple,{x,1}}}}] = Errors,
ok.
@@ -708,6 +726,25 @@ idv_1({_A, _B, _C, _D, _E, F, G},
idv_1(_A, _B) ->
error.
+%% ERL-998; type inference for select_val (#b_switch{}) was more clever than
+%% that for is_ne_exact (#b_br{}), sometimes failing validation when the type
+%% optimization pass acted on the former and the validator got the latter.
+
+-record(ion, {state}).
+
+infer_on_ne(Config) when is_list(Config) ->
+ #ion{state = closing} = ion_1(#ion{ state = id(open) }),
+ #ion{state = closing} = ion_close(#ion{ state = open }),
+ ok.
+
+ion_1(State = #ion{state = open}) -> ion_2(State);
+ion_1(State = #ion{state = closing}) -> ion_2(State).
+
+ion_2(State = #ion{state = open}) -> ion_close(State);
+ion_2(#ion{state = closing}) -> ok.
+
+ion_close(State = #ion{}) -> State#ion{state = closing}.
+
%% ERL-995: The first solution to ERIERL-348 was incomplete and caused
%% validation to fail when living values depended on delayed type inference on
%% "dead" values.
@@ -725,6 +762,17 @@ idv_2(State) ->
idv_called_once(_State) -> ok.
+%% Direct jumps to try/catch handlers crash the emulator and must fail
+%% validation. This is provoked by OTP-15945.
+
+branch_to_try_handler(Config) ->
+ Errors = do_val(branch_to_try_handler, Config),
+ [{{branch_to_try_handler,main,1},
+ {{bif,tuple_size,{f,3},[{y,0}],{x,0}},
+ 12,
+ {illegal_branch,try_handler,3}}}] = Errors,
+ ok.
+
receive_marker(Config) when is_list(Config) ->
Errors = do_val(receive_marker, Config),
diff --git a/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
new file mode 100644
index 0000000000..6d43ec7b54
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
@@ -0,0 +1,48 @@
+{module, branch_to_try_handler}. %% version = 0
+
+{exports, [{main,1}]}.
+
+{attributes, []}.
+
+{labels, 11}.
+
+{function, main, 1, 2}.
+ {label,1}.
+ {line,[{location,"t.erl",4}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,main},1}.
+ {label,2}.
+ {allocate,2,1}.
+ {move,{x,0},{y,0}}.
+ {'try',{y,1},{f,3}}.
+ {move,{atom,ignored},{x,0}}.
+ {line,[{location,"t.erl",6}]}.
+ {call,1,{f,6}}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ {line,[{location,"t.erl",7}]}.
+ %%
+ %% Fail directly to the try handler instead of throwing an exception; this
+ %% will crash the emulator.
+ %%
+ {bif,tuple_size,{f,3},[{y,0}],{x,0}}.
+ %%
+ {test,is_eq_exact,{f,4},[{x,0},{integer,1}]}.
+ {move,{atom,error},{x,0}}.
+ {try_end,{y,1}}.
+ {deallocate,2}.
+ return.
+ {label,3}.
+ {try_case,{y,1}}.
+ {move,{atom,ok},{x,0}}.
+ {deallocate,2}.
+ return.
+ {label,4}.
+ {line,[{location,"t.erl",7}]}.
+ {badmatch,{x,0}}.
+
+{function, id, 1, 6}.
+ {label,5}.
+ {line,[{location,"t.erl",13}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,id},1}.
+ {label,6}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ return.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/call_last.S b/lib/compiler/test/beam_validator_SUITE_data/call_last.S
index 827b6c0ae6..ff81da1b57 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/call_last.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/call_last.S
@@ -1,6 +1,6 @@
{module, call_last}. %% version = 0
-{exports, [{a,1},{b,1},{bar,1},{foo,1},{module_info,0},{module_info,1}]}.
+{exports, [{a,1},{b,1},{bar,1},{foo,1},{baz,2},{biz,2}]}.
{attributes, []}.
@@ -53,19 +53,16 @@
{'%live',1}.
return.
-
-{function, module_info, 0, 10}.
+{function, baz, 2, 10}.
{label,9}.
- {func_info,{atom,t},{atom,module_info},0}.
+ {func_info,{atom,t},{atom,baz},2}.
{label,10}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-
+ {allocate,0,2}.
+ {call_ext_only,2,{extfunc,erlang,put,2}}.
-{function, module_info, 1, 12}.
+{function, biz, 2, 12}.
{label,11}.
- {func_info,{atom,t},{atom,module_info},1}.
+ {func_info,{atom,t},{atom,biz},2}.
{label,12}.
- {move,{x,0},{x,1}}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
+ {allocate,0,2}.
+ {call_only,2,{f,10}}.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S b/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S
new file mode 100644
index 0000000000..9ccbc163e3
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S
@@ -0,0 +1,21 @@
+{module, call_without_stack}. %% version = 0
+
+{exports, [{remote,2},{local,2}]}.
+
+{attributes, []}.
+
+{labels, 9}.
+
+{function, remote, 2, 2}.
+ {label,1}.
+ {func_info,{atom,t},{atom,remote},2}.
+ {label,2}.
+ {call_ext,2,{extfunc,lists,seq,2}}.
+ if_end.
+
+{function, local, 2, 4}.
+ {label,3}.
+ {func_info,{atom,t},{atom,local},2}.
+ {label,4}.
+ {call,2,{f,2}}.
+ if_end.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
index 71e833446a..2d4cbc9388 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
@@ -21,12 +21,14 @@
{label,3}.
{func_info,{atom,t},{atom,sum_2},2}.
{label,4}.
+ {allocate,0,2}.
{fconv,{x,0},{fr,0}}.
{fconv,{x,1},{fr,1}}.
fclearerror.
{fcheckerror,{f,0}}.
{call,2,{f,6}}.
{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}.
+ {deallocate,0}.
return.
{function, foo, 2, 6}.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
index aa344807e4..3035471f04 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
@@ -1,15 +1,14 @@
{module, merge_undefined}. %% version = 0
-{exports, [{bar,2},{foo,1},{handle_call,2},{module_info,0},{module_info,1}]}.
+{exports, [{uninitialized,2},{undecided,2}]}.
{attributes, []}.
{labels, 15}.
-
-{function, handle_call, 2, 2}.
+{function, uninitialized, 2, 2}.
{label,1}.
- {func_info,{atom,t},{atom,handle_call},2}.
+ {func_info,{atom,t},{atom,uninitialized},2}.
{label,2}.
{test,is_atom,{f,1},[{x,0}]}.
{select_val,{x,0},{f,1},{list,[{atom,gurka},{f,3},{atom,delete},{f,4}]}}.
@@ -21,7 +20,7 @@
{move,{atom,nisse},{x,0}}.
{call_ext,1,{extfunc,erlang,exit,1}}.
{label,4}.
- {allocate_heap,1,6,2}.
+ {allocate_heap,2,6,2}.
{move,{x,1},{y,0}}.
{put_list,{integer,112},nil,{x,0}}.
{put_list,{integer,126},{x,0},{x,0}}.
@@ -51,37 +50,57 @@
{call_ext,1,{extfunc,erlang,exit,1}}.
{label,6}.
{move,{y,0},{x,0}}.
- {call_last,1,{f,8},1}.
+ {call_last,1,{f,14},1}.
-
-{function, foo, 1, 8}.
+{function, undecided, 2, 8}.
{label,7}.
- {func_info,{atom,t},{atom,foo},1}.
+ {func_info,{atom,t},{atom,undecided},2}.
{label,8}.
- {move,{atom,ok},{x,0}}.
- return.
-
-
-{function, bar, 2, 10}.
+ {test,is_atom,{f,7},[{x,0}]}.
+ {select_val,{x,0},{f,1},{list,[{atom,gurka},{f,9},{atom,delete},{f,10}]}}.
{label,9}.
- {func_info,{atom,t},{atom,bar},2}.
+ {allocate_heap,2,6,2}.
+ {test,is_eq_exact,{f,11},[{x,0},{atom,ok}]}.
+ %% This is unreachable since {x,0} is known not to be 'ok'. We should not
+ %% fail with "uninitialized y registers" on erlang:exit/1
+ {move,{atom,nisse},{x,0}}.
+ {call_ext,1,{extfunc,erlang,exit,1}}.
{label,10}.
- {move,{atom,ok},{x,0}}.
- return.
-
-
-{function, module_info, 0, 12}.
+ {allocate_heap,1,6,2}.
+ {move,{x,1},{y,0}}.
+ {put_list,{integer,112},nil,{x,0}}.
+ {put_list,{integer,126},{x,0},{x,0}}.
+ {put_list,{y,0},nil,{x,1}}.
+ {'%live',2}.
+ {call_ext,2,{extfunc,io,format,2}}.
+ {test,is_ne_exact,{f,12},[{x,0},{atom,ok}]}.
{label,11}.
- {func_info,{atom,t},{atom,module_info},0}.
+ %% The number of allocated Y registers are in conflict here.
+ {move,{atom,logReader},{x,1}}.
+ {move,{atom,console},{x,0}}.
+ {call_ext,2,{extfunc,debug,filter,2}}.
+ {test_heap,14,1}.
+ {put_list,{atom,logReader},nil,{x,1}}.
+ {put_list,{atom,console},{x,1},{x,1}}.
+ {put_tuple,3,{x,2}}.
+ {put,{atom,debug}}.
+ {put,{atom,filter}}.
+ {put,{x,1}}.
+ {put_tuple,2,{x,1}}.
+ {put,{x,2}}.
+ {put,{x,0}}.
+ {put_tuple,2,{x,0}}.
+ {put,{atom,badmatch}}.
+ {put,{x,1}}.
+ {'%live',1}.
+ {call_ext,1,{extfunc,erlang,exit,1}}.
{label,12}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-
+ {move,{y,0},{x,0}}.
+ {call_last,1,{f,8},1}.
-{function, module_info, 1, 14}.
+{function, foo, 1, 14}.
{label,13}.
- {func_info,{atom,t},{atom,module_info},1}.
+ {func_info,{atom,t},{atom,foo},1}.
{label,14}.
- {move,{x,0},{x,1}}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
+ {move,{atom,ok},{x,0}}.
+ return.
diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl
index 0419b16eea..eade3ff93f 100644
--- a/lib/compiler/test/bs_bincomp_SUITE.erl
+++ b/lib/compiler/test/bs_bincomp_SUITE.erl
@@ -191,7 +191,13 @@ coverage_trimmer(Params) ->
coverage_summer(A, B, C, D) -> A+B+C+D.
nomatch(Config) when is_list(Config) ->
+ Bin = id(<<1,2,3,4,5>>),
<<>> = << <<X:8>> || X = {_,_} = [_|_] <- [1,2,3] >>,
+ [] = [X || <<X:all/binary>> <= Bin],
+ [] = [X || <<X:bad/binary>> <= Bin],
+ <<>> = << <<X:32>> || <<X:all/binary>> <= Bin >>,
+ <<>> = << <<X:32>> || <<X:bad/binary>> <= Bin >>,
+
ok.
sizes(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index bccd70d6cb..1c61d6cce4 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -31,7 +31,7 @@
two/1,test1/1,fail/1,float_bin/1,in_guard/1,in_catch/1,
nasty_literals/1,coerce_to_float/1,side_effect/1,
opt/1,otp_7556/1,float_arith/1,otp_8054/1,
- cover/1]).
+ cover/1,bad_size/1]).
-include_lib("common_test/include/ct.hrl").
@@ -47,7 +47,7 @@ groups() ->
[verify_highest_opcode,
two,test1,fail,float_bin,in_guard,in_catch,
nasty_literals,side_effect,opt,otp_7556,float_arith,
- otp_8054,cover]}].
+ otp_8054,cover,bad_size]}].
init_per_suite(Config) ->
@@ -332,6 +332,24 @@ fail(Config) when is_list(Config) ->
%% Unaligned sizes with literal binaries.
{'EXIT',{badarg,_}} = (catch <<0,(<<7777:17>>)/binary>>),
+ %% Make sure that variables are bound even if binary
+ %% construction fails.
+ {'EXIT',{badarg,_}} = (catch case <<face:(V0 = 42)>> of
+ _Any -> V0
+ end),
+ {'EXIT',{badarg,_}} = (catch case <<face:(V1 = 3)>> of
+ a when V1 ->
+ office
+ end),
+ {'EXIT',{badarg,_}} = (catch <<13:(put(?FUNCTION_NAME, 17))>>),
+ 17 = erase(?FUNCTION_NAME),
+
+ %% Size exceeds length of binary. 'native' is redundant for
+ %% binaries, but when it was present sys_core_fold would not
+ %% detect the overlong binary and beam_ssa_opt would crash.
+ {'EXIT',{badarg,_}} = (catch << <<$t/little-signed>>:42/native-bytes >>),
+ {'EXIT',{badarg,_}} = (catch << <<$t/little-signed>>:42/bytes >>),
+
ok.
float_bin(Config) when is_list(Config) ->
@@ -559,6 +577,7 @@ otp_7556(Bin, A, B, C) ->
float_arith(Config) when is_list(Config) ->
{<<1,2,3,64,69,0,0,0,0,0,0>>,21.0} = do_float_arith(<<1,2,3>>, 42, 2),
+
ok.
do_float_arith(Bin0, X, Y) ->
@@ -597,3 +616,30 @@ cover(Config) ->
Bin = id(<<L:32,?LONG_STRING>>),
<<L:32,?LONG_STRING>> = Bin,
ok.
+
+bad_size(_Config) ->
+ {'EXIT',{badarg,_}} = (catch bad_float_size()),
+ {'EXIT',{badarg,_}} = (catch bad_float_size(<<"abc">>)),
+ {'EXIT',{badarg,_}} = (catch bad_integer_size()),
+ {'EXIT',{badarg,_}} = (catch bad_integer_size(<<"xyz">>)),
+ {'EXIT',{badarg,_}} = (catch bad_binary_size()),
+ {'EXIT',{badarg,_}} = (catch bad_binary_size(<<"xyz">>)),
+ ok.
+
+bad_float_size() ->
+ <<4.087073429964284:case 0 of 0 -> art end/float>>.
+
+bad_float_size(Bin) ->
+ <<Bin/binary,4.087073429964284:case 0 of 0 -> art end/float>>.
+
+bad_integer_size() ->
+ <<0:case 0 of 0 -> art end/integer>>.
+
+bad_integer_size(Bin) ->
+ <<Bin/binary,0:case 0 of 0 -> art end/integer>>.
+
+bad_binary_size() ->
+ <<<<"abc">>:case 0 of 0 -> art end/binary>>.
+
+bad_binary_size(Bin) ->
+ <<Bin/binary,<<"abc">>:case 0 of 0 -> art end/binary>>.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index a789b82910..226d526534 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -24,7 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- verify_highest_opcode/1,
+ verify_highest_opcode/1, expand_and_squeeze/1,
size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
bin_tail/1,save_restore/1,
partitioned_bs_match/1,function_clause/1,
@@ -45,7 +45,8 @@
expression_before_match/1,erl_689/1,restore_on_call/1,
restore_after_catch/1,matches_on_parameter/1,big_positions/1,
matching_meets_apply/1,bs_start_match2_defs/1,
- exceptions_after_match_failure/1, bad_phi_paths/1]).
+ exceptions_after_match_failure/1,
+ bad_phi_paths/1,many_clauses/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -61,10 +62,10 @@ all() ->
[{group,p}].
groups() ->
- [{p,[],
+ [{p,test_lib:parallel(),
[verify_highest_opcode,
size_shadow,int_float,otp_5269,null_fields,wiger,
- bin_tail,save_restore,
+ bin_tail,save_restore,expand_and_squeeze,
partitioned_bs_match,function_clause,unit,
shared_sub_bins,bin_and_float,dec_subidentifiers,
skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum,
@@ -82,8 +83,8 @@ groups() ->
expression_before_match,erl_689,restore_on_call,
matches_on_parameter,big_positions,
matching_meets_apply,bs_start_match2_defs,
- exceptions_after_match_failure,bad_phi_paths]}].
-
+ exceptions_after_match_failure,bad_phi_paths,
+ many_clauses]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -137,22 +138,59 @@ size_shadow(Config) when is_list(Config) ->
size_shadow_1() ->
L = 8,
- F = fun(<<L:L,B:L>>) -> B end,
- F(<<16:8, 7:16>>).
+ Fs = [fun(<<L:L,B:L>>) -> B end,
+ fun(A) ->
+ (fun([<<L:L,B:L>>]) -> B end)([A])
+ end,
+ fun(A) ->
+ (fun([<<L:L,B:L>>,<<L:L,B:L>>]) -> B end)([A,A])
+ end,
+ fun(A) ->
+ <<Size:L,_/bits>> = A,
+ Inner = fun([L], {#{key1 := <<L:L,B:L>>,
+ key2 := <<L:L,B:L>>}, L}) -> B end,
+ Inner([Size], {#{key1 => A,key2 => A},Size})
+ end],
+ size_shadow_apply(Fs, <<16:8, 7:16>>).
size_shadow_2(L) ->
- F = fun(<<L:L,B:L>>) -> B end,
- F(<<16:8, 7:16>>).
+ Fs = [fun(<<L:L,B:L>>) -> B end,
+ fun(A) ->
+ (fun([<<L:L,B:L>>]) -> B end)([A])
+ end,
+ fun(A) ->
+ (fun({<<L:L,B:L>>,<<L:L,B:L>>}) -> B end)({A,A})
+ end],
+ size_shadow_apply(Fs, <<16:8, 7:16>>).
size_shadow_3() ->
L = 8,
- F = fun(<<L:L,B:L,L:L>>) -> B end,
- F(<<16:8, 7:16,16:16>>).
+ Fs = [fun(<<L:L,B:L,L:L>>) -> B end,
+ fun(A) ->
+ (fun({tag,[<<L:L,B:L,L:L>>]}) -> B end)({tag,[A]})
+ end,
+ fun(A) ->
+ (fun({tag,<<L:L,B:L,L:L>>,<<L:L,B:L,L:L>>}) -> B end)({tag,A,A})
+ end],
+ size_shadow_apply(Fs, <<16:8, 7:16,16:16>>).
size_shadow_4(L) ->
- F = fun(<<L:L,B:L,L:L>>) -> B;
- (_) -> no end,
- F(<<16:8, 7:16,15:16>>).
+ Fs = [fun(<<L:L,B:L,L:L>>) -> B;
+ (_) -> no
+ end,
+ fun(A) ->
+ Inner = fun([<<L:L,B:L,L:L>>]) -> B;
+ (_) -> no
+ end,
+ Inner([A])
+ end,
+ fun(A) ->
+ Inner = fun({<<L:L,B:L,L:L>>,<<L:L,B:L,L:L>>}) -> B;
+ (_) -> no
+ end,
+ Inner({A,A})
+ end],
+ size_shadow_apply(Fs, <<16:8, 7:16,15:16>>).
size_shadow_5(X, Y) ->
fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end.
@@ -166,6 +204,14 @@ fum(A, B, C, D) ->
size_shadow_7({int,N}, <<N:16,B:N/binary,T/binary>>) ->
{B,T}.
+size_shadow_apply([F|Fs], Arg) when is_function(F, 1) ->
+ size_shadow_apply(Fs, Arg, F(Arg)).
+
+size_shadow_apply([F|Fs], Arg, Res) when is_function(F, 1) ->
+ Res = F(Arg),
+ size_shadow_apply(Fs, Arg, Res);
+size_shadow_apply([], _, Res) ->
+ Res.
int_float(Config) when is_list(Config) ->
%% OTP-5323
@@ -502,6 +548,9 @@ unit(Config) when is_list(Config) ->
{'EXIT',_} = (catch unit_opt_2(<<1:32,33:7>>)),
{'EXIT',_} = (catch unit_opt_2(<<2:32,55:7>>)),
+ <<0:64>> = unit_opt_3(<<1:128>>),
+ <<1:64>> = unit_opt_3(<<1:64>>),
+
ok.
peek1(<<B:8,_/bitstring>>) -> B.
@@ -533,6 +582,13 @@ unit_opt_2(<<St:32,KO/binary>> = Bin0) ->
end,
id(Bin).
+unit_opt_3(A) when is_binary(A) ->
+ %% There should be no test_unit instruction after the first segment, since
+ %% we already know A is a binary and its tail will still be a binary after
+ %% matching 8 bytes from it.
+ <<Bin:8/binary, _/binary>> = A,
+ Bin.
+
shared_sub_bins(Config) when is_list(Config) ->
{15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0),
ok.
@@ -1256,11 +1312,80 @@ zero_width(Config) when is_list(Config) ->
%% OTP_7650: A invalid size for binary segments could crash the compiler.
bad_size(Config) when is_list(Config) ->
Tuple = {a,b,c},
- {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)),
Binary = <<1,2,3>>,
+ Atom = an_atom,
+
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)),
{'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Binary>> = id(<<>>)),
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Atom>> = id(<<>>)),
+
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<42.0:Tuple/float>> = id(<<>>)),
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<42.0:Binary/float>> = id(<<>>)),
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<42.0:Atom/float>> = id(<<>>)),
+
+ %% Matched out value is ignored.
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<_:Binary>> = id(<<>>)),
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<_:Tuple>> = id(<<>>)),
+ {'EXIT',{{badmatch,<<>>},_}} = (catch <<_:Atom>> = id(<<>>)),
+
+ no_match = bad_all_size(<<>>),
+ no_match = bad_all_size(<<1,2,3>>),
+
ok.
+bad_all_size(Bin) ->
+ Res = bad_all_size_1(Bin),
+ Res = bad_all_size_2(Bin),
+ Res = bad_all_size_3(Bin),
+ Res = bad_all_size_4(Bin),
+ Res = bad_all_size_5(Bin),
+ Res = bad_all_size_6(Bin),
+ Res.
+
+bad_all_size_1(Bin) ->
+ case Bin of
+ <<B:all/binary>> -> B;
+ _ -> no_match
+ end.
+
+bad_all_size_2(Bin) ->
+ case Bin of
+ <<_:all/binary>> -> ok;
+ _ -> no_match
+ end.
+
+bad_all_size_3(Bin) ->
+ All = all,
+ case Bin of
+ <<B:All/binary>> -> B;
+ _ -> no_match
+ end.
+
+bad_all_size_4(Bin) ->
+ All = all,
+ case Bin of
+ <<_:All/binary>> -> ok;
+ _ -> no_match
+ end.
+
+bad_all_size_5(Bin) ->
+ All = case 0 of
+ 0 -> all
+ end,
+ case Bin of
+ <<B:All/binary>> -> B;
+ _ -> no_match
+ end.
+
+bad_all_size_6(Bin) ->
+ All = case 0 of
+ 0 -> all
+ end,
+ case Bin of
+ <<_:All/binary>> -> ok;
+ _ -> no_match
+ end.
+
haystack(Config) when is_list(Config) ->
<<0:10/unit:8>> = haystack_1(<<0:10/unit:8>>),
[<<0:10/unit:8>>,
@@ -1324,23 +1449,182 @@ matched_out_size(Config) when is_list(Config) ->
{<<1,2,3,7>>,19,42} = mos_bin(<<4,1,2,3,7,19,4,42>>),
<<1,2,3,7>> = mos_bin(<<4,1,2,3,7,"abcdefghij">>),
- ok.
+ false = mos_verify_sig(not_a_binary),
+ false = mos_verify_sig(<<>>),
+ false = mos_verify_sig(<<42:32>>),
+ <<"123456789">> = mos_verify_sig(<<77:32,0:77/unit:8,9:32,"123456789">>),
+
+ ok.
+
+mos_int(B) ->
+ Res = mos_int_plain(B),
+ Res = mos_int_list([B]),
+ Res = mos_int_tuple({a,[B],z}),
+
+ Res = mos_int_mixed([B]),
+ Res = mos_int_mixed({a,[B],z}),
+ 42 = mos_int_mixed({30,12}),
+ no_match = mos_int_mixed([B,B,B]),
+
+ Res = mos_int_pats1({tag,[B]}, {0,1,2,3,4,5,6,7,8,9}),
+ Res = mos_int_pats2({tag,[B]}, {a,a,a,a,a,a,a,a,a,a}, [z]),
+ {I,X} = Res,
+ Res = mos_int_pats3({tag,[B]}, [I,{X,B,X},I]),
+ Res = mos_int_map(#{key => [B]}),
+ Key = {my,key},
+ Res = mos_int_map(Key, #{Key => [B]}),
+ {I,X,B} = mos_int_alias([[B]]),
+ Res = {I,X},
+ Res = mos_int_try([B]),
+ Res = mos_int_receive(B),
+ Res = mos_int_fun([B]),
+ Res = mos_int_exported(B),
+ Res = mos_int_utf(B),
+ Res.
+
+mos_int_plain(<<L,I:L,X:32>>) ->
+ {I,X};
+mos_int_plain(<<L,I:L,X:64>>) ->
+ {I,X}.
+
+mos_int_list([<<L,I:L,X:32>>]) ->
+ {I,X};
+mos_int_list([<<L,I:L,X:64>>]) ->
+ {I,X}.
-mos_int(<<L,I:L,X:32>>) ->
+mos_int_tuple({a,[<<L,I:L,X:32>>],z}) ->
{I,X};
-mos_int(<<L,I:L,X:64>>) ->
+mos_int_tuple({a,[<<L,I:L,X:64>>],z}) ->
{I,X}.
-mos_bin(<<L,Bin:L/binary,X:8,L>>) ->
+mos_int_mixed({a,[<<L,I:L,X:32>>],z}) ->
+ {I,X};
+mos_int_mixed({a,[<<L,I:L,X:64>>],z}) ->
+ {I,X};
+mos_int_mixed([<<L,I:L,X:32>>]) ->
+ {I,X};
+mos_int_mixed([<<L,I:L,X:64>>]) ->
+ {I,X};
+mos_int_mixed({A,B}) when is_integer(A), is_integer(B) ->
+ A + B;
+mos_int_mixed(_) ->
+ no_match.
+
+mos_int_pats1({tag,[<<L,I:L,X:32>>]}, {_,_,_,_,_,_,_,_,_,_}) ->
+ {I,X};
+mos_int_pats1({tag,[<<L,I:L,X:64>>]}, {_,_,_,_,_,_,_,_,_,_}) ->
+ {I,X}.
+
+mos_int_pats2({tag,[<<L,I:L,X:32>>]}, {S,S,S,S,S,S,S,S,S,S}, [_|_]) ->
+ {I,X};
+mos_int_pats2({tag,[<<L,I:L,X:64>>]}, {S,S,S,S,S,S,S,S,S,S}, [_|_]) ->
+ {I,X}.
+
+mos_int_pats3({tag,[<<L,I:L,X:32>>]}, [I,{X,<<L,I:L,X:32>>,X},I]) ->
+ {I,X};
+mos_int_pats3({tag,[<<L,I:L,X:64>>]}, [I,{X,<<L,I:L,X:64>>,X},I]) ->
+ {I,X}.
+
+mos_int_map(#{key := [<<L,I:L,X:32>>]}) ->
+ {I,X};
+mos_int_map(#{key := [<<L,I:L,X:64>>]}) ->
+ {I,X}.
+
+mos_int_map(Key, Map) ->
+ case Map of
+ #{Key := [<<L,I:L,X:32>>]} -> {I,X};
+ #{Key := [<<L,I:L,X:64>>]} -> {I,X}
+ end.
+
+mos_int_alias([[<<L,I:L,X:32>> = B]]) ->
+ {I,X,B};
+mos_int_alias([[<<L,I:L,X:64>> = B]]) ->
+ {I,X,B}.
+
+mos_int_try(B) ->
+ try id(B) of
+ [<<L,I:L,X:32>>] -> {I,X};
+ [<<L,I:L,X:64>>] -> {I,X}
+ after
+ ok
+ end.
+
+mos_int_receive(Msg) ->
+ Res = (fun() ->
+ self() ! Msg,
+ receive
+ <<L,I:L,X:32>> -> {I,X};
+ <<L,I:L,X:64>> -> {I,X}
+ end
+ end)(),
+ self() ! Msg,
+ Res = receive
+ <<L,I:L,X:32>> -> {I,X};
+ <<L,I:L,X:64>> -> {I,X}
+ end,
+ self() ! {tag,[Msg]},
+ Res = receive
+ {tag,[<<L,I:L,X:32>>]} -> {I,X};
+ {tag,[<<L,I:L,X:64>>]} -> {I,X}
+ end,
+ Res.
+
+mos_int_fun(B) ->
+ L = ignore_me,
+ F = fun ([<<L,I:L,X:32>>]) -> {I,X};
+ ([<<L,I:L,X:64>>]) -> {I,X}
+ end,
+ F(B).
+
+mos_int_exported(B) ->
+ case B of
+ <<L,I:L,X:32>> -> ok;
+ <<L,I:L,X:64>> -> ok
+ end,
+ {I,X}.
+
+mos_int_utf(B0) ->
+ B = id(<<B0/bits,777/utf8,7777/utf16,9999/utf32>>),
+ case B of
+ <<L,I:L,X:32,777/utf8,7777/utf16,9999/utf32>> -> {I,X};
+ <<L,I:L,X:64,777/utf8,7777/utf16,9999/utf32>> -> {I,X}
+ end.
+
+mos_bin(B) ->
+ Res = mos_bin_plain(B),
+ Res = mos_bin_tuple({outer,{inner,B}}),
+ Res.
+
+mos_bin_plain(<<L,Bin:L/binary,X:8,L>>) ->
L = byte_size(Bin),
{Bin,X};
-mos_bin(<<L,Bin:L/binary,X:8,L,Y:8>>) ->
+mos_bin_plain(<<L,Bin:L/binary,X:8,L,Y:8>>) ->
L = byte_size(Bin),
{Bin,X,Y};
-mos_bin(<<L,Bin:L/binary,"abcdefghij">>) ->
+mos_bin_plain(<<L,Bin:L/binary,"abcdefghij">>) ->
L = byte_size(Bin),
Bin.
+mos_bin_tuple({outer,{inner,<<L,Bin:L/binary,X:8,L>>}}) ->
+ L = byte_size(Bin),
+ {Bin,X};
+mos_bin_tuple({outer,{inner,<<L,Bin:L/binary,X:8,L,Y:8>>}}) ->
+ L = byte_size(Bin),
+ {Bin,X,Y};
+mos_bin_tuple({outer,{inner,<<L,Bin:L/binary,"abcdefghij">>}}) ->
+ L = byte_size(Bin),
+ Bin.
+
+mos_verify_sig(AlgSig) ->
+ try
+ <<AlgLen:32, _Alg:AlgLen/binary,
+ SigLen:32, Sig:SigLen/binary>> = AlgSig,
+ Sig
+ catch
+ _:_ ->
+ false
+ end.
+
follow_fail_branch(_) ->
42 = ffb_1(<<0,1>>, <<0>>),
8 = ffb_1(<<0,1>>, [a]),
@@ -2034,3 +2318,187 @@ bad_phi_paths_1(Arg) ->
id(B).
id(I) -> I.
+
+expand_and_squeeze(Config) when is_list(Config) ->
+ %% UTF8 literals are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<$á/utf8,_/binary>>"),
+ ?Q("<<$é/utf8,_/binary>>")
+ ]),
+
+ %% Sized integers are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<0:32,_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits are squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits with empty binary are also squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<>>")
+ ]),
+
+ %% Groups of 8 bits with float lookup are not squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<_/float>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\",_/binary>>"),
+ ?Q("<<\"bb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible but are recursive...
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | RestDiverse
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaa\",_/binary>>"),
+ ?Q("<<\"abb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% so we still perform a 16 bits lookup for the remaining
+ true = lists:any(fun({test,bs_get_integer2,_,_,[_,{integer,16}|_],_}) -> true;
+ (_) -> false end, RestDiverse),
+
+ %% Large match is kept as is if there is a sized match later
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,64}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<255,255,255,255,255,255,255,255>>"),
+ ?Q("<<_:64>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0:32>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0,0,0,0>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with smaller but still large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32, _:A>>"),
+ ?Q("<<0:64>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% There is no squeezing for groups with more than 16 matches
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\", _/binary>>"),
+ ?Q("<<\"bb\", _/binary>>"),
+ ?Q("<<\"cc\", _/binary>>"),
+ ?Q("<<\"dd\", _/binary>>"),
+ ?Q("<<\"ee\", _/binary>>"),
+ ?Q("<<\"ff\", _/binary>>"),
+ ?Q("<<\"gg\", _/binary>>"),
+ ?Q("<<\"hh\", _/binary>>"),
+ ?Q("<<\"ii\", _/binary>>"),
+ ?Q("<<\"jj\", _/binary>>"),
+ ?Q("<<\"kk\", _/binary>>"),
+ ?Q("<<\"ll\", _/binary>>"),
+ ?Q("<<\"mm\", _/binary>>"),
+ ?Q("<<\"nn\", _/binary>>"),
+ ?Q("<<\"oo\", _/binary>>"),
+ ?Q("<<\"pp\", _/binary>>")
+ ]),
+
+ ok.
+
+binary_match_to_asm(Matches) ->
+ Clauses = [
+ begin
+ Ann = element(2, Match),
+ {clause,Ann,[Match],[],[{integer,Ann,Return}]}
+ end || {Match,Return} <- lists:zip(Matches, lists:seq(1, length(Matches)))
+ ],
+
+ Module = [
+ {attribute,1,module,match_to_asm},
+ {attribute,2,export,[{example,1}]},
+ {function,3,example,1,Clauses}
+ ],
+
+ {ok,match_to_asm,{match_to_asm,_Exports,_Attrs,Funs,_},_} =
+ compile:forms(Module, [return, to_asm]),
+
+ [{function,example,1,2,AllInstructions}|_] = Funs,
+ [{label,_},{line,_},{func_info,_,_,_},{label,_},{'%',_},
+ {test,bs_start_match3,_,_,_,_},{bs_get_position,_,_,_}|Instructions] = AllInstructions,
+ Instructions.
+
+many_clauses(_Config) ->
+ Mod = list_to_atom(?MODULE_STRING ++ "_" ++
+ atom_to_list(?FUNCTION_NAME)),
+ Seq = lists:seq(1, 200),
+ S = [one_clause(I) || I <- Seq],
+ Code = ?Q(["-module('@Mod@').\n"
+ "-export([f/1]).\n"
+ "f(Bin) ->\n"
+ "case Bin of\n"
+ " dummy -> _@_@S\n"
+ "end.\n"]),
+ %% merl:print(Code),
+ Opts = test_lib:opt_opts(?MODULE),
+ {ok,_} = merl:compile_and_load(Code, Opts),
+ _ = [begin
+ H = erlang:phash2(I),
+ Sz = 16,
+ <<Res0:Sz>> = <<H:Sz>>,
+ Res = I + Res0,
+ Res = Mod:f({I,<<Sz:8,H:Sz>>})
+ end || I <- Seq],
+ ok.
+
+one_clause(I) ->
+ ?Q(<<"{_@I@,<<L:8,Val:L>>} -> _@I@ + Val">>).
diff --git a/lib/compiler/test/bs_size_expr_SUITE.erl b/lib/compiler/test/bs_size_expr_SUITE.erl
new file mode 100644
index 0000000000..a9e562313b
--- /dev/null
+++ b/lib/compiler/test/bs_size_expr_SUITE.erl
@@ -0,0 +1,286 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_size_expr_SUITE).
+-compile(nowarn_shadow_vars).
+
+-export([all/0,suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ basic/1,size_shadow/1,complex/1,
+ recv/1,no_match/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+all() ->
+ [{group,p}].
+
+groups() ->
+ [{p,test_lib:parallel(),
+ [basic,
+ size_shadow,
+ complex,
+ recv,
+ no_match]}].
+
+init_per_suite(Config) ->
+ test_lib:recompile(?MODULE),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+ Config.
+
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+ ok.
+
+basic(_Config) ->
+ <<>> = do_basic(<<1:32>>),
+ <<"abcd">> = do_basic(<<2:32,"abcd">>),
+ no_match = do_basic(<<0:32>>),
+ no_match = do_basic(<<777:32>>),
+ ok.
+
+do_basic(Bin) ->
+ Res = do_basic_1(Bin),
+
+ Res = do_basic_2({tag,Bin}),
+ Res = do_basic_2([list,Bin]),
+ 6 = do_basic_2({2,4}),
+
+ Res = do_basic_3(Bin),
+ Res = do_basic_4(Bin),
+
+ {result,Res} = do_basic_5(Bin),
+ case Res of
+ no_match ->
+ ok;
+ _ ->
+ {result,{Res,7777777}} = do_basic_5(<<Bin/binary,7777777:32>>)
+ end,
+
+ Res.
+
+do_basic_1(<<Sz:32,Tail:(4*Sz-4)/binary>>) ->
+ Tail;
+do_basic_1(<<_/binary>>) ->
+ no_match.
+
+do_basic_2({tag,<<Sz:32,Tail:(4*Sz-4)/binary>>}) ->
+ Tail;
+do_basic_2([list,<<Sz:32,Tail:((Sz-1)*4)/binary>>]) ->
+ Tail;
+do_basic_2({A,B}) when is_integer(A), is_integer(B) ->
+ A + B;
+do_basic_2(_) ->
+ no_match.
+
+do_basic_3(Bin) ->
+ WordSize = id(4),
+ case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end.
+
+do_basic_4(Bin) ->
+ WordSize = id(4),
+ F = fun() ->
+ case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end
+ end,
+ F().
+
+do_basic_5(Bin) ->
+ WordSize = id(4),
+ F = fun() ->
+ Res = case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary,More:(8*WordSize)>> ->
+ {Tail,More};
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end,
+ {result,Res}
+ end,
+ F().
+
+size_shadow(_Config) ->
+ 12345678 = size_shadow_1(),
+ ok.
+
+size_shadow_1() ->
+ L = 8,
+ Offset = 16,
+ Fs = [fun(<<L:L,B:(L+16)>>) -> B end,
+ fun(<<L:L,B:(L+Offset)>>) -> B end,
+ fun(A) ->
+ Res = (fun([<<L:L,B:(L+16)>>]) -> B end)([A]),
+ Res = (fun([<<L:L,B:(L+Offset)>>]) -> B end)([A])
+ end,
+ fun(A) ->
+ Res = (fun({<<L:L,B:(L+16)>>,<<L:L,B:(L+16)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+Offset)>>,<<L:L,B:(L+16)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+16)>>,<<L:L,B:(L+Offset)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+Offset)>>,<<L:L,B:(L+Offset)>>}) -> B end)({A,A})
+ end,
+ fun(A) ->
+ <<Size:L,_/bits>> = A,
+ Inner = fun([L], {#{key1 := <<L:L,B:(L+Offset)>>,
+ key2 := <<L:L,B:(L+Offset)>>}, L}) -> B end,
+ Inner([Size], {#{key1 => A,key2 => A},Size})
+ end],
+ size_shadow_apply(Fs, <<16:8, 12345678:32>>).
+
+size_shadow_apply([F|Fs], Arg) when is_function(F, 1) ->
+ size_shadow_apply(Fs, Arg, F(Arg)).
+
+size_shadow_apply([F|Fs], Arg, Res) when is_function(F, 1) ->
+ Res = F(Arg),
+ size_shadow_apply(Fs, Arg, Res);
+size_shadow_apply([], _, Res) ->
+ Res.
+
+-record(r, {a,b,c}).
+complex(Config) ->
+ (fun() ->
+ Len = length(id(Config)),
+ Bin = << <<I:13>> || I <- lists:seq(1, Len) >>,
+ <<Bin:(length(Config))/binary-unit:13>> = Bin
+ end)(),
+
+ (fun() ->
+ V = id([a,b,c]),
+ F = fun(<<V:(bit_size(<<0:(length(V))>>)*8)/signed-integer>>) ->
+ V;
+ ({A,B}) ->
+ A + B
+ end,
+ -1 = F(<<-1:(length(V)*8)>>),
+ 7 = F({3,4})
+ end)(),
+
+ (fun() ->
+ A = a,
+ B = b,
+ F = fun(<<A:16,B:16,C:(A+B),D/bits>>) ->
+ {A,B,C,D};
+ (<<A:16,B:16>>) ->
+ {A,B};
+ (<<A:8,B:8>>) ->
+ {A,B}
+ end,
+ {13,21,16#cafebeef,<<"more">>} = F(<<13:16,21:16,16#cafebeef:34,"more">>),
+ {100,500} = F(<<100:16,500:16>>),
+ {157,77} = F(<<157:8,77:8>>),
+ {A,B}
+ end)(),
+
+ (fun() ->
+ Two = id(2),
+ F = fun(a, <<_:(#r.a - Two)/binary,Int:8,_/binary>>) -> Int;
+ (b, <<_:(#r.b - Two)/binary,Int:8,_/binary>>) -> Int;
+ (c, <<_:(#r.c - Two)/binary,Int:8,_/binary>>) -> Int
+ end,
+ 1 = F(a, <<1,2,3>>),
+ 2 = F(b, <<1,2,3>>),
+ 3 = F(c, <<1,2,3>>)
+ end)(),
+
+ (fun() ->
+ Bin = <<1,2,3,4>>,
+ F = fun(R) ->
+ <<First:(R#r.a)/binary,Tail/binary>> = Bin,
+ {First,Tail}
+ end,
+ {<<>>,<<1,2,3,4>>} = F(#r{a=0}),
+ {<<1>>,<<2,3,4>>} = F(#r{a=1}),
+ {<<1,2>>,<<3,4>>} = F(#r{a=2}),
+ {<<1,2,3>>,<<4>>} = F(#r{a=3}),
+ {<<1,2,3,4>>,<<>>} = F(#r{a=4})
+ end)(),
+
+ ok.
+
+recv(_Config) ->
+ R = fun(Msg) ->
+ self() ! Msg,
+ Res = receive
+ <<L,I:(L-1)/unit:8,X:32>> -> {I,X};
+ <<L,I:(L-1)/unit:8,X:64>> -> {I,X}
+ end,
+ self() ! {tag,[Msg]},
+ Res = receive
+ {tag,[<<L,I:(8*(L-1)),X:32>>]} -> {I,X};
+ {tag,[<<L,I:(8*(L-1)),X:64>>]} -> {I,X}
+ end
+ end,
+ {1234,16#deadbeef} = R(<<3,1234:16,16#deadbeef:32>>),
+ {99,16#cafebeeff00d} = R(<<2,99:8,16#cafebeeff00d:64>>),
+ ok.
+
+no_match(_Config) ->
+ B = id(<<1,2,3,4>>),
+ no_match = case B of
+ <<Int:(bit_size(B)-1)>> -> Int;
+ <<Int:(bit_size(B)*2)>> -> Int;
+ <<Int:(length(B))>> -> Int;
+ _ -> no_match
+ end,
+ no_match = case B of
+ <<L:8,Int2:(is_integer(L))>> -> Int2;
+ <<L:8,Int2:(L+3.0)>> -> Int2;
+ _ -> no_match
+ end,
+
+ no_match = case B of
+ <<Int3:(1/0)>> -> Int3;
+ _ -> no_match
+ end,
+
+ no_match = case B of
+ <<Int4:all>> -> Int4;
+ <<Int4:bad_size>> -> Int4;
+ _ -> no_match
+ end,
+
+ [] = [X || <<X:(is_list(B))/binary>> <= B],
+ <<>> = << <<X:32>> || <<X:(is_list(B))/binary>> <= B >>,
+
+ ok.
+
+id(I) ->
+ I.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 53627b9d81..c634c5841a 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -374,11 +374,12 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
{dcbsm, ".core_bsm"},
{dkern, ".kernel"},
{dssa, ".ssa"},
+ {dbool, ".bool"},
+ {dssashare, ".ssashare"},
{dssaopt, ".ssaopt"},
{dprecg, ".precodegen"},
{dcg, ".codegen"},
{dblk, ".block"},
- {dexcept, ".except"},
{djmp, ".jump"},
{dclean, ".clean"},
{dpeep, ".peep"},
@@ -1383,36 +1384,47 @@ env_compiler_options(_Config) ->
bc_options(Config) ->
DataDir = proplists:get_value(data_dir, Config),
- L = [{101, small_float, [no_get_hd_tl,no_line_info]},
- {103, big, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ L = [{101, small_float, [no_shared_fun_wrappers,
+ no_get_hd_tl,no_line_info]},
+ {103, big, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
no_line_info,no_stack_trimming]},
- {125, small_float, [no_get_hd_tl,no_line_info,no_ssa_opt_float]},
+ {125, small_float, [no_shared_fun_wrappers,no_get_hd_tl,
+ no_line_info,
+ no_ssa_opt_float]},
- {132, small, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ {132, small, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
no_ssa_opt_float,no_line_info,no_bsm3]},
+ {136, big, [no_shared_fun_wrappers,no_put_tuple2,no_get_hd_tl,
+ no_ssa_opt_record,no_line_info]},
+
{153, small, [r20]},
{153, small, [r21]},
- {136, big, [no_put_tuple2,no_get_hd_tl,
- no_ssa_opt_record,no_line_info]},
-
- {153, big, [no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]},
- {153, big, [r16]},
- {153, big, [r17]},
+ {153, big, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]},
{153, big, [r18]},
{153, big, [r19]},
- {153, small_float, [r16]},
- {153, small_float, []},
+ {153, small_float, [no_shared_fun_wrappers]},
- {158, small_maps, [r17]},
{158, small_maps, [r18]},
{158, small_maps, [r19]},
{158, small_maps, [r20]},
{158, small_maps, [r21]},
- {164, small_maps, []},
- {164, big, []}
+ {164, small_maps, [r22]},
+ {164, big, [r22]},
+ {164, small_maps, [no_shared_fun_wrappers]},
+ {164, big, [no_shared_fun_wrappers]},
+
+ {168, small, [r22]},
+ {170, small, [no_shared_fun_wrappers]},
+
+ {169, small_maps, []},
+ {169, big, []},
+ {170, small, []}
],
Test = fun({Expected,Mod,Options}) ->
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 72016c6d76..e20744f9cb 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -30,7 +30,8 @@
cover_v3_kernel_1/1,cover_v3_kernel_2/1,cover_v3_kernel_3/1,
cover_v3_kernel_4/1,cover_v3_kernel_5/1,
non_variable_apply/1,name_capture/1,fun_letrec_effect/1,
- get_map_element/1]).
+ get_map_element/1,receive_tests/1,
+ core_lint/1]).
-include_lib("common_test/include/ct.hrl").
@@ -59,7 +60,8 @@ groups() ->
cover_v3_kernel_1,cover_v3_kernel_2,cover_v3_kernel_3,
cover_v3_kernel_4,cover_v3_kernel_5,
non_variable_apply,name_capture,fun_letrec_effect,
- get_map_element
+ get_map_element,receive_tests,
+ core_lint
]}].
@@ -98,6 +100,7 @@ end_per_group(_GroupName, Config) ->
?comp(name_capture).
?comp(fun_letrec_effect).
?comp(get_map_element).
+?comp(receive_tests).
try_it(Mod, Conf) ->
Src = filename:join(proplists:get_value(data_dir, Conf),
@@ -112,3 +115,58 @@ compile_and_load(Src, Opts) ->
_ = code:delete(Mod),
_ = code:purge(Mod),
ok.
+
+core_lint(_Config) ->
+ OK = cerl:c_atom(ok),
+ core_lint_function(illegal),
+ core_lint_function(cerl:c_let([OK], OK, OK)),
+ core_lint_function(cerl:c_let([cerl:c_var(var)], cerl:c_var(999), OK)),
+ core_lint_function(cerl:c_let([cerl:c_var(var)], cerl:c_var(unknown), OK)),
+ core_lint_function(cerl:c_try(OK, [], OK, [], handler)),
+ core_lint_function(cerl:c_apply(cerl:c_var({OK,0}), [OK])),
+
+ core_lint_function([], [OK], OK),
+ core_lint_function([cerl:c_var({cerl:c_char($*),OK})], [], OK),
+
+ core_lint_pattern([cerl:c_var(99),cerl:c_var(99)]),
+ core_lint_pattern([cerl:c_let([cerl:c_var(var)], OK, OK)]),
+ core_lint_bs_pattern([OK]),
+ Flags = cerl:make_list([big,unsigned]),
+ core_lint_bs_pattern([cerl:c_bitstr(cerl:c_var(tail), cerl:c_atom(binary), Flags),
+ cerl:c_bitstr(cerl:c_var(value), cerl:c_atom(binary), Flags)]),
+
+ BadGuard1 = cerl:c_call(OK, OK, []),
+ BadGuard2 = cerl:c_call(cerl:c_atom(erlang), OK, []),
+ BadGuard3 = cerl:c_call(cerl:c_atom(erlang), cerl:c_atom(is_record), [OK,OK,OK]),
+ PatMismatch = cerl:c_case(cerl:c_nil(),
+ [cerl:c_clause([], OK),
+ cerl:c_clause([OK], OK),
+ cerl:c_clause([OK], BadGuard1, OK),
+ cerl:c_clause([OK], BadGuard2, OK),
+ cerl:c_clause([OK], BadGuard3, OK)]),
+ core_lint_function(PatMismatch),
+
+ ok.
+
+core_lint_bs_pattern(Ps) ->
+ core_lint_pattern([cerl:c_binary(Ps)]).
+
+core_lint_pattern(Ps) ->
+ Cs = [cerl:c_clause(Ps, cerl:c_float(42))],
+ core_lint_function(cerl:c_case(cerl:c_nil(), Cs)).
+
+core_lint_function(Body) ->
+ core_lint_function([], [], Body).
+
+core_lint_function(Exports, Attributes, Body) ->
+ ModName = cerl:c_atom(core_lint_test),
+ MainFun = cerl:c_fun([], Body),
+ MainVar = cerl:c_var({main,0}),
+ Mod = cerl:c_module(ModName, Exports, Attributes, [{MainVar,MainFun}]),
+ {error,[{core_lint_test,Errors}],[]} =
+ compile:forms(Mod, [from_core,clint0,return]),
+ io:format("~p\n", [Errors]),
+ [] = lists:filter(fun({none,core_lint,_}) -> false;
+ (_) -> true
+ end, Errors),
+ error = compile:forms(Mod, [from_core,clint0,report]).
diff --git a/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core
index 0ade037e05..2e59f9efde 100644
--- a/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core
+++ b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core
@@ -52,8 +52,14 @@ module 'bs_shadowed_size_var'
case T of
%% Variable 'Sz' repeated here. Should work.
<#{#<Sz>(32,1,'integer',['unsigned','big']),
- #<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' ->
- Data
+ #<Tail>('all',1,'binary',['unsigned','big'])}#> when 'true' ->
+ case Tail of
+ <#{#<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' ->
+ Data
+ <_cor5> when 'true' ->
+ primop 'match_fail'
+ ({'case_clause',{_cor5}})
+ end
<_cor5> when 'true' ->
primop 'match_fail'
({'case_clause',{_cor5}})
diff --git a/lib/compiler/test/core_SUITE_data/receive_tests.core b/lib/compiler/test/core_SUITE_data/receive_tests.core
new file mode 100644
index 0000000000..8e56af8cd4
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/receive_tests.core
@@ -0,0 +1,1761 @@
+%% Derived from receive_SUITE, with the ref_opt/1 test case removed.
+%% The purpose if this module is to make sure that the traditional
+%% syntax for receive in Core Erlang continues to work and is properly
+%% lowered.
+
+module 'receive_tests' ['module_info'/0,
+ 'module_info'/1,
+ 'receive_tests'/0]
+ attributes []
+'receive_tests'/0 =
+ %% Line 27
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ do %% Line 28
+ apply 'recv'/0
+ ()
+ do %% Line 28
+ apply 'coverage'/0
+ ()
+ do %% Line 28
+ apply 'otp_7980'/0
+ ()
+ do %% Line 28
+ apply 'export'/0
+ ()
+ do %% Line 28
+ apply 'wait'/0
+ ()
+ do %% Line 29
+ apply 'recv_in_try'/0
+ ()
+ do %% Line 29
+ apply 'double_recv'/0
+ ()
+ do %% Line 29
+ apply 'receive_var_zero'/0
+ ()
+ do %% Line 30
+ apply 'match_built_terms'/0
+ ()
+ do %% Line 30
+ apply 'elusive_common_exit'/0
+ ()
+ do %% Line 31
+ apply 'after_expression'/0
+ ()
+ %% Line 32
+ 'ok'
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'recv'/0 =
+ %% Line 36
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ fun () ->
+ %% Line 37
+ case <> of
+ <> when 'true' ->
+ apply 'loop'/1
+ ({'state','true'})
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+ in let <Pid> =
+ call %% Line 37
+ 'erlang':%% Line 37
+ 'spawn_link'
+ (_0)
+ in let <Self> =
+ call %% Line 38
+ 'erlang':%% Line 38
+ 'self'
+ ()
+ in do %% Line 39
+ call 'erlang':'!'
+ (Pid, {Self,'test'})
+ do %% Line 40
+ receive
+ %% Line 41
+ <{'ok','test'}> when 'true' ->
+ 'ok'
+ %% Line 42
+ <{'error',Other}> when 'true' ->
+ do %% Line 43
+ call 'io':'format'
+ ([71|[111|[116|[32|[117|[110|[112|[101|[120|[101|[99|[116|[101|[100|[32|[126|[112]]]]]]]]]]]]]]]]], [Other|[]])
+ %% Line 44
+ call 'ct':'fail'
+ ('unexpected')
+ after %% Line 45
+ 10000 ->
+ %% Line 46
+ call 'ct':'fail'
+ ('no_answer')
+ do %% Line 48
+ receive
+ %% Line 49
+ <X> when 'true' ->
+ do %% Line 50
+ call 'io':'format'
+ ([85|[110|[101|[120|[112|[101|[99|[116|[101|[100|[32|[101|[120|[116|[114|[97|[32|[109|[101|[115|[115|[97|[103|[101|[58|[32|[126|[112]]]]]]]]]]]]]]]]]]]]]]]]]]]], [X|[]])
+ %% Line 51
+ call 'ct':'fail'
+ ('unexpected')
+ after %% Line 52
+ 10 ->
+ do %% Line 53
+ call 'erlang':'unlink'
+ (Pid)
+ do %% Line 54
+ call 'erlang':'exit'
+ (Pid, 'kill')
+ %% Line 55
+ 'ok'
+ %% Line 57
+ 'ok'
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'loop'/1 =
+ %% Line 59
+ fun (_0) ->
+ case _0 of
+ <S> when 'true' ->
+ %% Line 60
+ receive
+ %% Line 61
+ <_8>
+ when ( try
+ ( let <_3> =
+ case ( call ( 'erlang'
+ -| ['compiler_generated'] ):( 'is_record'
+ -| ['compiler_generated'] )
+ (S, ( 'state'
+ -| ['compiler_generated'] ), ( 2
+ -| ['compiler_generated'] ))
+ -| ['compiler_generated'] ) of
+ ( <( 'true'
+ -| ['compiler_generated'] )> when 'true' ->
+ ( 'true'
+ -| ['compiler_generated'] )
+ -| ['compiler_generated'] )
+ ( <( 'false'
+ -| ['compiler_generated'] )> when 'true' ->
+ ( 'fail'
+ -| ['compiler_generated'] )
+ -| ['compiler_generated'] )
+ ( <( _1
+ -| ['compiler_generated'] )> when 'true' ->
+ ( _1
+ -| ['compiler_generated'] )
+ -| ['compiler_generated'] )
+ ( <_2> when 'true' ->
+ ( primop 'match_fail'
+ ({'case_clause',_2})
+ -| ['compiler_generated'] )
+ -| ['compiler_generated'] )
+ end
+ in let <_4> =
+ call 'erlang':'=:='
+ (( _3
+ -| ['compiler_generated'] ), 'true')
+ in let <_5> =
+ call 'erlang':'element'
+ (2, S)
+ in let <_6> =
+ call 'erlang':'=='
+ (_5, 'false')
+ in ( call ( 'erlang'
+ -| ['compiler_generated'] ):( 'and'
+ -| ['compiler_generated'] )
+ (_4, _6)
+ -| ['compiler_generated'] )
+ -| ['compiler_generated'] )
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false'
+ -| ['compiler_generated'] ) ->
+ %% Line 62
+ apply 'loop'/1
+ (S)
+ %% Line 63
+ <{P,'test'}> when 'true' ->
+ do %% Line 64
+ call 'erlang':'!'
+ (P, {'ok','test'})
+ %% Line 65
+ apply 'loop'/1
+ (S)
+ %% Line 66
+ <_X_X> when 'true' ->
+ %% Line 67
+ apply 'loop'/1
+ (S)
+ after 'infinity' ->
+ 'true'
+ ( <_7> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_7})
+ -| ['compiler_generated'] )
+ end
+'coverage'/0 =
+ %% Line 70
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 71
+ 'erlang':%% Line 71
+ 'self'
+ ()
+ in do %% Line 71
+ apply 'do_link'/1
+ (_0)
+ let <_1> =
+ call %% Line 72
+ 'erlang':%% Line 72
+ 'self'
+ ()
+ in do %% Line 72
+ apply 'do_unlink'/1
+ (_1)
+ let <_2> =
+ call %% Line 73
+ 'erlang':%% Line 73
+ 'node'
+ ()
+ in do %% Line 73
+ apply 'do_monitor_node'/2
+ (_2, 'true')
+ let <_3> =
+ call %% Line 74
+ 'erlang':%% Line 74
+ 'node'
+ ()
+ in do %% Line 74
+ apply 'do_monitor_node'/2
+ (_3, 'false')
+ let <_5> =
+ call %% Line 75
+ 'erlang':%% Line 75
+ 'group_leader'
+ ()
+ in let <_4> =
+ call %% Line 75
+ 'erlang':%% Line 75
+ 'self'
+ ()
+ in do %% Line 75
+ apply 'do_group_leader'/2
+ (_5, _4)
+ let <_6> =
+ call %% Line 76
+ 'erlang':%% Line 76
+ 'self'
+ ()
+ in let <_7> =
+ call %% Line 76
+ 'erlang':%% Line 76
+ 'node'
+ (_6)
+ in do %% Line 76
+ apply 'id'/1
+ (_7)
+ let <_8> =
+ call %% Line 78
+ 'erlang':%% Line 78
+ 'self'
+ ()
+ in do %% Line 78
+ call 'erlang':'!'
+ (_8, {'a',10})
+ let <_9> =
+ call %% Line 79
+ 'erlang':%% Line 79
+ 'self'
+ ()
+ in do %% Line 79
+ call 'erlang':'!'
+ (_9, {'b',20})
+ %% Line 80
+ case apply 'receive_all'/0
+ () of
+ <[{'a',10}|[{'b',20}]]> when 'true' ->
+ let <_11> =
+ call %% Line 81
+ 'erlang':%% Line 81
+ 'self'
+ ()
+ in do %% Line 81
+ call 'erlang':'!'
+ (_11, {'c',42})
+ do %% Line 82
+ receive
+ %% Line 83
+ <{'c',42}> when 'true' ->
+ %% Line 84
+ 'ok'
+ after %% Line 85
+ 'infinity' ->
+ %% Line 86
+ call 'erlang':'exit'
+ ('cant_happen')
+ let <_12> =
+ call %% Line 89
+ 'erlang':%% Line 89
+ 'self'
+ ()
+ in do %% Line 89
+ call 'erlang':'!'
+ (_12, 17)
+ let <_13> =
+ call %% Line 90
+ 'erlang':%% Line 90
+ 'self'
+ ()
+ in do %% Line 90
+ call 'erlang':'!'
+ (_13, 19)
+ %% Line 91
+ case apply 'tuple_to_values'/2
+ ('infinity', 'x') of
+ <59> when 'true' ->
+ %% Line 92
+ case apply 'tuple_to_values'/2
+ (999999, 'x') of
+ <61> when 'true' ->
+ %% Line 93
+ case apply 'tuple_to_values'/2
+ (1, 'x') of
+ <0> when 'true' ->
+ let <_18> =
+ catch
+ let <_17> =
+ call %% Line 95
+ 'erlang':%% Line 95
+ 'self'
+ ()
+ in %% Line 95
+ apply 'monitor_plus_badmap'/1
+ (_17)
+ in %% Line 95
+ case _18 of
+ <{'EXIT',{{'badmap',[]},_23}}> when 'true' ->
+ let <_20> =
+ call %% Line 98
+ 'erlang':%% Line 98
+ 'self'
+ ()
+ in do %% Line 98
+ call 'erlang':'!'
+ (_20, {'data','no_data'})
+ %% Line 99
+ case apply 'receive_sink_tuple'/1
+ ({'any','pattern'}) of
+ <'ok'> when 'true' ->
+ %% Line 100
+ case apply 'receive_sink_tuple'/1
+ ({'a','b'}) of
+ <{'b','a'}> when 'true' ->
+ %% Line 102
+ 'ok'
+ ( <_22> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_22})
+ -| ['compiler_generated'] )
+ end
+ ( <_21> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_21})
+ -| ['compiler_generated'] )
+ end
+ ( <_19> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_19})
+ -| ['compiler_generated'] )
+ end
+ ( <_16> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_16})
+ -| ['compiler_generated'] )
+ end
+ ( <_15> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_15})
+ -| ['compiler_generated'] )
+ end
+ ( <_14> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_14})
+ -| ['compiler_generated'] )
+ end
+ ( <_10> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_10})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'monitor_plus_badmap'/1 =
+ %% Line 104
+ fun (_0) ->
+ case _0 of
+ <Pid> when 'true' ->
+ let <_2> =
+ call %% Line 105
+ 'erlang':%% Line 105
+ 'monitor'
+ (%% Line 105
+ 'process', %% Line 105
+ Pid)
+ in let <_1> =
+ primop 'match_fail'
+ ({'badmap',[]})
+ in %% Line 105
+ call 'erlang':'+'
+ (_2, _1)
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_3})
+ -| ['compiler_generated'] )
+ end
+'receive_all'/0 =
+ %% Line 107
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ %% Line 108
+ receive
+ %% Line 109
+ <Any> when 'true' ->
+ let <_0> =
+ apply %% Line 110
+ 'receive_all'/0
+ ()
+ in %% Line 110
+ [Any|_0]
+ after %% Line 111
+ 0 ->
+ %% Line 112
+ []
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'do_monitor_node'/2 =
+ %% Line 115
+ fun (_0,_1) ->
+ case <_0,_1> of
+ <Node,Bool> when 'true' ->
+ %% Line 116
+ call 'erlang':'monitor_node'
+ (Node, Bool)
+ ( <_3,_2> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_3,_2})
+ -| ['compiler_generated'] )
+ end
+'do_link'/1 =
+ %% Line 118
+ fun (_0) ->
+ case _0 of
+ <Pid> when 'true' ->
+ %% Line 119
+ call 'erlang':'link'
+ (Pid)
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'do_unlink'/1 =
+ %% Line 121
+ fun (_0) ->
+ case _0 of
+ <Pid> when 'true' ->
+ %% Line 122
+ call 'erlang':'unlink'
+ (Pid)
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'do_group_leader'/2 =
+ %% Line 124
+ fun (_0,_1) ->
+ case <_0,_1> of
+ <Leader,Pid> when 'true' ->
+ %% Line 125
+ call 'erlang':'group_leader'
+ (Leader, Pid)
+ ( <_3,_2> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_3,_2})
+ -| ['compiler_generated'] )
+ end
+'tuple_to_values'/2 =
+ %% Line 129
+ fun (_0,_1) ->
+ case <_0,_1> of
+ <'infinity',X> when 'true' ->
+ let <_3> =
+ case %% Line 130
+ X of
+ %% Line 131
+ <'x'> when 'true' ->
+ %% Line 132
+ receive
+ %% Line 133
+ <Any> when 'true' ->
+ %% Line 134
+ {42,Any}
+ after 'infinity' ->
+ 'true'
+ ( <_2> when 'true' ->
+ %% Line 130
+ primop 'match_fail'
+ ({'case_clause',_2})
+ -| ['compiler_generated'] )
+ end
+ in %% Line 130
+ case _3 of
+ <{A,B}> when 'true' ->
+ %% Line 137
+ call 'erlang':'+'
+ (A, B)
+ ( <_4> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_4})
+ -| ['compiler_generated'] )
+ end
+ %% Line 138
+ <Timeout,X> when 'true' ->
+ let <_6> =
+ case %% Line 139
+ X of
+ %% Line 140
+ <'x'> when 'true' ->
+ %% Line 141
+ receive
+ %% Line 142
+ <Any> when 'true' ->
+ %% Line 143
+ {42,Any}
+ after %% Line 144
+ Timeout ->
+ %% Line 145
+ {0,0}
+ ( <_5> when 'true' ->
+ %% Line 139
+ primop 'match_fail'
+ ({'case_clause',_5})
+ -| ['compiler_generated'] )
+ end
+ in %% Line 139
+ case _6 of
+ <{A,B}> when 'true' ->
+ %% Line 148
+ call 'erlang':'+'
+ (A, B)
+ ( <_7> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_7})
+ -| ['compiler_generated'] )
+ end
+ ( <_9,_8> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_9,_8})
+ -| ['compiler_generated'] )
+ end
+'receive_sink_tuple'/1 =
+ %% Line 151
+ fun (_0) ->
+ case _0 of
+ <{Line,Pattern}> when 'true' ->
+ %% Line 152
+ receive
+ %% Line 153
+ <{'data',_2}> when 'true' ->
+ %% Line 154
+ 'ok'
+ after %% Line 155
+ 1 ->
+ %% Line 156
+ apply 'id'/1
+ ({Pattern,Line})
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'otp_7980'/0 =
+ %% Line 163
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ %% Line 164
+ case apply 'otp_7980_add_clients'/1
+ (10) of
+ <7> when 'true' ->
+ %% Line 165
+ 'ok'
+ ( <_0> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_0})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'otp_7980_add_clients'/1 =
+ %% Line 167
+ fun (_0) ->
+ case _0 of
+ <Count> when 'true' ->
+ let <Timeout> = 42
+ in let <_7> =
+ fun (_4,_3) ->
+ %% Line 169
+ case <_4,_3> of
+ <_9,N> when 'true' ->
+ do %% Line 170
+ case N of
+ %% Line 171
+ <1> when 'true' ->
+ 'ok'
+ %% Line 172
+ <_10> when 'true' ->
+ receive
+
+ after Timeout ->
+ 'ok'
+ ( <_2> when 'true' ->
+ primop 'match_fail'
+ ({'case_clause',_2})
+ -| ['compiler_generated'] )
+ end
+ %% Line 174
+ call 'erlang':'-'
+ (N, 1)
+ ( <_6,_5> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_6,_5})
+ -| ['compiler_generated'] )
+ end
+ in %% Line 169
+ call 'lists':'foldl'
+ (_7, %% Line 175
+ Count, %% Line 175
+ [1|[2|[3]]])
+ ( <_8> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_8})
+ -| ['compiler_generated'] )
+ end
+'export'/0 =
+ %% Line 177
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <Ref> =
+ call %% Line 178
+ 'erlang':%% Line 178
+ 'make_ref'
+ ()
+ in let <_1> =
+ call %% Line 179
+ 'erlang':%% Line 179
+ 'self'
+ ()
+ in do %% Line 179
+ call 'erlang':'!'
+ (_1, {'result',Ref,42})
+ %% Line 180
+ case apply 'export_1'/1
+ (Ref) of
+ <42> when 'true' ->
+ %% Line 181
+ case apply 'export_1'/1
+ (Ref) of
+ <{'error','timeout'}> when 'true' ->
+ let <_4> =
+ call %% Line 183
+ 'erlang':%% Line 183
+ 'self'
+ ()
+ in do %% Line 183
+ call 'erlang':'!'
+ (_4, {'result',Ref})
+ %% Line 184
+ case apply 'export_2'/0
+ () of
+ <{'ok',_6}>
+ when call 'erlang':'=:='
+ (_6,
+ Ref) ->
+ %% Line 186
+ 'ok'
+ ( <_5> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_5})
+ -| ['compiler_generated'] )
+ end
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_3})
+ -| ['compiler_generated'] )
+ end
+ ( <_2> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_2})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'export_1'/1 =
+ %% Line 188
+ fun (_0) ->
+ case _0 of
+ <Reference> when 'true' ->
+ do %% Line 189
+ apply 'id'/1
+ (Reference)
+ let <_5,Result> =
+ receive
+ %% Line 191
+ <{'result',_4,Result}>
+ when call 'erlang':'=:='
+ (_4,
+ Reference) ->
+ %% Line 192
+ <Result,Result>
+ after %% Line 193
+ 1 ->
+ let <Result> =
+ {'error','timeout'}
+ in %% Line 194
+ <Result,Result>
+ in let <_2> =
+ call %% Line 199
+ 'erlang':%% Line 199
+ 'self'
+ ()
+ in do %% Line 199
+ apply 'id'/1
+ ({'build',_2})
+ %% Line 200
+ Result
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_3})
+ -| ['compiler_generated'] )
+ end
+'export_2'/0 =
+ %% Line 202
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0,Result> =
+ receive
+ %% Line 203
+ <{'result',Result}> when 'true' ->
+ <'ok',Result>
+ after 'infinity' ->
+ <'true','true'>
+ in %% Line 204
+ {'ok',Result}
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'wait'/0 =
+ %% Line 206
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 207
+ 'erlang':%% Line 207
+ 'self'
+ ()
+ in do %% Line 207
+ call 'erlang':'!'
+ (_0, #{#<42>(8,1,'integer',['unsigned'|['big']])}#)
+ %% Line 208
+ case apply 'wait_1'/3
+ ('r', 1, 2) of
+ <#{#<42>(8,1,'integer',['unsigned'|['big']])}#> when 'true' ->
+ %% Line 209
+ case apply 'wait_1'/3
+ (1, 2, 3) of
+ <{1,2,3}> when 'true' ->
+ let <_3> =
+ catch
+ %% Line 210
+ receive
+
+ after [] ->
+ 'timeout'
+ in %% Line 210
+ case _3 of
+ <{'EXIT',{'timeout_value',_5}}> when 'true' ->
+ %% Line 211
+ 'ok'
+ ( <_4> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_4})
+ -| ['compiler_generated'] )
+ end
+ ( <_2> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_2})
+ -| ['compiler_generated'] )
+ end
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_1})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'wait_1'/3 =
+ %% Line 213
+ fun (_0,_1,_2) ->
+ case <_0,_1,_2> of
+ <'r',_7,_8> when 'true' ->
+ %% Line 214
+ receive
+ %% Line 215
+ <B>
+ when try
+ let <_3> =
+ call 'erlang':'byte_size'
+ (B)
+ in call 'erlang':'>'
+ (_3, 0)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false' ->
+ %% Line 216
+ B
+ after 'infinity' ->
+ 'true'
+ %% Line 220
+ <A,B,C> when 'true' ->
+ %% Line 221
+ {A,B,C}
+ ( <_6,_5,_4> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_6,_5,_4})
+ -| ['compiler_generated'] )
+ end
+'recv_in_try'/0 =
+ %% Line 223
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 224
+ 'erlang':%% Line 224
+ 'self'
+ ()
+ in do %% Line 224
+ call 'erlang':'!'
+ (_0, {'ok','fh'})
+ %% Line 224
+ case apply 'recv_in_try'/2
+ ('infinity', 'native') of
+ <{'ok','fh'}> when 'true' ->
+ let <_2> =
+ call %% Line 225
+ 'erlang':%% Line 225
+ 'self'
+ ()
+ in do %% Line 225
+ call 'erlang':'!'
+ (_2, {'ok','ignored'})
+ %% Line 225
+ case apply 'recv_in_try'/2
+ ('infinity', 'plain') of
+ <{'ok',42}> when 'true' ->
+ let <_4> =
+ call %% Line 226
+ 'erlang':%% Line 226
+ 'self'
+ ()
+ in do %% Line 226
+ call 'erlang':'!'
+ (_4, {'error','ignored'})
+ %% Line 226
+ case apply 'recv_in_try'/2
+ ('infinity', 'plain') of
+ <'nok'> when 'true' ->
+ %% Line 227
+ case apply 'recv_in_try'/2
+ (1, 'plain') of
+ <'timeout'> when 'true' ->
+ %% Line 228
+ 'ok'
+ ( <_6> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_6})
+ -| ['compiler_generated'] )
+ end
+ ( <_5> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_5})
+ -| ['compiler_generated'] )
+ end
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_3})
+ -| ['compiler_generated'] )
+ end
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_1})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'recv_in_try'/2 =
+ %% Line 230
+ fun (_0,_1) ->
+ case <_0,_1> of
+ <Timeout,Format> when 'true' ->
+ %% Line 231
+ try
+ %% Line 232
+ receive
+ %% Line 233
+ <{Status,History}> when 'true' ->
+ let <_3> =
+ case %% Line 244
+ Format of
+ %% Line 245
+ <'native'> when 'true' ->
+ %% Line 246
+ apply 'id'/1
+ (History)
+ %% Line 247
+ <'plain'> when 'true' ->
+ %% Line 248
+ apply 'id'/1
+ (42)
+ ( <_2> when 'true' ->
+ %% Line 244
+ primop 'match_fail'
+ ({'case_clause',_2})
+ -| ['compiler_generated'] )
+ end
+ in let <FH> = _3
+ in %% Line 250
+ case Status of
+ %% Line 251
+ <'ok'> when 'true' ->
+ %% Line 252
+ {'ok',FH}
+ %% Line 253
+ <'error'> when 'true' ->
+ %% Line 254
+ 'nok'
+ ( <_5> when 'true' ->
+ primop 'match_fail'
+ ({'case_clause',_5})
+ -| ['compiler_generated'] )
+ end
+ after %% Line 256
+ Timeout ->
+ %% Line 257
+ 'timeout'
+ of <_6> ->
+ _6
+ catch <_9,_8,_7> ->
+ %% Line 262
+ case {_9,_8,_7} of
+ <{'throw',{'error',Reason},_12}> when 'true' ->
+ %% Line 263
+ {'nok',Reason}
+ ( <{_9,_8,_7}> when 'true' ->
+ primop 'raise'
+ (_7, _8)
+ -| ['compiler_generated'] )
+ end
+ ( <_11,_10> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_11,_10})
+ -| ['compiler_generated'] )
+ end
+'double_recv'/0 =
+ %% Line 270
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 271
+ 'erlang':%% Line 271
+ 'self'
+ ()
+ in do %% Line 271
+ call 'erlang':'!'
+ (_0, {'more',{'a','term'}})
+ %% Line 272
+ case apply 'do_double_recv'/2
+ ({'more',{'a','term'}}, 'any') of
+ <'ok'> when 'true' ->
+ let <_2> =
+ call %% Line 273
+ 'erlang':%% Line 273
+ 'self'
+ ()
+ in do %% Line 273
+ call 'erlang':'!'
+ (_2, 'message')
+ %% Line 274
+ case apply 'do_double_recv'/2
+ ('whatever', 'message') of
+ <'ok'> when 'true' ->
+ %% Line 276
+ case apply 'do_double_recv'/2
+ ({'more',42}, 'whatever') of
+ <'error'> when 'true' ->
+ %% Line 277
+ case apply 'do_double_recv'/2
+ ('whatever', 'whatever') of
+ <'error'> when 'true' ->
+ %% Line 278
+ 'ok'
+ ( <_5> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_5})
+ -| ['compiler_generated'] )
+ end
+ ( <_4> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_4})
+ -| ['compiler_generated'] )
+ end
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_3})
+ -| ['compiler_generated'] )
+ end
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_1})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'do_double_recv'/2 =
+ %% Line 280
+ fun (_0,_1) ->
+ case <_0,_1> of
+ <{'more',Rest},_X_Msg> when 'true' ->
+ %% Line 281
+ receive
+ %% Line 282
+ <{'more',_4}>
+ when call 'erlang':'=:='
+ (_4,
+ Rest) ->
+ %% Line 283
+ 'ok'
+ after %% Line 284
+ 0 ->
+ %% Line 285
+ 'error'
+ %% Line 287
+ <_5,Msg> when 'true' ->
+ %% Line 288
+ receive
+ %% Line 289
+ <_6>
+ when call 'erlang':'=:='
+ (_6,
+ Msg) ->
+ %% Line 290
+ 'ok'
+ after %% Line 291
+ 0 ->
+ %% Line 292
+ 'error'
+ ( <_3,_2> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_3,_2})
+ -| ['compiler_generated'] )
+ end
+'receive_var_zero'/0 =
+ %% Line 297
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 298
+ 'erlang':%% Line 298
+ 'self'
+ ()
+ in do %% Line 298
+ call 'erlang':'!'
+ (_0, 'x')
+ let <_1> =
+ call %% Line 299
+ 'erlang':%% Line 299
+ 'self'
+ ()
+ in do %% Line 299
+ call 'erlang':'!'
+ (_1, 'y')
+ let <Z> =
+ apply %% Line 300
+ 'zero'/0
+ ()
+ in let <_3> =
+ receive
+ %% Line 302
+ <'z'> when 'true' ->
+ 'ok'
+ after %% Line 303
+ Z ->
+ %% Line 303
+ 'timeout'
+ in %% Line 301
+ case _3 of
+ <'timeout'> when 'true' ->
+ let <_5> =
+ receive
+
+ after %% Line 306
+ Z ->
+ %% Line 306
+ 'timeout'
+ in %% Line 305
+ case _5 of
+ <'timeout'> when 'true' ->
+ let <_7> =
+ call %% Line 308
+ 'erlang':%% Line 308
+ 'self'
+ ()
+ in do %% Line 308
+ call 'erlang':'!'
+ (_7, 'w')
+ %% Line 309
+ receive
+ %% Line 310
+ <'x'> when 'true' ->
+ do %% Line 311
+ receive
+ <'y'> when 'true' ->
+ 'ok'
+ after 'infinity' ->
+ 'true'
+ do %% Line 312
+ receive
+ <'w'> when 'true' ->
+ 'ok'
+ after 'infinity' ->
+ 'true'
+ %% Line 313
+ 'ok'
+ %% Line 314
+ <Other> when 'true' ->
+ %% Line 315
+ call 'ct':'fail'
+ ({'bad_message',Other})
+ after 'infinity' ->
+ 'true'
+ ( <_6> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_6})
+ -| ['compiler_generated'] )
+ end
+ ( <_4> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_4})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'zero'/0 =
+ %% Line 318
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ 0
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'match_built_terms'/0 =
+ %% Line 339
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_5> =
+ fun () ->
+ %% Line 340
+ case <> of
+ <> when 'true' ->
+ let <Ref> =
+ call 'erlang':'make_ref'
+ ()
+ in let <A> =
+ apply 'id'/1
+ (97)
+ in let <B> =
+ apply 'id'/1
+ (98)
+ in let <Built> =
+ apply 'id'/1
+ ([A|[B|[]]])
+ in let <_4> =
+ call 'erlang':'self'
+ ()
+ in do call 'erlang':'!'
+ (_4, {Ref,A,B})
+ receive
+ <{_28,_29,_30}>
+ when let <_35> =
+ call 'erlang':'=:='
+ (_28, Ref)
+ in let <_33> =
+ call 'erlang':'=:='
+ (_29, A)
+ in let <_31> =
+ call 'erlang':'=:='
+ (_30, B)
+ in let <_32> =
+ call 'erlang':'=:='
+ ([A|[B|[]]], Built)
+ in let <_34> =
+ call 'erlang':'and'
+ (_31, _32)
+ in let <_36> =
+ call 'erlang':'and'
+ (_33, _34)
+ in call 'erlang':'and'
+ (_35, _36) ->
+ 'ok'
+ after 5000 ->
+ call 'ct':'fail'
+ ([70|[97|[105|[108|[101|[100|[32|[116|[111|[32|[109|[97|[116|[99|[104|[32|[109|[101|[115|[115|[97|[103|[101|[32|[119|[105|[116|[104|[32|[116|[101|[114|[109|[32|[98|[117|[105|[108|[116|[32|[105|[110|[32|[114|[101|[99|[101|[105|[118|[101|[32|[103|[117|[97|[114|[100|[46]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+ in do %% Line 340
+ apply _5
+ ()
+ let <_11> =
+ fun () ->
+ %% Line 341
+ case <> of
+ <> when 'true' ->
+ let <Ref> =
+ call 'erlang':'make_ref'
+ ()
+ in let <A> =
+ apply 'id'/1
+ (97)
+ in let <B> =
+ apply 'id'/1
+ (98)
+ in let <Built> =
+ apply 'id'/1
+ ({A,B})
+ in let <_10> =
+ call 'erlang':'self'
+ ()
+ in do call 'erlang':'!'
+ (_10, {Ref,A,B})
+ receive
+ <{_37,_38,_39}>
+ when let <_44> =
+ call 'erlang':'=:='
+ (_37, Ref)
+ in let <_42> =
+ call 'erlang':'=:='
+ (_38, A)
+ in let <_40> =
+ call 'erlang':'=:='
+ (_39, B)
+ in let <_41> =
+ call 'erlang':'=:='
+ ({A,B}, Built)
+ in let <_43> =
+ call 'erlang':'and'
+ (_40, _41)
+ in let <_45> =
+ call 'erlang':'and'
+ (_42, _43)
+ in call 'erlang':'and'
+ (_44, _45) ->
+ 'ok'
+ after 5000 ->
+ call 'ct':'fail'
+ ([70|[97|[105|[108|[101|[100|[32|[116|[111|[32|[109|[97|[116|[99|[104|[32|[109|[101|[115|[115|[97|[103|[101|[32|[119|[105|[116|[104|[32|[116|[101|[114|[109|[32|[98|[117|[105|[108|[116|[32|[105|[110|[32|[114|[101|[99|[101|[105|[118|[101|[32|[103|[117|[97|[114|[100|[46]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+ in do %% Line 341
+ apply _11
+ ()
+ let <_19> =
+ fun () ->
+ %% Line 342
+ case <> of
+ <> when 'true' ->
+ let <Ref> =
+ call 'erlang':'make_ref'
+ ()
+ in let <A> =
+ apply 'id'/1
+ (97)
+ in let <B> =
+ apply 'id'/1
+ (98)
+ in let <_15> =
+ #{#<A>(8,1,'integer',['unsigned'|['big']]),
+ #<B>(8,1,'integer',['unsigned'|['big']])}#
+ in let <Built> =
+ apply 'id'/1
+ (_15)
+ in let <_17> =
+ call 'erlang':'self'
+ ()
+ in do call 'erlang':'!'
+ (_17, {Ref,A,B})
+ receive
+ <{_46,_47,_48}>
+ when let <_53> =
+ call 'erlang':'=:='
+ (_46, Ref)
+ in let <_51> =
+ call 'erlang':'=:='
+ (_47, A)
+ in let <_49> =
+ call 'erlang':'=:='
+ (_48, B)
+ in let <_50> =
+ try
+ let <_18> =
+ #{#<A>(8,1,'integer',['unsigned'|['big']]),
+ #<B>(8,1,'integer',['unsigned'|['big']])}#
+ in call 'erlang':'=:='
+ (_18, Built)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false'
+ in let <_52> =
+ call 'erlang':'and'
+ (_49, _50)
+ in let <_54> =
+ call 'erlang':'and'
+ (_51, _52)
+ in call 'erlang':'and'
+ (_53, _54) ->
+ 'ok'
+ after 5000 ->
+ call 'ct':'fail'
+ ([70|[97|[105|[108|[101|[100|[32|[116|[111|[32|[109|[97|[116|[99|[104|[32|[109|[101|[115|[115|[97|[103|[101|[32|[119|[105|[116|[104|[32|[116|[101|[114|[109|[32|[98|[117|[105|[108|[116|[32|[105|[110|[32|[114|[101|[99|[101|[105|[118|[101|[32|[103|[117|[97|[114|[100|[46]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+ in do %% Line 342
+ apply _19
+ ()
+ let <_27> =
+ fun () ->
+ %% Line 343
+ case <> of
+ <> when 'true' ->
+ let <Ref> =
+ call 'erlang':'make_ref'
+ ()
+ in let <A> =
+ apply 'id'/1
+ (97)
+ in let <B> =
+ apply 'id'/1
+ (98)
+ in let <_23> =
+ ~{1=>A,2=>B}~
+ in let <Built> =
+ apply 'id'/1
+ (_23)
+ in let <_25> =
+ call 'erlang':'self'
+ ()
+ in do call 'erlang':'!'
+ (_25, {Ref,A,B})
+ receive
+ <{_55,_56,_57}>
+ when let <_62> =
+ call 'erlang':'=:='
+ (_55, Ref)
+ in let <_60> =
+ call 'erlang':'=:='
+ (_56, A)
+ in let <_58> =
+ call 'erlang':'=:='
+ (_57, B)
+ in let <_59> =
+ try
+ let <_26> =
+ ~{1=>A,2=>B}~
+ in call 'erlang':'=:='
+ (_26, Built)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false'
+ in let <_61> =
+ call 'erlang':'and'
+ (_58, _59)
+ in let <_63> =
+ call 'erlang':'and'
+ (_60, _61)
+ in call 'erlang':'and'
+ (_62, _63) ->
+ 'ok'
+ after 5000 ->
+ call 'ct':'fail'
+ ([70|[97|[105|[108|[101|[100|[32|[116|[111|[32|[109|[97|[116|[99|[104|[32|[109|[101|[115|[115|[97|[103|[101|[32|[119|[105|[116|[104|[32|[116|[101|[114|[109|[32|[98|[117|[105|[108|[116|[32|[105|[110|[32|[114|[101|[99|[101|[105|[118|[101|[32|[103|[117|[97|[114|[100|[46]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+ in %% Line 343
+ apply _27
+ ()
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'elusive_common_exit'/0 =
+ %% Line 345
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 346
+ 'erlang':%% Line 346
+ 'self'
+ ()
+ in do %% Line 346
+ call 'erlang':'!'
+ (_0, {1,'a'})
+ let <_1> =
+ call %% Line 347
+ 'erlang':%% Line 347
+ 'self'
+ ()
+ in do %% Line 347
+ call 'erlang':'!'
+ (_1, {2,'b'})
+ %% Line 348
+ case apply 'elusive_loop'/3
+ (['x'|['y'|['z']]], 2, []) of
+ <{['z'],[{2,'b'}|[{1,'a'}]]}> when 'true' ->
+ let <CodeServer> =
+ call %% Line 350
+ 'erlang':%% Line 350
+ 'whereis'
+ (%% Line 350
+ 'code_server')
+ in let <Self> =
+ call %% Line 351
+ 'erlang':%% Line 351
+ 'self'
+ ()
+ in do %% Line 352
+ call 'erlang':'!'
+ (Self, {Self,'abc'})
+ do %% Line 353
+ call 'erlang':'!'
+ (Self, {CodeServer,[]})
+ do %% Line 354
+ call 'erlang':'!'
+ (Self, {Self,'other'})
+ do %% Line 355
+ try
+ apply 'elusive2'/1
+ ([])
+ of <_5> ->
+ case _5 of
+ %% Line 356
+ <Unexpected> when 'true' ->
+ %% Line 357
+ call 'ct':'fail'
+ ([69|[120|[112|[101|[99|[116|[101|[100|[32|[97|[110|[32|[101|[120|[99|[101|[112|[116|[105|[111|[110|[59|[32|[103|[111|[116|[32|[126|[112|[10]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [Unexpected|[]])
+ ( <_6> when 'true' ->
+ primop 'match_fail'
+ ({'try_clause',_6})
+ -| ['compiler_generated'] )
+ end
+ catch <_9,_8,_7> ->
+ %% Line 359
+ case {_9,_8,_7} of
+ <{'throw',['other'|[_10|[_11|[]]]],_12}>
+ when let <_13> =
+ call 'erlang':'=:='
+ (_10, CodeServer)
+ in let <_14> =
+ call 'erlang':'=:='
+ (_11, Self)
+ in call 'erlang':'and'
+ (_13, _14) ->
+ %% Line 360
+ 'ok'
+ ( <{_9,_8,_7}> when 'true' ->
+ primop 'raise'
+ (_7, _8)
+ -| ['compiler_generated'] )
+ end
+ %% Line 363
+ 'ok'
+ ( <_2> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_2})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'elusive_loop'/3 =
+ %% Line 365
+ fun (_0,_1,_2) ->
+ case <_0,_1,_2> of
+ <List,0,Results> when 'true' ->
+ %% Line 366
+ {List,Results}
+ %% Line 367
+ <List,ToReceive,Results> when 'true' ->
+ let <_4> =
+ receive
+ %% Line 370
+ <Res = {_X_Pos,_X_R}>
+ when call 'erlang':'=/='
+ (List,
+ []) ->
+ %% Line 371
+ case List of
+ <[_X_H|T]> when 'true' ->
+ %% Line 372
+ {Res,T}
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_3})
+ -| ['compiler_generated'] )
+ end
+ %% Line 373
+ <Res = {_X_Pos,_X_R}>
+ when call 'erlang':'=:='
+ (List,
+ []) ->
+ %% Line 374
+ {Res,[]}
+ after 'infinity' ->
+ 'true'
+ in %% Line 368
+ case _4 of
+ <{Result,RemList}> when 'true' ->
+ let <_6> =
+ call %% Line 379
+ 'erlang':%% Line 379
+ '-'
+ (%% Line 379
+ ToReceive, %% Line 379
+ 1)
+ in %% Line 379
+ apply 'elusive_loop'/3
+ (RemList, _6, [Result|Results])
+ ( <_5> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_5})
+ -| ['compiler_generated'] )
+ end
+ ( <_9,_8,_7> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_9,_8,_7})
+ -| ['compiler_generated'] )
+ end
+'elusive2'/1 =
+ %% Line 382
+ fun (_0) ->
+ case _0 of
+ <Acc> when 'true' ->
+ let <_2,Pid> =
+ receive
+ %% Line 384
+ <{Pid,'abc'}> when 'true' ->
+ %% Line 385
+ <'ok',Pid>
+ %% Line 386
+ <{Pid,[]}> when 'true' ->
+ %% Line 387
+ <'ok',Pid>
+ %% Line 388
+ <{Pid,Res}> when 'true' ->
+ %% Line 397
+ <call 'erlang':'throw'
+ ([Res|Acc]),Pid>
+ after 'infinity' ->
+ <'true','true'>
+ in %% Line 400
+ apply 'elusive2'/1
+ ([Pid|Acc])
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'after_expression'/0 =
+ %% Line 402
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_0> =
+ call %% Line 403
+ 'erlang':%% Line 403
+ 'self'
+ ()
+ in do %% Line 403
+ call 'erlang':'!'
+ (_0, {'a','message'})
+ %% Line 404
+ case apply 'after_expr'/1
+ (0) of
+ <{'a','message'}> when 'true' ->
+ %% Line 405
+ case apply 'after_expr'/1
+ (0) of
+ <'timeout'> when 'true' ->
+ %% Line 406
+ case apply 'after_expr'/1
+ (10) of
+ <'timeout'> when 'true' ->
+ %% Line 407
+ 'ok'
+ ( <_3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_3})
+ -| ['compiler_generated'] )
+ end
+ ( <_2> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_2})
+ -| ['compiler_generated'] )
+ end
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_1})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'after_expr'/1 =
+ %% Line 409
+ fun (_0) ->
+ case _0 of
+ <Timeout> when 'true' ->
+ %% Line 410
+ receive
+ %% Line 411
+ <Msg> when 'true' ->
+ Msg
+ after %% Line 412
+ apply 'id'/1
+ (Timeout) ->
+ %% Line 413
+ 'timeout'
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'id'/1 =
+ %% Line 416
+ fun (_0) ->
+ case _0 of
+ <I> when 'true' ->
+ I
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+'module_info'/0 =
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ call 'erlang':'get_module_info'
+ ('receive_tests')
+ ( <> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause'})
+ -| ['compiler_generated'] )
+ end
+'module_info'/1 =
+ fun (_0) ->
+ case _0 of
+ <X> when 'true' ->
+ call 'erlang':'get_module_info'
+ ('receive_tests', X)
+ ( <_1> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_1})
+ -| ['compiler_generated'] )
+ end
+end \ No newline at end of file
diff --git a/lib/compiler/test/core_alias_SUITE.erl b/lib/compiler/test/core_alias_SUITE.erl
index 737b1567d4..094d3c8557 100644
--- a/lib/compiler/test/core_alias_SUITE.erl
+++ b/lib/compiler/test/core_alias_SUITE.erl
@@ -47,11 +47,10 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
id(X) -> X.
tuples(Config) when is_list(Config) ->
- Tuple = {ok,id(value)},
+ Tuple = id({ok,id(value)}),
true = erts_debug:same(Tuple, simple_tuple(Tuple)),
true = erts_debug:same(Tuple, simple_tuple_in_map(#{hello => Tuple})),
@@ -59,24 +58,24 @@ tuples(Config) when is_list(Config) ->
true = erts_debug:same(Tuple, simple_tuple_fun_repeated(Tuple, Tuple)),
true = erts_debug:same(Tuple, simple_tuple_twice_head(Tuple, Tuple)),
- {Tuple1, Tuple2} = simple_tuple_twice_body(Tuple),
+ {Tuple1, Tuple2} = id(simple_tuple_twice_body(Tuple)),
true = erts_debug:same(Tuple, Tuple1),
true = erts_debug:same(Tuple, Tuple2),
- Nested = {nested,Tuple},
+ Nested = id({nested,Tuple}),
true = erts_debug:same(Tuple, nested_tuple_part(Nested)),
true = erts_debug:same(Nested, nested_tuple_whole(Nested)),
true = erts_debug:same(Nested, nested_tuple_with_alias(Nested)),
true = erts_debug:same(Tuple, tuple_rebinding_after(Tuple)),
- Tuple = unaliased_tuple_rebinding_before(Tuple),
+ Tuple = id(unaliased_tuple_rebinding_before(Tuple)),
false = erts_debug:same(Tuple, unaliased_tuple_rebinding_before(Tuple)),
- Nested = unaliased_literal_tuple_head(Nested),
+ Nested = id(unaliased_literal_tuple_head(Nested)),
false = erts_debug:same(Nested, unaliased_literal_tuple_head(Nested)),
- Nested = unaliased_literal_tuple_body(Nested),
+ Nested = id(unaliased_literal_tuple_body(Nested)),
false = erts_debug:same(Nested, unaliased_literal_tuple_body(Nested)),
- Nested = unaliased_different_var_tuple(Nested, Tuple),
+ Nested = id(unaliased_different_var_tuple(Nested, Tuple)),
false = erts_debug:same(Nested, unaliased_different_var_tuple(Nested, Tuple)).
simple_tuple({ok,X}) ->
@@ -119,7 +118,7 @@ unaliased_different_var_tuple({nested,{ok,value}=X}, Y) ->
{nested,Y}.
cons(Config) when is_list(Config) ->
- Cons = [ok|id(value)],
+ Cons = id([ok|id(value)]),
true = erts_debug:same(Cons, simple_cons(Cons)),
true = erts_debug:same(Cons, simple_cons_in_map(#{hello => Cons})),
@@ -127,27 +126,27 @@ cons(Config) when is_list(Config) ->
true = erts_debug:same(Cons, simple_cons_fun_repeated(Cons, Cons)),
true = erts_debug:same(Cons, simple_cons_twice_head(Cons, Cons)),
- {Cons1,Cons2} = simple_cons_twice_body(Cons),
+ {Cons1,Cons2} = id(simple_cons_twice_body(Cons)),
true = erts_debug:same(Cons, Cons1),
true = erts_debug:same(Cons, Cons2),
- Nested = [nested,Cons],
+ Nested = id([nested,Cons]),
true = erts_debug:same(Cons, nested_cons_part(Nested)),
true = erts_debug:same(Nested, nested_cons_whole(Nested)),
true = erts_debug:same(Nested, nested_cons_with_alias(Nested)),
true = erts_debug:same(Cons, cons_rebinding_after(Cons)),
Unstripped = id([a,b]),
- Stripped = cons_with_binary([<<>>|Unstripped]),
+ Stripped = id(cons_with_binary([<<>>|Unstripped])),
true = erts_debug:same(Unstripped, Stripped),
- Cons = unaliased_cons_rebinding_before(Cons),
+ Cons = id(unaliased_cons_rebinding_before(Cons)),
false = erts_debug:same(Cons, unaliased_cons_rebinding_before(Cons)),
- Nested = unaliased_literal_cons_head(Nested),
+ Nested = id(unaliased_literal_cons_head(Nested)),
false = erts_debug:same(Nested, unaliased_literal_cons_head(Nested)),
- Nested = unaliased_literal_cons_body(Nested),
+ Nested = id(unaliased_literal_cons_body(Nested)),
false = erts_debug:same(Nested, unaliased_literal_cons_body(Nested)),
- Nested = unaliased_different_var_cons(Nested, Cons),
+ Nested = id(unaliased_different_var_cons(Nested, Cons)),
false = erts_debug:same(Nested, unaliased_different_var_cons(Nested, Cons)).
simple_cons([ok|X]) ->
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index 8b9dbe4aa0..9436ad5d53 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -270,8 +270,7 @@ maps_warnings(Config) when is_list(Config) ->
id(I) -> I.
">>,
[return],
- {error,[{3,erl_lint,{unbound_var,'K'}},
- {6,erl_lint,illegal_map_key}],[]}}
+ {error,[{3,erl_lint,{unbound_var,'K'}}],[]}}
],
[] = run2(Config, Ts1),
ok.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index a61c56e331..c039da93f0 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -19,7 +19,7 @@
%%
-module(guard_SUITE).
--include_lib("common_test/include/ct.hrl").
+-include_lib("syntax_tools/include/merl.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
@@ -31,7 +31,8 @@
old_guard_tests/1,complex_guard/1,
build_in_guard/1,gbif/1,
t_is_boolean/1,is_function_2/1,
- tricky/1,rel_ops/1,rel_op_combinations/1,literal_type_tests/1,
+ tricky/1,rel_ops/1,rel_op_combinations/1,
+ generated_combinations/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_guards/1,
@@ -56,7 +57,7 @@ groups() ->
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE,
repeated_type_tests]},
- {slow,[],[literal_type_tests]}].
+ {slow,[],[literal_type_tests,generated_combinations]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -1222,6 +1223,10 @@ tricky(Config) when is_list(Config) ->
error = tricky_3(#{}),
error = tricky_3({a,b}),
+ {'EXIT',_} = (catch tricky_4(x)),
+ {'EXIT',_} = (catch tricky_4(42)),
+ {'EXIT',_} = (catch tricky_4(true)),
+
ok.
tricky_1(X, Y) when abs((X == 1) or (Y == 2)) -> ok;
@@ -1239,6 +1244,13 @@ tricky_3(X)
tricky_3(_) ->
error.
+tricky_4(X) ->
+ B = (abs(X) or abs(X)) =:= true,
+ case B of
+ true -> ok;
+ false -> error
+ end.
+
%% From dets_v9:read_buckets/11, simplified.
rb(Size, ToRead, SoFar) when SoFar + Size < 81920; ToRead == [] -> true;
@@ -1589,6 +1601,116 @@ redundant_12(X) when X >= 50, X =< 80 -> 2*X;
redundant_12(X) when X < 51 -> 5*X;
redundant_12(_) -> none.
+%% Exhaustively test all combinations of relational operators
+%% to ensure the correctness of the optimizations in beam_ssa_dead.
+
+generated_combinations(Config) ->
+ Mod = ?FUNCTION_NAME,
+ RelOps = ['=:=','=/=','==','/=','<','=<','>=','>'],
+ Combinations0 = [{Op1,Op2} || Op1 <- RelOps, Op2 <- RelOps],
+ Combinations1 = gen_lit_combs(Combinations0),
+ Combinations2 = [{neq,Comb} ||
+ {_Op1,_Lit1,Op2,_Lit2}=Comb <- Combinations1,
+ Op2 =:= '=/=' orelse Op2 =:= '/='] ++ Combinations1,
+ Combinations = gen_func_names(Combinations2, 0),
+ Fs = gen_rel_op_functions(Combinations),
+ Tree = ?Q(["-module('@Mod@').",
+ "-compile([export_all,nowarn_export_all])."]) ++ Fs,
+ %%merl:print(Tree),
+ Opts = test_lib:opt_opts(?MODULE),
+ {ok,_Bin} = merl:compile_and_load(Tree, Opts),
+ test_combinations(Combinations, Mod).
+
+gen_lit_combs([{Op1,Op2}|T]) ->
+ [{Op1,7,Op2,6},
+ {Op1,7.0,Op2,6},
+ {Op1,7,Op2,6.0},
+ {Op1,7.0,Op2,6.0},
+
+ {Op1,7,Op2,7},
+ {Op1,7.0,Op2,7},
+ {Op1,7,Op2,7.0},
+ {Op1,7.0,Op2,7.0},
+
+ {Op1,6,Op2,7},
+ {Op1,6.0,Op2,7},
+ {Op1,6,Op2,7.0},
+ {Op1,6.0,Op2,7.0}|gen_lit_combs(T)];
+gen_lit_combs([]) -> [].
+
+gen_func_names([E|Es], I) ->
+ Name = list_to_atom("f" ++ integer_to_list(I)),
+ [{Name,E}|gen_func_names(Es, I+1)];
+gen_func_names([], _) -> [].
+
+gen_rel_op_functions([{Name,{neq,{Op1,Lit1,Op2,Lit2}}}|T]) ->
+ %% Note that in the translation to SSA, '=/=' will be
+ %% translated to '=:=' in a guard (with switched success
+ %% and failure labels). Therefore, to test the optimization,
+ %% we must use '=/=' (or '/=') in a body context.
+ %%
+ %% Here is an example of a generated function:
+ %%
+ %% f160(A) when erlang:'>='(A, 7) ->
+ %% one;
+ %% f160(A) ->
+ %% true = erlang:'/='(A, 7),
+ %% two.
+ [?Q("'@Name@'(A) when erlang:'@Op1@'(A, _@Lit1@) -> one;
+ '@Name@'(A) -> true = erlang:'@Op2@'(A, _@Lit2@), two. ")|
+ gen_rel_op_functions(T)];
+gen_rel_op_functions([{Name,{Op1,Lit1,Op2,Lit2}}|T]) ->
+ %% Example of a generated function:
+ %%
+ %% f721(A) when erlang:'=<'(A, 7.0) -> one;
+ %% f721(A) when erlang:'<'(A, 6) -> two;
+ %% f721(_) -> three.
+ [?Q("'@Name@'(A) when erlang:'@Op1@'(A, _@Lit1@) -> one;
+ '@Name@'(A) when erlang:'@Op2@'(A, _@Lit2@) -> two;
+ '@Name@'(_) -> three.")|gen_rel_op_functions(T)];
+gen_rel_op_functions([]) -> [].
+
+test_combinations([{Name,E}|T], Mod) ->
+ try
+ test_combinations_1([5,6,7,8,9], E, fun Mod:Name/1),
+ test_combination(6.5, E, fun Mod:Name/1)
+ catch
+ error:Reason:Stk ->
+ io:format("~p: ~p\n", [Name,E]),
+ erlang:raise(error, Reason, Stk)
+ end,
+ test_combinations(T, Mod);
+test_combinations([], _Mod) -> ok.
+
+test_combinations_1([V|Vs], E, Fun) ->
+ test_combination(V, E, Fun),
+ test_combination(float(V), E, Fun),
+ test_combinations_1(Vs, E, Fun);
+test_combinations_1([], _, _) -> ok.
+
+test_combination(Val, {neq,Expr}, Fun) ->
+ Result = eval_combination_expr(Expr, Val),
+ Result = try
+ Fun(Val) %Returns 'one' or 'two'.
+ catch
+ error:{badmatch,_} ->
+ three
+ end;
+test_combination(Val, Expr, Fun) ->
+ Result = eval_combination_expr(Expr, Val),
+ Result = Fun(Val).
+
+eval_combination_expr({Op1,Lit1,Op2,Lit2}, Val) ->
+ case erlang:Op1(Val, Lit1) of
+ true ->
+ one;
+ false ->
+ case erlang:Op2(Val, Lit2) of
+ true -> two;
+ false -> three
+ end
+ end.
+
%% Test type tests on literal values. (From emulator test suites.)
literal_type_tests(Config) when is_list(Config) ->
%% Generate an Erlang module with all different type of type tests.
@@ -1818,6 +1940,15 @@ andalso_semi(Config) when is_list(Config) ->
ok = andalso_semi_bar([a,b,c]),
ok = andalso_semi_bar(1),
fc(catch andalso_semi_bar([a,b])),
+
+ ok = andalso_semi_dispatch(name, fun andalso_semi/1),
+ ok = andalso_semi_dispatch(name, fun ?MODULE:andalso_semi/1),
+ ok = andalso_semi_dispatch(name, {?MODULE,andalso_semi,1}),
+ fc(catch andalso_semi_dispatch(42, fun andalso_semi/1)),
+ fc(catch andalso_semi_dispatch(name, not_fun)),
+ fc(catch andalso_semi_dispatch(name, fun andalso_semi_dispatch/2)),
+ fc(catch andalso_semi_dispatch(42, {a,b})),
+
ok.
andalso_semi_foo(Bar) when is_integer(Bar) andalso Bar =:= 0; Bar =:= 1 ->
@@ -1826,6 +1957,10 @@ andalso_semi_foo(Bar) when is_integer(Bar) andalso Bar =:= 0; Bar =:= 1 ->
andalso_semi_bar(Bar) when is_list(Bar) andalso length(Bar) =:= 3; Bar =:= 1 ->
ok.
+andalso_semi_dispatch(Registry, MFAOrFun) when
+ is_atom(Registry) andalso is_function(MFAOrFun, 1);
+ is_atom(Registry) andalso tuple_size(MFAOrFun) == 3 ->
+ ok.
t_tuple_size(Config) when is_list(Config) ->
10 = do_tuple_size({1,2,3,4}),
@@ -2121,7 +2256,8 @@ do_guard_in_catch_bin(From) ->
%%%
%%% The beam_bool pass has been eliminated. Here are the tests from
-%%% beam_bool_SUITE.
+%%% beam_bool_SUITE, as well as new tests to test the new beam_ssa_bool
+%%% module.
%%%
beam_bool_SUITE(_Config) ->
@@ -2130,6 +2266,11 @@ beam_bool_SUITE(_Config) ->
y_registers(),
protected(),
maps(),
+ cover_shortcut_branches(),
+ wrong_order(),
+ megaco(),
+ looks_like_a_guard(),
+ fail_in_guard(),
ok.
before_and_inside_if() ->
@@ -2267,6 +2408,115 @@ maps() ->
evidence(#{0 := Charge}) when 0; #{[] => Charge} == #{[] => 42} ->
ok.
+cover_shortcut_branches() ->
+ ok = cover_shortcut_branches({r1}, 0, 42, false),
+ ok = cover_shortcut_branches({r1}, 42, 42, true),
+ error = cover_shortcut_branches({r1}, same, same, false),
+ error = cover_shortcut_branches({r1}, x, y, true),
+ error = cover_shortcut_branches({r2}, 0, 42, false),
+ error = cover_shortcut_branches({}, 0, 42, false),
+ error = cover_shortcut_branches(not_tuple, 0, 42, false),
+ ok.
+
+cover_shortcut_branches(St, X, Y, Z) ->
+ if
+ %% The ((Y =:= X) =:= Z) part will test handling of a comparison
+ %% operator followed by a one-way `br`.
+ ((element(1, St) =:= r1) orelse fail) and ((Y =:= X) =:= Z) ->
+ ok;
+ true ->
+ error
+ end.
+
+wrong_order() ->
+ ok = wrong_order(repeat_until_fail, true),
+ ok = wrong_order(repeat_until_fail, whatever),
+ error = wrong_order(repeat_until_fail, false),
+ error = wrong_order(nope, true),
+ ok.
+
+wrong_order(RepeatType, Mode) ->
+ Parallel = Mode =/= false,
+ RepeatStop = RepeatType =:= repeat_until_fail,
+ if
+ Parallel andalso RepeatStop ->
+ ok;
+ true ->
+ error
+ end.
+
+megaco() ->
+ ok = megaco('NULL', 0),
+ ok = megaco('NULL', 7),
+ ok = megaco('NULL', 15),
+ ok = megaco('NULL', asn1_NOVALUE),
+ ok = megaco(asn1_NOVALUE, 0),
+ ok = megaco(asn1_NOVALUE, 7),
+ ok = megaco(asn1_NOVALUE, 15),
+ ok = megaco(asn1_NOVALUE, asn1_NOVALUE),
+
+ error = megaco(bad, 0),
+ error = megaco(bad, 7),
+ error = megaco(bad, 15),
+ error = megaco(bad, asn1_NOVALUE),
+
+ error = megaco('NULL', not_integer),
+ error = megaco('NULL', -1),
+ error = megaco('NULL', 16),
+ error = megaco(asn1_NOVALUE, not_integer),
+ error = megaco(asn1_NOVALUE, -1),
+ error = megaco(asn1_NOVALUE, 16),
+
+ error = megaco(bad, bad),
+ error = megaco(bad, -1),
+ error = megaco(bad, 42),
+
+ ok.
+
+megaco(Top, SelPrio)
+ when (Top =:= 'NULL' orelse Top =:= asn1_NOVALUE) andalso
+ ((is_integer(SelPrio) andalso ((0 =< SelPrio) and (SelPrio =< 15))) orelse
+ SelPrio =:= asn1_NOVALUE) ->
+ ok;
+megaco(_, _) ->
+ error.
+
+%% ERL-1054.
+looks_like_a_guard() ->
+ ok = looks_like_a_guard(0),
+ ok = looks_like_a_guard(1),
+ ok.
+
+looks_like_a_guard(N) ->
+ GuessPosition = id(42),
+ %% The matching of `true` would look like a guard to
+ %% beam_ssa_bool. The optimized code would not be safe.
+ case {1 >= N, GuessPosition == 0} of
+ {true, _} -> ok;
+ {_, true} -> ok;
+ _ -> looks_like_a_guard(N)
+ end.
+
+fail_in_guard() ->
+ false = struct_or_map(a, "foo"),
+ false = struct_or_map(a, foo),
+ false = struct_or_map(#{}, "foo"),
+ true = struct_or_map(#{}, foo),
+ ok.
+
+%% ERL-1183. If Name is not an atom, the `fail` atom must cause the
+%% entire guard to fail.
+struct_or_map(Arg, Name) when
+ (is_map(Arg) andalso (is_atom(Name) orelse fail) andalso
+ is_map_key(struct, Arg)) orelse is_map(Arg) -> true;
+struct_or_map(_Arg, _Name) ->
+ false.
+
+
+%%%
+%%% End of beam_bool_SUITE tests.
+%%%
+
repeated_type_tests(_Config) ->
binary = repeated_type_test(<<42>>),
bitstring = repeated_type_test(<<1:1>>),
diff --git a/lib/compiler/test/lfe_andor_SUITE.core b/lib/compiler/test/lfe_andor_SUITE.core
index df58b39ae6..e8cb0919a0 100644
--- a/lib/compiler/test/lfe_andor_SUITE.core
+++ b/lib/compiler/test/lfe_andor_SUITE.core
@@ -34,6 +34,8 @@ module 'lfe_andor_SUITE' ['$handle_undefined_function'/2,
'init_per_suite'/1 =
%% Line 48
fun (_config) ->
+ do
+ call 'test_lib':'recompile_core'('lfe_andor_SUITE')
_config
'end_per_suite'/1 =
%% Line 50
diff --git a/lib/compiler/test/lfe_guard_SUITE.core b/lib/compiler/test/lfe_guard_SUITE.core
index 920be82f61..9d184ed166 100644
--- a/lib/compiler/test/lfe_guard_SUITE.core
+++ b/lib/compiler/test/lfe_guard_SUITE.core
@@ -53,6 +53,8 @@ module 'lfe_guard_SUITE' ['$handle_undefined_function'/2,
'init_per_suite'/1 =
%% Line 62
fun (_config) ->
+ do
+ call 'test_lib':'recompile_core'('lfe_guard_SUITE')
_config
'end_per_suite'/1 =
%% Line 64
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index 2bcb6133da..46c1acef4c 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -74,7 +74,10 @@
%% new in OTP 22
t_mixed_clause/1,cover_beam_trim/1,
- t_duplicate_keys/1
+ t_duplicate_keys/1,
+
+ %% new in OTP 23
+ t_key_expressions/1
]).
suite() -> [].
@@ -132,7 +135,10 @@ all() ->
%% new in OTP 22
t_mixed_clause,cover_beam_trim,
- t_duplicate_keys
+ t_duplicate_keys,
+
+ %% new in OTP 23
+ t_key_expressions
].
groups() -> [].
@@ -2193,6 +2199,92 @@ do_cover_beam_trim(Id, OldMax, Max, Id, M) ->
#{Id:=Val} = id(M),
Val.
+t_key_expressions(_Config) ->
+ Int = id(42),
+ #{{tag,Int} := 42} = id(#{{tag,Int} => 42}),
+ #{{tag,Int+1} := 42} = id(#{{tag,Int+1} => 42}),
+ #{{a,b} := x, {tag,Int} := 42, Int := 0} =
+ id(#{{a,b} => x, {tag,Int} => 42, Int => 0}),
+
+ F1 = fun(#{Int + 1 := Val}) -> Val end,
+ val = F1(#{43 => val}),
+ {'EXIT',_} = (catch F1(a)),
+
+ F2 = fun(M, X, Y) ->
+ case M of
+ #{element(X, Y) := <<Sz:16,Bin:Sz/binary>>} ->
+ Bin;
+ #{} ->
+ not_found;
+ {A,B} ->
+ A + B
+ end
+ end,
+ <<"xyz">> = F2(#{b => <<3:16,"xyz">>}, 2, {a,b,c}),
+ not_found = F2(#{b => <<3:16,"xyz">>}, 999, {a,b,c}),
+ 13 = F2({6,7}, 1, 2),
+
+ #{<<"Спутник"/utf8>> := 1} = id(#{<<"Спутник"/utf8>> => 1}),
+
+ F3 = fun(Arg) ->
+ erase(once),
+ RunOnce = fun(I) ->
+ undefined = put(once, twice),
+ id(I)
+ end,
+ case RunOnce(Arg) of
+ #{{tag,<<Int:42>>} := Value} -> Value;
+ {X,Y} -> X + Y
+ end
+ end,
+ 10 = F3({7,3}),
+ whatever = F3(#{{tag,<<Int:42>>} => whatever}),
+
+ F4 = fun(K1, K2, M) ->
+ case M of
+ #{K1 div K2 := V} -> V;
+ #{} -> no_match
+ end
+ end,
+ value = F4(42, 21, #{2 => value}),
+ no_match = F4(42, 21, #{}),
+ no_match = F4(42, 0, #{2 => value}),
+ no_match = F4(42, a, #{2 => value}),
+
+ F5 = fun(Term) ->
+ self() ! Term,
+ receive
+ #{[<<(3 bsr 30 + 2):0,$k:[]/signed-integer>>] := _} ->
+ ok;
+ 0.5 ->
+ error
+ end
+ end,
+ error = F5(0.5),
+
+ F6 = fun(Term) ->
+ self() ! Term,
+ receive
+ #{<<a/utf8>> := {a,b,c}} -> ok;
+ Other -> {error,Other}
+ end
+ end,
+ {error,any} = F6(any),
+
+ F7 = fun(Term) ->
+ self() ! Term,
+ (?MODULE:all()):a(catch
+ receive
+ <<1.14:{<<"a":(tuple_size(1))>>}>> ->
+ 4;
+ Other ->
+ Other
+ end)
+ end,
+ {'EXIT',{badarg,_}} = (catch F7(whatever)),
+
+ ok.
+
t_duplicate_keys(Config) when is_list(Config) ->
Map = #{ gurka => gaffel },
Map = dup_keys_1(id(Map)),
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index bc74ec4984..d1da114d3f 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -26,7 +26,7 @@
selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,
coverage/1,grab_bag/1,literal_binary/1,
unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1,
- tuple_size_in_try/1]).
+ tuple_size_in_try/1,match_boolean_list/1]).
-include_lib("common_test/include/ct.hrl").
@@ -43,7 +43,7 @@ groups() ->
underscore,match_map,map_vars_used,coverage,
grab_bag,literal_binary,unary_op,eq_types,
match_after_return,match_right_tuple,
- tuple_size_in_try]}].
+ tuple_size_in_try,match_boolean_list]}].
init_per_suite(Config) ->
@@ -260,6 +260,7 @@ non_matching_aliases(_Config) ->
none = mixed_aliases(<<6789:16>>),
none = mixed_aliases(#{key=>value}),
+ {'EXIT',{{badmatch,bar},_}} = (catch plus_plus_prefix()),
{'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)),
{'EXIT',{{badmatch,job},_}} = (catch entirely()),
{'EXIT',{{badmatch,associates},_}} = (catch printer()),
@@ -294,8 +295,12 @@ mixed_aliases([X] = #{key:=X}) -> {k,X};
mixed_aliases(#{key:=X} = [X]) -> {l,X};
mixed_aliases({a,X} = #{key:=X}) -> {m,X};
mixed_aliases(#{key:=X} = {a,X}) -> {n,X};
+mixed_aliases([] ++ (foo = [])) -> o;
mixed_aliases(_) -> none.
+plus_plus_prefix() ->
+ [] ++ (foo = []) = bar.
+
nomatch_alias(I) ->
{ok={A,B}} = id(I),
{A,B}.
@@ -939,4 +944,17 @@ tsit(A) ->
_:_ -> ok
end.
+match_boolean_list(Config) when is_list(Config) ->
+ BoolList = [N rem 2 =:= 0 || N <- lists:seq(1, 8)],
+ %% The compiler knows that all list elements are booleans, so it translates
+ %% the expression below to a #b_br{} on the list head.
+ %%
+ %% This is fine, but since the value was only used in that branch,
+ %% reserve_zregs/3 (pre_codegen) would place the variable in a z register,
+ %% crashing the compiler in a later pass.
+ ok = case BoolList of
+ [true | _] -> error;
+ [false | _] -> ok
+ end.
+
id(I) -> I.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index 6e81bafd61..883c713a79 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -164,6 +164,11 @@ md5_1(Beam) ->
%% Cover some code that handles internal errors.
silly_coverage(Config) when is_list(Config) ->
+ %% v3_core
+ BadAbstr = [{attribute,0,module,bad_module},
+ {function,0,foo,2,[bad_clauses]}],
+ expect_error(fun() -> v3_core:module(BadAbstr, []) end),
+
%% sys_core_fold, sys_core_alias, sys_core_bsm, v3_kernel
BadCoreErlang = {c_module,[],
name,[],[],
@@ -184,6 +189,7 @@ silly_coverage(Config) when is_list(Config) ->
expect_error(fun() -> beam_kernel_to_ssa:module(BadKernel, []) end),
%% beam_ssa_lint
+ %% beam_ssa_bool
%% beam_ssa_recv
%% beam_ssa_share
%% beam_ssa_pre_codegen
@@ -191,6 +197,7 @@ silly_coverage(Config) when is_list(Config) ->
BadSSA = {b_module,#{},a,b,c,
[{b_function,#{func_info=>{mod,foo,0}},args,bad_blocks,0}]},
expect_error(fun() -> beam_ssa_lint:module(BadSSA, []) end),
+ expect_error(fun() -> beam_ssa_bool:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_recv:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_share:module(BadSSA, []) end),
expect_error(fun() -> beam_ssa_pre_codegen:module(BadSSA, []) end),
@@ -198,7 +205,7 @@ silly_coverage(Config) when is_list(Config) ->
%% beam_ssa_opt
BadSSABlocks = #{0 => {b_blk,#{},[bad_code],{b_ret,#{},arg}}},
- BadSSAOpt = {b_module,#{},a,[],c,
+ BadSSAOpt = {b_module,#{},a,[],[],
[{b_function,#{func_info=>{mod,foo,0}},[],
BadSSABlocks,0}]},
expect_error(fun() -> beam_ssa_opt:module(BadSSAOpt, []) end),
@@ -230,15 +237,6 @@ silly_coverage(Config) when is_list(Config) ->
{label,2}|non_proper_list]}],99},
expect_error(fun() -> beam_block:module(BlockInput, []) end),
- %% beam_except
- ExceptInput = {?MODULE,[{foo,0}],[],
- [{function,foo,0,2,
- [{label,1},
- {line,loc},
- {func_info,{atom,?MODULE},{atom,foo},0},
- {label,2}|non_proper_list]}],99},
- expect_error(fun() -> beam_except:module(ExceptInput, []) end),
-
%% beam_jump
JumpInput = BlockInput,
expect_error(fun() -> beam_jump:module(JumpInput, []) end),
@@ -286,14 +284,34 @@ silly_coverage(Config) when is_list(Config) ->
bad_ssa_lint_input() ->
{b_module,#{},t,
- [{foobar,1},{module_info,0},{module_info,1}],
+ [{a,1},{b,1},{c,1},{module_info,0},{module_info,1}],
[],
[{b_function,
- #{func_info => {t,foobar,1},location => {"t.erl",4}},
+ #{func_info => {t,a,1},location => {"t.erl",4}},
[{b_var,0}],
#{0 => {b_blk,#{},[],{b_ret,#{},{b_var,'@undefined_var'}}}},
3},
{b_function,
+ #{func_info => {t,b,1},location => {"t.erl",5}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
+ #{func_info => {t,c,1},location => {"t.erl",6}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
#{func_info => {t,module_info,0}},
[],
#{0 =>
diff --git a/lib/compiler/test/property_test/beam_types_prop.erl b/lib/compiler/test/property_test/beam_types_prop.erl
new file mode 100644
index 0000000000..1e5da10aa0
--- /dev/null
+++ b/lib/compiler/test/property_test/beam_types_prop.erl
@@ -0,0 +1,315 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_types_prop).
+
+-compile([export_all, nowarn_export_all]).
+
+%% This module only supports proper, as we don't have an eqc license to test
+%% with.
+
+-proptest([proper]).
+
+-ifdef(PROPER).
+
+-define(BEAM_TYPES_INTERNAL, true).
+-include_lib("compiler/src/beam_types.hrl").
+
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-import(lists, [duplicate/2,foldl/3]).
+
+%% The default repetitions of 100 is a bit too low to reliably cover all type
+%% combinations, so we crank it up a bit.
+-define(REPETITIONS, 5000).
+
+absorption() ->
+ numtests(?REPETITIONS, absorption_1()).
+
+absorption_1() ->
+ ?FORALL({TypeA, TypeB},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(), {TypeA, TypeB})),
+ absorption_check(TypeA, TypeB)).
+
+absorption_check(A, B) ->
+ verified_type(A),
+ verified_type(B),
+
+ %% a ∨ (a ∧ b) = a
+ A = join(A, meet(A, B)),
+
+ %% a ∧ (a ∨ b) = a
+ A = meet(A, join(A, B)),
+
+ true.
+
+associativity() ->
+ numtests(?REPETITIONS, associativity_1()).
+
+associativity_1() ->
+ ?FORALL({TypeA, TypeB, TypeC},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(),
+ ?LET(TypeC, type(), {TypeA, TypeB, TypeC}))),
+ associativity_check(TypeA, TypeB, TypeC)).
+
+associativity_check(A, B, C) ->
+ verified_type(A),
+ verified_type(B),
+ verified_type(C),
+
+ %% a ∨ (b ∨ c) = (a ∨ b) ∨ c
+ LHS_Join = join(A, join(B, C)),
+ RHS_Join = join(join(A, B), C),
+ LHS_Join = RHS_Join,
+
+ %% a ∧ (b ∧ c) = (a ∧ b) ∧ c
+ LHS_Meet = meet(A, meet(B, C)),
+ RHS_Meet = meet(meet(A, B), C),
+ LHS_Meet = RHS_Meet,
+
+ true.
+
+commutativity() ->
+ numtests(?REPETITIONS, commutativity_1()).
+
+commutativity_1() ->
+ ?FORALL({TypeA, TypeB},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(), {TypeA, TypeB})),
+ commutativity_check(TypeA, TypeB)).
+
+commutativity_check(A, B) ->
+ verified_type(A),
+ verified_type(B),
+
+ %% a ∨ b = b ∨ a
+ true = join(A, B) =:= join(B, A),
+
+ %% a ∧ b = b ∧ a
+ true = meet(A, B) =:= meet(B, A),
+
+ true.
+
+idempotence() ->
+ numtests(?REPETITIONS, idempotence_1()).
+
+idempotence_1() ->
+ ?FORALL(Type, type(), idempotence_check(Type)).
+
+idempotence_check(Type) ->
+ verified_type(Type),
+
+ %% a ∨ a = a
+ Type = join(Type, Type),
+
+ %% a ∧ a = a
+ Type = meet(Type, Type),
+
+ true.
+
+identity() ->
+ ?FORALL(Type, type(), identity_check(Type)).
+
+identity_check(Type) ->
+ verified_type(Type),
+
+ %% a ∨ [bottom element] = a
+ Type = join(Type, none),
+
+ %% a ∧ [top element] = a
+ Type = meet(Type, any),
+
+ true.
+
+subtraction() ->
+ numtests(?REPETITIONS, subtraction_1()).
+
+subtraction_1() ->
+ ?FORALL({TypeA, TypeB},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(), {TypeA, TypeB})),
+ subtraction_check(TypeA, TypeB)).
+
+subtraction_check(A, B) ->
+ verified_type(A),
+ verified_type(B),
+
+ %% Subtraction can be thought of as `a ∧ ¬b`, so the result must be at
+ %% least as specific as `a`.
+ Res = subtract(A, B),
+ Res = meet(A, Res),
+
+ true.
+
+meet(A, B) -> beam_types:meet(A, B).
+join(A, B) -> beam_types:join(A, B).
+subtract(A, B) -> beam_types:subtract(A, B).
+verified_type(T) -> beam_types:verified_type(T).
+
+%%%
+%%% Generators
+%%%
+
+type() ->
+ type(?MAX_TYPE_DEPTH).
+
+type(Depth) ->
+ ?SHRINK(?LAZY(oneof([any, none] ++ term_types(Depth))),
+ [nil, any, none]).
+
+term_type(Depth) ->
+ ?SHRINK(?LAZY(oneof([any | term_types(Depth)])),
+ [nil, any]).
+
+term_types(Depth) ->
+ nested_generators(Depth) ++
+ numerical_generators() ++
+ [gen_atom(), gen_bs_matchable()].
+
+numerical_generators() ->
+ [gen_integer(), gen_float(), number].
+
+nested_generators(Depth) when Depth =< 0 ->
+ [nil];
+nested_generators(Depth) ->
+ [gen_list(Depth - 1),
+ gen_fun(Depth - 1),
+ gen_map(Depth - 1),
+ ?LAZY(gen_tuple(Depth - 1)),
+ ?LAZY(gen_union(Depth - 1))].
+
+%% Proper's atom generator is far too wide, generating strings like 'û\2144Bò}'
+%% which are both hard to read and fill up the atom table really fast.
+readable_atom() ->
+ ?LET(Atom, range($0, $~), list_to_atom([Atom])).
+
+%%
+
+gen_atom() ->
+ ?LET(Size, range(0, ?ATOM_SET_SIZE),
+ ?LET(Set, duplicate(Size, readable_atom()),
+ case ordsets:from_list(Set) of
+ [_|_]=Vs -> #t_atom{elements=ordsets:from_list(Vs)};
+ [] -> #t_atom{}
+ end)).
+
+gen_bs_matchable() ->
+ oneof([?LET(Unit, range(1, 16), #t_bs_matchable{tail_unit=Unit}),
+ ?LET(Unit, range(1, 16), #t_bs_context{tail_unit=Unit}),
+ ?LET(Unit, range(1, 16), #t_bitstring{size_unit=Unit})]).
+
+gen_float() ->
+ oneof([?LET({A, B}, {integer(), integer()},
+ begin
+ Min = float(min(A,B)),
+ Max = float(max(A,B)),
+ #t_float{elements={Min,Max}}
+ end),
+ #t_float{}]).
+
+gen_fun(Depth) ->
+ ?SHRINK(?LET({Type, Arity}, {type(Depth), oneof([any, range(1, 4)])},
+ #t_fun{type=Type,arity=Arity}),
+ [#t_fun{}]).
+
+gen_integer() ->
+ oneof([?LET({A, B}, {integer(), integer()},
+ #t_integer{elements={min(A,B), max(A,B)}}),
+ #t_integer{}]).
+
+gen_list(Depth) ->
+ ?SHRINK(oneof([?LET({Type, Term}, {term_type(Depth), term_type(Depth)},
+ #t_list{type=Type,terminator=Term}),
+ ?LET({Type, Term}, {term_type(Depth), term_type(Depth)},
+ #t_cons{type=Type,terminator=Term}),
+ nil]),
+ [nil]).
+
+gen_map(Depth) ->
+ ?SHRINK(?LET({SKey, SValue}, {term_type(Depth), term_type(Depth)},
+ #t_map{super_key=SKey,super_value=SValue}),
+ [#t_map{}]).
+
+gen_tuple(Depth) ->
+ ?SHRINK(oneof([gen_tuple_plain(Depth), gen_tuple_record(Depth)]),
+ [#t_tuple{}]).
+
+gen_tuple_record(Depth) ->
+ ?LET({Start, Size}, {range(2, ?TUPLE_ELEMENT_LIMIT),
+ range(1, ?TUPLE_ELEMENT_LIMIT * 2)},
+ ?LET({Tag, Es0}, {readable_atom(),
+ gen_tuple_elements(Start, Size, Depth)},
+ begin
+ Es = Es0#{ 1 => #t_atom{elements=[Tag]} },
+ #t_tuple{exact=true,size=Size,elements=Es}
+ end)).
+
+gen_tuple_plain(Depth) ->
+ ?LET({Start, Size}, {range(1, ?TUPLE_ELEMENT_LIMIT),
+ range(0, ?TUPLE_ELEMENT_LIMIT * 2)},
+ ?LET({Exact, Es}, {boolean(), gen_tuple_elements(Start, Size, Depth)},
+ #t_tuple{exact=Exact,size=Size,elements=Es})).
+
+gen_tuple_elements(Start, Size, Depth) ->
+ End = min(Size, ?TUPLE_ELEMENT_LIMIT),
+ ?SHRINK(?LET(Types, gen_tuple_elements_1(Start, End, term_type(Depth)),
+ foldl(fun({Index, Type}, Acc) ->
+ beam_types:set_tuple_element(Index, Type, Acc)
+ end, #{}, Types)),
+ [#{}]).
+
+gen_tuple_elements_1(Index, End, _Gen) when Index > End ->
+ [];
+gen_tuple_elements_1(Index, End, Gen) ->
+ case rand:uniform(2) of
+ 1 -> [{Index, Gen} | gen_tuple_elements_1(Index + 1, End, Gen)];
+ 2 -> gen_tuple_elements_1(Index + 1, End, Gen)
+ end.
+
+gen_union(Depth) ->
+ ?SHRINK(oneof([gen_union_wide(Depth), gen_union_record(Depth)]),
+ [gen_union_record(?MAX_TYPE_DEPTH)]).
+
+%% Creates a union with most (if not all) slots filled.
+gen_union_wide(Depth) ->
+ ?LET({A, B, C, D, E, F}, {gen_atom(),
+ gen_bs_matchable(),
+ gen_list(Depth),
+ gen_tuple(Depth),
+ oneof(nested_generators(Depth)),
+ oneof(numerical_generators())},
+ begin
+ T0 = join(A, B),
+ T1 = join(T0, C),
+ T2 = join(T1, D),
+ T3 = join(T2, E),
+ join(T3, F)
+ end).
+
+%% Creates a union consisting solely of records
+gen_union_record(Depth) ->
+ ?LET(Size, range(2, ?TUPLE_SET_LIMIT),
+ ?LET(Tuples, duplicate(Size, gen_tuple_record(Depth)),
+ foldl(fun join/2, none, Tuples))).
+
+-endif.
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index db84d16b06..b1f1099095 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -27,7 +27,8 @@
export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1,
wait/1,recv_in_try/1,double_recv/1,receive_var_zero/1,
match_built_terms/1,elusive_common_exit/1,
- return_before_receive/1,trapping/1]).
+ return_before_receive/1,trapping/1,
+ after_expression/1,in_after/1]).
-include_lib("common_test/include/ct.hrl").
@@ -49,7 +50,8 @@ groups() ->
[recv,coverage,otp_7980,export,wait,
recv_in_try,double_recv,receive_var_zero,
match_built_terms,elusive_common_exit,
- return_before_receive,trapping]},
+ return_before_receive,trapping,
+ after_expression,in_after]},
{slow,[],[ref_opt]}].
init_per_suite(Config) ->
@@ -94,6 +96,8 @@ recv(Config) when is_list(Config) ->
io:format("Unexpected extra message: ~p", [X]),
ct:fail(unexpected)
after 10 ->
+ unlink(Pid),
+ exit(Pid, kill),
ok
end,
ok.
@@ -136,6 +140,16 @@ coverage(Config) when is_list(Config) ->
{'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())),
+
+ self() ! {data,no_data},
+ ok = receive_sink_tuple({any,pattern}),
+ {b,a} = receive_sink_tuple({a,b}),
+
+ %% Basically a smoke test of no_clauses_left/0.
+ NoClausesLeft = spawn(fun no_clauses_left/0),
+ receive after 1 -> ok end,
+ exit(NoClausesLeft, kill),
+
ok.
monitor_plus_badmap(Pid) ->
@@ -184,6 +198,26 @@ tuple_to_values(Timeout, X) ->
end,
A+B.
+no_clauses_left() ->
+ receive
+ %% This clause would be removed because it cannot match...
+ a = b ->
+ V = whatever
+ end,
+ %% ... leaving a reference to an unbound variable. Crash.
+ V.
+
+
+%% Cover a help function for beam_ssa_opt:ssa_opt_sink/1.
+receive_sink_tuple({Line,Pattern}) ->
+ receive
+ {data,_} ->
+ ok
+ after 1 ->
+ id({Pattern,Line})
+ end.
+
+
%% OTP-7980. Thanks to Vincent de Phily. The following code would
%% be inccorrectly optimized by beam_jump.
@@ -401,7 +435,9 @@ receive_var_zero(Config) when is_list(Config) ->
end,
self() ! w,
receive
- x -> ok;
+ x ->
+ receive y -> ok end,
+ receive w -> ok end;
Other ->
ct:fail({bad_message,Other})
end.
@@ -541,4 +577,52 @@ do_trapping(N) ->
receive Ref -> ok end,
receive after 1 -> ok end.
+after_expression(_Config) ->
+ self() ! {a,message},
+ {a,message} = after_expr(0),
+ timeout = after_expr(0),
+ timeout = after_expr(10),
+ ok = after_expr_timeout(0),
+ ok = after_expr_timeout(1),
+ ok.
+
+after_expr(Timeout) ->
+ receive
+ Msg -> Msg
+ after id(Timeout) ->
+ timeout
+ end.
+
+after_expr_timeout(Timeout) ->
+ receive
+ after id(Timeout) ->
+ ok
+ end.
+
+in_after(_Config) ->
+ self() ! first,
+ self() ! message,
+ do_in_after(fun() -> ok end),
+ do_in_after(fun() -> ok end),
+ self() ! message,
+ catch do_in_after(fun() -> error(bad) end),
+ catch do_in_after(fun() -> error(bad) end),
+ self() ! last,
+ first = receive M1 -> M1 end,
+ last = receive M2 -> M2 end,
+ ok.
+
+do_in_after(E) ->
+ try
+ E()
+ after
+ receive
+ message ->
+ ok
+ after 1 ->
+ ok
+ end
+ end,
+ ok.
+
id(I) -> I.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_19.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_19.erl
new file mode 100644
index 0000000000..470bef54ae
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_19.erl
@@ -0,0 +1,15 @@
+-module(yes_19).
+-export([?MODULE/0,f/2]).
+
+?MODULE() ->
+ ok.
+
+f(Pid, Msg) ->
+ MyRef = make_ref(),
+ Pid ! Msg,
+ receive
+ {Ref,Reply} when Ref == MyRef ->
+ Reply
+ after 0 ->
+ ok
+ end.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_20.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_20.erl
new file mode 100644
index 0000000000..e85f1b99ca
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_20.erl
@@ -0,0 +1,16 @@
+-module(yes_20).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f() ->
+ Ref = spawn_request(fun () -> ok end),
+ Pid = receive
+ {spawn_reply, Ref, _, P} ->
+ P
+ end,
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ ok
+ end.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_21.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_21.erl
new file mode 100644
index 0000000000..5e0a92b10d
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_21.erl
@@ -0,0 +1,16 @@
+-module(yes_21).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f() ->
+ Ref = spawn_request(fun () -> ok end),
+ receive
+ {spawn_reply, Ref, _, _} ->
+ ok
+ end,
+ receive
+ {'DOWN', Ref, _, _, _} ->
+ ok
+ end.
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index 94804529b6..e272d95f2d 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -589,6 +589,7 @@ nested_access(Config) when is_list(Config) ->
ok.
-record(rr, {a,b,c}).
+-record(fileheader, {read_md5,md5,eof,trailer}).
coverage(Config) when is_list(Config) ->
%% There should only remain one record test in the code below.
@@ -600,8 +601,23 @@ coverage(Config) when is_list(Config) ->
ok
end,
#rr{a=1,b=2,c=42} = id(R), %Test for correctness.
+
+ %% Cover beam_ssa_opt:ssa_opt_element/1 and friends.
+ error1 = check_file_header(#fileheader{read_md5=1,md5=2}),
+ error2 = check_file_header(#fileheader{trailer=true,eof=false}),
+ error3 = check_file_header(#fileheader{}),
+
ok.
+check_file_header(FH) ->
+ if
+ FH#fileheader.read_md5 =/= FH#fileheader.md5 ->
+ error1;
+ FH#fileheader.trailer =/= FH#fileheader.eof ->
+ error2;
+ true ->
+ error3
+ end.
-record(default_fun, {a = fun(X) -> X*X end}).
@@ -609,8 +625,9 @@ coverage(Config) when is_list(Config) ->
-record(gb_nil, {}).
-record(gb_foo, {hello=1}).
-record(gb_bar, {hello=2,there=3}).
+-record(gb_rh, {mod,mid}).
-%% Taken from compilation_SUITE.
+%% Taken from compilation_SUITE and other places.
grab_bag(_Config) ->
T1 = fun() ->
X = #foo{},
@@ -654,6 +671,23 @@ grab_bag(_Config) ->
end,
T4(),
+ %% Used to crash beam_ssa_bool during its development.
+ T5 = fun(RH) ->
+ if
+ is_record(RH, gb_rh) andalso
+ is_atom(RH#gb_rh.mod) andalso
+ RH#gb_rh.mid /= 42 -> ok;
+ true -> error
+ end
+ end,
+ ok = T5(#gb_rh{}),
+ ok = T5(#gb_rh{mod=atom,mid=0}),
+ error = T5(#gb_rh{mod=100,mid=0}),
+ error = T5(#gb_rh{mod=atom,mid=42}),
+ error = T5(#gb_nil{}),
+ error = T5(#gb_bar{}),
+ error = T5(atom),
+
ok.
%% ERIERL-436; the following code used to be very slow to compile.
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 34410e4b2a..f3eeae9ccd 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -21,7 +21,8 @@
-include_lib("common_test/include/ct.hrl").
-compile({no_auto_import,[binary_part/2]}).
--export([id/1,recompile/1,parallel/0,uniq/0,opt_opts/1,get_data_dir/1,
+-export([id/1,recompile/1,recompile_core/1,parallel/0,
+ uniq/0,opt_opts/1,get_data_dir/1,
is_cloned_mod/1,smoke_disasm/1,p_run/2,
highest_opcode/1]).
@@ -45,6 +46,21 @@ recompile(Mod) when is_atom(Mod) ->
%% Smoke-test of beam disassembler.
smoke_disasm(Mod).
+recompile_core(Mod) when is_atom(Mod) ->
+ case whereis(cover_server) of
+ undefined -> ok;
+ _ ->
+ %% Re-compile the test suite if the cover server is running.
+ Beam = code:which(Mod),
+ Src = filename:rootname(Beam, ".beam"),
+ Opts = [bin_opt_info|opt_opts(Mod)],
+ io:format("Recompiling ~p (~p)\n", [Mod,Opts]),
+ c:c(Src, [from_core,{outdir,filename:dirname(Src)}|Opts])
+ end,
+
+ %% Smoke-test of beam disassembler.
+ smoke_disasm(Mod).
+
smoke_disasm(Mod) when is_atom(Mod) ->
smoke_disasm(code:which(Mod));
smoke_disasm(File) when is_list(File) ->
@@ -69,6 +85,7 @@ opt_opts(Mod) ->
{options,Opts} = lists:keyfind(options, 1, Comp),
lists:filter(fun
(debug_info) -> true;
+ (dialyzer) -> true;
(inline) -> true;
(no_bsm3) -> true;
(no_bsm_opt) -> true;
@@ -79,9 +96,11 @@ opt_opts(Mod) ->
(no_put_tuple2) -> true;
(no_recv_opt) -> true;
(no_share_opt) -> true;
+ (no_shared_fun_wrappers) -> true;
(no_ssa_float) -> true;
(no_ssa_opt) -> true;
(no_stack_trimming) -> true;
+ (no_swap) -> true;
(no_type_opt) -> true;
(_) -> false
end, Opts).
diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl
index 539f9d69fa..4cff129d1f 100644
--- a/lib/compiler/test/trycatch_SUITE.erl
+++ b/lib/compiler/test/trycatch_SUITE.erl
@@ -1079,7 +1079,7 @@ stacktrace(_Config) ->
error:{badmatch,_}:Stk2 ->
[{?MODULE,stacktrace_2,0,_},
{?MODULE,stacktrace,1,_}|_] = Stk2,
- Stk2 = erlang:get_stacktrace(),
+ [] = erlang:get_stacktrace(),
ok
end,
@@ -1087,7 +1087,7 @@ stacktrace(_Config) ->
stacktrace_3(a, b)
catch
error:function_clause:Stk3 ->
- Stk3 = erlang:get_stacktrace(),
+ [] = erlang:get_stacktrace(),
case lists:module_info(native) of
false ->
[{lists,prefix,[a,b],_}|_] = Stk3;
@@ -1108,14 +1108,16 @@ stacktrace_1(X, C1, Y) ->
C1 -> value1
catch
C1:D1:Stk1 ->
- Stk1 = erlang:get_stacktrace(),
+ [] = erlang:get_stacktrace(),
{caught1,D1,Stk1}
after
foo(Y)
end of
V2 -> {value2,V2}
catch
- C2:D2:Stk2 -> {caught2,{C2,D2},Stk2=erlang:get_stacktrace()}
+ C2:D2:Stk2 ->
+ [] = erlang:get_stacktrace(),
+ {caught2,{C2,D2},Stk2}
end.
stacktrace_2() ->
@@ -1160,12 +1162,10 @@ nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
V1 -> value1
catch
C1:V1:S1 ->
- S1 = erlang:get_stacktrace(),
T2 = try foo(X2) of
V2 -> value2
catch
C2:V2:S2 ->
- S2 = erlang:get_stacktrace(),
{caught2,S2}
end,
{caught1,S1,T2}
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 70b7100451..a91d8399ff 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -121,7 +121,7 @@ pattern2(Config) when is_list(Config) ->
Ts = [{pattern2,
Source,
[nowarn_unused_vars],
- {warnings,[{2,sys_core_fold,{nomatch_shadow,1}},
+ {warnings,[{2,sys_core_fold,{nomatch_shadow,1,{f,1}}},
{4,sys_core_fold,no_clause_match},
{5,sys_core_fold,nomatch_clause_type},
{6,sys_core_fold,nomatch_clause_type}]}}],
@@ -786,7 +786,7 @@ latin1_fallback(Conf) when is_list(Conf) ->
">>,
[],
{warnings,[{1,compile,reparsing_invalid_unicode},
- {3,sys_core_fold,{nomatch_shadow,2}}]}}],
+ {3,sys_core_fold,{nomatch_shadow,2,{t,1}}}]}}],
[] = run(Conf, Ts1),
Ts2 = [{latin1_fallback2,
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index afe56aa7d6..e5812bee15 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -38,3 +38,4 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index f922c3fb9b..20c32cb8d6 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -37,13 +37,14 @@ LIBS = @DED_LIBS@
LDFLAGS += @DED_LDFLAGS@
CFLAGS = @DED_CFLAGS@ @SSL_FLAGS@
-# From erts/configure
+# From configure
SSL_LIBDIR = @SSL_LIBDIR@
SSL_INCLUDE = @SSL_INCLUDE@
SSL_CRYPTO_LIBNAME = @SSL_CRYPTO_LIBNAME@
SSL_SSL_LIBNAME = @SSL_SSL_LIBNAME@
INCLUDES = $(SSL_INCLUDE) @DED_INCLUDE@
+SSL_EXTRA_LIBS=@SSL_EXTRA_LIBS@
ifeq ($(TYPE),debug)
TYPEMARKER = .debug
@@ -164,7 +165,7 @@ $(LIBDIR)/otp_test_engine$(TYPEMARKER).so: $(TEST_ENGINE_OBJS)
$(LIBDIR)/otp_test_engine$(TYPEMARKER).dll: $(TEST_ENGINE_OBJS)
$(V_at)$(INSTALL_DIR) $(LIBDIR)
- $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(TEST_ENGINE_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+ $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(TEST_ENGINE_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) $(SSL_EXTRA_LIBS)
$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(V_at)$(INSTALL_DIR) $(OBJDIR)
@@ -184,7 +185,7 @@ $(LIBDIR)/crypto$(TYPEMARKER).a: $(CRYPTO_STATIC_OBJS)
$(LIBDIR)/crypto$(TYPEMARKER).dll: $(CRYPTO_OBJS)
$(V_at)$(INSTALL_DIR) $(LIBDIR)
- $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(CRYPTO_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+ $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(CRYPTO_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) $(SSL_EXTRA_LIBS)
ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
$(LIBDIR)/crypto_callback$(TYPEMARKER).so: $(CALLBACK_OBJS)
diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c
index 5fc161f382..0b6960c022 100644
--- a/lib/crypto/c_src/api_ng.c
+++ b/lib/crypto/c_src/api_ng.c
@@ -29,6 +29,9 @@
ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+/*************************************************************************/
+/* Compatibility functions. */
+/*************************************************************************/
#ifdef HAVE_ECB_IVEC_BUG
/* <= 0.9.8l returns faulty ivec length */
# define GET_IV_LEN(Ciph) ((Ciph)->flags & ECB_BUG_0_9_8L) ? 0 : EVP_CIPHER_iv_length((Ciph)->cipher.p)
@@ -36,6 +39,48 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
# define GET_IV_LEN(Ciph) EVP_CIPHER_iv_length((Ciph)->cipher.p)
#endif
+#if !defined(HAVE_EVP_CIPHER_CTX_COPY)
+/*
+ The EVP_CIPHER_CTX_copy is not available in older cryptolibs although
+ the function is needed.
+ Instead of implement it in-place, we have a copy here as a compatibility
+ function
+*/
+
+int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in);
+
+int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
+{
+ if ((in == NULL) || (in->cipher == NULL))
+ {
+ return 0;
+ }
+#ifdef HAS_ENGINE_SUPPORT
+ /* Make sure it's safe to copy a cipher context using an ENGINE */
+ if (in->engine && !ENGINE_init(in->engine))
+ return 0;
+#endif
+
+ EVP_CIPHER_CTX_cleanup(out);
+ memcpy(out,in,sizeof *out);
+
+ if (in->cipher_data && in->cipher->ctx_size)
+ {
+ out->cipher_data=OPENSSL_malloc(in->cipher->ctx_size);
+ if (!out->cipher_data)
+ return 0;
+ memcpy(out->cipher_data,in->cipher_data,in->cipher->ctx_size);
+ }
+
+#if defined(EVP_CIPH_CUSTOM_COPY) && defined(EVP_CTRL_COPY)
+ if (in->cipher->flags & EVP_CIPH_CUSTOM_COPY)
+ return in->cipher->ctrl((EVP_CIPHER_CTX *)in, EVP_CTRL_COPY, 0, out);
+#endif
+ return 1;
+}
+/****** End of !defined(HAVE_EVP_CIPHER_CTX_COPY) ******/
+#endif
+
/*************************************************************************/
/* Get the arguments for the initialization of the EVP_CIPHER_CTX. Check */
/* them and initialize that context. */
@@ -46,27 +91,30 @@ static int get_init_args(ErlNifEnv* env,
const ERL_NIF_TERM key_arg,
const ERL_NIF_TERM ivec_arg,
const ERL_NIF_TERM encflg_arg,
+ const ERL_NIF_TERM padding_arg,
const struct cipher_type_t **cipherp,
ERL_NIF_TERM *return_term)
{
int ivec_len;
ErlNifBinary key_bin;
ErlNifBinary ivec_bin;
- int encflg;
ctx_res->ctx = NULL; /* For testing if *ctx should be freed after errors */
#if !defined(HAVE_EVP_AES_CTR)
ctx_res->env = NULL; /* For testing if *env should be freed after errors */
#endif
-
+ ctx_res->padding = atom_undefined;
+ ctx_res->padded_size = -1;
+ ctx_res->size = 0;
+
/* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
if (encflg_arg == atom_true)
- encflg = 1;
+ ctx_res->encflag = 1;
else if (encflg_arg == atom_false)
- encflg = 0;
+ ctx_res->encflag = 0;
else if (encflg_arg == atom_undefined)
/* For compat funcs in crypto.erl */
- encflg = -1;
+ ctx_res->encflag = -1;
else
{
*return_term = EXCP_BADARG(env, "Bad enc flag");
@@ -193,7 +241,7 @@ static int get_init_args(ErlNifEnv* env,
goto err;
}
- if (!EVP_CipherInit_ex(ctx_res->ctx, (*cipherp)->cipher.p, NULL, NULL, NULL, encflg))
+ if (!EVP_CipherInit_ex(ctx_res->ctx, (*cipherp)->cipher.p, NULL, NULL, NULL, ctx_res->encflag))
{
*return_term = EXCP_ERROR(env, "Can't initialize context, step 1");
goto err;
@@ -232,7 +280,20 @@ static int get_init_args(ErlNifEnv* env,
goto err;
}
- EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0);
+ /* Set padding */
+ if ((padding_arg == atom_undefined) ||
+ (padding_arg == atom_none) ||
+ (padding_arg == atom_zero) ||
+ (padding_arg == atom_random) )
+ EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0);
+
+ else if (padding_arg != atom_pkcs_padding) /* pkcs_padding is default */
+ {
+ *return_term = EXCP_BADARG(env, "Bad padding flag");
+ goto err;
+ }
+
+ ctx_res->padding = padding_arg;
*return_term = atom_ok;
@@ -266,8 +327,11 @@ static int get_update_args(ErlNifEnv* env,
ASSERT(in_data_bin.size <= INT_MAX);
+ ctx_res->size += in_data_bin.size;
+
#if !defined(HAVE_EVP_AES_CTR)
if (ctx_res->state != atom_undefined) {
+ /* Use AES_CTR compatibility code */
ERL_NIF_TERM state0, newstate_and_outdata;
const ERL_NIF_TERM *tuple_argv;
int tuple_argc;
@@ -288,6 +352,17 @@ static int get_update_args(ErlNifEnv* env,
}
} else
#endif
+#if defined(HAVE_UPDATE_EMPTY_DATA_BUG)
+ if (in_data_bin.size == 0)
+ {
+ if (!enif_alloc_binary(0, &out_data_bin))
+ {
+ *return_term = EXCP_ERROR(env, "Can't allocate outdata");
+ goto err;
+ }
+ *return_term = enif_make_binary(env, &out_data_bin);
+ } else
+#endif
{
block_size = EVP_CIPHER_CTX_block_size(ctx_res->ctx);
@@ -324,22 +399,186 @@ static int get_update_args(ErlNifEnv* env,
}
/*************************************************************************/
+/* Get the arguments for the EVP_CipherFinal function, and call it. */
+/*************************************************************************/
+
+static int get_final_args(ErlNifEnv* env,
+ struct evp_cipher_ctx *ctx_res,
+ ERL_NIF_TERM *return_term
+ )
+{
+ ErlNifBinary out_data_bin;
+ int block_size, pad_size;
+ int out_len, pad_offset;
+
+#if !defined(HAVE_EVP_AES_CTR)
+ if (ctx_res->state != atom_undefined) {
+ /* Use AES_CTR compatibility code */
+ /* Padding size is always 0, because the block_size is 1 and therefore
+ always filled
+ */
+ ctx_res->padded_size = 0;
+ out_len = 0;
+
+ if (!enif_alloc_binary(out_len, &out_data_bin))
+ {
+ *return_term = EXCP_ERROR(env, "Can't allocate empty outdata");
+ goto err0;
+ }
+ } else
+#endif
+ {
+ block_size = EVP_CIPHER_CTX_block_size(ctx_res->ctx);
+
+ pad_size = ctx_res->size % block_size;
+ if (pad_size)
+ pad_size = block_size - pad_size;
+
+ if (!enif_alloc_binary((size_t)block_size, &out_data_bin))
+ {
+ *return_term = EXCP_ERROR(env, "Can't allocate final outdata");
+ goto err0;
+ }
+
+ if (ctx_res->encflag)
+ {/* Maybe do padding */
+
+ /* First set lengths etc and do the otp_padding */
+ if (ctx_res->padding == atom_undefined)
+ {
+ ctx_res->padded_size = pad_size;
+ pad_offset = 0;
+ }
+
+ else if (ctx_res->padding == atom_none)
+ {
+ ASSERT(pad_size == 0);
+ ctx_res->padded_size = pad_size;
+ pad_offset = 0;
+ }
+
+ else if (ctx_res->padding == atom_pkcs_padding)
+ {
+ ctx_res->padded_size = pad_size ? pad_size : block_size;
+ pad_offset = 0;
+ }
+
+ else if ((ctx_res->padding == atom_zero) ||
+ (ctx_res->padding == atom_random))
+ {
+ if (pad_size)
+ {
+ unsigned char padding[EVP_MAX_BLOCK_LENGTH];
+ int i;
+ if (ctx_res->padding == atom_zero)
+ for(i=0; i<pad_size; i++) padding[i] = (unsigned char)0;
+ else
+ RAND_bytes(padding, pad_size);
+ if (!EVP_CipherUpdate(ctx_res->ctx, out_data_bin.data, &out_len, padding, pad_size))
+ {
+ *return_term = EXCP_ERROR(env, "Can't pad");
+ goto err;
+ }
+ }
+ else
+ out_len = 0;
+
+ ctx_res->padded_size = pad_size;
+ pad_offset = out_len;
+ }
+
+ else
+ {
+ *return_term = EXCP_ERROR(env, "Bad padding flg");
+ goto err;
+ }
+
+ /* Decide how many bytes that are to be returned and set out_len to that value */
+ if (ctx_res->padding == atom_undefined)
+ {
+ out_len = 0;
+ }
+
+ else
+ {
+ if (!EVP_CipherFinal_ex(ctx_res->ctx, out_data_bin.data+pad_offset, &out_len))
+ {
+ if (ctx_res->padding == atom_none)
+ *return_term = EXCP_ERROR(env, "Padding 'none' but unfilled last block");
+ else if (ctx_res->padding == atom_pkcs_padding)
+ *return_term = EXCP_ERROR(env, "Can't finalize");
+ else
+ *return_term = EXCP_ERROR(env, "Padding failed");
+ goto err;
+ }
+ else
+ out_len += pad_offset;
+ }
+
+ /* (end of encryption part) */
+ }
+ else
+ { /* decryption. */
+ /* Decide how many bytes that are to be returned and set out_len to that value */
+ if (ctx_res->padding == atom_undefined)
+ {
+ out_len = 0;
+ }
+
+ else if ((ctx_res->padding == atom_none) ||
+ (ctx_res->padding == atom_pkcs_padding) ||
+ (ctx_res->padding == atom_zero) ||
+ (ctx_res->padding == atom_random) )
+ {
+ if (!EVP_CipherFinal_ex(ctx_res->ctx, out_data_bin.data, &out_len))
+ {
+ *return_term = EXCP_ERROR(env, "Can't finalize");
+ goto err;
+ }
+ }
+ else
+ {
+ *return_term = EXCP_ERROR(env, "Bad padding flg");
+ goto err;
+ }
+ /* (end of decryption part) */
+ }
+ }
+
+ /* success: */
+ if (!enif_realloc_binary(&out_data_bin, (size_t)out_len))
+ {
+ *return_term = EXCP_ERROR(env, "Can't reallocate");
+ goto err;
+ }
+
+ *return_term = enif_make_binary(env, &out_data_bin);
+
+ return 1;
+
+ err:
+ enif_release_binary(&out_data_bin);
+ err0:
+ return 0;
+}
+
+
+/*************************************************************************/
/* Initialize the state for (de/en)cryption */
/*************************************************************************/
ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Cipher, Key, IVec, Encrypt) % if no IV for the Cipher, set IVec = <<>>
+{/* (Cipher, Key, IVec, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>>
*/
struct evp_cipher_ctx *ctx_res = NULL;
const struct cipher_type_t *cipherp;
ERL_NIF_TERM ret;
- int encflg;
if (enif_is_atom(env, argv[0])) {
if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL)
return EXCP_ERROR(env, "Can't allocate resource");
- if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[argc-1],
+ if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[3], argv[4],
&cipherp, &ret))
ret = enif_make_resource(env, ctx_res);
/* else error msg in ret */
@@ -349,16 +588,16 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
} else if (enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) {
/* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
if (argv[3] == atom_true)
- encflg = 1;
+ ctx_res->encflag = 1;
else if (argv[3] == atom_false)
- encflg = 0;
+ ctx_res->encflag = 0;
else {
ret = EXCP_BADARG(env, "Bad enc flag");
goto ret;
}
if (ctx_res->ctx) {
/* It is *not* a ctx_res for the compatibility handling of non-EVP aes_ctr */
- if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, NULL, NULL, encflg)) {
+ if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, NULL, NULL, ctx_res->encflag)) {
ret = EXCP_ERROR(env, "Can't initialize encflag");
goto ret;
}
@@ -378,49 +617,6 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
/* Encrypt/decrypt */
/*************************************************************************/
-#if !defined(HAVE_EVP_CIPHER_CTX_COPY)
-/*
- The EVP_CIPHER_CTX_copy is not available in older cryptolibs although
- the function is needed.
- Instead of implement it in-place, we have a copy here as a compatibility
- function
-*/
-
-int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in);
-
-int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
-{
- if ((in == NULL) || (in->cipher == NULL))
- {
- return 0;
- }
-#ifdef HAS_ENGINE_SUPPORT
- /* Make sure it's safe to copy a cipher context using an ENGINE */
- if (in->engine && !ENGINE_init(in->engine))
- return 0;
-#endif
-
- EVP_CIPHER_CTX_cleanup(out);
- memcpy(out,in,sizeof *out);
-
- if (in->cipher_data && in->cipher->ctx_size)
- {
- out->cipher_data=OPENSSL_malloc(in->cipher->ctx_size);
- if (!out->cipher_data)
- return 0;
- memcpy(out->cipher_data,in->cipher_data,in->cipher->ctx_size);
- }
-
-#if defined(EVP_CIPH_CUSTOM_COPY) && defined(EVP_CTRL_COPY)
- if (in->cipher->flags & EVP_CIPH_CUSTOM_COPY)
- return in->cipher->ctrl((EVP_CIPHER_CTX *)in, EVP_CTRL_COPY, 0, out);
-#endif
- return 1;
-}
-/****** End of compatibility function ******/
-#endif
-
-
ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context, Data [, IV]) */
struct evp_cipher_ctx *ctx_res;
@@ -433,6 +629,7 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return EXCP_BADARG(env, "Bad 1:st arg");
if (argc == 3) {
+ /* We have an IV in this call. Make a copy of the context */
ErlNifBinary ivec_bin;
memcpy(&ctx_res_copy, ctx_res, sizeof ctx_res_copy);
@@ -449,8 +646,6 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
}
}
- ctx_res = &ctx_res_copy;
-
if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin))
{
ret = EXCP_BADARG(env, "Bad iv type");
@@ -483,7 +678,9 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
}
get_update_args(env, &ctx_res_copy, argv[1], &ret);
+ ctx_res->size = ctx_res_copy.size;
} else
+ /* argc != 3, that is, argc = 2 (we don't have an IV in this call) */
get_update_args(env, ctx_res, argv[1], &ret);
err:
@@ -498,8 +695,6 @@ ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
{/* (Context, Data [, IV]) */
ErlNifBinary data_bin;
- ASSERT(argc <= 3);
-
if (!enif_inspect_binary(env, argv[1], &data_bin))
return EXCP_BADARG(env, "expected binary as data");
@@ -517,26 +712,78 @@ ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
}
/*************************************************************************/
+/* Final */
+/*************************************************************************/
+
+ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+ /* No need for enif_schedule_nif since maximum BlockSize-1 bytes are handled */
+ struct evp_cipher_ctx *ctx_res;
+ ERL_NIF_TERM ret;
+
+ if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
+ return EXCP_BADARG(env, "Bad arg");
+
+ get_final_args(env, ctx_res, &ret);
+
+ return ret;
+}
+
+/*************************************************************************/
/* One shot */
/*************************************************************************/
ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Cipher, Key, IVec, Data, Encrypt) */
+{/* (Cipher, Key, IVec, Data, Encrypt, PaddingType) */
struct evp_cipher_ctx ctx_res;
const struct cipher_type_t *cipherp;
ERL_NIF_TERM ret;
-
+ ErlNifBinary out_data_bin, final_data_bin;
+ unsigned char *append_buf;
+
ctx_res.ctx = NULL;
#if !defined(HAVE_EVP_AES_CTR)
ctx_res.env = NULL;
#endif
- if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], &cipherp, &ret))
- goto ret;
+ /* EVP_CipherInit */
+ if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], argv[5], &cipherp, &ret))
+ goto out;
- get_update_args(env, &ctx_res, argv[3], &ret);
+ /* out_data = EVP_CipherUpdate */
+ if (!get_update_args(env, &ctx_res, argv[3], &ret))
+ /* Got an exception as result in &ret */
+ goto out;
- ret:
+ if (!enif_inspect_binary(env, ret, &out_data_bin) )
+ {
+ ret = EXCP_ERROR(env, "Can't inspect first");
+ goto out;
+ }
+
+ /* final_data = EVP_CipherFinal_ex */
+ if (!get_final_args(env, &ctx_res, &ret))
+ /* Got an exception as result in &ret */
+ goto out;
+
+ if (!enif_inspect_binary(env, ret, &final_data_bin) )
+ {
+ ret = EXCP_ERROR(env, "Can't inspect final");
+ goto out;
+ }
+
+ /* Concatenate out_data and final_date into a new binary kept in the variable ret. */
+ append_buf = enif_make_new_binary(env, out_data_bin.size + final_data_bin.size, &ret);
+ if (!append_buf)
+ {
+ ret = EXCP_ERROR(env, "Can't append");
+ goto out;
+ }
+
+ memcpy(append_buf, out_data_bin.data, out_data_bin.size);
+ memcpy(append_buf+out_data_bin.size, final_data_bin.data, final_data_bin.size);
+
+ out:
if (ctx_res.ctx)
EVP_CIPHER_CTX_free(ctx_res.ctx);
@@ -550,12 +797,10 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Cipher, Key, IVec, Data, Encrypt) % if no IV for the Cipher, set IVec = <<>>
+{/* (Cipher, Key, IVec, Data, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>>
*/
ErlNifBinary data_bin;
- ASSERT(argc == 5);
-
if (!enif_inspect_binary(env, argv[3], &data_bin))
return EXCP_BADARG(env, "expected binary as data");
@@ -571,3 +816,37 @@ ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return ng_crypto_one_time(env, argc, argv);
}
+
+
+/*************************************************************************/
+/* Get data from the cipher resource */
+/*************************************************************************/
+
+ERL_NIF_TERM ng_crypto_get_data_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) -> map */
+ struct evp_cipher_ctx *ctx_res;
+ ERL_NIF_TERM ret;
+
+ if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
+ return EXCP_BADARG(env, "Bad arg");
+
+ ret = enif_make_new_map(env);
+
+ enif_make_map_put(env, ret, atom_size,
+ enif_make_int(env, ctx_res->size),
+ &ret);
+
+ enif_make_map_put(env, ret, atom_padding_size,
+ enif_make_int(env, ctx_res->padded_size),
+ &ret);
+
+ enif_make_map_put(env, ret, atom_padding_type,
+ ctx_res->padding,
+ &ret);
+
+ enif_make_map_put(env, ret, atom_encrypt,
+ (ctx_res->encflag) ? atom_true : atom_false,
+ &ret);
+
+ return ret;
+}
diff --git a/lib/crypto/c_src/api_ng.h b/lib/crypto/c_src/api_ng.h
index aaf67524ae..2803ae72ec 100644
--- a/lib/crypto/c_src/api_ng.h
+++ b/lib/crypto/c_src/api_ng.h
@@ -25,6 +25,8 @@
ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM ng_crypto_get_data_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
#endif /* E_AES_H__ */
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index bbeb329fa2..db7ba084ed 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -24,6 +24,9 @@ ERL_NIF_TERM atom_true;
ERL_NIF_TERM atom_false;
ERL_NIF_TERM atom_sha;
ERL_NIF_TERM atom_error;
+ERL_NIF_TERM atom_pkcs_padding;
+ERL_NIF_TERM atom_zero;
+ERL_NIF_TERM atom_random;
ERL_NIF_TERM atom_rsa_pkcs1_padding;
ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding;
ERL_NIF_TERM atom_rsa_no_padding;
@@ -52,6 +55,9 @@ ERL_NIF_TERM atom_block_size;
ERL_NIF_TERM atom_key_length;
ERL_NIF_TERM atom_iv_length;
ERL_NIF_TERM atom_mode;
+ERL_NIF_TERM atom_encrypt;
+ERL_NIF_TERM atom_padding_size;
+ERL_NIF_TERM atom_padding_type;
ERL_NIF_TERM atom_ecb_mode;
ERL_NIF_TERM atom_cbc_mode;
ERL_NIF_TERM atom_cfb_mode;
@@ -89,6 +95,8 @@ ERL_NIF_TERM atom_ecdsa;
#ifdef HAVE_ED_CURVE_DH
ERL_NIF_TERM atom_x25519;
ERL_NIF_TERM atom_x448;
+ERL_NIF_TERM atom_ed25519;
+ERL_NIF_TERM atom_ed448;
#endif
ERL_NIF_TERM atom_eddsa;
@@ -154,6 +162,9 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
atom_sha = enif_make_atom(env,"sha");
atom_error = enif_make_atom(env,"error");
+ atom_pkcs_padding = enif_make_atom(env,"pkcs_padding");
+ atom_zero = enif_make_atom(env,"zero");
+ atom_random = enif_make_atom(env,"random");
atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding");
atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding");
atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding");
@@ -176,6 +187,9 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
atom_key_length = enif_make_atom(env,"key_length");
atom_iv_length = enif_make_atom(env,"iv_length");
atom_mode = enif_make_atom(env,"mode");
+ atom_encrypt = enif_make_atom(env, "encrypt");
+ atom_padding_size = enif_make_atom(env, "padding_size");
+ atom_padding_type = enif_make_atom(env, "padding_type");
atom_ecb_mode = enif_make_atom(env,"ecb_mode");
atom_cbc_mode = enif_make_atom(env,"cbc_mode");
atom_cfb_mode = enif_make_atom(env,"cfb_mode");
@@ -219,6 +233,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
#ifdef HAVE_ED_CURVE_DH
atom_x25519 = enif_make_atom(env,"x25519");
atom_x448 = enif_make_atom(env,"x448");
+ atom_ed25519 = enif_make_atom(env,"ed25519");
+ atom_ed448 = enif_make_atom(env,"ed448");
#endif
atom_eddsa = enif_make_atom(env,"eddsa");
#ifdef HAVE_EDDSA
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 0e2f1a0022..4f77fd9d01 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -28,6 +28,9 @@ extern ERL_NIF_TERM atom_true;
extern ERL_NIF_TERM atom_false;
extern ERL_NIF_TERM atom_sha;
extern ERL_NIF_TERM atom_error;
+extern ERL_NIF_TERM atom_pkcs_padding;
+extern ERL_NIF_TERM atom_zero;
+extern ERL_NIF_TERM atom_random;
extern ERL_NIF_TERM atom_rsa_pkcs1_padding;
extern ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding;
extern ERL_NIF_TERM atom_rsa_no_padding;
@@ -56,6 +59,9 @@ extern ERL_NIF_TERM atom_block_size;
extern ERL_NIF_TERM atom_key_length;
extern ERL_NIF_TERM atom_iv_length;
extern ERL_NIF_TERM atom_mode;
+extern ERL_NIF_TERM atom_encrypt;
+extern ERL_NIF_TERM atom_padding_size;
+extern ERL_NIF_TERM atom_padding_type;
extern ERL_NIF_TERM atom_ecb_mode;
extern ERL_NIF_TERM atom_cbc_mode;
extern ERL_NIF_TERM atom_cfb_mode;
@@ -93,6 +99,8 @@ extern ERL_NIF_TERM atom_ecdsa;
#ifdef HAVE_ED_CURVE_DH
extern ERL_NIF_TERM atom_x25519;
extern ERL_NIF_TERM atom_x448;
+extern ERL_NIF_TERM atom_ed25519;
+extern ERL_NIF_TERM atom_ed448;
#endif
extern ERL_NIF_TERM atom_eddsa;
diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h
index c23e128824..b7cea017cc 100644
--- a/lib/crypto/c_src/cipher.h
+++ b/lib/crypto/c_src/cipher.h
@@ -62,9 +62,13 @@ extern ErlNifResourceType* evp_cipher_ctx_rtype;
struct evp_cipher_ctx {
EVP_CIPHER_CTX* ctx;
int iv_len;
+ ERL_NIF_TERM padding; /* id of the padding to add by get_final_args() */
+ int padded_size; /* Length of the padding that was added */
+ int encflag; /* 1 if encrypting, 0 if decrypting */
+ unsigned int size; /* The sum of all sizes of input texts to get_update_args() */
#if !defined(HAVE_EVP_AES_CTR)
ErlNifEnv* env;
- ERL_NIF_TERM state;
+ ERL_NIF_TERM state; /* Is == atom_undefined if not handling an aes_ctr crypto */
#endif
};
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 802818541b..589f79e672 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -79,10 +79,12 @@ static ErlNifFunc nif_funcs[] = {
{"mac_final_nif", 1, mac_final_nif, 0},
{"cipher_info_nif", 1, cipher_info_nif, 0},
{"aes_ige_crypt_nif", 4, aes_ige_crypt_nif, 0},
- {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0},
+ {"ng_crypto_init_nif", 5, ng_crypto_init_nif, 0},
{"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0},
{"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0},
- {"ng_crypto_one_time_nif", 5, ng_crypto_one_time_nif, 0},
+ {"ng_crypto_final_nif", 1, ng_crypto_final_nif, 0},
+ {"ng_crypto_get_data_nif", 1, ng_crypto_get_data_nif, 0},
+ {"ng_crypto_one_time_nif", 6, ng_crypto_one_time_nif, 0},
{"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0},
{"strong_rand_range_nif", 1, strong_rand_range_nif, 0},
{"rand_uniform_nif", 2, rand_uniform_nif, 0},
@@ -95,7 +97,7 @@ static ErlNifFunc nif_funcs[] = {
{"dh_generate_key_nif", 4, dh_generate_key_nif, 0},
{"dh_compute_key_nif", 3, dh_compute_key_nif, 0},
{"evp_compute_key_nif", 3, evp_compute_key_nif, 0},
- {"evp_generate_key_nif", 1, evp_generate_key_nif, 0},
+ {"evp_generate_key_nif", 2, evp_generate_key_nif, 0},
{"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif, 0},
{"srp_value_B_nif", 5, srp_value_B_nif, 0},
{"srp_user_secret_nif", 7, srp_user_secret_nif, 0},
@@ -130,7 +132,11 @@ ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
static int verify_lib_version(void)
{
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
const unsigned long libv = SSLeay();
+#else
+ const unsigned long libv = OpenSSL_version_num();
+#endif
const unsigned long hdrv = OPENSSL_VERSION_NUMBER;
# define MAJOR_VER(V) ((unsigned long)(V) >> (7*4))
@@ -145,9 +151,11 @@ static int verify_lib_version(void)
static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
{
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
#ifdef OPENSSL_THREADS
ErlNifSysInfo sys_info;
#endif
+#endif
get_crypto_callbacks_t* funcp;
struct crypto_callbacks* ccb;
int nlocks = 0;
@@ -217,6 +225,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
funcp = &get_crypto_callbacks;
#endif
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
#ifdef OPENSSL_THREADS
enif_system_info(&sys_info, sizeof(sys_info));
if (sys_info.scheduler_threads > 1) {
@@ -224,6 +233,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
}
/* else no need for locks */
#endif
+#endif
ccb = (*funcp)(nlocks);
@@ -237,6 +247,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
return __LINE__;
#endif
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
#ifdef OPENSSL_THREADS
if (nlocks > 0) {
CRYPTO_set_locking_callback(ccb->locking_function);
@@ -246,6 +257,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
CRYPTO_set_dynlock_destroy_callback(ccb->dyn_destroy_function);
}
#endif /* OPENSSL_THREADS */
+#endif
init_digest_types(env);
init_mac_types(env);
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c
index 0141ccd840..46b73cd996 100644
--- a/lib/crypto/c_src/crypto_callback.c
+++ b/lib/crypto/c_src/crypto_callback.c
@@ -22,10 +22,16 @@
#include <string.h>
#include <openssl/opensslconf.h>
#include <stdint.h>
-
#include <erl_nif.h>
+
#include "crypto_callback.h"
+#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \
+ ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf)
+
+#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \
+ PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1))
+
#ifdef DEBUG
# define ASSERT(e) \
((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\
@@ -100,8 +106,9 @@ static void crypto_free(void* ptr CCB_FILE_LINE_ARGS)
#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */
-
+#if OPENSSL_VERSION_NUMBER < 0x10100000
static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
+#endif
#include <openssl/crypto.h>
@@ -125,6 +132,7 @@ static INLINE void locking(int mode, ErlNifRWLock* lock)
}
}
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
static void locking_function(int mode, int n, const char *file, int line)
{
locking(mode, lock_vec[n]);
@@ -149,7 +157,7 @@ static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *f
{
enif_rwlock_destroy((ErlNifRWLock*)ptr);
}
-
+#endif /* ^^^^^^^^^^^^ OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) ^^^^^^^^^^^ */
#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */
DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks)
@@ -161,7 +169,8 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks)
&crypto_alloc,
&crypto_realloc,
&crypto_free,
-
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
#ifdef OPENSSL_THREADS
&locking_function,
&id_function,
@@ -169,9 +178,11 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks)
&dyn_lock_function,
&dyn_destroy_function
#endif /* OPENSSL_THREADS */
+#endif
};
if (!is_initialized) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000
#ifdef OPENSSL_THREADS
if (nlocks > 0) {
int i;
@@ -189,12 +200,17 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks)
}
}
#endif
+#endif
is_initialized = 1;
}
return &the_struct;
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+#ifdef OPENSSL_THREADS
err:
return NULL;
+#endif
+#endif
}
#ifdef HAVE_DYNAMIC_CRYPTO_LIB
diff --git a/lib/crypto/c_src/crypto_callback.h b/lib/crypto/c_src/crypto_callback.h
index d46266fd8b..f59165886b 100644
--- a/lib/crypto/c_src/crypto_callback.h
+++ b/lib/crypto/c_src/crypto_callback.h
@@ -34,6 +34,7 @@ struct crypto_callbacks
void (*crypto_free)(void* ptr CCB_FILE_LINE_ARGS);
/* openssl callbacks */
+#if OPENSSL_VERSION_NUMBER < 0x10100000
#ifdef OPENSSL_THREADS
void (*locking_function)(int mode, int n, const char *file, int line);
unsigned long (*id_function)(void);
@@ -44,6 +45,7 @@ struct crypto_callbacks
void (*dyn_destroy_function)(struct CRYPTO_dynlock_value *ptr,
const char *file, int line);
#endif /* OPENSSL_THREADS */
+#endif
};
typedef struct crypto_callbacks* get_crypto_callbacks_t(int nlocks);
diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c
index 3bf66bfffe..fb6495a640 100644
--- a/lib/crypto/c_src/evp.c
+++ b/lib/crypto/c_src/evp.c
@@ -106,25 +106,34 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *pkey = NULL;
ERL_NIF_TERM ret_pub, ret_prv, ret;
+ ErlNifBinary prv_key;
size_t key_len;
unsigned char *out_pub = NULL, *out_priv = NULL;
- ASSERT(argc == 1);
-
if (argv[0] == atom_x25519)
type = EVP_PKEY_X25519;
else if (argv[0] == atom_x448)
type = EVP_PKEY_X448;
+ else if (argv[0] == atom_ed25519)
+ type = EVP_PKEY_ED25519;
+ else if (argv[0] == atom_ed448)
+ type = EVP_PKEY_ED448;
else
goto bad_arg;
- if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL)
- goto bad_arg;
-
- if (EVP_PKEY_keygen_init(ctx) != 1)
- goto err;
- if (EVP_PKEY_keygen(ctx, &pkey) != 1)
- goto err;
+ if (argv[1] == atom_undefined) {
+ if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL)
+ goto bad_arg;
+ if (EVP_PKEY_keygen_init(ctx) != 1)
+ goto err;
+ if (EVP_PKEY_keygen(ctx, &pkey) != 1)
+ goto err;
+ } else {
+ if (!enif_inspect_binary(env, argv[1], &prv_key))
+ goto bad_arg;
+ if ((pkey = EVP_PKEY_new_raw_private_key(type, NULL, prv_key.data, prv_key.size)) == NULL)
+ goto bad_arg;
+ }
if (EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len) != 1)
goto err;
diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h
index 32a0830717..2634d70f16 100644
--- a/lib/crypto/c_src/openssl_config.h
+++ b/lib/crypto/c_src/openssl_config.h
@@ -28,6 +28,7 @@
#include <openssl/des.h>
/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */
+#include <openssl/dh.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
@@ -275,6 +276,7 @@
#if OPENSSL_VERSION_NUMBER <= PACKED_OPENSSL_VERSION(0,9,8,'l')
# define HAVE_ECB_IVEC_BUG
+# define HAVE_UPDATE_EMPTY_DATA_BUG
#endif
#ifndef HAS_LIBRESSL
@@ -294,8 +296,10 @@
/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h
So if EC is disabled, you can't use Engine either....
*/
+#if !defined(OPENSSL_NO_ENGINE)
# define HAS_ENGINE_SUPPORT
#endif
+#endif
#if defined(HAS_ENGINE_SUPPORT)
diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c
index c3bd9dfb55..7f70fdf5fc 100644
--- a/lib/crypto/c_src/otp_test_engine.c
+++ b/lib/crypto/c_src/otp_test_engine.c
@@ -50,8 +50,10 @@
&& !defined(OPENSSL_NO_EC) \
&& !defined(OPENSSL_NO_ECDH) \
&& !defined(OPENSSL_NO_ECDSA)
+#if !defined(OPENSSL_NO_ENGINE)
# define HAVE_EC
#endif
+#endif
#if defined(HAVE_EC)
/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h
diff --git a/lib/crypto/configure.in b/lib/crypto/configure.in
index a3b6673f29..1c36357ebd 100644
--- a/lib/crypto/configure.in
+++ b/lib/crypto/configure.in
@@ -84,7 +84,7 @@ elif test "x$with_ssl_zlib" = "xyes" || test "x$with_ssl_zlib" = "x"; then
AC_MSG_WARN([Cannot search for zlib; missing cross system root (erl_xcomp_sysroot).])
SSL_LINK_WITH_ZLIB=no
STATIC_ZLIB_LIBS=
- elif test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then
+ elif test "$host_os" = "win32"; then
SSL_LINK_WITH_ZLIB=no
STATIC_ZLIB_LIBS=
else
@@ -216,46 +216,18 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
# of Shining Light OpenSSL, which can be found by poking in
# the uninstall section in the registry, it's worth a try...
extra_dir=""
- if test "x$MIXED_CYGWIN" = "xyes"; then
- AC_CHECK_PROG(REGTOOL, regtool, regtool, false)
- if test "$ac_cv_prog_REGTOOL" != false; then
- wrp="/machine/software/microsoft/windows/currentversion/"
- if test "x$ac_cv_sizeof_void_p" = "x8"; then
- urp="uninstall/openssl (64-bit)_is1/inno setup: app path"
- regtool_subsystem=-w
- else
- urp="uninstall/openssl (32-bit)_is1/inno setup: app path"
- regtool_subsystem=-W
- fi
- rp="$wrp$urp"
- if regtool -q $regtool_subsystem get "$rp" > /dev/null; then
- true
- else
- # Fallback to unspecified wordlength
- urp="uninstall/openssl_is1/inno setup: app path"
- rp="$wrp$urp"
- fi
- if regtool -q $regtool_subsystem get "$rp" > /dev/null; then
- ssl_install_dir=`regtool -q $regtool_subsystem get "$rp"`
- # Try hard to get rid of spaces...
- if cygpath -d "$ssl_install_dir" > /dev/null 2>&1; then
- ssl_install_dir=`cygpath -d "$ssl_install_dir"`
- fi
- extra_dir=`cygpath $ssl_install_dir`
- fi
- fi
- elif test "x$MIXED_MSYS" = "xyes"; then
+ if test "$host_os" = "win32"; then
AC_CHECK_PROG(REGTOOL, reg_query.sh, reg_query.sh, false)
if test "$ac_cv_prog_REGTOOL" != false; then
if test "x$ac_cv_sizeof_void_p" = "x8"; then
rp="HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/OpenSSL (64-bit)_is1"
else
rp="HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/OpenSSL_is1"
- fi
+ fi
key="Inno Setup: App Path"
if "$ac_cv_prog_REGTOOL" "$rp" "$key" > /dev/null; then
ssl_install_dir=`"$ac_cv_prog_REGTOOL" "$rp" "$key"`
- extra_dir=`win2msys_path.sh "$ssl_install_dir"`
+ extra_dir=`w32_path.sh -u "$ssl_install_dir"`
fi
fi
fi
@@ -266,12 +238,15 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
SSL_CRYPTO_LIBNAME=crypto
SSL_SSL_LIBNAME=ssl
+ SSL_EXTRA_LIBS=""
- if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then
+ if test "$host_os" = "win32" ; then
if test "x$ac_cv_sizeof_void_p" = "x8"; then
- std_win_ssl_locations="/cygdrive/c/OpenSSL-Win64 /c/OpenSSL-Win64 /opt/local64/pgm/OpenSSL"
+ std_win_ssl_locations="/mnt/c/OpenSSL-Win64 /c/OpenSSL-Win64 /mnt/c/opt/local64/pgm/OpenSSL /opt/local64/pgm/OpenSSL /cygdrive/c/OpenSSL-Win64"
+ lib_bits=64
else
- std_win_ssl_locations="/cygdrive/c/OpenSSL-Win32 /c/OpenSSL-Win32 /cygdrive/c/OpenSSL /c/OpenSSL /opt/local/pgm/OpenSSL"
+ std_win_ssl_locations="/mnt/c/OpenSSL-Win32 /c/OpenSSL-Win32 /mnt/c/OpenSSL /c/OpenSSL /cygdrive/c/OpenSSL /opt/local/pgm/OpenSSL /opt/local32/pgm/OpenSSL /mnt/c/opt/local/pgm/OpenSSL /mnt/c/opt/local32/pgm/OpenSSL /cygdrive/c/OpenSSL-Win32"
+ lib_bits=32
fi
else
std_win_ssl_locations=""
@@ -279,12 +254,12 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
AC_MSG_CHECKING(for OpenSSL >= 0.9.8c in standard locations)
- for rdir in $extra_dir $std_win_ssl_locations $std_ssl_locations; do
+ for rdir in "$extra_dir" $std_win_ssl_locations $std_ssl_locations; do
dir="$erl_xcomp_sysroot$rdir"
if test -f "$erl_xcomp_isysroot$rdir/include/openssl/opensslv.h"; then
is_real_ssl=yes
SSL_INCDIR="$dir"
- if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then
+ if test "$host_os" = "win32" ; then
if test -f "$dir/lib/VC/libeay32.lib"; then
SSL_RUNTIME_LIBDIR="$rdir/lib/VC"
SSL_LIBDIR="$dir/lib/VC"
@@ -293,11 +268,11 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
elif test -f "$dir/lib/VC/openssl.lib"; then
SSL_RUNTIME_LIBDIR="$rdir/lib/VC"
SSL_LIBDIR="$dir/lib/VC"
- elif test -f $dir/lib/VC/libeay32MD.lib; then
+ elif test -f "$dir/lib/VC/libeay32MD.lib"; then
SSL_CRYPTO_LIBNAME=libeay32MD
SSL_SSL_LIBNAME=ssleay32MD
if test "x$enable_dynamic_ssl" = "xno" && \
- test -f $dir/lib/VC/static/libeay32MD.lib; then
+ test -f "$dir/lib/VC/static/libeay32MD.lib"; then
SSL_RUNTIME_LIBDIR="$rdir/lib/VC/static"
SSL_LIBDIR="$dir/lib/VC/static"
else
@@ -309,6 +284,19 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
SSL_LIBDIR="$dir/lib"
SSL_CRYPTO_LIBNAME=libeay32
SSL_SSL_LIBNAME=ssleay32
+ elif test -f "$dir/lib/VC/libcrypto${lib_bits}MD.lib"; then
+ SSL_CRYPTO_LIBNAME=libcrypto${lib_bits}MD
+ # NOTE: Additional ugly extra libs at the end
+ SSL_SSL_LIBNAME="libssl${lib_bits}MD"
+ if test "x$enable_dynamic_ssl" = "xno" && \
+ test -f "$dir/lib/VC/static/$SSL_CRYPTO_LIBNAME.lib"; then
+ SSL_EXTRA_LIBS="-lCRYPT32 -lWS2_32"
+ SSL_RUNTIME_LIBDIR="$rdir/lib/VC/static"
+ SSL_LIBDIR="$dir/lib/VC/static"
+ else
+ SSL_RUNTIME_LIBDIR="$rdir/lib/VC"
+ SSL_LIBDIR="$dir/lib/VC"
+ fi
elif test -f "$dir/lib/openssl.lib"; then
SSL_RUNTIME_LIBDIR="$rdir/lib"
SSL_LIBDIR="$dir/lib"
@@ -352,7 +340,7 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
SSL_INCLUDE="-I$dir/include"
old_CPPFLAGS=$CPPFLAGS
CPPFLAGS=$SSL_INCLUDE
- AC_EGREP_CPP(^yes$,[
+ AC_EGREP_CPP(^yes.?$,[
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x0090803fL
yes
@@ -365,7 +353,7 @@ yes
])
CPPFLAGS=$old_CPPFLAGS
if test "x$ssl_found" = "xyes"; then
- if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then
+ if test "x$host_os" = "xwin32" ; then
ssl_linkable=yes
elif test "x${SSL_CRYPTO_LIBNAME}" = "xsslcrypto"; then
# This should only be triggered seen OSE
@@ -485,7 +473,8 @@ dnl so it is - be adoptable
SSL_INCDIR="$with_ssl_incl"
SSL_CRYPTO_LIBNAME=crypto
SSL_SSL_LIBNAME=ssl
- if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes" && test -d "$with_ssl/lib/VC"; then
+ SSL_EXTRA_LIBS=""
+ if test "x$host_os" = "xwin32" && test -d "$with_ssl/lib/VC"; then
if test -f "$with_ssl/lib/VC/libeay32.lib"; then
SSL_LIBDIR="$with_ssl/lib/VC"
SSL_CRYPTO_LIBNAME=libeay32
@@ -500,15 +489,33 @@ dnl so it is - be adoptable
SSL_LIBDIR="$with_ssl/lib/VC/static"
else
SSL_LIBDIR="$with_ssl/lib/VC"
- fi
+ fi
+ elif test -f "$dir/lib/VC/libcrypto${lib_bits}MD.lib"; then
+ SSL_CRYPTO_LIBNAME=libcrypto${lib_bits}MD
+ # NOTE: Additional ugly extra libs at the end
+ SSL_SSL_LIBNAME="libssl${lib_bits}MD"
+ if test "x$enable_dynamic_ssl" = "xno" && \
+ test -f "$dir/lib/VC/static/$SSL_CRYPTO_LIBNAME.lib"; then
+ SSL_EXTRA_LIBS="-lCRYPT32 -lWS2_32"
+ SSL_RUNTIME_LIBDIR="$rdir/lib/VC/static"
+ SSL_LIBDIR="$dir/lib/VC/static"
+ else
+ SSL_RUNTIME_LIBDIR="$rdir/lib/VC"
+ SSL_LIBDIR="$dir/lib/VC"
+ fi
elif test -f "$with_ssl/lib/libeay32.lib"; then
SSL_LIBDIR="$with_ssl/lib"
SSL_CRYPTO_LIBNAME=libeay32
SSL_SSL_LIBNAME=ssleay32
+ elif test -f "$dir/lib/openssl.lib"; then
+ SSL_RUNTIME_LIBDIR="$rdir/lib"
+ SSL_LIBDIR="$dir/lib"
+ SSL_CRYPTO_LIBNAME=libcrypto
+ SSL_SSL_LIBNAME=openssl
else
# This probably wont work, but that's what the user said, so...
SSL_LIBDIR="$with_ssl/lib"
- fi
+ fi
elif test -f "$dir/lib/powerpc/libsslcrypto.a"; then
SSL_CRYPTO_LIBNAME=sslcrypto
SSL_LIBDIR="$with_ssl/lib/powerpc/"
@@ -530,7 +537,7 @@ dnl so it is - be adoptable
if test '!' -f "${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.a"; then
SSL_DYNAMIC_ONLY=yes
elif test '!' -f ${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.so -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then
- SSL_STATIC_ONLY=yes
+ SSL_STATIC_ONLY=yes
fi
SSL_INCLUDE="-I$with_ssl_incl/include"
SSL_APP=ssl
@@ -764,6 +771,7 @@ AC_SUBST(SSL_LIBDIR)
AC_SUBST(SSL_FLAGS)
AC_SUBST(SSL_CRYPTO_LIBNAME)
AC_SUBST(SSL_SSL_LIBNAME)
+AC_SUBST(SSL_EXTRA_LIBS)
AC_SUBST(SSL_DED_LD_RUNTIME_LIBRARY_PATH)
AC_SUBST(SSL_DYNAMIC_ONLY)
AC_SUBST(SSL_LINK_WITH_KERBEROS)
diff --git a/lib/crypto/doc/src/Makefile b/lib/crypto/doc/src/Makefile
index 8da494dad6..5460129c64 100644
--- a/lib/crypto/doc/src/Makefile
+++ b/lib/crypto/doc/src/Makefile
@@ -27,11 +27,6 @@ VSN=$(CRYPTO_VSN)
APPLICATION=crypto
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -47,78 +42,8 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES =
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+IMAGE_FILES =
TOP_SPECS_FILE = specs.xml
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-#in ssh it looks like this: SPECS_FLAGS = -I../../../public_key/include -I../../../public_key/src -I../../..
-
-# ----------------------------------------------------
-# 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) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt valgrind:
-
-clean clean_docs clean_tex:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECS_FILES)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index ff7479f605..e6c4bba129 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -215,6 +215,42 @@
</desc>
</datatype>
+ <datatype>
+ <name name="crypto_opts"/>
+ <name name="crypto_opt"/>
+ <desc>
+ <p>Selects encryption (<c>{encrypt,true}</c>) or decryption (<c>{encrypt,false}</c>)
+ in the <seealso marker="crypto:new_api#the-new-api"><i>New API</i></seealso>.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="padding"/>
+ <desc>
+ <p>This option handles padding in the last block. If not set, no padding is done
+ and any bytes in the last unfilled block is silently discarded.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="cryptolib_padding"/>
+ <desc>
+ <p>The <c>cryptolib_padding</c> are paddings that may be present in the underlying cryptolib
+ linked to the Erlang/OTP crypto app.
+ </p>
+ <p>For OpenSSL, see the <url href="http:www.openssl.org">OpenSSL documentation</url>.
+ and find <c>EVP_CIPHER_CTX_set_padding()</c> in cryptolib for your linked version.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="otp_padding"/>
+ <desc>
+ <p>Erlang/OTP adds a either padding of zeroes or padding with random bytes.</p>
+ </desc>
+ </datatype>
+
<datatype_title>Ciphers, old API</datatype_title>
<datatype>
<name name="block_cipher_with_iv"/>
@@ -647,7 +683,7 @@
<tag><c>error</c></tag>
<item><p>An error condition that should not occur, for example a memory allocation failed or
the underlying cryptolib returned an error code, for example "Can't initialize context, step 1".
- Thoose text usually needs searching the C-code to be understood.</p>
+ Those text usually needs searching the C-code to be understood.</p>
</item>
</taglist>
<p>To catch the exception, use for example:</p>
@@ -674,7 +710,12 @@
<name name="crypto_init" arity="3" since="OTP 22.0"/>
<fsummary>Initializes a series of encryptions or decryptions</fsummary>
<desc>
- <p>As <seealso marker="#crypto_init/4">crypto_init/4</seealso> but for ciphers without IVs.</p>
+ <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
+ </p>
+ <p>Equivalent to the call
+ <seealso marker="#crypto_init/4"><c>crypto_init(Cipher, Key, &lt;&lt;>>, FlagOrOptions)</c></seealso>.
+ It is intended for ciphers without an IV (nounce).
+ </p>
</desc>
</func>
@@ -685,10 +726,48 @@
<p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
Initializes a series of encryptions or decryptions and creates an internal state
with a reference that is returned.
+ </p>
+ <p>If <c>IV = &lt;&lt;>></c>, no IV is used. This is intended for ciphers without an IV (nounce).
+ See <seealso marker="#crypto_init/3">crypto_init/3</seealso>.
+ </p>
+ <p>
+ If <c>IV = undefined</c>, the IV must be added by calls to
+ <seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso>. This is intended
+ for cases where the IV (nounce) need to be changed for each encryption and decryption.
+ See <seealso marker="#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso>.
+ </p>
+ <p>
The actual encryption or decryption is done by
- <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso>.
+ <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso> (or
+ <seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso>
+ ).
+ </p>
+ <p>For encryption, set the <c>FlagOrOptions</c> to <c>true</c> or <c>[{encrypt,true}]</c>.
+ For decryption, set it to <c>false</c> or <c>[{encrypt,false}]</c>.
+ </p>
+ <p>Padding could be enabled with the option
+ <seealso marker="#type-padding">{padding,Padding}</seealso>. The
+ <seealso marker="#type-cryptolib_padding">cryptolib_padding</seealso> enables
+ <c>pkcs_padding</c> or no padding (<c>none</c>).
+ The paddings <c>zero</c> or <c>random</c> fills the last part of the last block with zeroes or random bytes.
+ If the last block is already full, nothing is added.
+ </p>
+ <p>In decryption, the <seealso marker="#type-cryptolib_padding">cryptolib_padding</seealso> removes
+ such padding, if present.
+ The <seealso marker="#type-otp_padding">otp_padding</seealso> is not
+ removed - it has to be done elsewhere.
+ </p>
+ <p>If padding is <c>{padding,none}</c> or not specifed and the total data from all subsequent
+ <seealso marker="crypto#crypto_update/2">crypto_updates</seealso> does
+ not fill the last block fully, that last data is lost. In case of <c>{padding,none}</c> there will
+ be an error in this case. If padding is not specified, the bytes of the unfilled block is silently
+ discarded.
+ </p>
+ <p>The actual padding is performed by
+ <seealso marker="crypto#crypto_final/1">crypto_final/1</seealso>.
</p>
- <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>.
+ <p>
+ For blocksizes call <seealso marker="#cipher_info/1">cipher_info/1</seealso>.
</p>
<p>See <seealso marker="crypto:new_api#examples-of-crypto_init-4-and-crypto_update-2">
examples in the User's Guide.</seealso>
@@ -720,12 +799,59 @@
<fsummary>Initializes a series of encryptions or decryptions where the IV is provided later</fsummary>
<desc>
<p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
- Initializes a series of encryptions or decryptions where the IV is provided later.
+ </p>
+ <p>Initializes a series of encryptions or decryptions where the IV is provided later.
The actual encryption or decryption is done by
<seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso>.
</p>
- <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>.
+ <p>The function is equivalent to
+ <seealso marker="#crypto_init/4"><c>crypto_init(Cipher, Key, undefined, FlagOrOptions)</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crypto_final" arity="1" since=""/>
+ <fsummary>Ends a series of encryptions or decryptions</fsummary>
+ <desc>
+ <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
+ </p>
+ <p>
+ Finalizes a series of encryptions or decryptions and delivers the final bytes of the final block.
+ The data returned from this function may be empty if no padding was enabled in
+ <seealso marker="#crypto_init/3">crypto_init/3,4</seealso> or
+ <seealso marker="#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crypto_get_data" arity="1" since=""/>
+ <fsummary>Get information about crypto states</fsummary>
+ <desc>
+ <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
+ </p>
+ <p>
+ Returns information about the State in the argument. The information is the form of a map,
+ which currently contains at least:
</p>
+ <taglist>
+ <tag><c>size</c></tag>
+ <item>The number of bytes encrypted or decrypted so far.
+ </item>
+ <tag><c>padding_size</c></tag>
+ <item>After a call to
+ <seealso marker="#crypto_final/1">crypto_final/1</seealso> it contains
+ the number of bytes padded. Otherwise 0.
+ </item>
+ <tag><c>padding_type</c></tag>
+ <item>The type of the padding as provided in the call ot
+ <seealso marker="#crypto_init/3">crypto_init/3,4</seealso>.
+ </item>
+ <tag><c>encrypt</c></tag>
+ <item>Is <c>true</c> if encryption is performed. It is <c>false</c> otherwise.
+ </item>
+ </taglist>
</desc>
</func>
@@ -756,7 +882,9 @@
<p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>.
Do a complete encrypt or decrypt of the full text in the argument <c>Data</c>.
</p>
- <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>.
+ <p>For encryption, set the <c>FlagOrOptions</c> to <c>true</c>. For decryption, set it to <c>false</c>.
+ For setting other options, see
+ <seealso marker="crypto#crypto_init/4">crypto_init/4</seealso>.
</p>
<p>See <seealso marker="crypto:new_api#example-of-crypto_one_time-5">examples in the User's Guide.</seealso>
</p>
diff --git a/lib/crypto/doc/src/insidecover.xml b/lib/crypto/doc/src/insidecover.xml
deleted file mode 100644
index bf2427afdf..0000000000
--- a/lib/crypto/doc/src/insidecover.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE bookinsidecover SYSTEM "bookinsidecover.dtd">
-
-<bookinsidecover>
-
- The Erlang/OTP SSL application includes software developed by the
- OpenSSL Project for use in the OpenSSL Toolkit
- (http://www.openssl.org/). Copyright (c) 1998-2002 The OpenSSL
- Project. All rights reserved.
-
- <br/>
- This product includes cryptographic software written by Eric Young
- (eay@cryptsoft.com). This product includes software written by Tim
- Hudson (tjh@cryptsoft.com). Copyright (C) 1995-1998 Eric Young
- (eay@cryptsoft.com). All rights reserved.
-
- <br/>
- For further OpenSSL and SSLeay license information se the chapter
- <bold>Licenses</bold>.
-
- <vfill/>
- <br/>
- <tt>http://www.erlang.org</tt>
- <br/>
-</bookinsidecover>
-
diff --git a/lib/crypto/doc/src/new_api.xml b/lib/crypto/doc/src/new_api.xml
index aacf5e4f76..009f29584f 100644
--- a/lib/crypto/doc/src/new_api.xml
+++ b/lib/crypto/doc/src/new_api.xml
@@ -59,6 +59,8 @@
<item><seealso marker="crypto#stream_init-2">stream_init/3</seealso></item>
<item><seealso marker="crypto#stream_encrypt-2">stream_encrypt/2</seealso></item>
<item><seealso marker="crypto#stream_decrypt-2">stream_decrypt/2</seealso></item>
+ <item><seealso marker="crypto#next_iv-2">next_iv/2</seealso></item>
+ <item><seealso marker="crypto#next_iv-3">next_iv/3</seealso></item>
</list>
<p>for lists of supported algorithms:</p>
<list>
@@ -76,7 +78,7 @@
<item><seealso marker="crypto#hmac_final_n-2">hmac_final_n/2</seealso></item>
<item><seealso marker="crypto#poly1305-2">poly1305/2</seealso></item>
</list>
- <p>They are not deprecated for now, but may be in a future release.
+ <p>They are deprecated from 23.0 and for removal in 24.0.
</p>
</section>
@@ -108,6 +110,7 @@
<item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item>
<item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item>
<item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item>
+ <item><seealso marker="crypto#crypto_final/1">crypto_final/1</seealso></item>
</list>
<p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of
<c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers
@@ -122,6 +125,10 @@
<item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item>
</list>
<p>An example of where those functions are needed, is when handling the TLS protocol.</p>
+ <p>If padding was not enabled, the call to
+ <seealso marker="crypto#crypto_final/1">crypto_final/1</seealso>
+ may be excluded.
+ </p>
<p>For information about available algorithms, use:
</p>
<list>
@@ -129,6 +136,12 @@
<item><seealso marker="crypto#hash_info-1">hash_info/1</seealso></item>
<item><seealso marker="crypto#cipher_info-1">cipher_info/1</seealso></item>
</list>
+
+ <p>The <seealso marker="crypto#next_iv-2">next_iv/2</seealso> and
+ <seealso marker="crypto#next_iv-3">next_iv/3</seealso> is not needed since the
+ <c>crypto_init</c> and <c>crypto_update</c> includes this functionality.
+ </p>
+
</section>
<section>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index b19fdadfcd..1557d80db4 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -38,17 +38,170 @@
-export([rand_plugin_uniform/2]).
-export([rand_cache_plugin_next/1]).
-export([rand_uniform/2]).
--export([next_iv/2, next_iv/3]).
-export([public_encrypt/4, private_decrypt/4]).
-export([private_encrypt/4, public_decrypt/4]).
-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
+%%%----------------------------------------------------------------
+%% Removed functions.
+%%
+-removed([{rand_bytes,1,"use crypto:strong_rand_bytes/1 instead"}]).
+
+-removed([{md4,1,"use crypto:hash/2 instead"}]).
+-removed([{md5,1,"use crypto:hash/2 instead"}]).
+-removed([{sha,1,"use crypto:hash/2 instead"}]).
+
+-removed([{md4_init,0,"use crypto:hash_init/1 instead"}]).
+-removed([{md5_init,0,"use crypto:hash_init/1 instead"}]).
+-removed([{sha_init,0,"use crypto:hash_init/1 instead"}]).
+
+-removed([{md4_update,2,"use crypto:hash_update/2 instead"}]).
+-removed([{md5_update,2,"use crypto:hash_update/2 instead"}]).
+-removed([{sha_update,2,"use crypto:hash_update/2 instead"}]).
+
+-removed([{md4_final,1,"use crypto:hash_final/1 instead"}]).
+-removed([{md5_final,1,"use crypto:hash_final/1 instead"}]).
+-removed([{sha_final,1,"use crypto:hash_final/1 instead"}]).
+
+-removed([{md5_mac,2,"use crypto:hmac/3 instead"}]).
+-removed([{md5_mac_96,2,"use crypto:hmac/4 instead"}]).
+
+-removed([{sha_mac,2,"use crypto:hmac/3 instead"}]).
+-removed([{sha_mac,3,"use crypto:hmac/4 instead"}]).
+-removed([{sha_mac_96,2,"use crypto:hmac/4 instead"}]).
+
+-removed([{rsa_sign,'_',"use crypto:sign/4 instead"}]).
+-removed([{rsa_verify,'_',"use crypto:verify/5 instead"}]).
+
+-removed([{dss_sign,'_',"use crypto:sign/4 instead"}]).
+-removed([{dss_verify,'_',"use crypto:verify/5 instead"}]).
+
+-removed([{mod_exp,3,"use crypto:mod_pow/3 instead"}]).
+
+-removed([{dh_compute_key,3,"use crypto:compute_key/4 instead"}]).
+-removed([{dh_generate_key,1,"use crypto:generate_key/2 instead"}]).
+-removed([{dh_generate_key,2,"use crypto:generate_key/3 instead"}]).
+
+%% DES
+
+-removed([{des_cfb_ivec,2,"use crypto:next_iv/3 instead"}]).
+-removed([{des_cbc_ivec,2,"use crypto:next_iv/2 instead"}]).
+
+-removed([{des_ecb_encrypt,2,"use crypto:block_encrypt/3 instead"}]).
+-removed([{des_cbc_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{des_ede3_cbc_encrypt,5,"use crypto:block_encrypt/4 instead"}]).
+-removed([{des_cfb_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+
+-removed([{des_ecb_decrypt,2,"use crypto:block_decrypt/3 instead"}]).
+-removed([{des_cbc_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{des_cfb_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+
+%% Triple-DES
+
+-removed([{des3_cbc_encrypt,5,"use crypto:block_encrypt/4 instead"}]).
+-removed([{des3_cfb_encrypt,5,"use crypto:block_encrypt/4 instead"}]).
+
+-removed([{des3_cbc_decrypt,5,"use crypto:block_decrypt/4 instead"}]).
+-removed([{des3_cfb_decrypt,5,"use crypto:block_decrypt/4 instead"}]).
+-removed([{des3_ede3_cbc_decrypt,5,"use crypto:block_decrypt/4 instead"}]).
+
+%% Blowfish
+
+-removed([{blowfish_ecb_encrypt,2,"use crypto:block_encrypt/3 instead"}]).
+-removed([{blowfish_cbc_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{blowfish_cfb64_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{blowfish_ofb64_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+
+-removed([{blowfish_ecb_decrypt,2,"use crypto:block_decrypt/3 instead"}]).
+-removed([{blowfish_cbc_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{blowfish_cfb64_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{blowfish_ofb64_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+
+%% AES
+
+-removed([{aes_cfb_128_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{aes_cbc_128_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{aes_cbc_256_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+
+-removed([{aes_cfb_128_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{aes_cbc_128_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{aes_cbc_256_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+
+-removed([{aes_ctr_stream_init,2,"use crypto:stream_init/3 instead"}]).
+-removed([{aes_ctr_stream_encrypt,2,"use crypto:stream_encrypt/2 instead"}]).
+-removed([{aes_ctr_encrypt,3,"use crypto:stream_encrypt/2 instead"}]).
+-removed([{aes_ctr_stream_decrypt,2,"use crypto:stream_decrypt/2 instead"}]).
+-removed([{aes_ctr_decrypt,3,"use crypto:stream_decrypt/2 instead"}]).
+
+-removed([{aes_cbc_ivec,2,"use crypto:next_iv/2 instead"}]).
+
+%% RC2
+
+-removed([{rc2_cbc_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+-removed([{rc2_40_cbc_encrypt,3,"use crypto:block_encrypt/4 instead"}]).
+
+-removed([{rc2_cbc_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+-removed([{rc2_40_cbc_decrypt,3,"use crypto:block_decrypt/4 instead"}]).
+
+%% RC4
+
+-removed([{rc4_set_key,2,"use crypto:stream_init/2 instead"}]).
+-removed([{rc4_encrypt,2,"use crypto:stream_encrypt/2 instead"}]).
+-removed([{rc4_encrypt_with_state,2,"use crypto:stream_encrypt/2 instead"}]).
+
+%% Other
+
+-removed([{info,0,"use crypto:module_info/0 instead"}]).
+
+-removed([{strong_rand_mpint,3,"only needed by other removed functions"}]).
+-removed([{erlint,1,"only needed by other removed functions"}]).
+-removed([{mpint,1,"only needed by other removed functions"}]).
+
+%%%----------------------------------------------------------------
%% Old interface. Now implemented with the New interface
+
+-deprecated([{next_iv, '_',
+ "see the 'New and Old API' chapter of the CRYPTO User's guide"}]).
+-export([next_iv/2, next_iv/3]).
+
+-deprecated([{hmac, 3, "use crypto:mac/4 instead"},
+ {hmac, 4, "use crypto:macN/5 instead"},
+ {hmac_init, 2, "use crypto:mac_init/3 instead"},
+ {hmac_update, 2, "use crypto:mac_update/2 instead"},
+ {hmac_final, 1, "use crypto:mac_final/1 instead"},
+ {hmac_final_n, 2, "use crypto:mac_finalN/2 instead"}]).
+
-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
+
+-deprecated([{cmac, 3, "use crypto:mac/4 instead"},
+ {cmac, 4, "use crypto:macN/5 instead"}]).
-export([cmac/3, cmac/4]).
+
+-deprecated([{poly1305, 2, "use crypto:mac/3 instead"}]).
-export([poly1305/2]).
+
+-deprecated([{stream_init, '_',
+ "use crypto:crypto_init/3 + crypto:crypto_update/2 + "
+ "crypto:crypto_final/1 or crypto:crypto_one_time/4 instead"},
+ {stream_encrypt, 2, "use crypto:crypto_update/2 instead"},
+ {stream_decrypt, 2, "use crypto:crypto_update/2 instead"},
+ {block_encrypt, 3,
+ "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + "
+ "crypto:crypto_update/2 + crypto:crypto_final/1 instead"},
+ {block_encrypt, 4,
+ "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 "
+ "or crypto:crypto_(dyn_iv)?_init + "
+ "crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead"},
+ {block_decrypt, 3,
+ "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + "
+ "crypto:crypto_update/2 + crypto:crypto_final/1 instead"},
+ {block_decrypt, 4,
+ "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 "
+ "or crypto:crypto_(dyn_iv)?_init + "
+ "crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead"}
+ ]).
-export([stream_init/2, stream_init/3,
stream_encrypt/2,
stream_decrypt/2,
@@ -56,19 +209,25 @@
block_decrypt/3, block_decrypt/4
]).
+%%%----------------------------------------------------------------
%% New interface
-export([crypto_init/4, crypto_init/3,
crypto_update/2,
+
crypto_one_time/4, crypto_one_time/5,
crypto_one_time_aead/6, crypto_one_time_aead/7,
+
crypto_dyn_iv_init/3,
crypto_dyn_iv_update/3,
+ crypto_final/1,
+ crypto_get_data/1,
+
supports/1,
mac/3, mac/4, macN/4, macN/5,
mac_init/2, mac_init/3, mac_update/2, mac_final/1, mac_finalN/2
]).
-
+%%%----------------------------------------------------------------
%% Engine
-export([
engine_get_all_methods/0,
@@ -121,7 +280,7 @@
get_test_engine/0]).
-export([rand_plugin_aes_jump_2pow20/1]).
--deprecated({rand_uniform, 2, next_major_release}).
+-deprecated({rand_uniform, 2, "use rand:rand_uniform/1 instead"}).
%% This should correspond to the similar macro in crypto.c
-define(MAX_BYTES_TO_NIF, 20000). %% Current value is: erlang:system_info(context_reductions) * 10
@@ -923,7 +1082,7 @@ block_encrypt(Type, Key0, Ivec, Data) ->
{AAD, PlainText, TagLength} ->
crypto_one_time_aead(alias(Type,Key), Key, Ivec, PlainText, AAD, TagLength, true);
PlainText ->
- crypto_one_time(alias(Type,Key), Key, Ivec, PlainText, true)
+ block_crypt(alias(Type,Key), Key, Ivec, PlainText, true)
end).
-spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) ->
@@ -931,7 +1090,7 @@ block_encrypt(Type, Key0, Ivec, Data) ->
block_encrypt(Type, Key0, PlainText) ->
Key = iolist_to_binary(Key0),
- ?COMPAT(crypto_one_time(alias(Type,Key), Key, PlainText, true)).
+ ?COMPAT(block_crypt(alias(Type,Key), Key, undefined, PlainText, true)).
%%%----------------------------------------------------------------
@@ -952,7 +1111,7 @@ block_decrypt(Type, Key0, Ivec, Data) ->
{AAD, CryptoText, Tag} ->
crypto_one_time_aead(alias(Type,Key), Key, Ivec, CryptoText, AAD, Tag, false);
CryptoText ->
- crypto_one_time(alias(Type,Key), Key, Ivec, CryptoText, false)
+ block_crypt(alias(Type,Key), Key, Ivec, CryptoText, false)
end).
@@ -961,7 +1120,16 @@ block_decrypt(Type, Key0, Ivec, Data) ->
block_decrypt(Type, Key0, CryptoText) ->
Key = iolist_to_binary(Key0),
- ?COMPAT(crypto_one_time(alias(Type,Key), Key, CryptoText, false)).
+ ?COMPAT(block_crypt(alias(Type,Key), Key, undefined, CryptoText, false)).
+
+
+
+block_crypt(Cipher, Key, IV, Data, EncryptFlag) ->
+ Ctx = case IV of
+ undefined -> crypto_init(Cipher, Key, EncryptFlag);
+ _ -> crypto_init(Cipher, Key, IV, EncryptFlag)
+ end,
+ crypto_update(Ctx, Data).
%%%-------- Stream ciphers API
@@ -979,7 +1147,7 @@ stream_init(Type, Key0, IVec) when is_binary(IVec) ->
Key = iolist_to_binary(Key0),
Ref = ?COMPAT(ng_crypto_init_nif(alias(Type,Key),
Key, iolist_to_binary(IVec),
- undefined)
+ get_crypto_opts([{encrypt,undefined}]))
),
{Type, {Ref,flg_undefined}}.
@@ -992,7 +1160,7 @@ stream_init(rc4 = Type, Key0) ->
Key = iolist_to_binary(Key0),
Ref = ?COMPAT(ng_crypto_init_nif(alias(Type,Key),
Key, <<>>,
- undefined)
+ get_crypto_opts([{encrypt,undefined}]))
),
{Type, {Ref,flg_undefined}}.
@@ -1017,7 +1185,8 @@ stream_decrypt(State, Data) ->
%%%-------- helpers
crypto_stream_emulate({Cipher,{Ref0,flg_undefined}}, Data, EncryptFlag) when is_reference(Ref0) ->
?COMPAT(begin
- Ref = ng_crypto_init_nif(Ref0, <<>>, <<>>, EncryptFlag),
+ Ref = ng_crypto_init_nif(Ref0, <<>>, <<>>,
+ get_crypto_opts([{encrypt,EncryptFlag}])),
{{Cipher,Ref}, crypto_update(Ref, Data)}
end);
@@ -1059,42 +1228,83 @@ next_iv(Type, Data, _Ivec) ->
-opaque crypto_state() :: reference() .
+-type crypto_opts() :: boolean()
+ | [ crypto_opt() ] .
+-type crypto_opt() :: {encrypt,boolean()}
+ | {padding, padding()} .
+-type padding() :: cryptolib_padding() | otp_padding().
+-type cryptolib_padding() :: none | pkcs_padding .
+-type otp_padding() :: zero | random .
+
%%%----------------------------------------------------------------
%%%
%%% Create and initialize a new state for encryption or decryption
%%%
--spec crypto_init(Cipher, Key, EncryptFlag) -> State | descriptive_error()
+-spec crypto_init(Cipher, Key, FlagOrOptions) -> State | descriptive_error()
when Cipher :: cipher_no_iv(),
Key :: iodata(),
- EncryptFlag :: boolean(),
+ FlagOrOptions :: crypto_opts() | boolean(),
State :: crypto_state() .
-crypto_init(Cipher, Key, EncryptFlag) ->
- %% The IV is supposed to be supplied by calling crypto_update/3
- ng_crypto_init_nif(Cipher, iolist_to_binary(Key), <<>>, EncryptFlag).
+crypto_init(Cipher, Key, FlagOrOptions) ->
+ crypto_init(Cipher, Key, <<>>, FlagOrOptions).
--spec crypto_init(Cipher, Key, IV, EncryptFlag) -> State | descriptive_error()
+-spec crypto_init(Cipher, Key, IV, FlagOrOptions) -> State | descriptive_error()
when Cipher :: cipher_iv(),
Key :: iodata(),
IV :: iodata(),
- EncryptFlag :: boolean(),
+ FlagOrOptions :: crypto_opts() | boolean(),
State :: crypto_state() .
-crypto_init(Cipher, Key, IV, EncryptFlag) ->
- ng_crypto_init_nif(Cipher, iolist_to_binary(Key), iolist_to_binary(IV), EncryptFlag).
+crypto_init(Cipher, Key, IV, FlagOrOptions) ->
+ ng_crypto_init_nif(Cipher,
+ iolist_to_binary(Key),
+ iolist_to_binary(IV),
+ get_crypto_opts(FlagOrOptions)).
+
+%%%----------------------------------------------------------------
+get_crypto_opts(Options) when is_list(Options) ->
+ lists:foldl(fun chk_opt/2,
+ #{encrypt => true,
+ padding => undefined
+ },
+ Options);
+get_crypto_opts(Flag) when is_boolean(Flag) ->
+ #{encrypt => Flag,
+ padding => undefined
+ };
+get_crypto_opts(X) ->
+ error({badarg,{bad_option,X}}).
+
+
+chk_opt({Tag,Val}, A) ->
+ case ok_opt(Tag,Val) of
+ true ->
+ A#{Tag => Val};
+ false ->
+ error({badarg,{bad_option,{Tag,Val}}})
+ end;
+chk_opt(X, _) ->
+ error({badarg,{bad_option,X}}).
+ok_opt(encrypt, V) -> lists:member(V, [true, false, undefined]);
+ok_opt(padding, V) -> lists:member(V, [none, pkcs_padding, zero, random, undefined]);
+ok_opt(_, _) -> false.
%%%----------------------------------------------------------------
--spec crypto_dyn_iv_init(Cipher, Key, EncryptFlag) -> State | descriptive_error()
+-spec crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> State | descriptive_error()
when Cipher :: cipher_iv(),
Key :: iodata(),
- EncryptFlag :: boolean(),
+ FlagOrOptions :: crypto_opts() | boolean(),
State :: crypto_state() .
-crypto_dyn_iv_init(Cipher, Key, EncryptFlag) ->
+crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) ->
%% The IV is supposed to be supplied by calling crypto_update/3
- ng_crypto_init_nif(Cipher, iolist_to_binary(Key), undefined, EncryptFlag).
+ ng_crypto_init_nif(Cipher,
+ iolist_to_binary(Key),
+ undefined,
+ get_crypto_opts(FlagOrOptions)).
%%%----------------------------------------------------------------
%%%
@@ -1107,14 +1317,8 @@ crypto_dyn_iv_init(Cipher, Key, EncryptFlag) ->
when State :: crypto_state(),
Data :: iodata(),
Result :: binary() .
-crypto_update(State, Data0) ->
- case iolist_to_binary(Data0) of
- <<>> ->
- <<>>; % Known to fail on OpenSSL 0.9.8h
- Data ->
- ng_crypto_update_nif(State, Data)
- end.
-
+crypto_update(State, Data) ->
+ ng_crypto_update_nif(State, iolist_to_binary(Data)).
%%%----------------------------------------------------------------
-spec crypto_dyn_iv_update(State, Data, IV) -> Result | descriptive_error()
@@ -1122,14 +1326,30 @@ crypto_update(State, Data0) ->
Data :: iodata(),
IV :: iodata(),
Result :: binary() .
-crypto_dyn_iv_update(State, Data0, IV) ->
- %% When State is from State = crypto_init(Cipher, Key, undefined, EncryptFlag)
- case iolist_to_binary(Data0) of
- <<>> ->
- <<>>; % Known to fail on OpenSSL 0.9.8h
- Data ->
- ng_crypto_update_nif(State, Data, iolist_to_binary(IV))
- end.
+crypto_dyn_iv_update(State, Data, IV) ->
+ ng_crypto_update_nif(State, iolist_to_binary(Data), iolist_to_binary(IV)).
+
+%%%----------------------------------------------------------------
+%%%
+%%% Finalize encrypt/decrypt bytes. If the size of the bytes in
+%%% to crypto_uptate was not an integer number of blocks, the rest
+%%% is returned from this function.
+
+-spec crypto_final(State) -> FinalResult | descriptive_error()
+ when State :: crypto_state(),
+ FinalResult :: binary() .
+crypto_final(State) ->
+ ng_crypto_final_nif(State).
+
+%%%----------------------------------------------------------------
+%%%
+%%% Get result of padding etc
+
+-spec crypto_get_data(State) -> Result
+ when State :: crypto_state(),
+ Result :: map() .
+crypto_get_data(State) ->
+ ng_crypto_get_data_nif(State).
%%%----------------------------------------------------------------
%%%
@@ -1137,44 +1357,34 @@ crypto_dyn_iv_update(State, Data0, IV) ->
%%% The size must be an integer multiple of the crypto's blocksize.
%%%
--spec crypto_one_time(Cipher, Key, Data, EncryptFlag) ->
+-spec crypto_one_time(Cipher, Key, Data, FlagOrOptions) ->
Result | descriptive_error()
when Cipher :: cipher_no_iv(),
Key :: iodata(),
Data :: iodata(),
- EncryptFlag :: boolean(),
+ FlagOrOptions :: crypto_opts() | boolean(),
Result :: binary() .
-crypto_one_time(Cipher, Key, Data0, EncryptFlag) ->
- case iolist_to_binary(Data0) of
- <<>> ->
- <<>>; % Known to fail on OpenSSL 0.9.8h
- Data ->
- ng_crypto_one_time_nif(Cipher,
- iolist_to_binary(Key), <<>>, Data,
- EncryptFlag)
- end.
+crypto_one_time(Cipher, Key, Data, FlagOrOptions) ->
+ crypto_one_time(Cipher, Key, <<>>, Data, FlagOrOptions).
--spec crypto_one_time(Cipher, Key, IV, Data, EncryptFlag) ->
+-spec crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
Result | descriptive_error()
when Cipher :: cipher_iv(),
Key :: iodata(),
IV :: iodata(),
Data :: iodata(),
- EncryptFlag :: boolean(),
+ FlagOrOptions :: crypto_opts() | boolean(),
Result :: binary() .
-crypto_one_time(Cipher, Key, IV, Data0, EncryptFlag) ->
- case iolist_to_binary(Data0) of
- <<>> ->
- <<>>; % Known to fail on OpenSSL 0.9.8h
- Data ->
- ng_crypto_one_time_nif(Cipher,
- iolist_to_binary(Key), iolist_to_binary(IV), Data,
- EncryptFlag)
- end.
-
+crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
+ ng_crypto_one_time_nif(Cipher,
+ iolist_to_binary(Key),
+ iolist_to_binary(IV),
+ iolist_to_binary(Data),
+ get_crypto_opts(FlagOrOptions)).
+%%%----------------------------------------------------------------
-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) ->
Result | descriptive_error()
when Cipher :: cipher_aead(),
@@ -1227,26 +1437,25 @@ aead_tag_len(_) -> error({badarg, "Not an AEAD cipher"}).
%%%----------------------------------------------------------------
%%% NIFs
--spec ng_crypto_init_nif(atom(), binary(), binary()|undefined, boolean()|undefined ) ->
- crypto_state() | descriptive_error()
- ; (crypto_state(), <<>>, <<>>, boolean())
- -> crypto_state() | descriptive_error().
-
-ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlg) -> ?nif_stub.
+ng_crypto_init_nif(Cipher, Key, IVec, #{encrypt := EncryptFlag,
+ padding := Padding}) ->
+ ng_crypto_init_nif(Cipher, Key, IVec, EncryptFlag, Padding).
+
+ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlag, _Padding) -> ?nif_stub.
--spec ng_crypto_update_nif(crypto_state(), binary()) ->
- binary() | descriptive_error() .
ng_crypto_update_nif(_State, _Data) -> ?nif_stub.
-
--spec ng_crypto_update_nif(crypto_state(), binary(), binary()) ->
- binary() | descriptive_error() .
ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub.
+ng_crypto_final_nif(_State) -> ?nif_stub.
+
+ng_crypto_get_data_nif(_State) -> ?nif_stub.
+
+ng_crypto_one_time_nif(Cipher, Key, IVec, Data, #{encrypt := EncryptFlag,
+ padding := Padding}) ->
+ ng_crypto_one_time_nif(Cipher, Key, IVec, Data, EncryptFlag, Padding).
--spec ng_crypto_one_time_nif(atom(), binary(), binary(), binary(), boolean() ) ->
- binary() | descriptive_error().
-ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlg) -> ?nif_stub.
+ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlag, _Padding) -> ?nif_stub.
%%%----------------------------------------------------------------
%%% Cipher aliases
@@ -1461,7 +1670,7 @@ rand_plugin_aes_next({Key,GenWords,F,_JumpBase,Count}) ->
%%
rand_plugin_aes_next(Key, GenWords, F, Count) ->
{Cleartext,NewCount} = aes_cleartext(<<>>, F, Count, GenWords),
- Encrypted = crypto:block_encrypt(aes_ecb, Key, Cleartext),
+ Encrypted = block_encrypt(aes_ecb, Key, Cleartext),
[V|Cache] = aes_cache(Encrypted, {Key,GenWords,F,Count,NewCount}),
{V,Cache}.
@@ -1791,21 +2000,21 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_
-spec generate_key(Type, Params)
-> {PublicKey, PrivKeyOut}
- when Type :: dh | ecdh | rsa | srp,
+ when Type :: dh | ecdh | eddsa | rsa | srp,
PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
- Params :: dh_params() | ecdh_params() | rsa_params() | srp_gen_params()
+ Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_gen_params()
.
generate_key(Type, Params) ->
generate_key(Type, Params, undefined).
-spec generate_key(Type, Params, PrivKeyIn)
-> {PublicKey, PrivKeyOut}
- when Type :: dh | ecdh | rsa | srp,
+ when Type :: dh | ecdh | eddsa | rsa | srp,
PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
PrivKeyIn :: undefined | dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
- Params :: dh_params() | ecdh_params() | rsa_params() | srp_comp_params()
+ Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_comp_params()
.
generate_key(dh, DHParameters0, PrivateKey) ->
@@ -1843,15 +2052,17 @@ generate_key(rsa, {ModulusSize, PublicExponent}, undefined) ->
{lists:sublist(Private, 2), Private}
end;
-
-generate_key(ecdh, Curve, undefined) when Curve == x448 ;
- Curve == x25519 ->
- evp_generate_key_nif(Curve);
+generate_key(ecdh, Curve, PrivKey) when Curve == x448 ;
+ Curve == x25519 ->
+ evp_generate_key_nif(Curve, ensure_int_as_bin(PrivKey));
generate_key(ecdh, Curve, PrivKey) ->
- ec_key_generate(nif_curve_params(Curve), ensure_int_as_bin(PrivKey)).
+ ec_key_generate(nif_curve_params(Curve), ensure_int_as_bin(PrivKey));
+generate_key(eddsa, Curve, PrivKey) when Curve == ed448 ;
+ Curve == ed25519 ->
+ evp_generate_key_nif(Curve, ensure_int_as_bin(PrivKey)).
-evp_generate_key_nif(_Curve) -> ?nif_stub.
+evp_generate_key_nif(_Curve, _PrivKey) -> ?nif_stub.
-spec compute_key(Type, OthersPublicKey, MyPrivateKey, Params)
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index ce515a9ba0..bb1ea2df20 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -35,6 +35,7 @@ all() ->
appup,
{group, fips},
{group, non_fips},
+ cipher_padding,
mod_pow,
exor,
rand_uniform,
@@ -200,11 +201,13 @@ groups() ->
{ecdsa, [], [sign_verify
%% Does not work yet: ,public_encrypt, private_encrypt
]},
- {ed25519, [], [sign_verify
+ {ed25519, [], [sign_verify,
%% Does not work yet: ,public_encrypt, private_encrypt
+ generate
]},
- {ed448, [], [sign_verify
+ {ed448, [], [sign_verify,
%% Does not work yet: ,public_encrypt, private_encrypt
+ generate
]},
{dh, [], [generate_compute, compute_bug]},
{ecdh, [], [use_all_elliptic_curves, compute, generate]},
@@ -551,7 +554,8 @@ api_ng_cipher_increment({Type, Key, IV, PlainText0, ExpectedEncText}=_X) ->
RefEnc = crypto:crypto_init(Type, Key, IV, true),
RefDec = crypto:crypto_init(Type, Key, IV, false),
EncTexts = api_ng_cipher_increment_loop(RefEnc, PlainTexts),
- Enc = iolist_to_binary(EncTexts),
+ EncFinal = crypto:crypto_final(RefEnc),
+ Enc = iolist_to_binary(EncTexts++[EncFinal]),
case ExpectedEncText of
undefined ->
ok;
@@ -562,7 +566,9 @@ api_ng_cipher_increment({Type, Key, IV, PlainText0, ExpectedEncText}=_X) ->
ct:fail("api_ng_cipher_increment (encode)",[])
end,
Plain = iolist_to_binary(PlainTexts),
- case iolist_to_binary(api_ng_cipher_increment_loop(RefDec, EncTexts)) of
+ DecTexts = api_ng_cipher_increment_loop(RefDec, EncTexts),
+ DecFinal = crypto:crypto_final(RefDec),
+ case iolist_to_binary(DecTexts++[DecFinal]) of
Plain ->
ok;
OtherPT ->
@@ -697,6 +703,60 @@ do_api_ng_tls({Type, Key, IV, PlainText0, ExpectedEncText}=_X) ->
end.
%%--------------------------------------------------------------------
+cipher_padding(_Config) ->
+ Ciphers = [{C,pkcs_padding}
+ || C <- crypto:supports(ciphers),
+ C =/= aes_ige256,
+ C =/= chacha20_poly1305,
+ case crypto:cipher_info(C) of
+ #{mode := ccm_mode} -> false;
+ #{mode := gcm_mode} -> false;
+ _ -> true
+ end],
+ lists:foreach(fun cipher_padding_test/1, Ciphers).
+
+cipher_padding_test({Cipher, Padding}) ->
+ #{block_size := Sblock,
+ iv_length := Siv,
+ key_length := Skey} = Inf = crypto:cipher_info(Cipher),
+ ct:log("~p ~p", [Cipher,Inf]),
+
+ Key = <<1:Skey/unit:8>>,
+ IV = <<0:Siv/unit:8>>,
+ MsgLen = 5*Sblock + 3,
+ Tplain = crypto:strong_rand_bytes(MsgLen),
+ PadSize = if
+ (Padding == zero) ; (Padding == random) ->
+ (Sblock - (MsgLen rem Sblock)) rem Sblock;
+ true ->
+ 0
+ end,
+ Tcrypt =
+ case Siv of
+ 0 ->
+ crypto:crypto_one_time(Cipher, Key, Tplain, [{encrypt,true},{padding,Padding}]);
+ _ ->
+ crypto:crypto_one_time(Cipher, Key, IV, Tplain, [{encrypt,true},{padding,Padding}])
+ end,
+
+ TdecryptPadded =
+ case Siv of
+ 0 ->
+ crypto:crypto_one_time(Cipher, Key, Tcrypt, [{encrypt,false},{padding,Padding}]);
+ _ ->
+ crypto:crypto_one_time(Cipher, Key, IV, Tcrypt, [{encrypt,false},{padding,Padding}])
+ end,
+
+ case split_binary(TdecryptPadded, size(TdecryptPadded) - PadSize) of
+ {Tplain, _} ->
+ ok;
+ {Tdecrypt,Tpad} ->
+ ct:log("Key = ~p~nIV = ~p~nTplain = ~p~nTcrypt = ~p~nTdecrypt = ~p~nPadding = ~p",
+ [Key, IV, Tplain, Tcrypt, Tdecrypt, Tpad]),
+ ct:fail("~p", [Cipher])
+ end.
+
+%%--------------------------------------------------------------------
no_aead() ->
[{doc, "Test disabled aead ciphers"}].
no_aead(Config) when is_list(Config) ->
@@ -1584,7 +1644,7 @@ do_compute({ecdh = Type, Pub, Priv, Curve, SharedSecret}) ->
ct:fail({{crypto, compute_key, [Type, Pub, Priv, Curve]}, {expected, SharedSecret}, {got, Other}})
end.
-do_generate({ecdh = Type, Curve, Priv, Pub}) ->
+do_generate({Type, Curve, Priv, Pub}) when Type == ecdh ; Type == eddsa ->
case crypto:generate_key(Type, Curve, Priv) of
{Pub, _} ->
ok;
@@ -2011,7 +2071,10 @@ group_config(ecdsa = Type, Config) ->
[{sign_verify, SignVerify}, {pub_priv_encrypt, PubPrivEnc} | Config];
group_config(Type, Config) when Type == ed25519 ; Type == ed448 ->
TestVectors = eddsa(Type),
- [{sign_verify,TestVectors} | Config];
+ Generate = lists:map(fun({Curve, _Hash, Priv, Pub, _Msg, _Signature}) ->
+ {eddsa, Curve, Priv, Pub}
+ end, TestVectors),
+ [{sign_verify,TestVectors}, {generate, Generate} | Config];
group_config(srp, Config) ->
GenerateCompute = [srp3(), srp6(), srp6a(), srp6a_smaller_prime()],
[{generate_compute, GenerateCompute} | Config];
@@ -3508,7 +3571,7 @@ srp(ClientPrivate, Generator, Prime, Version, Verifier, ServerPublic, ServerPriv
eddsa(ed25519) ->
%% https://tools.ietf.org/html/rfc8032#section-7.1
- %% {ALGORITHM, (SHA)}, SECRET KEY, PUBLIC KEY, MESSAGE, SIGNATURE}
+ %% {ALGORITHM, (SHA), SECRET KEY, PUBLIC KEY, MESSAGE, SIGNATURE}
[
%% TEST 1
{ed25519, undefined,
@@ -4074,12 +4137,31 @@ ecc() ->
"782C37E372BA4520AA62E0FED121D49EF3B543660CFD05FD")},
{ecdh,secp192r1,4,
hexstr2point("35433907297CC378B0015703374729D7A4FE46647084E4BA",
- "A2649984F2135C301EA3ACB0776CD4F125389B311DB3BE32")}],
+ "A2649984F2135C301EA3ACB0776CD4F125389B311DB3BE32")},
+ %% RFC 7748, 6.2
+ {ecdh, x448,
+ hexstr2bin("9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
+ "d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b"),
+ hexstr2bin("9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
+ "22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0")},
+ {ecdh, x448,
+ hexstr2bin("1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
+ "6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d"),
+ hexstr2bin("3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
+ "27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609")},
+ %% RFC 7748, 6.1
+ {ecdh, x25519,
+ hexstr2bin("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"),
+ hexstr2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")},
+ {ecdh, x25519,
+ hexstr2bin("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"),
+ hexstr2bin("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")}],
lists:filter(fun ({_Type, Curve, _Priv, _Pub}) ->
lists:member(Curve, Curves)
end,
TestCases).
+
int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []);
int_to_bin(X) -> int_to_bin_pos(X, []).
diff --git a/lib/crypto/test/crypto_property_test_SUITE.erl b/lib/crypto/test/crypto_property_test_SUITE.erl
index bcfd5d3de1..03d030c702 100644
--- a/lib/crypto/test/crypto_property_test_SUITE.erl
+++ b/lib/crypto/test/crypto_property_test_SUITE.erl
@@ -61,7 +61,7 @@ encrypt_decrypt_one_time(Config) ->
init_update(Config) ->
ct_property_test:quickcheck(
- crypto_ng_api:prop__crypto_init_update(),
+ crypto_ng_api:prop__crypto_init_update_final(),
Config
).
diff --git a/lib/crypto/test/property_test/crypto_ng_api.erl b/lib/crypto/test/property_test/crypto_ng_api.erl
index 14753a4eba..20c6db4dd7 100644
--- a/lib/crypto/test/property_test/crypto_ng_api.erl
+++ b/lib/crypto/test/property_test/crypto_ng_api.erl
@@ -32,27 +32,28 @@
prop__crypto_one_time() ->
numtests(10000,
- ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(),
- {text_plain(), Ciph, key(Ciph), iv(Ciph)}),
+ ?FORALL({TextPlain, Cipher, Key, IV, Padding}, ?LET(Ciph,cipher(),
+ {text_plain(), Ciph, key(Ciph), iv(Ciph), padding()}),
begin
R = equal(TextPlain,
- full_blocks(TextPlain, Cipher),
- decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)),
+ full_blocks(TextPlain, Cipher, Padding),
+ decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain, Padding)),
prt_inf(Cipher, TextPlain, R)
end
)
).
-prop__crypto_init_update() ->
+prop__crypto_init_update_final() ->
numtests(10000,
- ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(),
- {text_plain(), Ciph, key(Ciph), iv(Ciph)}),
+ ?FORALL({TextPlain, Cipher, Key, IV, Padding}, ?LET(Ciph,cipher(),
+ {text_plain(), Ciph, key(Ciph), iv(Ciph), padding()}),
begin
R = equal(TextPlain,
- full_blocks(TextPlain, Cipher),
- decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)),
+ full_blocks(TextPlain, Cipher, Padding),
+ decrypt_encrypt_init_update_final(Cipher, Key, IV, TextPlain, Padding)),
prt_inf(Cipher, TextPlain, R)
- end)
+ end
+ )
).
prt_inf(Cipher, TextPlain, R) ->
@@ -69,6 +70,7 @@ prt_inf(Cipher, TextPlain, R) ->
%%%================================================================
%%% Lib
+equal(_, _, correct_exception) -> true;
equal(_, T, T) -> true;
equal(F, Tp, Td) ->
ct:pal("Full: ~p~n"
@@ -78,42 +80,94 @@ equal(F, Tp, Td) ->
false.
-decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain) ->
- io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks)",
+decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain, Padding) ->
+ io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks), Padding: ~p",
[?MODULE,?LINE, Cipher, block_size(Cipher), size(Key), size(IV), size(iolist_to_binary(TextPlain)),
- num_chunks(TextPlain)]),
- TextCrypto = crypto:crypto_one_time(Cipher, Key, IV, TextPlain, true),
- io:format("~p:~p TextCrypto: ~p", [?MODULE,?LINE, size(TextCrypto)]),
- TextDecrypt = crypto:crypto_one_time(Cipher, Key, IV, TextCrypto, false),
- io:format("~p:~p TextDecrypt: ~p", [?MODULE,?LINE, size(TextDecrypt)]),
- TextDecrypt.
-
+ num_chunks(TextPlain), Padding]),
+ Sblock = block_size(Cipher),
+ ExcessBytesLastBlock = size(iolist_to_binary(TextPlain)) rem Sblock,
+ PadSize = if
+ (Padding == zero) ; (Padding == random) ->
+ (Sblock - ExcessBytesLastBlock) rem Sblock;
+ true ->
+ 0
+ end,
+ try
+ crypto:crypto_one_time(Cipher, Key, IV, TextPlain, [{encrypt,true},{padding,Padding}])
+ of
+ TextCrypto ->
+ io:format("~p:~p PadSize: ~p, TextCrypto: ~p",
+ [?MODULE,?LINE, PadSize, size(TextCrypto)]),
+ TextDecryptPadded =
+ crypto:crypto_one_time(Cipher, Key, IV, TextCrypto, [{encrypt,false},{padding,Padding}]),
+ io:format("~p:~p TextDecryptPadded: ~p",
+ [?MODULE,?LINE, size(TextDecryptPadded)]),
+ element(1, split_binary(TextDecryptPadded, size(TextDecryptPadded) - PadSize))
+ catch
+ error:{error,{"api_ng.c",Line},Msg} when ExcessBytesLastBlock>0,
+ Padding == none,
+ Msg == "Padding 'none' but unfilled last block" ->
+ io:format("~p:~p Correct exception: ~p",
+ [?MODULE,?LINE, {error,{"api_ng.c",Line},Msg}]),
+ correct_exception
+ end.
+
-decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain) when is_binary(TextPlain) ->
- decrypt_encrypt_init_update(Cipher, Key, IV, [TextPlain]);
+decrypt_encrypt_init_update_final(Cipher, Key, IV, TextPlain, Padding) when is_binary(TextPlain) ->
+ decrypt_encrypt_init_update_final(Cipher, Key, IV, [TextPlain], Padding);
-decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain) ->
- io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks)",
+decrypt_encrypt_init_update_final(Cipher, Key, IV, TextPlain, Padding) ->
+ io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks), Padding: ~p",
[?MODULE,?LINE, Cipher, block_size(Cipher), size(Key), size(IV), size(iolist_to_binary(TextPlain)),
- num_chunks(TextPlain)]),
- Cenc = crypto:crypto_init(Cipher, Key, IV, true),
+ num_chunks(TextPlain), Padding]),
+ Sblock = block_size(Cipher),
+ ExcessBytesLastBlock = size(iolist_to_binary(TextPlain)) rem Sblock,
+
+ Cenc = crypto:crypto_init(Cipher, Key, IV, [{encrypt,true},{padding,Padding}]),
TextOut = lists:foldl(fun(TextIn, TextOutAcc) ->
[crypto:crypto_update(Cenc,TextIn) | TextOutAcc]
end, [], TextPlain),
- TextCrypto = lists:reverse(TextOut),
- io:format("~p:~p TextCrypto: ~p",
- [?MODULE,?LINE, size(iolist_to_binary(TextCrypto))]),
+ try
+ Rf = crypto:crypto_final(Cenc),
+ Rps = maps:get(padding_size,crypto:crypto_get_data(Cenc)),
+ {Rps,Rf}
+ of
+ {PadSize0,LastTextOut} ->
+ TextCrypto = lists:reverse([LastTextOut|TextOut]),
+ io:format("~p:~p PadSize0: ~p, TextCrypto: ~p",
+ [?MODULE,?LINE, PadSize0, size(iolist_to_binary(TextCrypto))]),
+ PadSize = case Padding of
+ pkcs_padding -> 0;
+ none -> 0;
+ undefined -> 0;
+ _ -> PadSize0
+ end,
- Cdec = crypto:crypto_init(Cipher, Key, IV, false),
- TextDec = lists:foldl(fun(TextC, TextDecAcc) ->
- [crypto:crypto_update(Cdec,TextC) | TextDecAcc]
- end, [], TextCrypto),
- iolist_to_binary(lists:reverse(TextDec)).
-
-full_blocks(TextPlain, Cipher) ->
+ Cdec = crypto:crypto_init(Cipher, Key, IV, [{encrypt,false},{padding,Padding}]),
+ TextDec = lists:foldl(fun(TextC, TextDecAcc) ->
+ [crypto:crypto_update(Cdec,TextC) | TextDecAcc]
+ end, [], TextCrypto),
+ LastDecOut = crypto:crypto_final(Cdec),
+ TextDecryptPadded = iolist_to_binary(lists:reverse([LastDecOut|TextDec])),
+ io:format("~p:~p TextDecryptPadded: ~p",
+ [?MODULE,?LINE, size(TextDecryptPadded)]),
+ element(1, split_binary(TextDecryptPadded, size(TextDecryptPadded) - PadSize))
+ catch
+ error:{error,{"api_ng.c",Line},Msg} when ExcessBytesLastBlock>0,
+ Padding == none ->
+ io:format("~p:~p Correct exception: ~p",
+ [?MODULE,?LINE, {error,{"api_ng.c",Line},Msg}]),
+ correct_exception
+ end.
+
+
+full_blocks(TextPlain, Cipher, Pad) when Pad == undefined ; Pad == none ->
TextPlainBin = iolist_to_binary(TextPlain),
{Head,_Tail} = split_binary(TextPlainBin, (size(TextPlainBin) - num_rest_bytes(TextPlainBin,Cipher))),
- Head.
+ Head;
+full_blocks(TextPlain, _Cipher, _) ->
+ iolist_to_binary(TextPlain).
+
num_chunks(B) when is_binary(B) -> 1;
num_chunks(L) when is_list(L) -> length(L).
diff --git a/lib/crypto/test/property_test/crypto_prop_generators.erl b/lib/crypto/test/property_test/crypto_prop_generators.erl
index 3a7e9ecb87..0b363aab36 100644
--- a/lib/crypto/test/property_test/crypto_prop_generators.erl
+++ b/lib/crypto/test/property_test/crypto_prop_generators.erl
@@ -27,6 +27,7 @@
iv/1,
iolist/0,
mybinary/1,
+ padding/0,
non_aead_ciphers/0,
block_size/1,
key_length/1,
@@ -58,6 +59,9 @@ iolist() -> frequency([{5, list( oneof([list(byte()),
mybinary(MaxSize) -> ?LET(Sz, integer(0,MaxSize), binary(Sz)).
+padding() -> oneof([pkcs_padding, none,
+ zero, random,
+ undefined]).
%%%================================================================
non_aead_ciphers() ->
diff --git a/lib/crypto/test/property_test/crypto_prop_generators.hrl b/lib/crypto/test/property_test/crypto_prop_generators.hrl
index 56a762e651..81e95a4d82 100644
--- a/lib/crypto/test/property_test/crypto_prop_generators.hrl
+++ b/lib/crypto/test/property_test/crypto_prop_generators.hrl
@@ -28,6 +28,7 @@
iv/1,
iolist/0,
mybinary/1,
+ padding/0,
non_aead_ciphers/0,
block_size/1,
diff --git a/lib/debugger/Makefile b/lib/debugger/Makefile
index 8c8b617831..3a06d72c5d 100644
--- a/lib/debugger/Makefile
+++ b/lib/debugger/Makefile
@@ -34,3 +34,7 @@ SPECIAL_TARGETS =
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+
+DIA_PLT_APPS=wx
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 49b5a4be57..b02d35b802 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -30,11 +30,6 @@ VSN=$(DEBUGGER_VSN)
APPLICATION=debugger
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -49,74 +44,13 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) $(XML_PART_FILES) \
$(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES = \
- images/attach.jpg \
- images/cond_break_dialog.jpg \
- images/function_break_dialog.jpg \
- images/interpret.jpg \
- images/line_break_dialog.jpg \
- images/monitor.jpg \
- images/view.jpg
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.jpg: %.jpg
- $(INSTALL_DIR) $(HTMLDIR)/images
- $(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)/%)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-debug opt:
-
-# ----------------------------------------------------
-# 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_DATA) $(HTMLDIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+IMAGE_FILES = \
+ attach.jpg \
+ cond_break_dialog.jpg \
+ function_break_dialog.jpg \
+ interpret.jpg \
+ line_break_dialog.jpg \
+ monitor.jpg \
+ view.jpg
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/debugger/doc/src/images/attach.jpg b/lib/debugger/doc/src/attach.jpg
index 95f227d21b..95f227d21b 100644
--- a/lib/debugger/doc/src/images/attach.jpg
+++ b/lib/debugger/doc/src/attach.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/images/cond_break_dialog.jpg b/lib/debugger/doc/src/cond_break_dialog.jpg
index 40bcb299a9..40bcb299a9 100644
--- a/lib/debugger/doc/src/images/cond_break_dialog.jpg
+++ b/lib/debugger/doc/src/cond_break_dialog.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/debugger_chapter.xml b/lib/debugger/doc/src/debugger_chapter.xml
index 3c37d4b924..fc7edf07a0 100644
--- a/lib/debugger/doc/src/debugger_chapter.xml
+++ b/lib/debugger/doc/src/debugger_chapter.xml
@@ -153,7 +153,7 @@
<p>A line breakpoint is created at a certain line in a module.</p>
- <image file="images/line_break_dialog.jpg">
+ <image file="line_break_dialog.jpg">
<icaption>Line Break Dialog Window</icaption>
</image>
@@ -185,7 +185,7 @@
<seealso marker="int#get_binding/2"><c>int:get_binding(Variable,Bindings)</c></seealso>.
The function returns <c>unbound</c> or <c>{value,Value}</c>.</p>
- <image file="images/cond_break_dialog.jpg">
+ <image file="cond_break_dialog.jpg">
<icaption>Conditional Break Dialog Window</icaption>
</image>
@@ -225,7 +225,7 @@ c_break(Bindings) ->
<p>A function breakpoint is a set of line breakpoints, one at
the first line of each clause in the specified function.</p>
- <image file="images/function_break_dialog.jpg">
+ <image file="function_break_dialog.jpg">
<icaption>Function Break Dialog Window</icaption>
</image>
@@ -307,7 +307,7 @@ c_break(Bindings) ->
modules</p></item>
</list>
- <image file="images/monitor.jpg">
+ <image file="monitor.jpg">
<icaption>Monitor Window</icaption>
</image>
@@ -593,7 +593,7 @@ c_break(Bindings) ->
<pre>
4> c(module, debug_info).</pre>
- <image file="images/interpret.jpg">
+ <image file="interpret.jpg">
<icaption>Interpret Modules Window</icaption>
</image>
@@ -624,7 +624,7 @@ c_break(Bindings) ->
been attached to. Notice that when attaching to a process, its
execution is automatically stopped.</p>
- <image file="images/attach.jpg">
+ <image file="attach.jpg">
<icaption>Attach Process Window</icaption>
</image>
@@ -819,7 +819,7 @@ c_break(Bindings) ->
<p>The View Module window displays the contents of an interpreted
module and makes it possible to set breakpoints.</p>
- <image file="images/view.jpg">
+ <image file="view.jpg">
<icaption>View Module Window</icaption>
</image>
diff --git a/lib/debugger/doc/src/images/function_break_dialog.jpg b/lib/debugger/doc/src/function_break_dialog.jpg
index db56d5a096..db56d5a096 100644
--- a/lib/debugger/doc/src/images/function_break_dialog.jpg
+++ b/lib/debugger/doc/src/function_break_dialog.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/images/interpret.jpg b/lib/debugger/doc/src/interpret.jpg
index 030c06fa23..030c06fa23 100644
--- a/lib/debugger/doc/src/images/interpret.jpg
+++ b/lib/debugger/doc/src/interpret.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/images/line_break_dialog.jpg b/lib/debugger/doc/src/line_break_dialog.jpg
index 18ac6a9f81..18ac6a9f81 100644
--- a/lib/debugger/doc/src/images/line_break_dialog.jpg
+++ b/lib/debugger/doc/src/line_break_dialog.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/images/monitor.jpg b/lib/debugger/doc/src/monitor.jpg
index 32f210cbf2..32f210cbf2 100644
--- a/lib/debugger/doc/src/images/monitor.jpg
+++ b/lib/debugger/doc/src/monitor.jpg
Binary files differ
diff --git a/lib/debugger/doc/src/images/view.jpg b/lib/debugger/doc/src/view.jpg
index 7ffd511eff..7ffd511eff 100644
--- a/lib/debugger/doc/src/images/view.jpg
+++ b/lib/debugger/doc/src/view.jpg
Binary files differ
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index b6703d5d9e..38c2035c5c 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -693,7 +693,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) ->
end
catch
Class:Reason when CatchCs =/= [] ->
- catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval)
+ catch_clauses({Class,Reason,get_stacktrace()}, CatchCs, Bs0, Ieval)
end;
expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) ->
Ieval = Ieval0#ieval{line=Line},
@@ -706,7 +706,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) ->
end
catch
Class:Reason when CatchCs =/= [] ->
- catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval)
+ catch_clauses({Class,Reason,get_stacktrace()}, CatchCs, Bs0, Ieval)
after
seq(As, Bs0, Ieval#ieval{top=false})
end;
@@ -905,14 +905,9 @@ expr({dbg,Line,self,[]}, Bs, #ieval{level=Le}) ->
Self = get(self),
trace(return, {Le,Self}),
{value,Self,Bs};
-expr({dbg,Line,get_stacktrace,[]}, Bs, #ieval{level=Le}) ->
- trace(bif, {Le,Line,erlang,get_stacktrace,[]}),
- Stacktrace = get_stacktrace(),
- trace(return, {Le,Stacktrace}),
- {value,Stacktrace,Bs};
expr({dbg,Line,raise,As0}, Bs0, #ieval{level=Le}=Ieval0) ->
- %% Since erlang:get_stacktrace/0 is emulated, we will
- %% need to emulate erlang:raise/3 too so that we can
+ %% Since stacktraces are emulated, we will
+ %% need to emulate erlang:raise/3 so that we can
%% capture the stacktrace.
Ieval = Ieval0#ieval{line=Line},
{[Class,Reason,Stk0]=As,Bs} = eval_list(As0, Bs0, Ieval),
@@ -1383,7 +1378,7 @@ catch_clauses(Exception, [{clause,_,[P],G,B}|CatchCs], Bs0, Ieval) ->
nomatch ->
catch_clauses(Exception, CatchCs, Bs0, Ieval)
end;
-catch_clauses({Class,Reason,[]}, [], _Bs, _Ieval) ->
+catch_clauses({Class,Reason,_}, [], _Bs, _Ieval) ->
erlang:Class(Reason).
receive_clauses(Cs, Bs0, [Msg|Msgs]) ->
@@ -1592,12 +1587,17 @@ match_tuple([], _, _, Bs, _BBs) ->
{match,Bs}.
match_map([{map_field_exact,_,K0,Pat}|Fs], Map, Bs0, BBs) ->
- {value,K,BBs} = expr(K0, BBs, #ieval{}),
- case maps:find(K, Map) of
- {ok,Value} ->
- {match,Bs} = match1(Pat, Value, Bs0, BBs),
- match_map(Fs, Map, Bs, BBs);
- error -> throw(nomatch)
+ try guard_expr(K0, BBs) of
+ {value,K} ->
+ case Map of
+ #{K := Value} ->
+ {match,Bs} = match1(Pat, Value, Bs0, BBs),
+ match_map(Fs, Map, Bs, BBs);
+ #{} ->
+ throw(nomatch)
+ end
+ catch _:_ ->
+ throw(nomatch)
end;
match_map([], _, Bs, _BBs) ->
{match,Bs}.
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 468f6a809f..d15292d51d 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -204,7 +204,7 @@ pattern({tuple,Anno,Ps0}) ->
{tuple,ln(Anno),Ps1};
pattern({map,Anno,Fs0}) ->
Fs1 = lists:map(fun ({map_field_exact,L,K,V}) ->
- {map_field_exact,L,expr(K, false),pattern(V)}
+ {map_field_exact,L,gexpr(K),pattern(V)}
end, Fs0),
{map,ln(Anno),Fs1};
pattern({op,_,'-',{integer,Anno,I}}) ->
@@ -226,7 +226,7 @@ pattern({bin_element,Anno,Expr0,Size0,Type0}) ->
{Size1,Type} = make_bit_type(Anno, Size0, Type0),
Expr1 = pattern(Expr0),
Expr = coerce_to_float(Expr1, Type0),
- Size = pattern(Size1),
+ Size = expr(Size1, false),
{bin_element,ln(Anno),Expr,Size,Type};
%% Evaluate compile-time expressions.
pattern({op,_,'++',{nil,_},R}) ->
@@ -438,8 +438,6 @@ expr({'fun',Anno,{function,M,F,A}}, _Lc) ->
{make_ext_fun,ln(Anno),MFA};
expr({call,Anno,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) ->
{dbg,ln(Anno),self,[]};
-expr({call,Anno,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) ->
- {dbg,ln(Anno),get_stacktrace,[]};
expr({call,Anno,{remote,_,{atom,_,erlang},{atom,_,throw}},[_]=As}, _Lc) ->
{dbg,ln(Anno),throw,expr_list(As)};
expr({call,Anno,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}, _Lc) ->
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index efb6d9ed8b..0f942923f3 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -32,6 +32,7 @@ MODULES= \
bs_match_int_SUITE \
bs_match_misc_SUITE \
bs_match_tail_SUITE \
+ bs_size_expr_SUITE \
bs_utf_SUITE \
bug_SUITE \
erl_eval_SUITE \
diff --git a/lib/debugger/test/bs_size_expr_SUITE.erl b/lib/debugger/test/bs_size_expr_SUITE.erl
new file mode 100644
index 0000000000..864233f282
--- /dev/null
+++ b/lib/debugger/test/bs_size_expr_SUITE.erl
@@ -0,0 +1,273 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_size_expr_SUITE).
+-compile(nowarn_shadow_vars).
+
+-export([all/0,suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ basic/1,size_shadow/1,complex/1,
+ recv/1,no_match/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+all() ->
+ [{group,p}].
+
+groups() ->
+ [{p,[],
+ [basic,
+ size_shadow,
+ complex,
+ recv,
+ no_match]}].
+
+init_per_suite(Config) ->
+ test_lib:interpret(?MODULE),
+ true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+ test_lib:interpret(?MODULE),
+ Config.
+
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+ ok.
+
+basic(_Config) ->
+ <<>> = do_basic(<<1:32>>),
+ <<"abcd">> = do_basic(<<2:32,"abcd">>),
+ no_match = do_basic(<<0:32>>),
+ no_match = do_basic(<<777:32>>),
+ ok.
+
+do_basic(Bin) ->
+ Res = do_basic_1(Bin),
+
+ Res = do_basic_2({tag,Bin}),
+ Res = do_basic_2([list,Bin]),
+ 6 = do_basic_2({2,4}),
+
+ Res = do_basic_3(Bin),
+ Res = do_basic_4(Bin),
+
+ {result,Res} = do_basic_5(Bin),
+ case Res of
+ no_match ->
+ ok;
+ _ ->
+ {result,{Res,7777777}} = do_basic_5(<<Bin/binary,7777777:32>>)
+ end,
+
+ Res.
+
+do_basic_1(<<Sz:32,Tail:(4*Sz-4)/binary>>) ->
+ Tail;
+do_basic_1(<<_/binary>>) ->
+ no_match.
+
+do_basic_2({tag,<<Sz:32,Tail:(4*Sz-4)/binary>>}) ->
+ Tail;
+do_basic_2([list,<<Sz:32,Tail:((Sz-1)*4)/binary>>]) ->
+ Tail;
+do_basic_2({A,B}) when is_integer(A), is_integer(B) ->
+ A + B;
+do_basic_2(_) ->
+ no_match.
+
+do_basic_3(Bin) ->
+ WordSize = id(4),
+ case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end.
+
+do_basic_4(Bin) ->
+ WordSize = id(4),
+ F = fun() ->
+ case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end
+ end,
+ F().
+
+do_basic_5(Bin) ->
+ WordSize = id(4),
+ F = fun() ->
+ Res = case Bin of
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary,More:(8*WordSize)>> ->
+ {Tail,More};
+ <<Sz:32,Tail:(WordSize*Sz-WordSize)/binary>> ->
+ Tail;
+ _ ->
+ no_match
+ end,
+ {result,Res}
+ end,
+ F().
+
+size_shadow(_Config) ->
+ 12345678 = size_shadow_1(),
+ ok.
+
+size_shadow_1() ->
+ L = 8,
+ Offset = 16,
+ Fs = [fun(<<L:L,B:(L+16)>>) -> B end,
+ fun(<<L:L,B:(L+Offset)>>) -> B end,
+ fun(A) ->
+ Res = (fun([<<L:L,B:(L+16)>>]) -> B end)([A]),
+ Res = (fun([<<L:L,B:(L+Offset)>>]) -> B end)([A])
+ end,
+ fun(A) ->
+ Res = (fun({<<L:L,B:(L+16)>>,<<L:L,B:(L+16)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+Offset)>>,<<L:L,B:(L+16)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+16)>>,<<L:L,B:(L+Offset)>>}) -> B end)({A,A}),
+ Res = (fun({<<L:L,B:(L+Offset)>>,<<L:L,B:(L+Offset)>>}) -> B end)({A,A})
+ end,
+ fun(A) ->
+ <<Size:L,_/bits>> = A,
+ Inner = fun([L], {#{key1 := <<L:L,B:(L+Offset)>>,
+ key2 := <<L:L,B:(L+Offset)>>}, L}) -> B end,
+ Inner([Size], {#{key1 => A,key2 => A},Size})
+ end],
+ size_shadow_apply(Fs, <<16:8, 12345678:32>>).
+
+size_shadow_apply([F|Fs], Arg) when is_function(F, 1) ->
+ size_shadow_apply(Fs, Arg, F(Arg)).
+
+size_shadow_apply([F|Fs], Arg, Res) when is_function(F, 1) ->
+ Res = F(Arg),
+ size_shadow_apply(Fs, Arg, Res);
+size_shadow_apply([], _, Res) ->
+ Res.
+
+-record(r, {a,b,c}).
+complex(Config) ->
+ (fun() ->
+ Len = length(id(Config)),
+ Bin = << <<I:13>> || I <- lists:seq(1, Len) >>,
+ <<Bin:(length(Config))/binary-unit:13>> = Bin
+ end)(),
+
+ (fun() ->
+ V = id([a,b,c]),
+ F = fun(<<V:(bit_size(<<0:(length(V))>>)*8)/signed-integer>>) ->
+ V;
+ ({A,B}) ->
+ A + B
+ end,
+ -1 = F(<<-1:(length(V)*8)>>),
+ 7 = F({3,4})
+ end)(),
+
+ (fun() ->
+ A = a,
+ B = b,
+ F = fun(<<A:16,B:16,C:(A+B),D/bits>>) ->
+ {A,B,C,D};
+ (<<A:16,B:16>>) ->
+ {A,B};
+ (<<A:8,B:8>>) ->
+ {A,B}
+ end,
+ {13,21,16#cafebeef,<<"more">>} = F(<<13:16,21:16,16#cafebeef:34,"more">>),
+ {100,500} = F(<<100:16,500:16>>),
+ {157,77} = F(<<157:8,77:8>>),
+ {A,B}
+ end)(),
+
+ (fun() ->
+ Two = id(2),
+ F = fun(a, <<_:(#r.a - Two)/binary,Int:8,_/binary>>) -> Int;
+ (b, <<_:(#r.b - Two)/binary,Int:8,_/binary>>) -> Int;
+ (c, <<_:(#r.c - Two)/binary,Int:8,_/binary>>) -> Int
+ end,
+ 1 = F(a, <<1,2,3>>),
+ 2 = F(b, <<1,2,3>>),
+ 3 = F(c, <<1,2,3>>)
+ end)(),
+
+ (fun() ->
+ Bin = <<1,2,3,4>>,
+ F = fun(R) ->
+ <<First:(R#r.a)/binary,Tail/binary>> = Bin,
+ {First,Tail}
+ end,
+ {<<>>,<<1,2,3,4>>} = F(#r{a=0}),
+ {<<1>>,<<2,3,4>>} = F(#r{a=1}),
+ {<<1,2>>,<<3,4>>} = F(#r{a=2}),
+ {<<1,2,3>>,<<4>>} = F(#r{a=3}),
+ {<<1,2,3,4>>,<<>>} = F(#r{a=4})
+ end)(),
+
+ ok.
+
+recv(_Config) ->
+ R = fun(Msg) ->
+ self() ! Msg,
+ Res = receive
+ <<L,I:(L-1)/unit:8,X:32>> -> {I,X};
+ <<L,I:(L-1)/unit:8,X:64>> -> {I,X}
+ end,
+ self() ! {tag,[Msg]},
+ Res = receive
+ {tag,[<<L,I:(8*(L-1)),X:32>>]} -> {I,X};
+ {tag,[<<L,I:(8*(L-1)),X:64>>]} -> {I,X}
+ end
+ end,
+ {1234,16#deadbeef} = R(<<3,1234:16,16#deadbeef:32>>),
+ {99,16#cafebeeff00d} = R(<<2,99:8,16#cafebeeff00d:64>>),
+ ok.
+
+no_match(_Config) ->
+ B = id(<<1,2,3,4>>),
+ no_match = case B of
+ <<Int:(bit_size(B)-1)>> -> Int;
+ <<Int:(bit_size(B)*2)>> -> Int;
+ <<Int:(length(B))>> -> Int;
+ _ -> no_match
+ end,
+ no_match = case B of
+ <<L:8,Int2:(is_integer(L))>> -> Int2;
+ <<L:8,Int2:(L+3.0)>> -> Int2;
+ _ -> no_match
+ end,
+ ok.
+
+id(I) ->
+ I.
diff --git a/lib/debugger/test/exception_SUITE.erl b/lib/debugger/test/exception_SUITE.erl
index ef824b00be..506d4d0366 100644
--- a/lib/debugger/test/exception_SUITE.erl
+++ b/lib/debugger/test/exception_SUITE.erl
@@ -275,67 +275,43 @@ ba_bnot(A) ->
{'EXIT', {badarith, _}} = (catch bnot A).
stacktrace(Conf) when is_list(Conf) ->
- Tag = make_ref(),
- {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
- {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
V = [make_ref()|self()],
{value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
stacktrace_1({'abs',V}, error, {value,V}),
- St1 = erase(stacktrace1),
- St1 = erase(stacktrace2),
- St1 = erlang:get_stacktrace(),
{caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} =
stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
- [{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
- St2 = erase(stacktrace2),
- St2 = erlang:get_stacktrace(),
{caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
stacktrace_1({value,V}, error, {value,V}),
- St3 = erase(stacktrace1),
- St3 = erase(stacktrace2),
- St3 = erlang:get_stacktrace(),
{caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
stacktrace_1({value,V}, error, {throw,V}),
- [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
- St4 = erase(stacktrace2),
- St4 = erlang:get_stacktrace(),
ok.
stacktrace_1(X, C1, Y) ->
- erase(stacktrace1),
- erase(stacktrace2),
try try foo(X) of
C1 -> value1
catch
- C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
+ C1:D1:S1 -> {caught1,D1,S1}
after
- put(stacktrace1, erlang:get_stacktrace()),
foo(Y)
end of
V2 -> {value2,V2}
catch
- C2:D2 -> {caught2,{C2,D2},erlang:get_stacktrace()}
- after
- put(stacktrace2, erlang:get_stacktrace())
+ C2:D2:S2 -> {caught2,{C2,D2},S2}
end.
nested_stacktrace(Conf) when is_list(Conf) ->
V = [{make_ref()}|[self()]],
- value1 =
- nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
- {void,void,void}),
+ value1 = nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
+ {void,void,void}),
{caught1,
[{?MODULE,my_add,2,_}|_],
- value2,
- [{?MODULE,my_add,2,_}|_]} =
- nested_stacktrace_1({{'add',{V,x1}},error,badarith},
- {{value,{V,x2}},void,{V,x2}}),
+ value2} = nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{value,{V,x2}},void,{V,x2}}),
{caught1,
[{?MODULE,my_add,2,_}|_],
- {caught2,[{erlang,abs,[V],_}|_]},
- [{erlang,abs,[V],_}|_]} =
+ {caught2,[{erlang,abs,[V],_}|_]}} =
nested_stacktrace_1({{'add',{V,x1}},error,badarith},
{{'abs',V},error,badarg}),
ok.
@@ -344,15 +320,13 @@ nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
try foo(X1) of
V1 -> value1
catch
- C1:V1 ->
- S1 = erlang:get_stacktrace(),
- T2 =
- try foo(X2) of
- V2 -> value2
- catch
- C2:V2 -> {caught2,erlang:get_stacktrace()}
+ C1:V1:S1 ->
+ T2 = try foo(X2) of
+ V2 -> value2
+ catch
+ C2:V2:S2 -> {caught2,S2}
end,
- {caught1,S1,T2,erlang:get_stacktrace()}
+ {caught1,S1,T2}
end.
@@ -363,17 +337,14 @@ raise(Conf) when is_list(Conf) ->
try
try foo({'div',{1,0}})
catch
- error:badarith ->
- put(raise, A0 = erlang:get_stacktrace()),
+ error:badarith:A0 ->
+ put(raise, A0),
erlang:raise(error, badarith, A0)
end
catch
- error:badarith ->
- A1 = erlang:get_stacktrace(),
+ error:badarith:A1 ->
A1 = get(raise)
end,
- A = erlang:get_stacktrace(),
- A = get(raise),
[{?MODULE,my_div,2,_}|_] = A,
%%
N = 8, % Must be even
@@ -381,19 +352,18 @@ raise(Conf) when is_list(Conf) ->
try even(N)
catch error:function_clause -> ok
end,
- B = odd_even(N, []),
- B = erlang:get_stacktrace(),
%%
- C0 = odd_even(N+1, []),
- C = lists:sublist(C0, N),
- try odd(N+1)
- catch error:function_clause -> ok
+ C = odd_even(N+1, []),
+ try
+ odd(N+1)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
- try erlang:raise(error, function_clause, C0)
- catch error:function_clause -> ok
+ try
+ erlang:raise(error, function_clause, C)
+ catch
+ error:function_clause -> ok
end,
- C = erlang:get_stacktrace(),
ok.
odd_even(N, R) when is_integer(N), N > 1 ->
@@ -436,7 +406,6 @@ my_abs(X) -> abs(X).
gunilla(Config) when is_list(Config) ->
{throw,kalle} = gunilla_1(),
- [] = erlang:get_stacktrace(),
ok.
gunilla_1() ->
diff --git a/lib/debugger/test/int_eval_SUITE_data/stacktrace.erl b/lib/debugger/test/int_eval_SUITE_data/stacktrace.erl
index 3380178fdc..591841ada3 100644
--- a/lib/debugger/test/int_eval_SUITE_data/stacktrace.erl
+++ b/lib/debugger/test/int_eval_SUITE_data/stacktrace.erl
@@ -3,10 +3,15 @@
?MODULE() ->
OldDepth = erlang:system_flag(backtrace_depth, 32),
- done = (catch do_try()),
- Stk = trim(erlang:get_stacktrace()),
- erlang:system_flag(backtrace_depth, OldDepth),
- {done,Stk}.
+ try
+ do_try()
+ catch
+ throw:done:Stk0 ->
+ Stk = trim(Stk0),
+ {done,Stk}
+ after
+ erlang:system_flag(backtrace_depth, OldDepth)
+ end.
trim([{int_eval_SUITE,_,_,_}|_]) ->
[];
diff --git a/lib/debugger/test/line_number_SUITE.erl b/lib/debugger/test/line_number_SUITE.erl
index 276473b95f..4ad84b5a3b 100644
--- a/lib/debugger/test/line_number_SUITE.erl
+++ b/lib/debugger/test/line_number_SUITE.erl
@@ -90,8 +90,8 @@ close_calls(Where) -> %Line 86
call2(), %Line 90
call3(), %Line 91
no_crash %Line 92
- catch error:crash ->
- erlang:get_stacktrace() %Line 94
+ catch error:crash:Stk ->
+ Stk %Line 94
end. %Line 95
call1() -> %Line 97
diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl
index 4d8a86f5a2..7c4ded5082 100644
--- a/lib/debugger/test/map_SUITE.erl
+++ b/lib/debugger/test/map_SUITE.erl
@@ -88,7 +88,10 @@
%% misc
t_pdict/1,
- t_ets/1
+ t_ets/1,
+
+ %% new in OTP 23
+ t_key_expressions/1
]).
-include_lib("stdlib/include/ms_transform.hrl").
@@ -150,7 +153,10 @@ all() -> [
%% Other functions
t_pdict,
- t_ets
+ t_ets,
+
+ %% new in OTP 23
+ t_key_expressions
].
groups() -> [].
@@ -2233,6 +2239,48 @@ validate_frequency([{T,C}|Fs],Tf) ->
end;
validate_frequency([], _) -> ok.
+t_key_expressions(_Config) ->
+ Int = id(42),
+ #{{tag,Int} := 42} = id(#{{tag,Int} => 42}),
+ #{{tag,Int+1} := 42} = id(#{{tag,Int+1} => 42}),
+ #{{a,b} := x, {tag,Int} := 42, Int := 0} =
+ id(#{{a,b} => x, {tag,Int} => 42, Int => 0}),
+
+ F1 = fun(#{Int + 1 := Val}) -> Val end,
+ val = F1(#{43 => val}),
+ {'EXIT',_} = (catch F1(a)),
+
+ F2 = fun(M, X, Y) ->
+ case M of
+ #{element(X, Y) := <<Sz:16,Bin:Sz/binary>>} ->
+ Bin;
+ #{} ->
+ not_found;
+ {A,B} ->
+ A + B
+ end
+ end,
+ <<"xyz">> = F2(#{b => <<3:16,"xyz">>}, 2, {a,b,c}),
+ not_found = F2(#{b => <<3:16,"xyz">>}, 999, {a,b,c}),
+ 13 = F2({6,7}, 1, 2),
+
+ #{<<"Спутник"/utf8>> := 1} = id(#{<<"Спутник"/utf8>> => 1}),
+
+ F3 = fun(Arg) ->
+ erase(once),
+ RunOnce = fun(I) ->
+ undefined = put(once, twice),
+ id(I)
+ end,
+ case RunOnce(Arg) of
+ #{{tag,<<Int:42>>} := Value} -> Value;
+ {X,Y} -> X + Y
+ end
+ end,
+ 10 = F3({7,3}),
+ whatever = F3(#{{tag,<<Int:42>>} => whatever}),
+
+ ok.
%% aux
diff --git a/lib/dialyzer/Makefile b/lib/dialyzer/Makefile
index e4f681dcd9..53083f267d 100644
--- a/lib/dialyzer/Makefile
+++ b/lib/dialyzer/Makefile
@@ -42,3 +42,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler syntax_tools hipe wx
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/dialyzer/doc/src/Makefile b/lib/dialyzer/doc/src/Makefile
index 3ce777392b..bcceb3b661 100644
--- a/lib/dialyzer/doc/src/Makefile
+++ b/lib/dialyzer/doc/src/Makefile
@@ -26,15 +26,11 @@ VSN=$(DIALYZER_VSN)
APPLICATION=dialyzer
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = dialyzer.xml typer.xml
+XML_REF3_FILES = dialyzer.xml
+XML_REF1_FILES = typer_cmd.xml
XML_PART_FILES = part.xml
XML_CHAPTER_FILES = dialyzer_chapter.xml notes.xml
@@ -43,72 +39,10 @@ BOOK_FILES = book.xml
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
- $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-
-
-# ----------------------------------------------------
-
-TEXT_FILES = \
- ../about.txt \
- ../manual.txt \
- ../warnings.txt
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF1_FILES) \
+ $(XML_APPLICATION_FILES)
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-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 -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/dialyzer/doc/src/ref_man.xml b/lib/dialyzer/doc/src/ref_man.xml
index d8cf324232..74455143b1 100644
--- a/lib/dialyzer/doc/src/ref_man.xml
+++ b/lib/dialyzer/doc/src/ref_man.xml
@@ -31,6 +31,5 @@
<description>
</description>
<xi:include href="dialyzer.xml"/>
- <xi:include href="typer.xml"/>
+ <xi:include href="typer_cmd.xml"/>
</application>
-
diff --git a/lib/dialyzer/doc/src/typer.xml b/lib/dialyzer/doc/src/typer_cmd.xml
index 524956ed4b..b9e75bdd2a 100644
--- a/lib/dialyzer/doc/src/typer.xml
+++ b/lib/dialyzer/doc/src/typer_cmd.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE comref SYSTEM "comref.dtd">
-<erlref>
+<comref>
<header>
<copyright>
<year>2006</year><year>2017</year>
@@ -29,9 +29,9 @@
<rev></rev>
<file>type.xml</file>
</header>
- <module>typer</module>
- <modulesummary>Typer, a Type annotator for ERlang programs.
- </modulesummary>
+ <com>typer</com>
+ <comsummary>Typer, a Type annotator for ERlang programs.
+ </comsummary>
<description>
<p>TypEr shows type information for Erlang modules to the user.
Additionally, it can annotate the code of files with such type
@@ -154,4 +154,4 @@ typer [--help] [--version] [--plt PLT] [--edoc]
</section>
-</erlref>
+</comref>
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index bddd761705..1f5b308c7d 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -53,6 +53,7 @@ MODULES = \
dialyzer_callgraph \
dialyzer_cl \
dialyzer_cl_parse \
+ dialyzer_clean_core \
dialyzer_codeserver \
dialyzer_contracts \
dialyzer_dataflow \
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index e3a0fc967d..36f5d96ea6 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -28,6 +28,7 @@
dialyzer_callgraph,
dialyzer_cl,
dialyzer_cl_parse,
+ dialyzer_clean_core,
dialyzer_codeserver,
dialyzer_contracts,
dialyzer_coordinator,
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 5e680062fb..f887f661bd 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -320,6 +320,12 @@ report_analysis_start(#options{analysis_type = Type,
end
end.
+report_native_comp(#options{report_mode = ReportMode}) ->
+ case ReportMode of
+ quiet -> ok;
+ _ -> io:format(" Compiling some key modules to native code...")
+ end.
+
report_elapsed_time(T1, T2, #options{report_mode = ReportMode}) ->
case ReportMode of
quiet -> ok;
@@ -369,6 +375,7 @@ do_analysis(Options) ->
do_analysis(Files, Options, Plt, PltInfo) ->
assert_writable(Options#options.output_plt),
+ hipe_compile(Files, Options),
report_analysis_start(Options),
State0 = new_state(),
State1 = init_output(State0, Options),
@@ -477,6 +484,106 @@ expand_dependent_modules_1([Mod|Mods], Included, ModDeps) ->
expand_dependent_modules_1([], Included, _ModDeps) ->
Included.
+-define(MIN_PARALLELISM, 7).
+-define(MIN_FILES_FOR_NATIVE_COMPILE, 20).
+
+-spec hipe_compile([file:filename()], #options{}) -> 'ok'.
+
+hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
+ NoNative = (get(dialyzer_options_native) =:= false),
+ FewFiles = (length(Files) < ?MIN_FILES_FOR_NATIVE_COMPILE),
+ case NoNative orelse FewFiles orelse ErlangMode of
+ true -> ok;
+ false ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> ok;
+ _ ->
+ Mods = [lists, dict, digraph, digraph_utils, ets,
+ gb_sets, gb_trees, ordsets, sets, sofs,
+ cerl, erl_types, cerl_trees, erl_bif_types,
+ dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
+ dialyzer_codeserver, dialyzer_contracts,
+ dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
+ dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig,
+ dialyzer_worker],
+ report_native_comp(Options),
+ {T1, _} = statistics(wall_clock),
+ Cache = (get(dialyzer_options_native_cache) =/= false),
+ native_compile(Mods, Cache),
+ {T2, _} = statistics(wall_clock),
+ report_elapsed_time(T1, T2, Options)
+ end
+ end.
+
+native_compile(Mods, Cache) ->
+ case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
+ true ->
+ Parent = self(),
+ Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods],
+ lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
+ false ->
+ lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods)
+ end.
+
+hc(Mod, Cache) ->
+ {module, Mod} = code:ensure_loaded(Mod),
+ case code:is_module_native(Mod) of
+ true -> ok;
+ false ->
+ %% io:format(" ~w", [Mod]),
+ case Cache of
+ false ->
+ {ok, Mod} = hipe:c(Mod),
+ ok;
+ true ->
+ hc_cache(Mod)
+ end
+ end.
+
+hc_cache(Mod) ->
+ CacheBase = cache_base_dir(),
+ %% Use HiPE architecture, version and erts checksum in directory name,
+ %% to avoid clashes between incompatible binaries.
+ HipeArchVersion =
+ lists:concat(
+ [erlang:system_info(hipe_architecture), "-",
+ hipe:version(), "-",
+ hipe:erts_checksum()]),
+ CacheDir = filename:join(CacheBase, HipeArchVersion),
+ OrigBeamFile = code:which(Mod),
+ {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile),
+ CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])),
+ ok = filelib:ensure_dir(CachedBeamFile),
+ ModBin =
+ case filelib:is_file(CachedBeamFile) of
+ true ->
+ {ok, BinFromFile} = file:read_file(CachedBeamFile),
+ BinFromFile;
+ false ->
+ {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]),
+ ok = file:write_file(CachedBeamFile, CompiledBin),
+ CompiledBin
+ end,
+ code:unstick_dir(filename:dirname(OrigBeamFile)),
+ {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin),
+ true = code:is_module_native(Mod),
+ ok.
+
+cache_base_dir() ->
+ %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
+ %% If XDG_CACHE_HOME is set to an absolute path, use it as base.
+ XdgCacheHome = os:getenv("XDG_CACHE_HOME"),
+ CacheHome =
+ case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of
+ true ->
+ XdgCacheHome;
+ false ->
+ %% Otherwise, the default is $HOME/.cache.
+ {ok, [[Home]]} = init:get_argument(home),
+ filename:join(Home, ".cache")
+ end,
+ filename:join([CacheHome, "dialyzer_hipe_cache"]).
+
new_state() ->
#cl_state{}.
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index cadc2116b0..a3ec1b92f1 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -330,12 +330,16 @@ get_lib_dir(Apps) ->
get_lib_dir([H|T], Acc) ->
NewElem =
case code:lib_dir(list_to_atom(H)) of
- {error, bad_name} ->
- case H =:= "erts" of % hack for including erts in an un-installed system
- true -> filename:join(code:root_dir(), "erts/preloaded/ebin");
- false -> H
- end;
- LibDir -> LibDir ++ "/ebin"
+ {error, bad_name} -> H;
+ LibDir when H =:= "erts" -> % hack for including erts in an un-installed system
+ EbinDir = filename:join([LibDir,"ebin"]),
+ case file:read_file_info(EbinDir) of
+ {error,enoent} ->
+ filename:join([LibDir,"preloaded","ebin"]);
+ _ ->
+ EbinDir
+ end;
+ LibDir -> filename:join(LibDir,"ebin")
end,
get_lib_dir(T, [NewElem|Acc]);
get_lib_dir([], Acc) ->
diff --git a/lib/dialyzer/src/dialyzer_clean_core.erl b/lib/dialyzer/src/dialyzer_clean_core.erl
new file mode 100644
index 0000000000..d591ad3473
--- /dev/null
+++ b/lib/dialyzer/src/dialyzer_clean_core.erl
@@ -0,0 +1,225 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+
+-module(dialyzer_clean_core).
+-export([clean/1]).
+
+-spec clean(cerl:cerl()) -> cerl:cerl().
+
+clean(Tree) ->
+ case cerl:type(Tree) of
+ apply ->
+ Op = clean(cerl:apply_op(Tree)),
+ Args = clean_list(cerl:apply_args(Tree)),
+ cerl:update_c_apply(Tree, Op, Args);
+ binary ->
+ Segments = clean_list(cerl:binary_segments(Tree)),
+ cerl:update_c_binary(Tree, Segments);
+ bitstr ->
+ Val = clean(cerl:bitstr_val(Tree)),
+ Size = clean(cerl:bitstr_size(Tree)),
+ Unit = cerl:bitstr_unit(Tree),
+ Type = cerl:bitstr_type(Tree),
+ Flags = cerl:bitstr_flags(Tree),
+ cerl:update_c_bitstr(Tree, Val, Size, Unit, Type, Flags);
+ 'case' ->
+ Arg = clean(cerl:case_arg(Tree)),
+ Clauses = clean_clauses(cerl:case_clauses(Tree)),
+ cerl:update_c_case(Tree, Arg, Clauses);
+ call ->
+ Args = clean_list(cerl:call_args(Tree)),
+ Module = clean(cerl:call_module(Tree)),
+ Name = clean(cerl:call_name(Tree)),
+ cerl:update_c_call(Tree, Module, Name, Args);
+ 'catch' ->
+ Body = clean(cerl:catch_body(Tree)),
+ cerl:update_c_catch(Tree, Body);
+ cons ->
+ Hd = clean(cerl:cons_hd(Tree)),
+ Tl = clean(cerl:cons_tl(Tree)),
+ cerl:update_c_cons_skel(Tree, Hd, Tl);
+ 'fun' ->
+ Body = clean(cerl:fun_body(Tree)),
+ Vars = cerl:fun_vars(Tree),
+ cerl:update_c_fun(Tree, Vars, Body);
+ 'let' ->
+ Arg = clean(cerl:let_arg(Tree)),
+ Body = clean(cerl:let_body(Tree)),
+ Vars = cerl:let_vars(Tree),
+ cerl:update_c_let(Tree, Vars, Arg, Body);
+ letrec ->
+ clean_letrec(Tree);
+ literal ->
+ Tree;
+ module ->
+ Defs = clean_defs(cerl:module_defs(Tree)),
+ Name = cerl:module_name(Tree),
+ Exports = cerl:module_exports(Tree),
+ Attrs = cerl:module_attrs(Tree),
+ cerl:update_c_module(Tree, Name, Exports, Attrs, Defs);
+ primop ->
+ Args = clean_list(cerl:primop_args(Tree)),
+ Name = cerl:primop_name(Tree),
+ cerl:update_c_primop(Tree, Name, Args);
+ 'receive' ->
+ Clauses = clean_clauses(cerl:receive_clauses(Tree)),
+ Timeout = clean(cerl:receive_timeout(Tree)),
+ Action = clean(cerl:receive_action(Tree)),
+ cerl:update_c_receive(Tree, Clauses, Timeout, Action);
+ seq ->
+ Arg = clean(cerl:seq_arg(Tree)),
+ Body = clean(cerl:seq_body(Tree)),
+ cerl:update_c_seq(Tree, Arg, Body);
+ 'try' ->
+ Arg = clean(cerl:try_arg(Tree)),
+ Body = clean(cerl:try_body(Tree)),
+ Handler = clean(cerl:try_handler(Tree)),
+ Vs = cerl:try_vars(Tree),
+ Evs = cerl:try_evars(Tree),
+ Try = cerl:update_c_try(Tree, Arg, Vs, Body, Evs, Handler),
+ Try;
+ tuple ->
+ Elements = clean_list(cerl:tuple_es(Tree)),
+ cerl:update_c_tuple_skel(Tree, Elements);
+ map ->
+ Arg = clean(cerl:map_arg(Tree)),
+ Entries = clean_map_pairs(cerl:map_es(Tree)),
+ cerl:update_c_map(Tree, Arg, Entries);
+ values ->
+ Elements = clean_list(cerl:values_es(Tree)),
+ cerl:update_c_values(Tree, Elements);
+ var ->
+ Tree
+ end.
+
+clean_letrec(Tree) ->
+ case lists:member(letrec_goto, cerl:get_ann(Tree)) of
+ true ->
+ %% This is a restricted form of letrec used to allow rewriting
+ %% pattern matching without duplicating code. When a letrec is
+ %% used in this way, Dialyzer will not be able to infer much
+ %% type information, so we will need to eliminate the letrec.
+ [{_Name, Fun}] = cerl:letrec_defs(Tree),
+ FunBody = cerl:fun_body(Fun),
+ FunBody1 = clean(FunBody),
+ Body = clean(cerl:letrec_body(Tree)),
+ case dialyzer_ignore(Body) of
+ true ->
+ %% The body of the letrec directly transfer controls to
+ %% defined function in the letrec. We only need to keep
+ %% the body of that function. (This is is the code for
+ %% a receive construct.)
+ FunBody1;
+ false ->
+ %% The body is non-trivial. Here is an example:
+ %%
+ %% letrec 'more_matching'/0 =
+ %% fun () ->
+ %% case CaseExpr of . . . end
+ %% end
+ %% in case CaseExpr of
+ %% <<..., Tail>> ->
+ %% case Tail of
+ %% <<...>> -> . . .
+ %% _ -> apply 'more_matching'/0()
+ %% end
+ %% _ -> apply 'more_matching'/0()
+ %% end
+ %%
+ %% The clauses that invoke `apply` are marked with
+ %% a `dialyzer_ignore` annotation to indicate that
+ %% Dialyzer should ignore them.
+ %%
+ %% The example is translated like this:
+ %%
+ %% case primop:dialyzer_unknown() of
+ %% 'a' ->
+ %% case Var of
+ %% <<..., Tail>> ->
+ %% case Tail of
+ %% <<...>> -> . . .
+ %% end
+ %% end
+ %% 'b' ->
+ %% %% Body of more_matching/0.
+ %% case Var of . . . end
+ %% end
+ %%
+ PrimopUnknown = cerl:c_primop(cerl:abstract(dialyzer_unknown), []),
+ Clauses = [cerl:c_clause([cerl:abstract(a)], Body),
+ cerl:c_clause([cerl:abstract(b)], FunBody1)],
+ cerl:c_case(PrimopUnknown, Clauses)
+ end;
+ false ->
+ %% This is a plain letrec. (Originating from a list or binary comprehension.)
+ Defs = clean_defs(cerl:letrec_defs(Tree)),
+ Body = clean(cerl:letrec_body(Tree)),
+ cerl:update_c_letrec(Tree, Defs, Body)
+ end.
+
+clean_defs(Defs) ->
+ [{Name, clean(Fun)} || {Name, Fun} <- Defs].
+
+clean_clauses([Clause|Tail]) ->
+ case clean_clause(Clause) of
+ ignore ->
+ %% The clause is either annotated with `dialyzer_ignore` or its
+ %% body is primop that raises an exception.
+ clean_clauses(Tail);
+ Clause1 ->
+ Tail1 = clean_clauses(Tail),
+ [Clause1|Tail1]
+ end;
+clean_clauses([]) ->
+ [].
+
+clean_clause(Clause) ->
+ Body = cerl:clause_body(Clause),
+ case dialyzer_ignore(Clause) orelse is_raising_body(Body) of
+ true ->
+ ignore;
+ false ->
+ G = clean(cerl:clause_guard(Clause)),
+ Body1 = clean(Body),
+ Pats = cerl:clause_pats(Clause),
+ cerl:update_c_clause(Clause, Pats, G, Body1)
+ end.
+
+is_raising_body(Body) ->
+ case cerl:type(Body) of
+ primop ->
+ case cerl:atom_val(cerl:primop_name(Body)) of
+ match_fail -> true;
+ raise -> true;
+ _ -> false
+ end;
+ _ ->
+ false
+ end.
+
+clean_list(Trees) ->
+ [clean(Tree) || Tree <- Trees].
+
+clean_map_pairs([Pair|Pairs]) ->
+ Key = clean(cerl:map_pair_key(Pair)),
+ Val = clean(cerl:map_pair_val(Pair)),
+ Pairs1 = clean_map_pairs(Pairs),
+ Op = cerl:map_pair_op(Pair),
+ Pair1 = cerl:update_c_map_pair(Pair, Op, Key, Val),
+ [Pair1|Pairs1];
+clean_map_pairs([]) ->
+ [].
+
+dialyzer_ignore(Tree) ->
+ lists:member(dialyzer_ignore, cerl:get_ann(Tree)).
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 55c77814f8..cff3981393 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -298,15 +298,36 @@ traverse(Tree, Map, State) ->
module ->
handle_module(Tree, Map, State);
primop ->
- Type =
- case cerl:atom_val(cerl:primop_name(Tree)) of
- match_fail -> t_none();
- raise -> t_none();
- bs_init_writable -> t_from_term(<<>>);
- build_stacktrace -> erl_bif_types:type(erlang, build_stacktrace, 0);
- Other -> erlang:error({'Unsupported primop', Other})
- end,
- {State, Map, Type};
+ case cerl:atom_val(cerl:primop_name(Tree)) of
+ match_fail ->
+ {State, Map, t_none()};
+ raise ->
+ {State, Map, t_none()};
+ bs_init_writable ->
+ {State, Map, t_from_term(<<>>)};
+ build_stacktrace ->
+ {State, Map, erl_bif_types:type(erlang, build_stacktrace, 0)};
+ dialyzer_unknown ->
+ {State, Map, t_any()};
+ recv_peek_message ->
+ {State, Map, t_product([t_boolean(), t_any()])};
+ recv_wait_timeout ->
+ [Arg] = cerl:primop_args(Tree),
+ {State1, Map1, TimeoutType} = traverse(Arg, Map, State),
+ Opaques = State1#state.opaques,
+ case t_is_atom(TimeoutType, Opaques) andalso
+ t_atom_vals(TimeoutType, Opaques) =:= ['infinity'] of
+ true ->
+ {State1, Map1, t_boolean()};
+ false ->
+ {State1, Map1, t_boolean()}
+ end;
+ remove_message ->
+ {State, Map, t_any()};
+ timeout ->
+ {State, Map, t_any()};
+ Other -> erlang:error({'Unsupported primop', Other})
+ end;
'receive' ->
handle_receive(Tree, Map, State);
seq ->
@@ -967,7 +988,7 @@ handle_call(Tree, Map, State) ->
handle_case(Tree, Map, State) ->
Arg = cerl:case_arg(Tree),
- Clauses = filter_match_fail(cerl:case_clauses(Tree)),
+ Clauses = cerl:case_clauses(Tree),
{State1, Map1, ArgType} = SMA = traverse(Arg, Map, State),
case t_is_none_or_unit(ArgType) of
true -> SMA;
@@ -1084,7 +1105,7 @@ handle_module(Tree, Map, State) ->
%%----------------------------------------
handle_receive(Tree, Map, State) ->
- Clauses = filter_match_fail(cerl:receive_clauses(Tree)),
+ Clauses = cerl:receive_clauses(Tree),
Timeout = cerl:receive_timeout(Tree),
State1 =
case is_race_analysis_enabled(State) of
@@ -3019,24 +3040,6 @@ is_lc_simple_list(Tree, TreeType, State) ->
andalso t_is_list(TreeType)
andalso t_is_simple(t_list_elements(TreeType, Opaques), State).
-filter_match_fail([Clause] = Cls) ->
- Body = cerl:clause_body(Clause),
- case cerl:type(Body) of
- primop ->
- case cerl:atom_val(cerl:primop_name(Body)) of
- match_fail -> [];
- raise -> [];
- _ -> Cls
- end;
- _ -> Cls
- end;
-filter_match_fail([H|T]) ->
- [H|filter_match_fail(T)];
-filter_match_fail([]) ->
- %% This can actually happen, for example in
- %% receive after 1 -> ok end
- [].
-
%%% ===========================================================================
%%%
%%% The State.
@@ -3819,7 +3822,20 @@ find_terminals(Tree) ->
%% We cannot make assumptions. Say that both are true.
{true, true}
end;
- 'case' -> find_terminals_list(cerl:case_clauses(Tree));
+ 'case' ->
+ case cerl:case_clauses(Tree) of
+ [] ->
+ case lists:member(receive_timeout, cerl:get_ann(Tree)) of
+ true ->
+ %% Handle a never ending receive without any
+ %% clauses specially. (Not sure why.)
+ {false, true};
+ false ->
+ {false, false}
+ end;
+ [_|_] ->
+ find_terminals_list(cerl:case_clauses(Tree))
+ end;
'catch' -> find_terminals(cerl:catch_body(Tree));
clause -> find_terminals(cerl:clause_body(Tree));
cons -> {false, true};
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index 36cdc0876c..c8e9256bc8 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -87,7 +87,8 @@ traverse(Tree, Out, State, CurrentFun) ->
true ->
%% Op is a variable and should not be marked as escaping
%% based on its use.
- OpFuns = case map__lookup(cerl_trees:get_label(Op), Out) of
+ OpLabel = cerl_trees:get_label(Op),
+ OpFuns = case map__lookup(OpLabel, Out) of
none -> output(none);
{value, OF} -> OF
end,
@@ -96,7 +97,13 @@ traverse(Tree, Out, State, CurrentFun) ->
State4 = state__add_deps(CurrentFun, OpFuns, State3),
State5 = state__store_callsite(cerl_trees:get_label(Tree),
OpFuns, length(Args), State4),
- {output(set__singleton(external)), State5}
+ case state__get_rvals(OpLabel, State5) of
+ 1 ->
+ {output(set__singleton(external)), State5};
+ NumRvals ->
+ List = lists:duplicate(NumRvals, output(set__singleton(external))),
+ {output(List), State5}
+ end
end;
binary ->
{output(none), State};
@@ -137,9 +144,12 @@ traverse(Tree, Out, State, CurrentFun) ->
Vars = cerl:let_vars(Tree),
Arg = cerl:let_arg(Tree),
Body = cerl:let_body(Tree),
- {ArgFuns, State1} = traverse(Arg, Out, State, CurrentFun),
+ OldNumRvals = state__num_rvals(State),
+ State1 = state__store_num_rvals(length(Vars), State),
+ {ArgFuns, State2} = traverse(Arg, Out, State1, CurrentFun),
Out1 = bind_list(Vars, ArgFuns, Out),
- traverse(Body, Out1, State1, CurrentFun);
+ State3 = state__store_num_rvals(OldNumRvals, State2),
+ traverse(Body, Out1, State3, CurrentFun);
letrec ->
Defs = cerl:letrec_defs(Tree),
Body = cerl:letrec_body(Tree),
@@ -147,14 +157,15 @@ traverse(Tree, Out, State, CurrentFun) ->
state__add_letrecs(cerl_trees:get_label(Var), cerl_trees:get_label(Fun), Acc)
end, State, Defs),
Out1 = bind_defs(Defs, Out),
- State2 = traverse_defs(Defs, Out1, State1, CurrentFun),
+ NumRvals = state__num_rvals(State1),
+ State2 = traverse_defs(Defs, Out1, State1, CurrentFun, NumRvals),
traverse(Body, Out1, State2, CurrentFun);
literal ->
{output(none), State};
module ->
Defs = cerl:module_defs(Tree),
Out1 = bind_defs(Defs, Out),
- State1 = traverse_defs(Defs, Out1, State, CurrentFun),
+ State1 = traverse_defs(Defs, Out1, State, CurrentFun, 1),
{output(none), State1};
primop ->
Args = cerl:primop_args(Tree),
@@ -223,14 +234,15 @@ traverse_list([Tree|Left], Out, State, CurrentFun, Acc) ->
traverse_list([], _Out, State, _CurrentFun, Acc) ->
{output(lists:reverse(Acc)), State}.
-traverse_defs([{_, Fun}|Left], Out, State, CurrentFun) ->
- {_, State1} = traverse(Fun, Out, State, CurrentFun),
- traverse_defs(Left, Out, State1, CurrentFun);
-traverse_defs([], _Out, State, _CurrentFun) ->
+traverse_defs([{_, Fun}|Left], Out, State, CurrentFun, NumRvals) ->
+ State1 = state__store_num_rvals(NumRvals, State),
+ {_, State2} = traverse(Fun, Out, State1, CurrentFun),
+ traverse_defs(Left, Out, State2, CurrentFun, NumRvals);
+traverse_defs([], _Out, State, _CurrentFun, _NumRvals) ->
State.
traverse_clauses(Clauses, ArgFuns, Out, State, CurrentFun) ->
- case filter_match_fail(Clauses) of
+ case Clauses of
[] ->
%% Can happen for example with receives used as timouts.
{output(none), State};
@@ -249,24 +261,6 @@ traverse_clauses([Clause|Left], ArgFuns, Out, State, CurrentFun, Acc) ->
traverse_clauses([], _ArgFuns, _Out, State, _CurrentFun, Acc) ->
{merge_outs(Acc), State}.
-filter_match_fail([Clause]) ->
- Body = cerl:clause_body(Clause),
- case cerl:type(Body) of
- primop ->
- case cerl:atom_val(cerl:primop_name(Body)) of
- match_fail -> [];
- raise -> [];
- _ -> [Clause]
- end;
- _ -> [Clause]
- end;
-filter_match_fail([H|T]) ->
- [H|filter_match_fail(T)];
-filter_match_fail([]) ->
- %% This can actually happen, for example in
- %% receive after 1 -> ok end
- [].
-
remote_call(Tree, ArgFuns, State) ->
M = cerl:call_module(Tree),
F = cerl:call_name(Tree),
@@ -483,12 +477,16 @@ all_vars(Tree, AccIn) ->
%%
-type local_set() :: 'none' | #set{}.
+-type rvals() :: #{label() => non_neg_integer()}.
-record(state, {deps :: deps(),
esc :: local_set(),
calls :: calls(),
arities :: dict:dict(label() | 'top', arity()),
- letrecs :: letrecs()}).
+ letrecs :: letrecs(),
+ num_rvals = 1 :: non_neg_integer(),
+ rvals = #{} :: rvals()
+ }).
state__new(Tree) ->
Exports = set__from_list([X || X <- cerl:module_exports(Tree)]),
@@ -526,8 +524,11 @@ state__add_deps(From, #output{type = single, content = To},
%% io:format("Adding deps from ~w to ~w\n", [From, set__to_ordsets(To)]),
State#state{deps = map__add(From, To, Map)}.
-state__add_letrecs(Var, Fun, #state{letrecs = Map} = State) ->
- State#state{letrecs = map__store(Var, Fun, Map)}.
+state__add_letrecs(Var, Fun, #state{letrecs = Map,
+ num_rvals = NumRvals,
+ rvals = Rvals} = State) ->
+ State#state{letrecs = map__store(Var, Fun, Map),
+ rvals = Rvals#{Var => NumRvals}}.
state__deps(#state{deps = Deps}) ->
Deps.
@@ -539,6 +540,10 @@ state__add_esc(#output{content = none}, State) ->
State;
state__add_esc(#output{type = single, content = Set},
#state{esc = Esc} = State) ->
+ State#state{esc = set__union(Set, Esc)};
+state__add_esc(#output{type = list, content = [H|T]},
+ #state{esc = Esc} = State) ->
+ #output{type = single, content = Set} = merge_outs(T, H),
State#state{esc = set__union(Set, Esc)}.
state__esc(#state{esc = Esc}) ->
@@ -559,6 +564,19 @@ state__store_callsite(From, To, CallArity,
state__calls(#state{calls = Calls}) ->
Calls.
+state__store_num_rvals(NumRval, State) ->
+ State#state{num_rvals = NumRval}.
+
+state__num_rvals(#state{num_rvals = NumRvals}) ->
+ NumRvals.
+
+state__get_rvals(FunLabel, #state{rvals = Rvals}) ->
+ case Rvals of
+ #{FunLabel := NumRvals} -> NumRvals;
+ #{} -> 1
+ end.
+
+
%%------------------------------------------------------------
%% A test function. Not part of the intended interface.
%%
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 2af4534396..fe85fa81de 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -539,7 +539,7 @@ compute_md5_from_file(File) ->
Filtered = [[ID, Chunk] || {ID, Chunk} <- Chunks, ID =/= "CInf", ID =/= "Docs"],
erlang:md5(lists:sort(Filtered));
{error, beam_lib, {file_error, _, enoent}} ->
- Msg = io_lib:format("Not a regular file: ~ts\n", [File]),
+ Msg = io_lib:format("File not found: ~ts\n", [File]),
throw({dialyzer_error, Msg});
{error, beam_lib, _} ->
Msg = io_lib:format("Could not compute MD5 for .beam: ~ts\n", [File]),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index dede475f98..5f40f80ae7 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -296,7 +296,7 @@ traverse(Tree, DefinedVars, State) ->
{State4, mk_var(Tree)};
'case' ->
Arg = cerl:case_arg(Tree),
- Clauses = filter_match_fail(cerl:case_clauses(Tree)),
+ Clauses = cerl:case_clauses(Tree),
{State1, ArgVar} = traverse(Arg, DefinedVars, State),
handle_clauses(Clauses, mk_var(Tree), ArgVar, DefinedVars, State1);
call ->
@@ -423,10 +423,31 @@ traverse(Tree, DefinedVars, State) ->
Type = erl_bif_types:type(erlang, build_stacktrace, 0),
State1 = state__store_conj(V, sub, Type, State),
{State1, V};
+ dialyzer_unknown ->
+ %% See dialyzer_clean_core:clean_letrec/1.
+ {State, mk_var(Tree)};
+ recv_peek_message ->
+ {State1, Vars} = state__mk_vars(2, State),
+ {State1, t_product(Vars)};
+ recv_wait_timeout ->
+ [Timeout] = cerl:primop_args(Tree),
+ case cerl:is_c_atom(Timeout) andalso
+ cerl:atom_val(Timeout) =:= infinity of
+ true ->
+ {State, t_none()};
+ false ->
+ {State1, TimeoutVar} = traverse(Timeout, DefinedVars, State),
+ State2 = state__store_conj(TimeoutVar, sub, t_timeout(), State1),
+ {State2, mk_var(Tree)}
+ end;
+ remove_message ->
+ {State, t_any()};
+ timeout ->
+ {State, t_any()};
Other -> erlang:error({'Unsupported primop', Other})
end;
'receive' ->
- Clauses = filter_match_fail(cerl:receive_clauses(Tree)),
+ Clauses = cerl:receive_clauses(Tree),
Timeout = cerl:receive_timeout(Tree),
case (cerl:is_c_atom(Timeout) andalso
(cerl:atom_val(Timeout) =:= infinity)) of
@@ -829,24 +850,6 @@ get_plt_constr(MFA, Dst, ArgVars, State) ->
get_contract_return(C, ArgTypes) ->
dialyzer_contracts:get_contract_return(C, ArgTypes).
-filter_match_fail([Clause] = Cls) ->
- Body = cerl:clause_body(Clause),
- case cerl:type(Body) of
- primop ->
- case cerl:atom_val(cerl:primop_name(Body)) of
- match_fail -> [];
- raise -> [];
- _ -> Cls
- end;
- _ -> Cls
- end;
-filter_match_fail([H|T]) ->
- [H|filter_match_fail(T)];
-filter_match_fail([]) ->
- %% This can actually happen, for example in
- %% receive after 1 -> ok end
- [].
-
%% If there is a significant number of clauses, we cannot apply the
%% list subtraction scheme since it causes the analysis to be too
%% slow. Typically, this only affects automatically generated files.
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 245c099fef..f679f146cc 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -99,7 +99,7 @@ get_core_from_src(File, Opts) ->
case compile:noenv_file(File, Opts ++ src_compiler_opts()) of
error -> {error, []};
{error, Errors, _} -> {error, format_errors(Errors)};
- {ok, _, Core} -> {ok, Core}
+ {ok, _, Core} -> {ok, dialyzer_clean_core:clean(Core)}
end.
-type get_core_from_beam_ret() :: {'ok', cerl:c_module()} | {'error', string()}.
@@ -116,7 +116,7 @@ get_core_from_beam(File, Opts) ->
{ok, {Module, [{debug_info, {debug_info_v1, Backend, Metadata}}]}} ->
case Backend:debug_info(core_v1, Module, Metadata, Opts ++ src_compiler_opts()) of
{ok, Core} ->
- {ok, Core};
+ {ok, dialyzer_clean_core:clean(Core)};
{error, _} ->
{error, " Could not get Core Erlang code for: " ++ File ++ "\n"}
end;
diff --git a/lib/dialyzer/test/map_SUITE_data/results/maps_remove b/lib/dialyzer/test/map_SUITE_data/results/maps_remove
new file mode 100644
index 0000000000..32e43466b0
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/results/maps_remove
@@ -0,0 +1,4 @@
+
+maps_remove.erl:22: Function get/2 has no local return
+maps_remove.erl:23: The call maps:get(K::'a',M::#{}) will never return since the success typing arguments are (any(),map())
+maps_remove.erl:7: Function t1/0 has no local return
diff --git a/lib/dialyzer/test/map_SUITE_data/src/maps_remove.erl b/lib/dialyzer/test/map_SUITE_data/src/maps_remove.erl
new file mode 100644
index 0000000000..b913e389f8
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/maps_remove.erl
@@ -0,0 +1,23 @@
+%% ERL-1002, maps:remove
+
+-module(maps_remove).
+
+-export([t1/0]).
+
+t1() ->
+ A = new(),
+ B = put(a, 1, A),
+ C = remove(a, B),
+ get(a, C).
+
+new() ->
+ maps:new().
+
+put(K, V, M) ->
+ maps:put(K, V, M).
+
+remove(K, M) ->
+ maps:remove(K, M).
+
+get(K, M) ->
+ maps:get(K, M).
diff --git a/lib/dialyzer/test/small_SUITE_data/results/stacktrace b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
index fd60881953..5126a88297 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/stacktrace
+++ b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
@@ -1,5 +1,5 @@
stacktrace.erl:11: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
stacktrace.erl:19: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
-stacktrace.erl:44: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
-stacktrace.erl:53: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:43: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:51: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
diff --git a/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
index de79e710e9..b2a0c04554 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
@@ -39,8 +39,7 @@ t4() ->
s1() ->
try foo:bar()
catch
- E:P ->
- S = erlang:get_stacktrace(),
+ E:P:S ->
{a,b} = S, % can never match
{E, P}
end.
@@ -48,8 +47,7 @@ s1() ->
s2() ->
try foo:bar()
catch
- E:P ->
- S = erlang:get_stacktrace(),
+ E:P:S ->
[a,b] = S, % can never match
{E, P}
end.
@@ -57,8 +55,7 @@ s2() ->
s3() ->
try foo:bar()
catch
- E:P ->
- S = erlang:get_stacktrace(),
+ E:P:S ->
[{m,f,[],[]}] = S,
{E, P}
end.
@@ -66,8 +63,7 @@ s3() ->
s4() ->
try foo:bar()
catch
- E:P ->
- S = erlang:get_stacktrace(),
+ E:P:S ->
[{m,f,1,[{file,"tjo"},{line,95}]}] = S,
{E, P}
end.
diff --git a/lib/diameter/Makefile b/lib/diameter/Makefile
index a0195a0988..a25baeb929 100644
--- a/lib/diameter/Makefile
+++ b/lib/diameter/Makefile
@@ -27,7 +27,6 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-info:
- @echo "APP_VSN = $(APP_VSN)"
+DIA_PLT_APPS=ssl runtime_tools syntax_tools
-.PHONY: info
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index 7c7fbeafef..3553b68510 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -24,8 +24,6 @@ include ../../vsn.mk
VSN = $(DIAMETER_VSN)
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
@@ -35,118 +33,29 @@ XML_REF_FILES = $(XML_REF1_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES)
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) \
$(XML_REF_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES) \
- $(XML_EXTRA_FILES)
-
-INFO_FILE = ../../info
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
+XML_GEN_FILES = $(XMLDIR)/seehere.ent $(patsubst %.ent,$(XMLDIR)/%.ent,$(XML_EXTRA_FILES))
-PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+EXTRA_FILES=depend.mk $(XMLDIR)/seehere.ent
-STANDARD_DIR = ../standard
+NO_CHUNKS = diameter_app.xml diameter_transport.xml
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+include $(ERL_TOP)/make/doc.mk
-docs: pdf html man
+$(XMLDIR)/seehere.ent: Makefile seealso.ent
+ $(gen_verbose) sed -f seehere.sed seealso.ent > $@
+$(XMLDIR)/%.ent: %.ent
+ $(gen_verbose) cp $< $@
ldocs: local_docs $(INDEX_TARGET)
-$(PDF_FILE): $(XML_FILES)
-
-pdf: $(PDF_FILE)
-
-html: gifs $(HTMLDIR)/index.html
-
-clean clean_docs: clean_pdf clean_html clean_man
- rm -f errs core *~
- rm -f depend.mk seehere.ent
-
-clean_pdf:
- rm -f $(PDFDIR)/*
-
-clean_man:
- rm -f $(MAN1DIR)/* $(MAN3DIR)/* $(MAN4DIR)/*
-
-clean_html:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-man: $(MAN1_FILES) $(MAN3_FILES) $(MAN4_FILES)
-
-$(INDEX_TARGET): $(INDEX_SRC) $(APP_FILE)
+depend.mk: depend.sed Makefile $(XMLDIR)/seehere.ent $(XMLDIR)/seealso.ent $(XML_REF_FILES) $(XML_CHAPTER_FILES)
$(gen_verbose) \
- sed -e 's/%VSN%/$(VSN)/; \
- s/%ERLANG_SITE%/www\.erlang\.se\//; \
- s/%UP_ONE_LEVEL%/..\/..\/..\/doc\/index.html/; \
- s/%OFF_PRINT%/pdf\/diameter-$(VSN).pdf/' $< > $@
-
-depend: depend.mk
-
-debug opt:
-
-info:
- @echo ""
- @echo "INDEX_FILE = $(INDEX_FILE)"
- @echo "INDEX_SRC = $(INDEX_SRC)"
- @echo "INDEX_TARGET = $(INDEX_TARGET)"
- @echo ""
- @echo "XML_APPLICATION_FILES = $(XML_APPLICATION_FILES)"
- @echo "XML_PART_FILES = $(XML_PART_FILES)"
- @echo "XML_REF1_FILES = $(XML_REF1_FILES)"
- @echo "XML_REF3_FILES = $(XML_REF3_FILES)"
- @echo "XML_REF4_FILES = $(XML_REF4_FILES)"
- @echo "XML_CHAPTER_FILES = $(XML_CHAPTER_FILES)"
- @echo ""
- @echo "GIF_FILES = $(GIF_FILES)"
- @echo ""
- @echo "MAN1_FILES = $(MAN1_FILES)"
- @echo "MAN3_FILES = $(MAN3_FILES)"
- @echo "MAN4_FILES = $(MAN4_FILES)"
- @echo ""
- @echo "DEFAULT_HTML_FILES = $(DEFAULT_HTML_FILES)"
- @echo "DEFAULT_GIF_FILES = $(DEFAULT_GIF_FILES)"
- @echo ""
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: $(LOCAL)docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DATA) $(PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(HTMLDIR)/*.* "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4"
- [ -z "$(LOCAL)" ] || cp -r $(HTMLDIR)/js "$(RELSYSDIR)/doc/html"
- echo $(LOCAL)
-
-release_spec:
-
-depend.mk: depend.sed Makefile seealso.ent \
- $(XML_REF_FILES) $(XML_CHAPTER_FILES)
- $(gen_verbose)
- $(V_at) \
- sed -f seehere.sed seealso.ent > seehere.ent
- $(V_at) \
(for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \
sed -f $< $$f | sed "s@%FILE%@`basename $$f .xml`@g"; \
done) \
@@ -154,7 +63,4 @@ depend.mk: depend.sed Makefile seealso.ent \
-include depend.mk
-.PHONY: clean clean_html clean_man clean_pdf \
- depend debug opt info \
- docs gifs html ldocs man pdf \
- release_docs_spec release_spec
+.PHONY: depend ldocs
diff --git a/lib/diameter/doc/src/diameterc.xml b/lib/diameter/doc/src/diameterc_cmd.xml
index 8f1c660989..8f1c660989 100644
--- a/lib/diameter/doc/src/diameterc.xml
+++ b/lib/diameter/doc/src/diameterc_cmd.xml
diff --git a/lib/diameter/doc/src/files.mk b/lib/diameter/doc/src/files.mk
index 4c1297f6cc..ab47beb99e 100644
--- a/lib/diameter/doc/src/files.mk
+++ b/lib/diameter/doc/src/files.mk
@@ -22,7 +22,7 @@ XML_APPLICATION_FILES = \
ref_man.xml
XML_REF1_FILES = \
- diameterc.xml
+ diameterc_cmd.xml
XML_REF3_FILES = \
diameter.xml \
@@ -52,4 +52,4 @@ XML_CHAPTER_FILES = \
BOOK_FILES = \
book.xml
-GIF_FILES =
+IMAGE_FILES =
diff --git a/lib/diameter/doc/src/ref_man.xml b/lib/diameter/doc/src/ref_man.xml
index a0ef28844d..d57e121c60 100644
--- a/lib/diameter/doc/src/ref_man.xml
+++ b/lib/diameter/doc/src/ref_man.xml
@@ -40,7 +40,7 @@ applications on top of the Diameter protocol. </p>
</description>
<xi:include href="diameter.xml"/>
-<xi:include href="diameterc.xml"/>
+<xi:include href="diameterc_cmd.xml"/>
<xi:include href="diameter_app.xml"/>
<xi:include href="diameter_codec.xml"/>
<xi:include href="diameter_dict.xml"/>
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index d6854cfd27..36e8fefd4c 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -96,6 +96,12 @@ APPUP_FILE = diameter.appup
APPUP_SRC = $(APPUP_FILE).src
APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+ifeq ($(TARGET),win32)
+ EXE_SUFFIX=.exe
+else
+ EXE_SUFFIX=
+endif
+
# ----------------------------------------------------
# Flags
# ----------------------------------------------------
@@ -123,7 +129,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
$(dia_verbose) \
- escript ../bin/diameterc -o gen -i $(EBIN) $<
+ escript$(EXE_SUFFIX) ../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
diff --git a/lib/edoc/Makefile b/lib/edoc/Makefile
index 70bf1f3d48..a8258015b1 100644
--- a/lib/edoc/Makefile
+++ b/lib/edoc/Makefile
@@ -69,7 +69,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
+.PHONY: version
version:
@@ -92,9 +92,6 @@ edocs:
-pa $(XMERL_DIR)/ebin -run edoc_run application \
"'$(APPNAME)'" '"."' '$(DOC_OPTS)'
-info:
- @echo $(HTML_FILES)
-
app_release: tar
@@ -124,3 +121,7 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+
+DIA_PLT_APPS=syntax_tools xmerl inets
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile
index 3e53e75c75..29244467f9 100644
--- a/lib/edoc/doc/src/Makefile
+++ b/lib/edoc/doc/src/Makefile
@@ -25,22 +25,10 @@ VSN=$(EDOC_VSN)
APPLICATION=edoc
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Man page source directory (with .erl files)
-# ----------------------------------------------------
-SRC_DIR = $(ERL_TOP)/lib/edoc/src
-INC_DIR = $(ERL_TOP)/lib/edoc/include
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = \
- edoc.xml \
+EDOC_REF3_FILES = edoc.xml \
edoc_doclet.xml \
edoc_extract.xml \
edoc_layout.xml \
@@ -48,7 +36,7 @@ XML_REF3_FILES = \
edoc_run.xml
XML_PART_FILES = part.xml
-XML_CHAPTER_FILES = chapter.xml
+EDOC_CHAPTER_FILE = chapter.xml
XML_NOTES_FILES = notes.xml
BOOK_FILES = book.xml
@@ -57,89 +45,12 @@ XML_FILES=\
$(BOOK_FILES) $(XML_APPLICATION_FILES) \
$(XML_PART_FILES) $(XML_NOTES_FILES)
-XML_GEN_FILES=$(XML_REF3_FILES:%=$(XMLDIR)/%) $(XML_CHAPTER_FILES:%=$(XMLDIR)/%)
-
-# ----------------------------------------------------
-INFO_FILE = ../../info
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-EXTRA_FILES = \
- $(DEFAULT_GIF_FILES) \
- $(DEFAULT_HTML_FILES) \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_NOTES_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
-
INCLUDES_DIR = ../../include
INCLUDES = $(INCLUDES_DIR)/edoc_doclet.hrl
DTDS_DIR = ../../priv
DTDS = $(DTDS_DIR)/edoc.dtd
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_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)
-
-$(XML_REF3_FILES:%=$(XMLDIR)/%):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EDOC_VSN) -i $(ERL_TOP)/lib/edoc/include -dir $(XMLDIR) $(SRC_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
-
-$(XML_CHAPTER_FILES:%=$(XMLDIR)/%): ../overview.edoc
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EDOC_VSN) -chapter -dir $(XMLDIR) $<
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(INCLUDES) $(DTDS) "$(RELSYSDIR)/doc/html"
+HTML_EXTRA_FILES = $(DTDS) $(INCLUDES)
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index 43343e2ae8..834c0eb005 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -23,4 +23,4 @@
{applications, [compiler,kernel,stdlib,syntax_tools]},
{env, []},
{runtime_dependencies, ["xmerl-1.3.7","syntax_tools-1.6.14","stdlib-2.5",
- "kernel-3.0","inets-5.10","erts-6.0"]}]}.
+ "kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index 62483602aa..0fdc818fae 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -259,10 +259,9 @@ opt_negations() ->
%% </dd>
%% <dt>{@type {doc_path, [string()]@}}
%% </dt>
-%% <dd>Specifies a list of URI:s pointing to directories that contain
-%% EDoc-generated documentation. URI without a `scheme://' part are
-%% taken as relative to `file://'. (Note that such paths must use
-%% `/' as separator, regardless of the host operating system.)
+%% <dd>Specifies a list of file system paths pointing to directories that
+%% contain EDoc-generated documentation. All paths for applications
+%% in the code path are automatically added.
%% </dd>
%% <dt>{@type {doclet, Module::atom()@}}
%% </dt>
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index a8373d6536..ed81c9f4c3 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -328,8 +328,6 @@ get_deprecated(Ts, F, A, Env) ->
case otp_internal:obsolete(M, F, A) of
{Tag, Text} when Tag =:= deprecated; Tag =:= removed ->
deprecated([Text]);
- {Tag, Repl, _Rel} when Tag =:= deprecated; Tag =:= removed ->
- deprecated(Repl, Env);
_ ->
[]
end;
@@ -337,24 +335,9 @@ get_deprecated(Ts, F, A, Env) ->
Es
end.
-deprecated(Repl, Env) ->
- {Text, Ref} = replacement_function(Env#env.module, Repl),
- Desc = ["Use ", {a, href(Ref, Env), [{code, [Text]}]}, " instead."],
- deprecated(Desc).
-
deprecated(Desc) ->
[{deprecated, description(Desc)}].
--dialyzer({no_match, replacement_function/2}).
-
-replacement_function(M0, {M,F,A}) when is_list(A) ->
- %% refer to the largest listed arity - the most general version
- replacement_function(M0, {M,F,lists:last(lists:sort(A))});
-replacement_function(M, {M,F,A}) ->
- {io_lib:fwrite("~w/~w", [F, A]), edoc_refs:function(F, A)};
-replacement_function(_, {M,F,A}) ->
- {io_lib:fwrite("~w:~w/~w", [M, F, A]), edoc_refs:function(M, F, A)}.
-
get_expr_ref(Expr) ->
case catch {ok, erl_syntax_lib:analyze_application(Expr)} of
{ok, {F, A}} when is_atom(F), is_integer(A) ->
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index d00a283794..5959fa6f08 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -32,12 +32,12 @@
-export([count/2, lines/1, split_at/2, split_at_stop/1,
split_at_space/1, filename/1, transpose/1, segment/2,
get_first_sentence/1, is_space/1, strip_space/1, parse_expr/2,
- parse_contact/2, escape_uri/1, join_uri/2, is_relative_uri/1,
+ parse_contact/2, escape_uri/1, join_uri/2,
is_name/1, to_label/1, find_doc_dirs/0, find_sources/2,
find_file/2, try_subdir/2, unique/1,
write_file/3, write_file/4, write_info_file/3,
read_info_file/1, get_doc_env/1, get_doc_env/3, copy_file/2,
- uri_get/1, run_doclet/2, run_layout/2,
+ run_doclet/2, run_layout/2,
simplify_path/1, timestr/1, datestr/1, read_encoding/2]).
-import(edoc_report, [report/2, warning/2]).
@@ -438,128 +438,6 @@ join_uri("", Path) ->
join_uri(Base, Path) ->
Base ++ "/" ++ Path.
-%% Check for relative URI; "network paths" ("//...") not included!
-
-%% @private
-is_relative_uri([$: | _]) ->
- false;
-is_relative_uri([$/, $/ | _]) ->
- false;
-is_relative_uri([$/ | _]) ->
- true;
-is_relative_uri([$? | _]) ->
- true;
-is_relative_uri([$# | _]) ->
- true;
-is_relative_uri([_ | Cs]) ->
- is_relative_uri(Cs);
-is_relative_uri([]) ->
- true.
-
-%% @private
-uri_get("file:///" ++ Path) ->
- uri_get_file(Path);
-uri_get("file://localhost/" ++ Path) ->
- uri_get_file(Path);
-uri_get("file://" ++ Path) ->
- Msg = io_lib:format("cannot handle 'file:' scheme with "
- "nonlocal network-path: 'file://~ts'.",
- [Path]),
- {error, Msg};
-uri_get("file:/" ++ Path) ->
- uri_get_file(Path);
-uri_get("file:" ++ Path) ->
- Msg = io_lib:format("ignoring malformed URI: 'file:~ts'.", [Path]),
- {error, Msg};
-uri_get("http:" ++ Path) ->
- uri_get_http("http:" ++ Path);
-uri_get("ftp:" ++ Path) ->
- uri_get_ftp("ftp:" ++ Path);
-uri_get("//" ++ Path) ->
- Msg = io_lib:format("cannot access network-path: '//~ts'.", [Path]),
- {error, Msg};
-uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
- uri_get_file(Path); % special case for Windows
-uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
- uri_get_file(Path); % special case for Windows
-uri_get(URI) ->
- case is_relative_uri(URI) of
- true ->
- uri_get_file(URI);
- false ->
- Msg = io_lib:format("cannot handle URI: '~ts'.", [URI]),
- {error, Msg}
- end.
-
-uri_get_file(File0) ->
- File = filename:join(?FILE_BASE, File0),
- case read_file(File) of
- {ok, Text} ->
- {ok, Text};
- {error, R} ->
- {error, file:format_error(R)}
- end.
-
-uri_get_http(URI) ->
- %% Try using option full_result=false
- case catch {ok, httpc:request(get, {URI,[]}, [],
- [{full_result, false}])} of
- {'EXIT', _} ->
- uri_get_http_r10(URI);
- Result ->
- uri_get_http_1(Result, URI)
- end.
-
-uri_get_http_r10(URI) ->
- %% Try most general form of request
- Result = (catch {ok, httpc:request(get, {URI,[]}, [], [])}),
- uri_get_http_1(Result, URI).
-
-uri_get_http_1(Result, URI) ->
- case Result of
- {ok, {ok, {200, Text}}} when is_list(Text) ->
- %% new short result format
- {ok, Text};
- {ok, {ok, {Status, Text}}} when is_integer(Status), is_list(Text) ->
- %% new short result format when status /= 200
- Phrase = httpd_util:reason_phrase(Status),
- {error, http_errmsg(Phrase, URI)};
- {ok, {ok, {{_Vsn, 200, _Phrase}, _Hdrs, Text}}} when is_list(Text) ->
- %% new long result format
- {ok, Text};
- {ok, {ok, {{_Vsn, _Status, Phrase}, _Hdrs, Text}}} when is_list(Text) ->
- %% new long result format when status /= 200
- {error, http_errmsg(Phrase, URI)};
- {ok, {200,_Hdrs,Text}} when is_list(Text) ->
- %% old result format
- {ok, Text};
- {ok, {Status,_Hdrs,Text}} when is_list(Text) ->
- %% old result format when status /= 200
- Phrase = httpd_util:reason_phrase(Status),
- {error, http_errmsg(Phrase, URI)};
- {ok, {error, R}} ->
- Reason = inet:format_error(R),
- {error, http_errmsg(Reason, URI)};
- {ok, R} ->
- Reason = io_lib:format("bad return value ~tP", [R, 5]),
- {error, http_errmsg(Reason, URI)};
- {'EXIT', R} ->
- Reason = io_lib:format("crashed with reason ~tw", [R]),
- {error, http_errmsg(Reason, URI)};
- R ->
- Reason = io_lib:format("uncaught throw: ~tw", [R]),
- {error, http_errmsg(Reason, URI)}
- end.
-
-http_errmsg(Reason, URI) ->
- io_lib:format("http error: ~ts: '~ts'", [Reason, URI]).
-
-%% TODO: implement ftp access method
-
-uri_get_ftp(URI) ->
- Msg = io_lib:format("cannot access ftp scheme yet: '~ts'.", [URI]),
- {error, Msg}.
-
%% @private
to_label([$\s | Cs]) ->
to_label(Cs);
@@ -754,18 +632,6 @@ read_info_file(Dir) ->
{?NO_APP, []}
end.
-%% URI access
-
-uri_get_info_file(Base) ->
- URI = join_uri(Base, ?INFO_FILE),
- case uri_get(URI) of
- {ok, Text} ->
- parse_info_file(Text, URI);
- {error, Msg} ->
- warning("could not read '~ts': ~ts.", [URI, Msg]),
- {?NO_APP, []}
- end.
-
parse_info_file(Text, Name) ->
case parse_terms(Text) of
{ok, Vs} ->
@@ -897,7 +763,7 @@ find_doc_dirs([]) ->
get_doc_links(App, Modules, Opts) ->
Path = proplists:append_values(doc_path, Opts) ++ find_doc_dirs(),
- Ds = [{P, uri_get_info_file(P)} || P <- Path],
+ Ds = [{P, read_info_file(P)} || P <- Path],
Ds1 = [{"", {App, Modules}} | Ds],
D = dict:new(),
make_links(Ds1, D, D).
diff --git a/lib/eldap/Makefile b/lib/eldap/Makefile
index 28f995e068..30a8f778ab 100644
--- a/lib/eldap/Makefile
+++ b/lib/eldap/Makefile
@@ -38,3 +38,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=asn ssl
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/eldap/doc/src/Makefile b/lib/eldap/doc/src/Makefile
index bf1eca267a..3eaee276a0 100644
--- a/lib/eldap/doc/src/Makefile
+++ b/lib/eldap/doc/src/Makefile
@@ -27,11 +27,6 @@ VSN=$(ELDAP_VSN)
APPLICATION=eldap
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -45,73 +40,6 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES =
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# 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) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt valgrind:
-
-clean clean_docs clean_tex:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-# $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
-# $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-
-
-release_spec:
+IMAGE_FILES =
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 790a2f4e26..4a8c2d1647 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -317,7 +317,7 @@
</type>
<desc>
<p> Modify the DN of an entry. <c>DeleteOldRDN</c> indicates
- whether the current RDN should be removed from the attribute list after the after operation.
+ whether the current RDN should be removed from the attribute list after the operation.
<c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should
remain as parent, <c>NewSupDN</c> shall be "".</p>
<code>
diff --git a/lib/erl_docgen/Makefile b/lib/erl_docgen/Makefile
index 30ff2bf16e..7e9cc824ec 100644
--- a/lib/erl_docgen/Makefile
+++ b/lib/erl_docgen/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=edoc xmerl
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile
index d6d2550425..33eb44a049 100644
--- a/lib/erl_docgen/doc/src/Makefile
+++ b/lib/erl_docgen/doc/src/Makefile
@@ -29,11 +29,6 @@ VSN=$(ERL_DOCGEN_VSN)
APPLICATION=erl_docgen
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -52,7 +47,8 @@ XML_CHAPTER_FILES = \
inline_tags.xml \
header_tags.xml \
character_entities.xml \
- block_tags.xml
+ block_tags.xml \
+ doc_storage.xml
BOOK_FILES = book.xml
@@ -64,75 +60,9 @@ TECHNICAL_DESCR_FILES =
EXAMPLE_FILES = \
example.txt
-GIF_FILES = \
+IMAGE_FILES = \
man.gif
# ----------------------------------------------------
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-docs: pdf html man
-
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-$(HTMLDIR)/example.txt: example.txt
- $(INSTALL_DATA) $< $@
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs examples $(HTML_REF_MAN_FILE)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
- rm -f $(JD_HTML) $(JD_PACK)
-
-man: $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-examples: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/erl_docgen/doc/src/block_tags.xml b/lib/erl_docgen/doc/src/block_tags.xml
index ceed9305f4..bcaec0fbd1 100644
--- a/lib/erl_docgen/doc/src/block_tags.xml
+++ b/lib/erl_docgen/doc/src/block_tags.xml
@@ -42,7 +42,6 @@
<seealso marker="#listTAG">&lt;list&gt;</seealso>,
<seealso marker="#taglistTAG">&lt;taglist&gt;</seealso>,
<seealso marker="#codeincludeTAG">&lt;codeinclude&gt;</seealso> and
- <seealso marker="#erlevalTAG">&lt;erleval&gt;</seealso>.
</p>
<section>
@@ -131,21 +130,6 @@ start(Pid) ->
</section>
<section>
- <marker id="erlevalTAG"></marker>
- <title>&lt;erleval&gt; - Erlang Evaluation</title>
-
- <p>Include the result from evaluating an Erlang expression. Example:
- </p>
- <code><![CDATA[
-<erleval expr="{A,b,C}={a,b,c}. "/>
- ]]></code>
- <p>results in:</p>
- <erleval expr="{A,b,C}={a,b,c}. "></erleval>
-
- <p>Note the '.' and space after the expression.</p>
- </section>
-
- <section>
<marker id="listTAG"></marker>
<title>&lt;list&gt; - List</title>
diff --git a/lib/erl_docgen/doc/src/doc_storage.xml b/lib/erl_docgen/doc/src/doc_storage.xml
new file mode 100644
index 0000000000..9eee863e3d
--- /dev/null
+++ b/lib/erl_docgen/doc/src/doc_storage.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+ <title>Documentation Storage</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>doc_storage.xml</file>
+ </header>
+
+ <section>
+ <title>EEP-48: Documentation storage and format</title>
+ <p><url href="https://www.erlang.org/erlang-enhancement-proposals/eep-0048.html">EEP-48</url>
+ defines a common documentation storage format for module documentation in the Erlang/OTP
+ ecosystem. Erl_Docgen can generate documentation in this format from XML files following
+ the DTD's descibed in the other User's Guides in this application.</p>
+ <p>Some special considerations have to be taken when writing documentation that
+ should also be available through EEP-48 style storage.</p>
+ <list>
+ <item>The <c>#PCDATA</c> within <c>&lt;name&gt;</c> tags must be parseable to figure out the arity of the function.</item>
+ <item>It is not allowed to mix <c>&lt;name&gt;</c> tags with #PCDATA and attributes.</item>
+ <item>All <c>&lt;name&gt;</c> tags within <c>&lt;func&gt;</c> has to have a <c>since</c> attribute.</item>
+ <item>All callback function documentations have to start with a <c>Module</c> prefix.</item>
+ </list>
+ </section>
+
+ <section>
+ <title>Erlang Documentation Format</title>
+ <p>When generating documentation for generic storage</p>
+ </section>
+
+ <section>
+ <title>See Also</title>
+ <p>
+ <seealso marker="stdlib:shell_docs"><c>shell_docs(3)</c></seealso>,
+ <seealso marker="kernel:code#get_doc-1"><c>code:get_doc(3)</c></seealso>
+ </p>
+ </section>
+
+</chapter>
diff --git a/lib/erl_docgen/doc/src/inline_tags.xml b/lib/erl_docgen/doc/src/inline_tags.xml
index 25b0cd4d87..2b5239c855 100644
--- a/lib/erl_docgen/doc/src/inline_tags.xml
+++ b/lib/erl_docgen/doc/src/inline_tags.xml
@@ -171,45 +171,5 @@
</p>
</section>
- <section>
- <marker id="termTAG"></marker>
- <marker id="termdefTAG"></marker>
- <title>&lt;term&gt;, &lt;termdef&gt; - Glossary</title>
-
- <p>Used to highlight a term with a local (for this document only) or
- global definition. The identity of the term is given by
- the <c>id</c> attribute.</p>
-
- <p>For a locally defined term, the tag contains a
- <c>&lt;termdef&gt;</c>, which in turn contains an explanation of
- the term as plain text. Example:</p>
- <pre><![CDATA[
-<term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term>
- ]]></pre>
-
- <p>In the generated HTML, it is the term name which will be visible.
- For locally defined terms, the id and the name are the same.
- The name has a hypertext link to the definition in the glossary.
- Example:</p>
- <pre><![CDATA[
-<term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term>
- ]]></pre>
- <p>results in: <term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term>
- </p>
-
- <p>If a term is defined both locally and globally, the global
- definition takes precedence.</p>
- </section>
-
- <section>
- <marker id="citeTAG"></marker>
- <marker id="citedefTAG"></marker>
- <title>&lt;cite&gt;, &lt;citedef&gt; - Bibliography</title>
-
- <p>Works the same way as <c>&lt;term&gt;</c> and
- <c>&lt;termdef&gt;</c>, but for a bibliography list rather than
- a glossary.</p>
-
- </section>
</chapter>
diff --git a/lib/erl_docgen/doc/src/part.xml b/lib/erl_docgen/doc/src/part.xml
index 0e97af7169..91ff979af5 100644
--- a/lib/erl_docgen/doc/src/part.xml
+++ b/lib/erl_docgen/doc/src/part.xml
@@ -40,5 +40,6 @@
<xi:include href="block_tags.xml"/>
<xi:include href="inline_tags.xml"/>
<xi:include href="character_entities.xml"/>
+ <xi:include href="doc_storage.xml"/>
</part>
diff --git a/lib/erl_docgen/priv/bin/chunk.escript b/lib/erl_docgen/priv/bin/chunk.escript
new file mode 100644
index 0000000000..a9ea6b9b73
--- /dev/null
+++ b/lib/erl_docgen/priv/bin/chunk.escript
@@ -0,0 +1,30 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! +A 1 +SDio 1 +S 1 -mode minimal
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%----------------------------------------------------------------------
+%% File : chunk.escript
+%%
+%% Created : 1 Nov 2018 by Kenneth Lundin <uabkeld@elxa31hr002>
+%%
+%% Trampoline to xml to chunk creation.
+%%----------------------------------------------------------------------
+
+main(Args) ->
+ docgen_xml_to_chunk:main(Args).
diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript
index 859f3c21f5..96b63aa667 100644
--- a/lib/erl_docgen/priv/bin/specs_gen.escript
+++ b/lib/erl_docgen/priv/bin/specs_gen.escript
@@ -48,7 +48,8 @@ main(Args) ->
parse(["-o"++Dir | Opts], InclFs, _, Module) ->
parse(Opts, InclFs, Dir, Module);
parse(["-I"++I | Opts], InclFs, Dir, Module) ->
- parse(Opts, [I | InclFs], Dir, Module);
+ Is = filelib:wildcard(I),
+ parse(Opts, Is ++ InclFs, Dir, Module);
parse(["-module", Module | Opts], InclFs, Dir, _) ->
parse(Opts, InclFs, Dir, Module);
parse([File], InclFs, Dir, no_module) ->
@@ -131,7 +132,7 @@ write_text(Text, File, Dir) ->
ok;
{error, R} ->
R1 = file:format_error(R),
- io:format("could not write file '~s': ~s\n", [File, R1]),
+ io:format("could not write file '~s': ~s\n", [OutFile, R1]),
halt(2)
end.
diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile
index e3d2ee7e3f..da2ef5ad15 100644
--- a/lib/erl_docgen/priv/css/Makefile
+++ b/lib/erl_docgen/priv/css/Makefile
@@ -53,7 +53,9 @@ CSS_FILES = \
# ----------------------------------------------------
debug opt:
-docs:
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
clean:
$(RM) $(TARGET_FILES)
@@ -64,16 +66,17 @@ clean:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/css"
$(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/css"
-release_docs_spec:
+release_html_spec: html
$(INSTALL_DIR) "$(RELEASE_PATH)/doc"
$(INSTALL_DATA) $(CSS_FILES) ../nyi.html "$(RELEASE_PATH)/doc"
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+
release_tests_spec:
diff --git a/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile
index e35e5f8826..fd8d8a43c7 100644
--- a/lib/erl_docgen/priv/dtd/Makefile
+++ b/lib/erl_docgen/priv/dtd/Makefile
@@ -46,7 +46,6 @@ DTD_FILES = \
fileref.dtd \
xhtml1-frameset.dtd \
appref.dtd \
- cites.dtd \
common.image.dtd \
cref.dtd \
part.dtd \
@@ -55,13 +54,9 @@ DTD_FILES = \
common.dtd \
common.refs.dtd \
erlref.dtd \
- report.dtd \
xhtml1-transitional.dtd \
- bookinsidecover.dtd \
common.entities.dtd \
- common.table.dtd \
- fascicules.dtd \
- terms.dtd
+ common.table.dtd
ENT_FILES = \
xhtml-special.ent \
diff --git a/lib/erl_docgen/priv/dtd/bookinsidecover.dtd b/lib/erl_docgen/priv/dtd/bookinsidecover.dtd
deleted file mode 100644
index ae22c45884..0000000000
--- a/lib/erl_docgen/priv/dtd/bookinsidecover.dtd
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- Portions created by Ericsson are Copyright 1999-2007, Ericsson AB.
- All Rights Reserved.''
-
- $Id$
--->
-<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" >
-%ISOlat1;
-
-<!ENTITY amp "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!ELEMENT bookinsidecover (#PCDATA|br|theheader|vfill|tt|bold)* >
-
-<!ELEMENT tt (#PCDATA|br|theheader|vfill)* >
-<!ELEMENT bold (#PCDATA|br|theheader|vfill)* >
-<!ELEMENT vfill EMPTY >
-<!ELEMENT theheader EMPTY >
-<!ATTLIST theheader tag (title|prepared|responsible|docno|
- approved|checked|date|rev|file|
- none) "none" >
-
-<!ELEMENT br EMPTY >
diff --git a/lib/erl_docgen/priv/dtd/cites.dtd b/lib/erl_docgen/priv/dtd/cites.dtd
deleted file mode 100644
index 4558947db0..0000000000
--- a/lib/erl_docgen/priv/dtd/cites.dtd
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- Portions created by Ericsson are Copyright 1999-2007, Ericsson AB.
- All Rights Reserved.''
-
- $Id$
--->
-<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" >
-%ISOlat1;
-
-<!ENTITY amp "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- Structure -->
-
-<!ELEMENT cites (cite)* >
-<!ELEMENT cite (id, shortdef, def, resp?) >
-<!ELEMENT id (#PCDATA) >
-<!ELEMENT shortdef (#PCDATA) >
-<!ELEMENT def (#PCDATA|c|i|em)* >
-<!ELEMENT resp (#PCDATA) >
-<!ELEMENT c (#PCDATA) >
-<!ELEMENT em (#PCDATA|c)* >
diff --git a/lib/erl_docgen/priv/dtd/common.dtd b/lib/erl_docgen/priv/dtd/common.dtd
index 0ccd52068b..ca680c15b6 100644
--- a/lib/erl_docgen/priv/dtd/common.dtd
+++ b/lib/erl_docgen/priv/dtd/common.dtd
@@ -22,9 +22,8 @@
<!ENTITY % common.entities SYSTEM "common.entities.dtd" >
%common.entities;
-<!ENTITY % block "p|pre|code|list|taglist|codeinclude|
- erleval" >
-<!ENTITY % inline "#PCDATA|c|i|em|strong|term|cite|br|path|seealso|
+<!ENTITY % block "p|pre|code|list|taglist|codeinclude" >
+<!ENTITY % inline "#PCDATA|c|i|em|strong|term|br|seealso|
url|marker|anno|image" >
<!-- XXX -->
<!ELEMENT p (%inline;)* >
@@ -43,33 +42,16 @@
<!ELEMENT strong (#PCDATA|c|anno)* >
<!ELEMENT anno (#PCDATA) >
-<!-- XXX -->
-<!ELEMENT term (termdef?) >
-<!ATTLIST term id CDATA #REQUIRED >
-<!ELEMENT termdef (#PCDATA) >
-<!ELEMENT cite (citedef?) >
-<!ATTLIST cite id CDATA #REQUIRED >
-<!ELEMENT citedef (ctitle,cauthor,chowpublished) >
-<!ELEMENT ctitle (#PCDATA) >
-<!ELEMENT cauthor (#PCDATA) >
-<!ELEMENT chowpublished (#PCDATA) >
-
<!-- XXX -->
<!ELEMENT br EMPTY >
-<!-- Path -->
-
-<!ELEMENT path (#PCDATA) >
-<!ATTLIST path unix CDATA ""
- windows CDATA "" >
-
<!-- List -->
<!ELEMENT list (item+) >
<!ATTLIST list type (ordered|bulleted) "bulleted" >
-<!ELEMENT taglist (tag,item+)+ >
+<!ELEMENT taglist (marker*,tag,item+)+ >
<!ELEMENT tag (#PCDATA|c|i|em|br|seealso|url|marker|anno)* >
-<!ELEMENT item (%inline;|%block;|warning|note|dont|do|quote)* >
+<!ELEMENT item (%inline;|%block;|warning|note|dont|do|quote|table)* >
<!-- References -->
@@ -86,8 +68,3 @@
<!ATTLIST codeinclude file CDATA #REQUIRED
tag CDATA ""
type (erl|c|none) "none" >
-
-<!-- ErlEval -->
-
-<!ELEMENT erleval EMPTY >
-<!ATTLIST erleval expr CDATA #REQUIRED >
diff --git a/lib/erl_docgen/priv/dtd/fascicules.dtd b/lib/erl_docgen/priv/dtd/fascicules.dtd
deleted file mode 100644
index 073d0cc1d9..0000000000
--- a/lib/erl_docgen/priv/dtd/fascicules.dtd
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- Portions created by Ericsson are Copyright 1999-2007, Ericsson AB.
- All Rights Reserved.''
-
- $Id$
--->
-
-<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" >
-%ISOlat1;
-
-<!ENTITY amp "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- Structure -->
-
-<!ELEMENT fascicules (fascicule)+ >
-<!ELEMENT fascicule (#PCDATA) >
-<!ATTLIST fascicule file CDATA #REQUIRED
- href CDATA #REQUIRED
- entry (yes|no) "no" >
-
diff --git a/lib/erl_docgen/priv/dtd/report.dtd b/lib/erl_docgen/priv/dtd/report.dtd
deleted file mode 100644
index 3dd1c3d347..0000000000
--- a/lib/erl_docgen/priv/dtd/report.dtd
+++ /dev/null
@@ -1,141 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- Portions created by Ericsson are Copyright 1999-2007, Ericsson AB.
- All Rights Reserved.''
-
- $Id$
--->
-
-<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" >
-%ISOlat1;
-
-<!ENTITY amp "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!ENTITY % header "title,prepared,responsible,docno,approved,
- checked,date,rev,file" >
-<!ENTITY % block "p|pre|code|list|taglist|erlinclude|
- codeinclude|erleval" >
-<!ENTITY % inline "#PCDATA|i|b|c|em|term|cite|br|path|seealso|
- url|marker" >
-
-<!-- Structure -->
-
-<!ELEMENT report (header,section+) >
-<!ELEMENT header (title,prepared,responsible?,docno,approved?,
- checked?,date,rev,file?) >
-<!ELEMENT title (#PCDATA) >
-<!ELEMENT prepared (#PCDATA) >
-<!ELEMENT responsible (#PCDATA) >
-<!ELEMENT docno (#PCDATA) >
-<!ELEMENT approved (#PCDATA) >
-<!ELEMENT checked (#PCDATA) >
-<!ELEMENT date (#PCDATA) >
-<!ELEMENT rev (#PCDATA) >
-<!ELEMENT file (#PCDATA) >
-
-<!ELEMENT section (marker*,title,
- (%block;|quote|warning|note|dont|do|br|image|marker|
- table|section)*) >
-<!ELEMENT p (%inline;|index)* >
-<!ELEMENT pre (#PCDATA|seealso|url|input)* >
-<!ELEMENT input (#PCDATA|seealso|url)* >
-<!ELEMENT code (#PCDATA) >
-<!ATTLIST code type (erl|c|none) "none" >
-<!ELEMENT quote (p)* >
-<!ELEMENT warning (%block;|quote|br|image|marker|table)* >
-<!ELEMENT note (%block;|quote|br|image|marker|table)* >
-<!ELEMENT dont (%block;|quote|br|image|marker|table)* >
-<!ELEMENT do (%block;|quote|br|image|marker|table)* >
-<!ELEMENT i (#PCDATA|b|c|em)* >
-<!ELEMENT b (#PCDATA|i|c|em)* >
-<!ELEMENT c (#PCDATA) >
-<!ELEMENT em (#PCDATA|i|b|c)* >
-<!ELEMENT term (termdef?) >
-<!ATTLIST term id CDATA #REQUIRED >
-<!ELEMENT termdef (#PCDATA) >
-<!ELEMENT cite (citedef?) >
-<!ATTLIST cite id CDATA #REQUIRED >
-<!ELEMENT citedef (ctitle,cauthor,chowpublished) >
-<!ELEMENT ctitle (#PCDATA) >
-<!ELEMENT cauthor (#PCDATA) >
-<!ELEMENT chowpublished (#PCDATA) >
-<!ELEMENT br EMPTY >
-
-<!-- Path -->
-
-<!ELEMENT path (#PCDATA) >
-<!ATTLIST path unix CDATA ""
- windows CDATA "" >
-
-<!-- List -->
-
-<!ELEMENT list (item+) >
-<!ATTLIST list type (ordered|bulleted) "bulleted" >
-<!ELEMENT taglist (tag,item)+ >
-<!ELEMENT tag (#PCDATA|i|b|c|em|seealso|url)* >
-<!ELEMENT item (%inline;|%block;)* >
-
-<!-- Image -->
-
-<!ELEMENT image (icaption?) >
-<!ATTLIST image file CDATA #REQUIRED >
-<!ELEMENT icaption (#PCDATA) >
-
-<!-- References -->
-
-<!ELEMENT seealso (#PCDATA) >
-<!ATTLIST seealso marker CDATA #REQUIRED >
-<!ELEMENT url (#PCDATA) >
-<!ATTLIST url href CDATA #REQUIRED >
-<!ELEMENT marker EMPTY >
-<!ATTLIST marker id CDATA #REQUIRED >
-
-<!-- Table -->
-
-<!ELEMENT table (row+,tcaption?) >
-<!ATTLIST table width CDATA "0"
- colspec CDATA "" >
-<!ELEMENT row (cell+) >
-<!ELEMENT cell (%inline;)* >
-<!ATTLIST cell align (left|center|right) "left"
- valign (top|middle|bottom) "middle" >
-<!ELEMENT tcaption (#PCDATA) >
-
-<!-- ErlInclude -->
-
-<!ELEMENT erlinclude EMPTY >
-<!ATTLIST erlinclude file CDATA #REQUIRED
- tag CDATA #REQUIRED >
-
-<!-- CodeInclude -->
-
-<!ELEMENT codeinclude EMPTY >
-<!ATTLIST codeinclude file CDATA #REQUIRED
- tag CDATA ""
- type (erl|c|none) "none" >
-
-<!-- ErlEval -->
-
-<!ELEMENT erleval EMPTY >
-<!ATTLIST erleval expr CDATA #REQUIRED >
-
-<!-- Index FOR COMPATIBILITY -->
-
-<!ELEMENT index EMPTY >
-<!ATTLIST index txt CDATA #REQUIRED >
-
diff --git a/lib/erl_docgen/priv/dtd/terms.dtd b/lib/erl_docgen/priv/dtd/terms.dtd
deleted file mode 100644
index c2965eb61c..0000000000
--- a/lib/erl_docgen/priv/dtd/terms.dtd
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
- ``Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- Portions created by Ericsson are Copyright 1999-2007, Ericsson AB.
- All Rights Reserved.''
-
- $Id$
--->
-<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" >
-%ISOlat1;
-
-<!ENTITY amp "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- Structure -->
-
-<!ELEMENT terms (term)* >
-<!ELEMENT term (id, shortdef, def, resp?) >
-<!ELEMENT id (#PCDATA) >
-<!ELEMENT shortdef (#PCDATA) >
-<!ELEMENT def (#PCDATA|c|i|em)* >
-<!ELEMENT resp (#PCDATA) >
-<!ELEMENT c (#PCDATA) >
-<!ELEMENT em (#PCDATA|c)* >
-
diff --git a/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile
index cd98399b6a..b0263524fb 100644
--- a/lib/erl_docgen/priv/images/Makefile
+++ b/lib/erl_docgen/priv/images/Makefile
@@ -54,7 +54,9 @@ PNG_FILES = \
# ----------------------------------------------------
debug opt:
-docs:
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
clean:
$(RM) $(TARGET_FILES)
@@ -70,11 +72,12 @@ release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/images"
$(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) "$(RELSYSDIR)/priv/images"
-
-release_docs_spec:
+release_html_spec:
$(INSTALL_DIR) "$(RELEASE_PATH)/doc"
$(INSTALL_DATA) $(PNG_FILES) "$(RELEASE_PATH)/doc"
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+
release_tests_spec:
diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile
index ad6d4acb6c..be0bed74fb 100644
--- a/lib/erl_docgen/priv/js/flipmenu/Makefile
+++ b/lib/erl_docgen/priv/js/flipmenu/Makefile
@@ -56,7 +56,9 @@ JS_FILES = \
# ----------------------------------------------------
debug opt:
-docs:
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
clean:
$(RM) $(TARGET_FILES)
@@ -67,17 +69,17 @@ clean:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/js/flipmenu"
$(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELSYSDIR)/priv/js/flipmenu"
-
-release_docs_spec:
+release_html_spec: html
$(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu"
$(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu"
$(INSTALL_DATA) ../highlight.js ../highlight.pack.js "$(RELEASE_PATH)/doc/js/"
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+
release_tests_spec:
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index aee496b948..8408d634bd 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -2532,30 +2532,6 @@
</xsl:template>
- <xsl:template match="term">
- <xsl:value-of select="@id"/>
- <!-- xsl:choose>
- <xsl:when test="boolean(termdef)">
- <xsl:choose>
- <xsl:when test="ancestor::parts">
- <a href="users_guide_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
- <xsl:when test="ancestor::applications">
- <a href="ref_man_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
- </xsl:choose>
- </xsl:when>
- <xsl:otherwise>
- <a href="{$topdocdir}/glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:otherwise>
- </xsl:choose -->
- </xsl:template>
-
- <xsl:template match="cite">
- <xsl:value-of select="@id"/>
- </xsl:template>
-
-
<!-- Release Notes -->
<xsl:template match="releasenotes">
@@ -2609,118 +2585,6 @@
</div>
</xsl:template>
- <!-- Glossary -->
- <xsl:template name="glossary">
- <xsl:param name="type"/>
- <xsl:document href="{$outdir}/{$type}_glossary.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/>
- <title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
- </head>
- <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
- <div id="container">
- <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/>
- <script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script>
-
- <!-- Generate menu -->
- <xsl:call-template name="menu"/>
-
- <div id="content">
- <div class="innertube">
- <h1>Glossary</h1>
- </div>
-
- <dl>
- <xsl:for-each select="descendant::term">
- <xsl:sort select="@id"/>
- <xsl:if test="boolean(termdef)">
- <dt><a name="{@id}"><strong><xsl:value-of select="@id"/></strong></a></dt>
- <dd><xsl:value-of select="termdef"/></dd>
- </xsl:if>
- </xsl:for-each>
- </dl>
-
- <div class="footer">
- <hr/>
- <p>
- <xsl:value-of select="$copyright"/>
- <xsl:value-of select="header/copyright/year[1]"/>
- <xsl:text>-</xsl:text>
- <xsl:value-of select="header/copyright/year[2]"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="header/copyright/holder"/>
- </p>
- </div>
-
- </div>
- </div>
-
- </body>
- </html>
-
- </xsl:document>
- </xsl:template>
-
- <!-- Bibliography -->
- <xsl:template name="bibliography">
-
- <xsl:param name="type"/>
- <xsl:document href="{$outdir}/{$type}_bibliography.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/>
- <title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
- </head>
- <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
- <div id="container">
- <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/>
- <script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script>
-
- <!-- Generate menu -->
- <xsl:call-template name="menu"/>
-
- <div id="content">
- <div class="innertube">
- <h1>Bibliography</h1>
- </div>
-
- <table>
- <xsl:for-each select="descendant::cite">
- <xsl:sort select="@id"/>
- <xsl:if test="boolean(citedef)">
- <tr>
- <td><xsl:value-of select="@id"/></td>
- <td><xsl:value-of select="citedef"/></td>
- </tr>
- </xsl:if>
- </xsl:for-each>
- </table>
-
- <div class="footer">
- <hr/>
- <p>
- <xsl:value-of select="$copyright"/>
- <xsl:value-of select="header/copyright/year[1]"/>
- <xsl:text>-</xsl:text>
- <xsl:value-of select="header/copyright/year[2]"/>
- <xsl:text> </xsl:text>
- <xsl:value-of select="header/copyright/holder"/>
- </p>
- </div>
-
- </div>
- </div>
-
- </body>
- </html>
-
- </xsl:document>
- </xsl:template>
-
-
<!-- Special templates to calculate the arity of functions -->
<xsl:template name="calc-arity">
<xsl:param name="string"/>
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index 7080394298..0e559f6067 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -980,7 +980,7 @@
</fo:block>
- <xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude|erleval">
+ <xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude">
<xsl:with-param name="partnum" select="$partnum"/>
<xsl:with-param name="chapnum"><xsl:number/></xsl:with-param>
</xsl:apply-templates>
diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile
index 82d051e9bb..4c6f542ebb 100644
--- a/lib/erl_docgen/src/Makefile
+++ b/lib/erl_docgen/src/Makefile
@@ -38,7 +38,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/erl_docgen-$(VSN)
MODULES = \
docgen_otp_specs \
docgen_edoc_xml_cb \
- docgen_xmerl_xml_cb
+ docgen_xmerl_xml_cb \
+ docgen_xml_to_chunk
HRL_FILES =
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 5342d02947..3354597de8 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -781,7 +781,7 @@ functions(Fs) ->
function(_Name, E=#xmlElement{content = Es}) ->
TypeSpec = get_content(typespec, Es),
[?NL,{func, [ ?NL,
- {name,
+ {name, [{since,""}],
case funcheader(TypeSpec) of
[] ->
signature(get_content(args, Es),
diff --git a/lib/erl_docgen/src/docgen_xml_to_chunk.erl b/lib/erl_docgen/src/docgen_xml_to_chunk.erl
new file mode 100644
index 0000000000..32bfe980ef
--- /dev/null
+++ b/lib/erl_docgen/src/docgen_xml_to_chunk.erl
@@ -0,0 +1,757 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%----------------------------------------------------------------------
+%% File : docgen_xml_to_chunk
+%%
+%% Created : 1 Nov 2018 by Kenneth Lundin <uabkeld@elxa31hr002>
+%%
+%% Does translation of Erlang XML docs to EEP-48 doc chunks.
+%%----------------------------------------------------------------------
+-module(docgen_xml_to_chunk).
+-export([main/1]).
+
+-include_lib("kernel/include/eep48.hrl").
+
+main([FromBeam, _Escript, ToChunk]) ->
+ %% The given module is not documented, generate a hidden beam chunk file
+ Name = filename:basename(filename:rootname(FromBeam)) ++ ".erl",
+
+ EmptyDocs = #docs_v1{ anno = erl_anno:set_file(Name, erl_anno:new(0)),
+ module_doc = hidden, docs = []},
+ ok = file:write_file(ToChunk, term_to_binary(EmptyDocs,[compressed])),
+ ok;
+main([FromXML, FromBeam, _Escript, ToChunk]) ->
+ _ = erlang:process_flag(max_heap_size,20 * 1000 * 1000),
+ case docs(FromXML, FromBeam) of
+ {error, Reason} ->
+ io:format("Failed to create chunks: ~p~n",[Reason]),
+ erlang:halt(1);
+ {docs_v1,_,_,_,_,#{ source := S },[]} when
+ %% This is a list of all modules that do are known not have any functions
+ S =/= "../xml/gen_fsm.xml",
+ S =/= "../xml/shell_default.xml",
+ S =/= "../xml/user.xml",
+ S =/= "../xml/wxClipboardTextEvent.xml",
+ S =/= "../xml/wxDisplayChangedEvent.xml",
+ S =/= "../xml/wxGBSizerItem.xml",
+ S =/= "../xml/wxGraphicsBrush.xml",
+ S =/= "../xml/wxGraphicsFont.xml",
+ S =/= "../xml/wxGraphicsPen.xml",
+ S =/= "../xml/wxInitDialogEvent.xml",
+ S =/= "../xml/wxMaximizeEvent.xml",
+ S =/= "../xml/wxMouseCaptureLostEvent.xml",
+ S =/= "../xml/wxPaintEvent.xml",
+ S =/= "../xml/wxPreviewCanvas.xml",
+ S =/= "../xml/wxSysColourChangedEvent.xml",
+ S =/= "../xml/wxTaskBarIconEvent.xml",
+ S =/= "../xml/wxWindowCreateEvent.xml",
+ S =/= "../xml/wxWindowDestroyEvent.xml",
+ S =/= "../xml/wxDataObject.xml"
+ ->
+ io:format("Failed to create chunks: no functions found ~s~n",[S]),
+ erlang:halt(1),
+ ok;
+ Docs ->
+ ok = file:write_file(ToChunk, term_to_binary(Docs,[compressed]))
+ end.
+
+%% Error handling
+%%----------------------------------------------------------------------
+
+-define(error(Reason),
+ throw({dom_error, Reason})).
+
+%%----------------------------------------------------------------------
+
+%%======================================================================
+%% Records
+%%======================================================================
+
+%%----------------------------------------------------------------------
+%% State record for the validator
+%%----------------------------------------------------------------------
+-record(state, {
+ tags=[], %% Tag stack
+ cno=[], %% Current node number
+ namespaces = [], %% NameSpace stack
+ dom=[] %% DOM structure
+ }).
+
+%%======================================================================
+%% External functions
+%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Function: initial_state() -> Result
+%% Parameters:
+%% Result:
+%% Description:
+%%----------------------------------------------------------------------
+initial_state() ->
+ #state{}.
+
+%%----------------------------------------------------------------------
+%% Function: get_dom(State) -> Result
+%% Parameters:
+%% Result:
+%% Description:
+%%----------------------------------------------------------------------
+get_dom(#state{dom=Dom}) ->
+ Dom.
+
+%%----------------------------------------------------------------------
+%% Function: event(Event, LineNo, State) -> Result
+%% Parameters:
+%% Result:
+%% Description:
+%%----------------------------------------------------------------------
+event(Event, _LineNo, State) ->
+ build_dom(Event, State).
+
+
+%%======================================================================
+%% Internal functions
+%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Function : build_dom(Event, State) -> Result
+%% Parameters: Event = term()
+%% State = #xmerl_sax_simple_dom_state{}
+%% Result : #xmerl_sax_simple_dom_state{} |
+%% Description:
+%%----------------------------------------------------------------------
+
+%% Document
+%%----------------------------------------------------------------------
+build_dom(startDocument, State) ->
+ State#state{dom=[startDocument]};
+build_dom(endDocument,
+ #state{dom=[{Tag, Attributes, Content} |D]} = State) ->
+ case D of
+ [startDocument] ->
+ State#state{dom=[{Tag, Attributes,
+ lists:reverse(Content)}]};
+ [Decl, startDocument] ->
+ State#state{dom=[Decl, {Tag, Attributes,
+ lists:reverse(Content)}]};
+ _ ->
+ %% endDocument is also sent by the parser when a fault occur to tell
+ %% the event receiver that no more input will be sent
+ State
+ end;
+
+%% Element
+%%----------------------------------------------------------------------
+build_dom({startElement, _Uri, LocalName, _QName, Attributes},
+ #state{tags=T, dom=D} = State) ->
+
+ A = parse_attributes(LocalName, Attributes),
+ CName = list_to_atom(LocalName),
+
+ State#state{tags=[CName |T],
+ dom=[{CName,
+ lists:reverse(A),
+ []
+ } | D]};
+build_dom({endElement, _Uri, LocalName, _QName},
+ #state{tags=[_ |T],
+ dom=[{CName, CAttributes, CContent},
+ {PName, PAttributes, PContent} = _Parent | D]} = State) ->
+ case list_to_atom(LocalName) of
+ CName ->
+ SectionDepth = length([E || E <- T, E =:= section]),
+ MappedCName =
+ case CName of
+ title ->
+ lists:nth(SectionDepth+1,[h1,h2,h3]);
+ section when SectionDepth > 0 ->
+ p;
+ CName -> CName
+ end,
+
+ State#state{tags=T,
+ dom=[{PName, PAttributes,
+ [{MappedCName, CAttributes,
+ lists:reverse(CContent)}
+ |PContent]
+ } | D]};
+ _ ->
+ ?error("Got end of element: " ++ LocalName ++ " but expected: " ++
+ CName)
+ end;
+
+%% Text
+%%----------------------------------------------------------------------
+build_dom({characters, String},
+ #state{dom=[{Name, Attributes, Content}| D]} = State) ->
+ HtmlEnts = [{"&nbsp;",[160]},
+ {"&times;",[215]},
+ {"&plusmn;",[177]},
+ {"&ouml;","ö"},
+ {"&auml;","ä"},
+ {"&aring;","å"}
+ ],
+
+ NoHtmlEnt =
+ lists:foldl(
+ fun({Pat,Sub},Str) ->
+ re:replace(Str,Pat,Sub,[global,unicode])
+ end,String,HtmlEnts),
+
+ case re:run(NoHtmlEnt,"&[a-z]*;",[{capture,first,binary},unicode]) of
+ nomatch -> ok;
+ {match,[<<"&lt;">>]} -> ok;
+ {match,[<<"&gt;">>]} -> ok;
+ Else -> throw({found_illigal_thing,Else,String})
+ end,
+ NewContent =
+ [unicode:characters_to_binary(NoHtmlEnt,utf8)| Content],
+ State#state{dom=[{Name, Attributes, NewContent} | D]};
+
+build_dom({ignorableWhitespace, String},
+ #state{dom=[{Name,_,_} = _E|_]} = State) ->
+ case lists:member(Name,
+ [p,pre,input,code,quote,warning,
+ note,dont,do,c,i,em,strong,
+ seealso,tag,item]) of
+ true ->
+% io:format("Keep ign white: ~p ~p~n",[String, _E]),
+ build_dom({characters, String}, State);
+ false ->
+ State
+ end;
+
+build_dom({startEntity, SysId}, State) ->
+ io:format("startEntity:~p~n",[SysId]),
+ State;
+
+%% Default
+%%----------------------------------------------------------------------
+build_dom(_E, State) ->
+ State.
+
+%%----------------------------------------------------------------------
+%% Function : parse_attributes(ElName, Attributes) -> Result
+%% Parameters:
+%% Result :
+%% Description:
+%%----------------------------------------------------------------------
+parse_attributes(ElName, Attributes) ->
+ parse_attributes(ElName, Attributes, 1, []).
+
+parse_attributes(_, [], _, Acc) ->
+ Acc;
+parse_attributes(ElName, [{_Uri, _Prefix, LocalName, AttrValue} |As], N, Acc) ->
+ parse_attributes(ElName, As, N+1, [{list_to_atom(LocalName), AttrValue} |Acc]).
+
+docs(OTPXml, FromBEAM)->
+ case xmerl_sax_parser:file(OTPXml,
+ [skip_external_dtd,
+ {event_fun,fun event/3},
+ {event_state,initial_state()}]) of
+ {ok,Tree,_} ->
+ {ok, {Module, Chunks}} = beam_lib:chunks(FromBEAM,[exports,abstract_code]),
+ Dom = get_dom(Tree),
+ NewDom = transform(Dom,[]),
+ Chunk = to_chunk(NewDom, OTPXml, Module, proplists:get_value(abstract_code, Chunks)),
+ verify_chunk(Module,proplists:get_value(exports, Chunks), Chunk),
+ Chunk;
+ Else ->
+ {error,Else}
+ end.
+
+verify_chunk(M, Exports, #docs_v1{ docs = Docs } = Doc) ->
+
+ %% Make sure that each documented function actually is exported
+ Exported = [begin
+ FA = {F,A},
+ {M,F,A,lists:member(FA,Exports)}
+ end || {{function,F,A},_,_,_,_} <- Docs],
+ lists:foreach(fun({_M,_F,_A,true}) ->
+ ok
+ end,Exported),
+
+ try
+ shell_docs:validate(Doc)
+ catch Err ->
+ throw({maps:get(<<"en">>,Doc#docs_v1.module_doc), Err})
+ end.
+
+%% skip <erlref> but transform and keep its content
+transform([{erlref,_Attr,Content}|T],Acc) ->
+ Module = [Mod || Mod = {module,_,_} <- Content],
+ NewContent = Content -- Module,
+ [{module,SinceAttr,[Mname]}] = Module,
+ Since = case proplists:get_value(since,SinceAttr) of
+ undefined -> [];
+ [] -> [];
+ Vsn -> [{since,Vsn}]
+ end,
+ transform([{module,[{name,Mname}|Since],NewContent}|T],Acc);
+
+%% skip <header> and all of its content
+transform([{header,_Attr,_Content}|T],Acc) ->
+ transform(T,Acc);
+transform([{section,Attr,Content}|T],Acc) ->
+ transform(T,[{section,Attr,transform(Content,[])}|Acc]);
+
+%% transform <list><item> to <ul><li> or <ol><li> depending on type attribute
+transform([{list,Attr,Content}|T],Acc) ->
+ transform([transform_list(Attr,Content)|T],Acc);
+
+%% transform <taglist>(tag,item+)+ to <dl>(dt,item+)+
+transform([{taglist,Attr,Content}|T],Acc) ->
+ transform([transform_taglist(Attr,Content)|T],Acc);
+
+%% remove <anno> as it is only used to validate specs vs xml src
+transform([{anno,[],Content}|T],Acc) ->
+ transform([Content|T],Acc);
+
+%% transform <c> to <code>
+transform([{c,[],Content}|T],Acc) ->
+ transform(T, [{code,[],transform(Content,[])}|Acc]);
+
+%% transform <code> to <pre><code>
+transform([{code,Attr,Content}|T],Acc) ->
+ transform(T, [{pre,[],[{code,Attr,transform(Content,[])}]}|Acc]);
+%% transform <pre> to <pre><code>
+transform([{pre,Attr,Content}|T],Acc) ->
+ transform(T, [{pre,[],[{code,Attr,transform(Content,[])}]}|Acc]);
+
+%% transform <funcs> with <func> as children
+transform([{funcs,_Attr,Content}|T],Acc) ->
+ Fns = {functions,[],transform_funcs(Content, [])},
+ transform(T,[Fns|Acc]);
+%% transform <datatypes> with <datatype> as children
+transform([{datatypes,_Attr,Content}|T],Acc) ->
+ Dts = transform(Content, []),
+ transform(T,[{datatypes,[],Dts}|Acc]);
+transform([{datatype,_Attr,Content}|T],Acc) ->
+ transform(T,transform_datatype(Content, []) ++ Acc);
+%% Ignore <datatype_title>
+transform([{datatype_title,_Attr,_Content}|T],Acc) ->
+ transform(T,Acc);
+%% transform <desc>Content</desc> to Content
+transform([{desc,_Attr,Content}|T],Acc) ->
+ transform(T,[transform(Content,[])|Acc]);
+transform([{strong,Attr,Content}|T],Acc) ->
+ transform([{em,Attr,Content}|T],Acc);
+%% transform <marker id="name"/> to <a id="name"/>....
+transform([{marker,Attr,Content}|T],Acc) ->
+ transform(T,[{a,Attr,transform(Content,[])}|Acc]);
+%% transform <url href="external URL"> Content</url> to <a href....
+transform([{url,Attr,Content}|T],Acc) ->
+ transform(T,[{a,Attr,transform(Content,[])}|Acc]);
+%% transform note/warning/do/don't to <p class="thing">
+transform([{What,[],Content}|T],Acc)
+ when What =:= note; What =:= warning; What =:= do; What =:= dont ->
+ WhatP = {p,[{class,atom_to_list(What)}], transform(Content,[])},
+ transform(T,[WhatP|Acc]);
+
+transform([{type,_,[]}|_] = Dom,Acc) ->
+ %% Types are laid out sequentially in the source xml so we need to
+ %% parse them like that here too.
+ case transform_types(Dom,[]) of
+ {[],T} ->
+ transform(T,Acc);
+ {Types,T} ->
+ %% We sort the types here because in the source xml
+ %% the description and the declaration do not have
+ %% to be next to each other. But we want to have that
+ %% for the doc chunks.
+ NameSort = fun({li,A,_},{li,B,_}) ->
+ NameA = proplists:get_value(name,A),
+ NameB = proplists:get_value(name,B),
+ if NameA == NameB ->
+ length(A) =< length(B);
+ true ->
+ NameA < NameB
+ end
+ end,
+ transform(T,[{ul,[{class,"types"}],lists:sort(NameSort,Types)}|Acc])
+ end;
+transform([{type_desc,Attr,_Content}|T],Acc) ->
+ %% We skip any type_desc with the variable attribute
+ true = proplists:is_defined(variable, Attr),
+ transform(T,Acc);
+transform([{type,[],Content}|T],Acc) ->
+ transform(T,[{ul,[{class,"types"}],transform(Content,[])}|Acc]);
+transform([{v,[],Content}|T],Acc) ->
+ transform(T, [{li,[{class,"type"}],transform(Content,[])}|Acc]);
+transform([{d,[],Content}|T],Acc) ->
+ transform(T, [{li,[{class,"description"}],transform(Content,[])}|Acc]);
+
+transform([Tag = {seealso,_Attr,_Content}|T],Acc) ->
+ transform([transform_seealso(Tag)|T],Acc);
+
+transform([{term,Attr,[]}|T],Acc) ->
+ transform([list_to_binary(proplists:get_value(id,Attr))|T],Acc);
+
+transform([{fsummary,_,_}|T],Acc) ->
+ %% We skip fsummary as it many times is just a duplicate of the
+ %% first line of the docs.
+ transform(T,Acc);
+
+transform([{input,_,Content}|T],Acc) ->
+ %% Just remove input as it is not used by anything
+ transform(T,[transform(Content,[])|Acc]);
+
+%% Tag and Attr is used as is but Content is transformed
+transform([{Tag,Attr,Content}|T],Acc) ->
+ transform(T,[{Tag,Attr,transform(Content,[])}|Acc]);
+transform([Binary|T],Acc) ->
+ transform(T,[Binary|Acc]);
+transform([],Acc) ->
+ lists:flatten(lists:reverse(Acc)).
+
+transform_list([{type,"ordered"}],Content) ->
+ {ol,[],[{li,A2,C2}||{item,A2,C2}<-Content]};
+transform_list(_,Content) ->
+ {ul,[],[{li,A2,C2}||{item,A2,C2}<-Content]}.
+
+transform_types([{type,Attr,[]}|T],Acc) ->
+ case proplists:is_defined(name,Attr) of
+ true ->
+ transform_types(T, [{li,Attr,[]}|Acc]);
+ false ->
+ true = proplists:is_defined(variable, Attr),
+ transform_types(T, Acc)
+ end;
+transform_types([{type_desc,Attr,Content}|T],Acc) ->
+ case proplists:is_defined(name,Attr) of
+ true ->
+ TypeDesc = transform(Content,[]),
+ transform_types(T, [{li,Attr ++ [{class,"description"}],TypeDesc}|Acc]);
+ false ->
+ true = proplists:is_defined(variable, Attr),
+ transform_types(T, Acc)
+ end;
+transform_types([{type,_,_}|_T],_Acc) ->
+ throw(mixed_type_declarations);
+transform_types(Dom,Acc) ->
+ {lists:reverse(Acc),Dom}.
+
+transform_taglist(Attr,Content) ->
+ Items =
+ lists:map(fun({tag,A,C}) ->
+ {dt,A,C};
+ ({item,A,C}) ->
+ {dd,A,C}
+ end, Content),
+ {dl,Attr,Items}.
+
+%% if we have {func,[],[{name,...},{name,....},...]}
+%% we convert it to one {func,[],[{name,...}] per arity lowest first.
+transform_funcs([Func|T],Acc) ->
+ transform_funcs(T,func2func(Func) ++ Acc);
+transform_funcs([],Acc) ->
+ lists:reverse(Acc).
+
+func2func({func,Attr,Contents}) ->
+
+ ContentsNoName = [NC||NC <- Contents, element(1,NC) /= name],
+
+ EditLink =
+ case proplists:get_value(ghlink,Attr) of
+ undefined ->
+ #{};
+ GhLink ->
+ #{ edit_url =>
+ iolist_to_binary(["https://github.com/erlang/otp/edit/",GhLink]) }
+ end,
+
+ VerifyNameList =
+ fun(NameList, Test) ->
+ %% Assert that we don't mix ways to write <name>
+ [begin
+ ok = Test(C),
+ {proplists:get_value(name,T),proplists:get_value(arity,T)}
+ end || {name,T,C} <- NameList]
+ end,
+
+ NameList = [Name || {name,_,_} = Name <- Contents],
+
+ %% "Since" is hard to accurately as there can be multiple <name> per <func> and they
+ %% can refer to the same or other arities. This should be improved in the future but
+ %% for now we set since to a comma separated list of all since attributes.
+ SinceMD =
+ case [proplists:get_value(since, SinceAttr) ||
+ {name,SinceAttr,_} <- NameList, proplists:get_value(since, SinceAttr) =/= []] of
+ [] -> EditLink;
+ Sinces ->
+ EditLink#{ since => unicode:characters_to_binary(
+ lists:join(",",lists:usort(Sinces))) }
+ end,
+
+ Functions =
+ case NameList of
+ [{name,_,[]}|_] ->
+ %% Spec style function docs
+ TagsToFA =
+ fun(Tags) ->
+ {proplists:get_value(name,Tags),
+ proplists:get_value(arity,Tags)}
+ end,
+
+ _ = VerifyNameList(NameList,fun([]) -> ok end),
+
+ FAs = [TagsToFA(FAttr) || {name,FAttr,[]} <- NameList ],
+ FAClauses = lists:usort([{TagsToFA(FAttr),proplists:get_value(clause_i,FAttr)}
+ || {name,FAttr,[]} <- NameList ]),
+ Signature = [iolist_to_binary([F,"/",A]) || {F,A} <- FAs],
+ lists:map(
+ fun({F,A}) ->
+ Specs = [{func_to_atom(CF),list_to_integer(CA),C}
+ || {{CF,CA},C} <- FAClauses,
+ F =:= CF, A =:= CA],
+ {function,[{name,F},{arity,list_to_integer(A)},
+ {signature,Signature},
+ {meta,SinceMD#{ signature => Specs }}],
+ ContentsNoName}
+ end, lists:usort(FAs));
+ NameList ->
+ %% Manual style function docs
+ FAs = lists:flatten([func_to_tuple(NameString) || {name, _Attr, NameString} <- NameList]),
+
+ _ = VerifyNameList(NameList,fun([_|_]) -> ok end),
+
+ Signature = [strip_tags(NameString) || {name, _Attr, NameString} <- NameList],
+ [{function,[{name,F},{arity,A},
+ {signature,Signature},
+ {meta,SinceMD}],ContentsNoName}
+ || {F,A} <- lists:usort(FAs)]
+ end,
+ transform(Functions,[]).
+
+func_to_tuple(Chars) ->
+ try
+ [Name,Args] = string:split(strip_tags(Chars),"("),
+ Arities = parse_args(unicode:characters_to_list(Args)),
+ [{unicode:characters_to_list(Name),Arity} || Arity <- Arities]
+ catch E:R:ST ->
+ io:format("Failed to parse: ~p~n",[Chars]),
+ erlang:raise(E,R,ST)
+ end.
+
+%% This function parses a documentation <name> attribute to figure
+%% out the arities if that function. Example:
+%% "start([go,Mode] [,Extra])" returns [1, 2].
+%%
+%% This assumes that when a single <name> describes many arities
+%% the arities are listed with [, syntax.
+parse_args(")" ++ _) ->
+ [0];
+parse_args(Args) ->
+ parse_args(unicode:characters_to_list(Args),1,[]).
+parse_args([$[,$,|T],Arity,[]) ->
+ parse_args(T,Arity,[$[]) ++ parse_args(T,Arity+1,[]);
+parse_args([$,|T],Arity,[]) ->
+ parse_args(T,Arity+1,[]);
+parse_args([Open|T],Arity,Stack)
+ when Open =:= $[; Open =:= ${; Open =:= $( ->
+ parse_args(T,Arity,[Open|Stack]);
+parse_args([$]|T],Arity,[$[|Stack]) ->
+ parse_args(T,Arity,Stack);
+parse_args([$}|T],Arity,[${|Stack]) ->
+ parse_args(T,Arity,Stack);
+parse_args([$)|T],Arity,[$(|Stack]) ->
+ parse_args(T,Arity,Stack);
+parse_args([$)|_T],Arity,[]) ->
+ [Arity];
+parse_args([_H|T],Arity,Stack) ->
+ parse_args(T,Arity,Stack).
+
+strip_tags([{_Tag,_Attr,Content}|T]) ->
+ [Content | strip_tags(T)];
+strip_tags([H|T]) when not is_tuple(H) ->
+ [H | strip_tags(T)];
+strip_tags([]) ->
+ [].
+
+transform_datatype(Dom,_Acc) ->
+ ContentsNoName = transform([NC||NC <- Dom, element(1,NC) /= name],[]),
+ [case N of
+ {name,NameAttr,[]} ->
+ {datatype,NameAttr,ContentsNoName};
+ {name,[],Content} ->
+ [{Name,Arity}] = func_to_tuple(Content),
+ Signature = strip_tags(Content),
+ {datatype,[{name,Name},{n_vars,integer_to_list(Arity)},
+ {signature,Signature}],ContentsNoName}
+ end || N = {name,_,_} <- Dom].
+
+transform_seealso({seealso,Attr,_Content}) ->
+ {a, Attr, _Content}.
+
+to_chunk(Dom, Source, Module, AST) ->
+ [{module,MAttr,Mcontent}] = Dom,
+
+ ModuleDocs = lists:flatmap(
+ fun({Tag,_,Content}) when Tag =:= description;
+ Tag =:= section ->
+ Content;
+ ({_,_,_}) ->
+ []
+ end, Mcontent),
+
+ TypeMeta = add_types(AST, maps:from_list([{source,Source}|MAttr])),
+
+ TypeMap = maps:get(types, TypeMeta, []),
+
+ Anno = erl_anno:set_file(atom_to_list(Module)++".erl",erl_anno:new(0)),
+
+ Types = lists:flatten([Types || {datatypes,[],Types} <- Mcontent]),
+
+ TypeEntries =
+ lists:map(
+ fun({datatype,Attr,Descr}) ->
+ TypeName = func_to_atom(proplists:get_value(name,Attr)),
+ TypeArity = case proplists:get_value(n_vars,Attr) of
+ undefined ->
+ find_type_arity(TypeName, TypeMap);
+ Arity ->
+ list_to_integer(Arity)
+ end,
+ TypeArgs = lists:join(",",[lists:concat(["Arg",I]) || I <- lists:seq(1,TypeArity)]),
+ PlaceholderSig = io_lib:format("-type ~p(~s) :: term().",[TypeName,TypeArgs]),
+ TypeSignature = proplists:get_value(
+ signature,Attr,[iolist_to_binary(PlaceholderSig)]),
+ MetaSig =
+ case maps:get({TypeName, TypeArity}, TypeMap, undefined) of
+ undefined ->
+ #{};
+ Sig ->
+ #{ signature => [Sig] }
+ end,
+ docs_v1_entry(type, Anno, TypeName, TypeArity, TypeSignature, MetaSig, Descr)
+ end, Types),
+
+ Functions = lists:flatten([Functions || {functions,[],Functions} <- Mcontent]),
+
+ FuncEntrys =
+ lists:flatmap(
+ fun({function,Attr,Fdoc}) ->
+ case func_to_atom(proplists:get_value(name,Attr)) of
+ callback ->
+ [];
+ Name ->
+ Arity = proplists:get_value(arity,Attr),
+ Signature = proplists:get_value(signature,Attr),
+ FMeta = proplists:get_value(meta,Attr),
+ MetaWSpec = add_spec(AST,FMeta),
+ [docs_v1_entry(function, Anno, Name, Arity, Signature, MetaWSpec, Fdoc)]
+ end
+ end, Functions),
+
+ docs_v1(ModuleDocs, Anno, TypeMeta, FuncEntrys ++ TypeEntries).
+
+docs_v1(DocContents, Anno, Metadata, Docs) ->
+ #docs_v1{ anno = Anno,
+ module_doc = #{<<"en">> => shell_docs:normalize(DocContents)},
+ metadata = maps:merge(Metadata, (#docs_v1{})#docs_v1.metadata),
+ docs = Docs }.
+
+docs_v1_entry(Kind, Anno, Name, Arity, Signature, Metadata, DocContents) ->
+ AnnoWLine =
+ case Metadata of
+ #{ signature := [Sig|_] } ->
+ erl_anno:set_line(element(2, Sig), Anno);
+ _NoSignature ->
+ Anno
+ end,
+ {{Kind, Name, Arity}, AnnoWLine, lists:flatten(Signature),
+ #{ <<"en">> => shell_docs:normalize(DocContents)}, Metadata}.
+
+%% A special list_to_atom that handles
+%% 'and'
+%% Destroy
+%% 'begin'
+func_to_atom(List) ->
+ case erl_scan:string(List) of
+ {ok,[{atom,_,Fn}],_} -> Fn;
+ {ok,[{var,_,Fn}],_} -> Fn;
+ {ok,[{Fn,_}],_} -> Fn;
+ {ok,[{var,_,_},{':',_},_],_} ->
+ callback
+ end.
+
+-define(IS_TYPE(TO),(TO =:= type orelse TO =:= opaque)).
+
+add_spec(no_abstract_code, Meta) ->
+ Meta;
+add_spec({raw_abstract_v1, AST}, Meta = #{ signature := Specs } ) ->
+ Meta#{ signature := add_spec_clauses(AST, merge_clauses(Specs,#{})) };
+add_spec(_, Meta) ->
+ Meta.
+
+add_types(no_abstract_code, Meta) ->
+ Meta;
+add_types({raw_abstract_v1, AST}, Meta) ->
+ Meta#{ types =>
+ maps:from_list(
+ [{{Name,length(Args)},T} || T = {attribute,_,TO,{Name, _, Args}} <- AST,
+ ?IS_TYPE(TO)]) }.
+
+add_spec_clauses(AST, [{{F,A},Clauses}|T]) ->
+ [filter_clauses(find_spec(AST,F,A),Clauses) | add_spec_clauses(AST,T)];
+add_spec_clauses(_AST, []) ->
+ [].
+
+filter_clauses(Spec,[undefined]) ->
+ Spec;
+filter_clauses({attribute,Ln,spec,{FA,Clauses}},ClauseIds) ->
+ {_,FilteredClauses} =
+ lists:foldl(
+ fun({TO,_,_,_} = C,{Cnt,Acc}) when ?IS_TYPE(TO) ->
+ case lists:member(integer_to_list(Cnt),ClauseIds) of
+ true ->
+ {Cnt+1,[C | Acc]};
+ false ->
+ {Cnt+1,Acc}
+ end
+ end, {1, []}, Clauses),
+ {attribute,Ln,spec,{FA,lists:reverse(FilteredClauses)}}.
+
+merge_clauses([{F,A,Clause}|T],Acc) ->
+ merge_clauses(T,Acc#{ {F,A} => [Clause | maps:get({F,A},Acc,[])]});
+merge_clauses([],Acc) ->
+ maps:to_list(Acc).
+
+find_type_arity(Name, [{{Name,_},{attribute,_,TO,{Name,_,Args}}}|_T]) when ?IS_TYPE(TO) ->
+ length(Args);
+find_type_arity(Name, [_|T]) ->
+ find_type_arity(Name,T);
+find_type_arity(Name, Map) when is_map(Map) ->
+ find_type_arity(Name, maps:to_list(Map)).
+
+find_spec(AST, Func, Arity) ->
+ Specs = lists:filter(fun({attribute,_,spec,{{F,A},_}}) ->
+ F =:= Func andalso A =:= Arity;
+ ({attribute,_,spec,{{_,F,A},_}}) ->
+ F =:= Func andalso A =:= Arity;
+ (_) ->
+ false
+ end, AST),
+ case Specs of
+ [S] ->
+ S;
+ [] ->
+ io:format("Could not find spec for ~p/~p~n",[Func,Arity]),
+ exit(1)
+ end.
diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src
index 171c697585..c2641f30df 100644
--- a/lib/erl_docgen/src/erl_docgen.app.src
+++ b/lib/erl_docgen/src/erl_docgen.app.src
@@ -3,7 +3,8 @@
{vsn, "%VSN%"},
{modules, [docgen_otp_specs,
docgen_edoc_xml_cb,
- docgen_xmerl_xml_cb
+ docgen_xmerl_xml_cb,
+ docgen_xml_to_chunk
]
},
{registered,[]},
diff --git a/lib/erl_interface/Makefile b/lib/erl_interface/Makefile
index 9471b0df18..633e705b3f 100644
--- a/lib/erl_interface/Makefile
+++ b/lib/erl_interface/Makefile
@@ -31,3 +31,5 @@ SPECIAL_TARGETS =
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index 230dba25d6..55baae7479 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -336,6 +336,10 @@ if test "X$host" = "Xwin32"; then
LIB_CFLAGS="$CFLAGS"
else
if test "x$GCC" = xyes; then
+ # Remove all PIE stuff
+ CFLAGS=`echo $CFLAGS | sed 's/-f\(no-\)\?PIE//g'`
+ LDFLAGS=`echo $LDFLAGS | sed 's/-\(no-\)\?pie//g'`
+
LIB_CFLAGS="$CFLAGS -fPIC"
else
LIB_CFLAGS="$CFLAGS"
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 03044a0ddd..95f3e77c61 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -36,93 +36,24 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
-XML_REF1_FILES = erl_call.xml
-XML_REF3_FILES = erl_connect.xml \
- erl_error.xml \
- erl_eterm.xml \
- erl_format.xml \
- erl_malloc.xml \
- erl_marshal.xml \
- erl_global.xml \
+XML_REF1_FILES = erl_call_cmd.xml
+XML_REF3_FILES = ei_global.xml \
ei.xml \
ei_connect.xml \
registry.xml
BOOK_FILES = book.xml
XML_APPLICATION_FILES = ref_man.xml
-#ref_man_ei.xml ref_man_erl_interface.xml
+
XML_PART_FILES = \
part.xml
XML_CHAPTER_FILES = ei_users_guide.xml notes.xml
XML_FILES = $(XML_REF1_FILES) $(XML_REF3_FILES) $(BOOK_FILES) \
$(XML_APPLICATION_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-GIF_FILES =
-
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+NO_CHUNKS=$(XML_REF3_FILES)
# ----------------------------------------------------
-# 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: $(MAN1_FILES) $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt lcnt:
-
-clean clean_docs clean_tex:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 70af5642da..232871e864 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -49,14 +49,9 @@
<p><c>ei</c> also handles C-nodes, C-programs that talks Erlang
distribution with Erlang nodes (or other C-nodes) using the
- Erlang distribution format. The difference between <c>ei</c>
- and <c>erl_interface</c> is that <c>ei</c> uses
- the binary format directly when sending and receiving terms. It is also
+ Erlang distribution format.The <c>ei</c> library is
thread safe, and using threads, one process can handle multiple
- C-nodes. The <c>erl_interface</c> library is built on top of
- <c>ei</c>, but of legacy reasons, it does not allow for
- multiple C-nodes. In general, <c>ei</c> is the preferred way
- of doing C-nodes.</p>
+ C-nodes.</p>
<p>The decode and encode functions use a buffer and an index into the
buffer, which points at the point where to encode and
@@ -378,22 +373,6 @@ typedef enum {
</func>
<func>
- <name since=""><ret>int</ret><nametext>ei_decode_term(const char *buf, int *index, void *t)</nametext></name>
- <fsummary>Decode a <c>ETERM</c>.</fsummary>
- <desc>
- <p>Decodes a term from the binary format. The term
- is return in <c>t</c> as a <c>ETERM*</c>, so
- <c>t</c> is actually an <c>ETERM**</c> (see
- <seealso marker="erl_eterm"><c>erl_eterm</c></seealso>).
- The term is later to be deallocated.</p>
- <note><p>This function is deprecated as of OTP 22 and will be removed in
- OTP 23 together with the old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>).</p>
- </note>
- </desc>
- </func>
-
- <func>
<name since=""><ret>int</ret><nametext>ei_decode_trace(const char *buf, int *index, erlang_trace *p)</nametext></name>
<fsummary>Decode a trace token.</fsummary>
<desc>
@@ -707,22 +686,6 @@ ei_x_encode_string(&amp;x, "Banana");</pre>
the <c>ei_x_encode_string_len()</c> function.</p>
</desc>
</func>
-
- <func>
- <name since=""><ret>int</ret><nametext>ei_encode_term(char *buf, int *index, void *t)</nametext></name>
- <name since=""><ret>int</ret><nametext>ei_x_encode_term(ei_x_buff* x, void *t)</nametext></name>
- <fsummary>Encode an <c>erl_interface</c> term.</fsummary>
- <desc>
- <p>Encodes an <c>ETERM</c>, as obtained from
- <c>erl_interface</c>. Parameter <c>t</c> is
- actually an <c>ETERM</c> pointer. This function
- does not free the <c>ETERM</c>.</p>
- <note><p>These functions are deprecated as of OTP 22 and will be removed in
- OTP 23 together with the old legacy <c>erl_interface</c> library
- (functions with prefix <c>erl_</c>).</p>
- </note>
- </desc>
- </func>
<func>
<name since=""><ret>int</ret><nametext>ei_encode_trace(char *buf, int *index, const erlang_trace *p)</nametext></name>
<name since=""><ret>int</ret><nametext>ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p)</nametext></name>
@@ -806,11 +769,7 @@ ei_encode_tuple_header(buf, &amp;i, 0);</pre>
<desc>
<p>Initialize the <c>ei</c> library. This function should be called once
(and only once) before calling any other functionality in the <c>ei</c>
- library. However, note the exception below.</p>
- <p>If the <c>ei</c> library is used together with the <c>erl_interface</c>
- library, this function should <em>not</em> be called directly. It will be
- called by the <c>erl_init()</c> function which should be used to initialize
- the combination of the two libraries instead.</p>
+ library.</p>
<p>On success zero is returned. On failure a posix error code is returned.</p>
</desc>
</func>
@@ -990,8 +949,4 @@ encodes the tuple {numbers,12,3.14159}</pre>
</list>
</section>
- <section>
- <title>See Also</title>
- <p><seealso marker="erl_eterm"><c>erl_eterm</c></seealso></p>
- </section>
</cref>
diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml
index 795f1249b3..06fe9d1960 100644
--- a/lib/erl_interface/doc/src/ei_connect.xml
+++ b/lib/erl_interface/doc/src/ei_connect.xml
@@ -422,6 +422,8 @@ typedef struct {
<func>
<name since=""><ret>int</ret><nametext>ei_connect(ei_cnode* ec, char *nodename)</nametext></name>
<name since=""><ret>int</ret><nametext>ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)</nametext></name>
+ <name since="OTP @OTP-16251@"><ret>int</ret><nametext>ei_connect_host_port(ei_cnode* ec, char *hostname, int port)</nametext></name>
+ <name since="OTP @OTP-16251@"><ret>int</ret><nametext>ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr adr, int port)</nametext></name>
<fsummary>Establish a connection to an Erlang node.</fsummary>
<desc>
<p>Sets up a connection to an Erlang node.</p>
@@ -429,13 +431,21 @@ typedef struct {
remote host and the alive name of the remote node to be
specified. <c>ei_connect()</c> provides an alternative
interface and determines the information from the node name
- provided.</p>
+ provided. The <c>ei_xconnect_host_port()</c> function provides
+ yet another alternative that will work even if there is no
+ EPMD instance on the host where the remote node is running. The
+ <c>ei_xconnect_host_port()</c> function requires the IP
+ address and port of the remote node to be specified.
+ The <c>ei_connect_host_port()</c> function is an alternative
+ to <c>ei_xconnect_host_port()</c> that lets the user specify
+ a hostname instead of an IP address.</p>
<list type="bulleted">
- <item><c>addr</c> is the 32-bit IP address of the remote
+ <item><c>adr</c> is the 32-bit IP address of the remote
host.</item>
<item><c>alive</c> is the alivename of the remote node.
</item>
<item><c>node</c> is the name of the remote node.</item>
+ <item><c>port</c> is the port number of the remote node.</item>
</list>
<p>These functions return an open file descriptor on success, or
a negative value indicating that an error occurred. In the latter
@@ -571,13 +581,16 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) {
<func>
<name since=""><ret>int</ret><nametext>ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)</nametext></name>
<name since=""><ret>int</ret><nametext>ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)</nametext></name>
+ <name since="OTP @OTP-16251@"><ret>int</ret><nametext>ei_connect_host_port_tmo(ei_cnode* ec, char *hostname, int port, unsigned ms)</nametext></name>
+ <name since="OTP @OTP-16251@"><ret>int</ret><nametext>ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr adr, int port, unsigned ms)</nametext></name>
<fsummary>Establish a connection to an Erlang node with optional
time-out.</fsummary>
<desc>
- <p>Equivalent to
- <c>ei_connect</c> and <c>ei_xconnect</c> with an optional time-out
- argument, see the description at the beginning of this manual
- page.</p>
+ <p>Equivalent to <c>ei_connect</c>, <c>ei_xconnect</c>,
+ <c>ei_connect_host_port</c> and
+ <c>ei_xconnect_host_port</c> with an optional time-out
+ argument, see the description at the beginning of this manual
+ page.</p>
</desc>
</func>
diff --git a/lib/erl_interface/doc/src/erl_global.xml b/lib/erl_interface/doc/src/ei_global.xml
index 39085b46f0..4c6b94f7dd 100644
--- a/lib/erl_interface/doc/src/erl_global.xml
+++ b/lib/erl_interface/doc/src/ei_global.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>erl_global</title>
+ <title>ei_global</title>
<prepared>Gordon Beaton</prepared>
<responsible>Gordon Beaton</responsible>
<docno></docno>
@@ -30,20 +30,14 @@
<checked>Gordon Beaton</checked>
<date>1998-07-03</date>
<rev>A</rev>
- <file>erl_global.xml</file>
+ <file>ei_global.xml</file>
</header>
- <lib>erl_global</lib>
+ <lib>ei_global</lib>
<libsummary>Access globally registered names.</libsummary>
<description>
<note><p>The support for VxWorks is deprecated as of OTP 22, and
will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
-
+
<p>This module provides support for registering, looking
up, and unregistering names in the <c>global</c> module.
For more information, see
@@ -57,15 +51,17 @@
<funcs>
<func>
- <name since=""><ret>char **</ret><nametext>erl_global_names(fd,count)</nametext></name>
+ <name since=""><ret>char **</ret><nametext>ei_global_names(ec,fd,count)</nametext></name>
<fsummary>Obtain list of global names.</fsummary>
<type>
+ <v>ei_cnode *ec;</v>
<v>int fd;</v>
<v>int *count;</v>
</type>
<desc>
<p>Retrieves a list of all known global names.</p>
<list type="bulleted">
+ <item><c>ec</c> is the ei_cnode representing the current cnode.</item>
<item><c>fd</c> is an open descriptor to an Erlang
connection.</item>
<item><c>count</c> is the address of an integer, or
@@ -88,12 +84,12 @@
</func>
<func>
- <name since=""><ret>int</ret><nametext>erl_global_register(fd,name,pid)</nametext></name>
+ <name since=""><ret>int</ret><nametext>ei_global_register(fd,name,pid)</nametext></name>
<fsummary>Register a name in global.</fsummary>
<type>
<v>int fd;</v>
<v>const char *name;</v>
- <v>ETERM *pid;</v>
+ <v>erlang_pid *pid;</v>
</type>
<desc>
<p>Registers a name in <c>global</c>.</p>
@@ -112,15 +108,17 @@
</func>
<func>
- <name since=""><ret>int</ret><nametext>erl_global_unregister(fd,name)</nametext></name>
+ <name since=""><ret>int</ret><nametext>ei_global_unregister(ec,fd,name)</nametext></name>
<fsummary>Unregister a name from global.</fsummary>
<type>
+ <v>ei_cnode *ec;</v>
<v>int fd;</v>
<v>const char *name;</v>
</type>
<desc>
<p>Unregisters a name from <c>global</c>.</p>
<list type="bulleted">
+ <item><c>ec</c> is the ei_cnode representing the current cnode.</item>
<item><c>fd</c> is an open descriptor to an Erlang
connection.</item>
<item><c>name</c> is the name to unregister from
@@ -131,30 +129,35 @@
</func>
<func>
- <name since=""><ret>ETERM *</ret><nametext>erl_global_whereis(fd,name,node)</nametext></name>
+ <name since=""><ret>int</ret><nametext>ei_global_whereis(ec,fd,name,pid,node)</nametext></name>
<fsummary>Look up a name in global.</fsummary>
<type>
+ <v>ei_cnode *ec;</v>
<v>int fd;</v>
<v>const char *name;</v>
+ <v>erlang_pid* pid;</v>
<v>char *node;</v>
</type>
<desc>
<p>Looks up a name in <c>global</c>.</p>
<list type="bulleted">
+ <item><c>ec</c> is the ei_cnode representing the current cnode.</item>
<item><c>fd</c> is an open descriptor to an Erlang
connection.</item>
<item><c>name</c> is the name that is to be looked up in
<c>global</c>.</item>
</list>
+ <p>The <c>pid</c> parameter is a pointer to a
+ <c>erlang_pid</c> that the function will update with the pid associated
+ with the global name, if successful.</p>
<p>If <c>node</c> is not <c>NULL</c>, it is a pointer to a
buffer where the function can fill in the name of the node where
<c>name</c> is found. <c>node</c> can be
- passed directly to <c>erl_connect()</c> if necessary.</p>
- <p>On success, the function returns an Erlang pid containing the address
- of the specified name, and the node is initialized to
+ passed directly to <c>ei_connect()</c> if necessary.</p>
+ <p>On success, the function returns 0, updates the <c>erlang_pid</c>
+ pointed to by the pid parameter, and the <c>node</c> parameter is initialized to
the node name where <c>name</c> is found. On failure,
- <c>NULL</c> is returned and <c>node</c> is not
- modified.</p>
+ a negative number is returned.</p>
</desc>
</func>
</funcs>
diff --git a/lib/erl_interface/doc/src/ei_users_guide.xml b/lib/erl_interface/doc/src/ei_users_guide.xml
index 7ca10d1a99..4ce8b9237f 100644
--- a/lib/erl_interface/doc/src/ei_users_guide.xml
+++ b/lib/erl_interface/doc/src/ei_users_guide.xml
@@ -37,12 +37,6 @@
<title>Deprecation and Removal</title>
<note><p>The support for VxWorks is deprecated as of OTP 22, and
will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
</section>
<section>
@@ -60,14 +54,12 @@
<seealso marker="mnesia:mnesia">Mnesia</seealso></item>
</list>
<note>
- <p>By default, the <c>Erl_Interface</c> libraries are only guaranteed
+ <p>By default, the <c>Erl_Interface</c> library is only guaranteed
to be compatible with other Erlang/OTP components from the same
release as the libraries themselves. For information about how to
communicate with Erlang/OTP components from earlier releases, see
function <seealso marker="ei#ei_set_compat_rel">
- <c>ei:ei_set_compat_rel</c></seealso> and
- <seealso marker="erl_eterm#erl_set_compat_rel">
- <c>erl_eterm:erl_set_compat_rel</c></seealso>.</p>
+ <c>ei_set_compat_rel</c></seealso>.</p>
</note>
<section>
@@ -98,10 +90,9 @@
<section>
<title>Compiling and Linking Your Code</title>
<p>To use any of the <c>Erl_Interface</c> functions, include the
- following lines in your code:</p>
+ following line in your code:</p>
<code type="none"><![CDATA[
-#include "erl_interface.h"
#include "ei.h" ]]></code>
<p>Determine where the top directory of your OTP installation is.
@@ -114,7 +105,7 @@ Eshell V4.7.4 (abort with ^G)
/usr/local/otp ]]></code>
<p>To compile your code, ensure that your C compiler knows where
- to find <c>erl_interface.h</c> by specifying an appropriate
+ to find <c>ei.h</c> by specifying an appropriate
<c>-I</c> argument on the command line, or add it to
the <c>CFLAGS</c> definition in your
<c>Makefile</c>. The correct value for this path is
@@ -140,11 +131,9 @@ $ cc -c -I/usr/local/otp/lib/erl_interface-3.2.3/include myprog.c ]]></code>
<p>When linking:</p>
<list type="bulleted">
- <item>Specify the path to <c>liberl_interface.a</c> and
- <c>libei.a</c> with
+ <item>Specify the path to <c>libei.a</c> with
<c>-L$OTPROOT/lib/erl_interface-3.2.3/lib</c>.</item>
- <item>Specify the name of the libraries with
- <c>-lerl_interface -lei</c>.</item>
+ <item>Specify the name of the library with <c>-lei</c>.</item>
</list>
<p>Do this on the command line or add the flags to the
@@ -155,7 +144,7 @@ $ cc -c -I/usr/local/otp/lib/erl_interface-3.2.3/include myprog.c ]]></code>
<code type="none"><![CDATA[
$ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
- lib myprog.o -lerl_interface -lei -o myprog ]]></code>
+ lib myprog.o -lei -o myprog ]]></code>
<p>On some systems it can be necessary to link with some more
libraries (for example, <c>libnsl.a</c> and
@@ -174,19 +163,11 @@ $ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
</section>
<section>
- <title>Initializing the Libraries</title>
+ <title>Initializing the Library</title>
<p>
- Before calling any of the other functions in the <c>erl_interface</c>
- and <c>ei</c> libraries, call <c>erl_init()</c> exactly once to initialize
- both libraries.
- <c>erl_init()</c> takes two arguments. However, the arguments
- are no longer used by <c>erl_interface</c> and are therefore to be
- specified as <c>erl_init(NULL,0)</c>.
- </p>
- <p>
- If you only use the <c>ei</c> library, instead initialize it by calling
- <c>ei_init()</c> exactly once before calling any other functions in
- the <c>ei</c> library.
+ Before calling any of the other functions in the library,
+ initialize it by calling
+ <c>ei_init()</c> exactly once.
</p>
</section>
@@ -199,187 +180,93 @@ $ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
<p>The <c>Erl_Interface</c> library supports this activity. It has
several C functions that create and manipulate Erlang data
- structures. The library also contains an encode and a decode function.
- The following example shows how to create and encode an Erlang tuple
- <c>{tobbe,3928}</c>:</p>
+ structures. The following example shows how to create and encode
+ an Erlang tuple <c>{tobbe,3928}</c>:</p>
<code type="none"><![CDATA[
-ETERM *arr[2], *tuple;
-char buf[BUFSIZ];
-int i;
-
-arr[0] = erl_mk_atom("tobbe");
-arr[1] = erl_mk_integer(3928);
-tuple = erl_mk_tuple(arr, 2);
-i = erl_encode(tuple, buf); ]]></code>
-
- <p>Alternatively, you can use <c>erl_send()</c> and
- <c>erl_receive_msg</c>, which handle the encoding and
- decoding of messages transparently.</p>
-
- <p>For a complete description, see the following modules:</p>
- <list type="bulleted">
- <item><seealso marker="erl_eterm"><c>erl_eterm</c></seealso>
- for creating Erlang terms</item>
- <item><seealso marker="erl_marshal"><c>erl_marshal</c></seealso>
- for encoding and decoding routines</item>
- </list>
+ei_x_buff buf;
+ei_x_new(&buf);
+int i = 0;
+ei_x_encode_tuple_header(&buf, 2);
+ei_x_encode_atom(&buf, "tobbe");
+ei_x_encode_long(&buf, 3928); ]]></code>
+
+ <p>For a complete description, see the
+ <seealso marker="ei"><c>ei</c></seealso> module.</p>
</section>
<section>
<marker id="building_terms_and_patterns"/>
- <title>Building Terms and Patterns</title>
+ <title>Building Terms</title>
<p>The previous example can be simplified by using the
- <seealso marker="erl_format"><c>erl_format</c></seealso> module
+ <seealso marker="ei#ei_x_format_wo_ver"><c>ei_x_format_wo_ver</c></seealso> function
to create an Erlang term:</p>
<code type="none"><![CDATA[
-ETERM *ep;
-ep = erl_format("{~a,~i}", "tobbe", 3928); ]]></code>
+ei_x_buff buf;
+ei_x_new(&buf);
+ei_x_format_wo_ver(&buf, "{~a,~i}", "tobbe", 3928); ]]></code>
- <p>For a complete description of the different format directives, see
- the <seealso marker="erl_format"><c>erl_format</c></seealso> module.</p>
+ <p>For a complete description of the different format directives, see the
+ the <seealso marker="ei#ei_x_format_wo_ver"><c>ei_x_format_wo_ver</c></seealso> function.</p>
<p>The following example is more complex:</p>
<code type="none"><![CDATA[
-ETERM *ep;
-ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
- "madonna",
- 21,
- erl_format("[{adr,~s,~i}]", "E-street", 42));
-erl_free_compound(ep); ]]></code>
+ei_x_buff buf;
+int i = 0;
+ei_x_new(&buf);
+ei_x_format_wo_ver(&buf,
+ "[{name,~a},{age,~i},{data,[{adr,~s,~i}]}]",
+ "madonna",
+ 21,
+ "E-street", 42);
+ei_print_term(stdout, buf.buff, &i);
+ei_x_free(&buf); ]]></code>
<p>As in the previous examples, it is your responsibility to free the
memory allocated for Erlang terms. In this example,
- <c>erl_free_compound()</c> ensures that the complete term
- pointed to by <c>ep</c> is released. This is necessary
- because the pointer from the second call to <c>erl_format</c> is lost.</p>
-
- <p>The following example shows a slightly different solution:</p>
-
- <code type="none"><![CDATA[
-ETERM *ep,*ep2;
-ep2 = erl_format("[{adr,~s,~i}]","E-street",42);
-ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
- "madonna", 21, ep2);
-erl_free_term(ep);
-erl_free_term(ep2); ]]></code>
-
- <p>In this case, you free the two terms independently. The order in
- which you free the terms <c>ep</c> and <c>ep2</c>
- is not important,
- because the <c>Erl_Interface</c> library uses reference counting to
- determine when it is safe to remove objects.</p>
-
- <p>If you are unsure whether you have freed the terms properly, you
- can use the following function to see the status of the fixed term
- allocator:</p>
-
- <code type="none"><![CDATA[
-long allocated, freed;
-
-erl_eterm_statistics(&allocated,&freed);
-printf("currently allocated blocks: %ld\n",allocated);
-printf("length of freelist: %ld\n",freed);
-
-/* really free the freelist */
-erl_eterm_release();
- ]]></code>
-
- <p>For more information, see the
- <seealso marker="erl_malloc"><c>erl_malloc</c></seealso> module.</p>
- </section>
-
- <section>
- <title>Pattern Matching</title>
- <p>An Erlang pattern is a term that can contain unbound variables or
- <c>"do not care"</c> symbols. Such a pattern can be matched
- against a
- term and, if the match is successful, any unbound variables in the
- pattern will be bound as a side effect. The content of a bound
- variable can then be retrieved:</p>
-
- <code type="none"><![CDATA[
-ETERM *pattern;
-pattern = erl_format("{madonna,Age,_}"); ]]></code>
-
- <p>The <seealso marker="erl_format#erl_match">
- <c>erl_format:erl_match</c></seealso> function
- performs pattern matching. It takes a
- pattern and a term and tries to match them. As a side effect any unbound
- variables in the pattern will be bound. In the following example, a
- pattern is created with a variable <c>Age</c>, which is included at two
- positions in the tuple. The pattern match is performed as follows:</p>
-
- <list type="bulleted">
- <item>
- <p><c>erl_match</c> binds the contents of <c>Age</c> to <c>21</c>
- the first time it reaches the variable.</p>
- </item>
- <item>
- <p>The second occurrence of <c>Age</c> causes a test for
- equality between the terms, as <c>Age</c> is already bound to
- <c>21</c>. As <c>Age</c> is bound to <c>21</c>, the equality test
- succeeds and the match continues until the end of the pattern.</p>
- </item>
- <item>
- <p>If the end of the pattern is reached, the match succeeds and you
- can retrieve the contents of the variable.</p>
- </item>
- </list>
-
- <code type="none"><![CDATA[
-ETERM *pattern,*term;
-pattern = erl_format("{madonna,Age,Age}");
-term = erl_format("{madonna,21,21}");
-if (erl_match(pattern, term)) {
- fprintf(stderr, "Yes, they matched: Age = ");
- ep = erl_var_content(pattern, "Age");
- erl_print_term(stderr, ep);
- fprintf(stderr,"\n");
- erl_free_term(ep);
-}
-erl_free_term(pattern);
-erl_free_term(term); ]]></code>
+ <c>ei_x_free()</c> ensures that the data
+ pointed to by <c>buf</c> is released.</p>
- <p>For more information, see the
- <seealso marker="erl_format#erl_match">
- <c>erl_format:erl_match</c></seealso> function.</p>
</section>
<section>
<title>Connecting to a Distributed Erlang Node</title>
<p>To connect to a distributed Erlang node, you must first
- initialize the connection routine with
- <seealso marker="erl_connect#erl_connect_init">
- <c>erl_connect:erl_connect_init</c></seealso>,
- which stores information, such as the hostname, node name, and IP
- address for later use:</p>
+ initialize the connection routine with one of the
+ <seealso marker="ei_connect#ei_connect_init">
+ <c>ei_connect_init_*</c></seealso> functions,
+ which stores information, such as the hostname, and node name
+ for later use:</p>
<code type="none"><![CDATA[
int identification_number = 99;
int creation=1;
char *cookie="a secret cookie string"; /* An example */
-erl_connect_init(identification_number, cookie, creation); ]]></code>
+const char* node_name = "einode@durin";
+const char *cookie = NULL;
+short creation = time(NULL) + 1;
+ei_cnode ec;
+ei_connect_init(ec,
+ node_name,
+ cookie,
+ creation); ]]></code>
<p>For more information, see the
- <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso> module.</p>
<p>After initialization, you set up the connection to the Erlang node.
- To specify the Erlang node you want to connect to, use
- <c>erl_connect()</c>. The following example sets up the
+ To specify the Erlang node you want to connect to, use the
+ <c>ei_connect_*()</c> family of functions. The following example sets up the
connection and is to result in a valid socket file descriptor:</p>
<code type="none"><![CDATA[
int sockfd;
-char *nodename="xyz@chivas.du.etx.ericsson.se"; /* An example */
-if ((sockfd = erl_connect(nodename)) < 0)
- erl_err_quit("ERROR: erl_connect failed"); ]]></code>
+const char* node_name = "einode@durin"; /* An example */
+if ((sockfd = ei_connect(ec, nodename)) < 0)
+ fprintf(stderr, "ERROR: ei_connect failed"); ]]></code>
- <p><c>erl_err_quit()</c> prints the specified string and
- terminates the program. For more information, see the
- <seealso marker="erl_error"><c>erl_error</c></seealso> module.</p>
</section>
<section>
@@ -394,7 +281,7 @@ if ((sockfd = erl_connect(nodename)) < 0)
correct port number to connect to.</p>
<p>When you use
- <seealso marker="erl_connect"><c>erl_connect</c></seealso>
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso>
to connect to an Erlang node, a connection is first made to
<c>epmd</c> and, if the node is known, a
connection is then made to the Erlang node.</p>
@@ -409,7 +296,7 @@ if ((sockfd = erl_connect(nodename)) < 0)
<code type="none"><![CDATA[
int pub;
-pub = erl_publish(port); ]]></code>
+pub = ei_publish(ec, port); ]]></code>
<p><c>pub</c> is a file descriptor now connected to
<c>epmd</c>. <c>epmd</c>
@@ -424,17 +311,9 @@ pub = erl_publish(port); ]]></code>
failed. If a node has failed in this way, <c>epmd</c>
prevents you from
registering a new node with the old name, as it thinks that the old
- name is still in use. In this case, you must unregister the name
- explicitly:</p>
+ name is still in use. In this case, you must close the port
+ explicitly</p>
- <code type="none"><![CDATA[
-erl_unpublish(node); ]]></code>
-
- <p>This causes <c>epmd</c> to close the connection from the
- far end. Notice
- that if the name was in fact still in use by a node, the results of
- this operation are unpredictable. Also, doing this does not cause the
- local end of the connection to close, so resources can be consumed.</p>
</section>
<section>
@@ -442,10 +321,10 @@ erl_unpublish(node); ]]></code>
<p>Use one of the following two functions to send messages:</p>
<list type="bulleted">
- <item><seealso marker="erl_connect#erl_send">
- <c>erl_connect:erl_send</c></seealso></item>
- <item><seealso marker="erl_connect#erl_reg_send">
- <c>erl_connect:erl_reg_send</c></seealso></item>
+ <item><seealso marker="ei_connect#ei_send">
+ <c>ei_send</c></seealso></item>
+ <item><seealso marker="ei_connect#ei_reg_send">
+ <c>ei_reg_send</c></seealso></item>
</list>
<p>As in Erlang, messages can be sent to a
@@ -456,82 +335,79 @@ erl_unpublish(node); ]]></code>
<p>Use one of the following two functions to receive messages:</p>
<list type="bulleted">
- <item><seealso marker="erl_connect#erl_receive">
- <c>erl_connect:erl_receive</c></seealso></item>
- <item><seealso marker="erl_connect#erl_receive_msg">
- <c>erl_connect:erl_receive_msg</c></seealso></item>
+ <item><seealso marker="ei_connect#ei_receive">
+ <c>ei_receive</c></seealso></item>
+ <item><seealso marker="ei_connect#ei_receive_msg">
+ <c>ei_receive_msg</c></seealso></item>
</list>
- <p><c>erl_receive()</c> receives the message into a buffer,
- while <c>erl_receive_msg()</c> decodes the message into an
- Erlang term.</p>
-
<section>
<title>Example of Sending Messages</title>
<p>In the following example, <c>{Pid, hello_world}</c> is
- sent to a registered process <c>my_server</c>. The message
- is encoded by <c>erl_send()</c>:</p>
+ sent to a registered process <c>my_server</c>:</p>
<code type="none"><![CDATA[
-extern const char *erl_thisnodename(void);
-extern short erl_thiscreation(void);
-#define SELF(fd) erl_mk_pid(erl_thisnodename(),fd,0,erl_thiscreation())
-ETERM *arr[2], *emsg;
-int sockfd, creation=1;
-
-arr[0] = SELF(sockfd);
-arr[1] = erl_mk_atom("Hello world");
-emsg = erl_mk_tuple(arr, 2);
-
-erl_reg_send(sockfd, "my_server", emsg);
-erl_free_term(emsg); ]]></code>
+ei_x_buff buf;
+ei_x_new_with_version(&buf);
+
+ei_x_encode_tuple_header(&buf, 2);
+ei_x_encode_pid(&buf, ei_self(ec));
+ei_x_encode_atom(&buf, "Hello world");
+
+ei_reg_send(ec,fd,"my_server",buf,buf.index);]]></code>
<p>The first element of the tuple that is sent is your own
pid. This enables <c>my_server</c> to reply.
For more information about the primitives, see the
- <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso> module.</p>
</section>
<section>
<title>Example of Receiving Messages</title>
- <p>In this example, <c>{Pid, Something}</c> is received. The
- received pid is then used to return
- <c>{goodbye,Pid}</c>.</p>
-
- <code type="none"><![CDATA[
-ETERM *arr[2], *answer;
-int sockfd,rc;
-char buf[BUFSIZE];
-ErlMessage emsg;
-
-if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
- arr[0] = erl_mk_atom("goodbye");
- arr[1] = erl_element(1, emsg.msg);
- answer = erl_mk_tuple(arr, 2);
- erl_send(sockfd, arr[1], answer);
- erl_free_term(answer);
- erl_free_term(emsg.msg);
- erl_free_term(emsg.to);
-} ]]></code>
+ <p>In this example, <c>{Pid, Something}</c> is received.</p>
+
+ <code type="none"><![CDATA[
+erlang_msg msg;
+int index = 0;
+int version;
+int arity = 0;
+erlang_pid pid;
+ei_x_buff buf;
+ei_x_new(&buf);
+for (;;) {
+ int got = ei_xreceive_msg(fd, &msg, &x);
+ if (got == ERL_TICK)
+ continue;
+ if (got == ERL_ERROR) {
+ fprintf(stderr, "ei_xreceive_msg, got==%d", got);
+ exit(1);
+ }
+ break;
+}
+ei_decode_version(buf.buff, &index, &version);
+ei_decode_tuple_header(buf.buff, &index, &arity);
+if (arity != 2) {
+ fprintf(stderr, "got wrong message");
+ exit(1);
+}
+ei_decode_pid(buf.buff, &index, &pid); ]]></code>
<p>To provide robustness, a distributed Erlang node
occasionally polls all its connected neighbors in an attempt to
detect failed nodes or communication links. A node that receives such
a message is expected to respond immediately with an
<c>ERL_TICK</c> message. This is done automatically by
- <c>erl_receive()</c>. However, when this has occurred,
- <c>erl_receive</c> returns <c>ERL_TICK</c> to
+ <c>ei_xreceive_msg()</c>. However, when this has occurred,
+ <c>ei_xreceive_msg</c> returns <c>ERL_TICK</c> to
the caller without storing a message into the
- <c>ErlMessage</c> structure.</p>
+ <c>erlang_msg</c> structure.</p>
<p>When a message has been received, it is the caller's responsibility
- to free the received message <c>emsg.msg</c> and
- <c>emsg.to</c> or <c>emsg.from</c>,
- depending on the type of message received.</p>
+ to free the received message.</p>
<p>For more information, see the
- <seealso marker="erl_connect"><c>erl_connect</c></seealso> and
- <seealso marker="erl_eterm"><c>erl_eterm</c></seealso> modules.</p>
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso> and
+ <seealso marker="ei"><c>ei</c></seealso> modules.</p>
</section>
</section>
@@ -542,31 +418,30 @@ if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
included in a function call at a remote node and is called a remote
procedure call.</p>
- <p>The following example shows how the
- <c>Erl_Interface</c> library supports remote procedure calls:</p>
+ <p>The following example checks if a specific Erlang process is alive:</p>
<code type="none"><![CDATA[
-char modname[]=THE_MODNAME;
-ETERM *reply,*ep;
-ep = erl_format("[~a,[]]", modname);
-if (!(reply = erl_rpc(fd, "c", "c", ep)))
- erl_err_msg("<ERROR> when compiling file: %s.erl !\n", modname);
-erl_free_term(ep);
-ep = erl_format("{ok,_}");
-if (!erl_match(ep, reply))
- erl_err_msg("<ERROR> compiler errors !\n");
-erl_free_term(ep);
-erl_free_term(reply); ]]></code>
-
- <p><c>c:c/1</c> is called to compile the specified module on
- the remote node. <c>erl_match()</c> checks that the
- compilation was
- successful by testing for the expected <c>ok</c>.</p>
-
- <p>For more information about <c>erl_rpc()</c> and its
- companions <c>erl_rpc_to()</c> and
- <c>erl_rpc_from()</c>, see the
- <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
+int index = 0, is_alive;
+ei_x_buff args, result;
+
+ei_x_new(&result);
+ei_x_new(&args);
+ei_x_encode_list_header(&args, 1);
+ei_x_encode_pid(&args, &check_pid);
+ei_x_encode_empty_list(&args);
+
+if (ei_rpc(&ec, fd, "erlang", "is_process_alive",
+ args.buff, args.index, &result) < 0)
+ handle_error();
+
+if (ei_decode_version(result.buff, &index) < 0
+ || ei_decode_bool(result.buff, &index, &is_alive) < 0)
+ handle_error(); ]]></code>
+
+ <p>For more information about <c>ei_rpc()</c> and its
+ companions <c>ei_rpc_to()</c> and
+ <c>ei_rpc_from()</c>, see the
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso> module.</p>
</section>
<section>
@@ -590,7 +465,7 @@ char **names;
int count;
int i;
-names = erl_global_names(fd,&count);
+names = ei_global_names(ec,fd,&count);
if (names)
for (i=0; i<count; i++)
@@ -598,8 +473,8 @@ if (names)
free(names); ]]></code>
- <p><seealso marker="erl_global#erl_global_names">
- <c>erl_global:erl_global_names</c></seealso>
+ <p><seealso marker="ei_global#ei_global_names">
+ <c>ei_global_names</c></seealso>
allocates and returns a buffer containing
all the names known to the <c>global</c> module in <c>Kernel</c>.
<c>count</c> is initialized to
@@ -608,7 +483,7 @@ free(names); ]]></code>
<c>count</c> to determine when the last name is reached.</p>
<p>It is the caller's responsibility to free the array.
- <c>erl_global_names</c> allocates the array and all the strings
+ <c>ei_global_names</c> allocates the array and all the strings
using a single call to <c>malloc()</c>, so
<c>free(names)</c> is all that is necessary.</p>
@@ -617,16 +492,18 @@ free(names); ]]></code>
<code type="none"><![CDATA[
ETERM *pid;
char node[256];
+erlang_pid the_pid;
-pid = erl_global_whereis(fd,"schedule",node); ]]></code>
+if (ei_global_whereis(ec,fd,"schedule",&the_pid,node) < 0)
+ fprintf(stderr, "ei_global_whereis error\n"); ]]></code>
<p>If <c>"schedule"</c> is known to the
<c>global</c> module in <c>Kernel</c>, an Erlang pid is
- returned that can be used to send messages to the schedule service.
+ written to the_pid. This pid that can be used to send messages to the schedule service.
Also, <c>node</c> is initialized to contain the name of
the node where the service is registered, so that you can make a
connection to it by simply passing the variable to
- <seealso marker="erl_connect"><c>erl_connect</c></seealso>.</p>
+ <seealso marker="ei_connect"><c>ei_connect</c></seealso>.</p>
<p>Before registering a name, you should already have registered your
port number with <c>epmd</c>. This is not strictly necessary,
@@ -634,30 +511,27 @@ pid = erl_global_whereis(fd,"schedule",node); ]]></code>
neglect to do so, then other nodes wishing to communicate with your
service cannot find or connect to your process.</p>
- <p>Create a pid that Erlang processes can use to communicate with your
+ <p>Create a name that Erlang processes can use to communicate with your
service:</p>
<code type="none"><![CDATA[
-ETERM *pid;
-
-pid = erl_mk_pid(thisnode,14,0,0);
-erl_global_register(fd,servicename,pid); ]]></code>
+ei_global_register(fd,servicename,ei_self(ec)); ]]></code>
<p>After registering the name, use
- <seealso marker="erl_connect#erl_accept">
- <c>erl_connect:erl_accept</c></seealso>
+ <seealso marker="ei_connect#ei_accept">
+ <c>ei_accept</c></seealso>
to wait for incoming connections.</p>
<note>
<p>Remember to free <c>pid</c> later with
- <seealso marker="erl_malloc#erl_free_term">
- <c>erl_malloc:erl_free_term</c></seealso>.</p>
+ <seealso marker="ei#ei_x_free">
+ <c>ei_x_free</c></seealso>.</p>
</note>
<p>To unregister a name:</p>
<code type="none"><![CDATA[
-erl_global_unregister(fd,servicename); ]]></code>
+ei_global_unregister(ec,fd,servicename); ]]></code>
</section>
<section>
@@ -755,7 +629,7 @@ ei_reg_close(reg); ]]></code>
<p>The contents of a registry can be backed up to
<seealso marker="mnesia:mnesia"><c>Mnesia</c></seealso> on a "nearby" Erlang
node. You must provide an open connection to the Erlang node
- (see <seealso marker="erl_connect"><c>erl_connect</c></seealso>).
+ (see <seealso marker="ei_connect"><c>ei_connect</c></seealso>).
Also, <c>Mnesia</c> 3.0 or later must be running
on the Erlang node before the backup is initiated:</p>
@@ -818,9 +692,9 @@ ei_reg_restore(fd, reg, "mtab"); ]]></code>
<c>Mnesia</c> backup of the registry contents. This can be avoided if
you mark the object as dirty after any such changes with
<seealso marker="registry#ei_reg_markdirty">
- <c>registry:ei_reg_markdirty</c></seealso>, or pass appropriate flags to
+ <c>ei_reg_markdirty</c></seealso>, or pass appropriate flags to
<seealso marker="registry#ei_reg_dump">
- <c>registry:ei_reg_dump</c></seealso>.</p>
+ <c>ei_reg_dump</c></seealso>.</p>
</section>
</section>
</chapter>
diff --git a/lib/erl_interface/doc/src/erl_call.xml b/lib/erl_interface/doc/src/erl_call_cmd.xml
index 73b9b13e4d..91cb9dbd32 100644
--- a/lib/erl_interface/doc/src/erl_call.xml
+++ b/lib/erl_interface/doc/src/erl_call_cmd.xml
@@ -78,6 +78,22 @@
<c>Fun</c>, and <c>Args</c> in a manner
dependent on the behavior of your command shell.</p>
</item>
+ <tag><c>-address [Hostname:]Port</c></tag>
+ <item>
+ <p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
+ <c>-address</c> is required.) <c>Hostname</c> is the
+ hostname of the machine that is running the node that
+ <c>erl_call</c> shall communicate with. The default
+ hostname is the hostname of the local machine. <c>Port</c>
+ is the port number of the node that <c>erl_call</c> shall
+ communicate with. The <c>-address</c> flag cannot be
+ combined with any of the flags <c>-n</c>, <c>-name</c>,
+ <c>-sname</c> or <c>-s</c>.</p>
+ <p>The <c>-address</c> flag is typically useful when one
+ wants to call a node that is running on machine without an
+ accessible <seealso marker="erts:epmd">epmd</seealso>
+ instance.</p>
+ </item>
<tag><c>-c Cookie</c></tag>
<item>
<p>(<em>Optional.</em>) Use this option to specify a certain cookie.
@@ -112,13 +128,15 @@
</item>
<tag><c>-n Node</c></tag>
<item>
- <p>(One of <c>-n, -name, -sname</c> is required.)
+ <p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
+ <c>-address</c> is required.)
Has the same meaning as <c>-name</c> and can still be
used for backward compatibility reasons.</p>
</item>
<tag><c>-name Node</c></tag>
<item>
- <p>(One of <c>-n, -name, -sname</c> is required.)
+ <p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
+ <c>-address</c> is required.)
<c>Node</c> is the name of the node to be
started or communicated with. It is assumed that
<c>Node</c> is started with
@@ -149,7 +167,8 @@
</item>
<tag><c>-sname Node</c></tag>
<item>
- <p>(One of <c>-n, -name, -sname</c> is required.)
+ <p>(One of <c>-n</c>, <c>-name</c>, <c>-sname</c> or
+ <c>-address</c> is required.)
<c>Node</c> is the name of the node to be started
or communicated with. It is assumed that <c>Node</c>
is started with <c>erl -sname</c>, which means that
diff --git a/lib/erl_interface/doc/src/erl_connect.xml b/lib/erl_interface/doc/src/erl_connect.xml
deleted file mode 100644
index 9492a82864..0000000000
--- a/lib/erl_interface/doc/src/erl_connect.xml
+++ /dev/null
@@ -1,662 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_connect</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1998-07-03</date>
- <rev>A</rev>
- <file>erl_connect.xml</file>
- </header>
- <lib>erl_connect</lib>
- <libsummary>Communicate with distributed Erlang.</libsummary>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
-
- <p>This module provides support for communication between distributed
- Erlang nodes and C-nodes, in a manner that is transparent to Erlang
- processes.</p>
-
- <p>A C-node appears to Erlang as a <em>hidden node</em>.
- That is, Erlang processes that know the name of the
- C-node can communicate with it in a normal manner, but
- the node name does not appear in the listing provided by
- <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>
- in <c>ERTS</c>.</p>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>int</ret><nametext>erl_accept(listensock, conp)</nametext></name>
- <fsummary>Accept a connection.</fsummary>
- <type>
- <v>int listensock;</v>
- <v>ErlConnect *conp;</v>
- </type>
- <desc>
- <p>This function is used by a server process to accept a
- connection from a client process.</p>
- <list type="bulleted">
- <item><c>listensock</c> is an open socket descriptor on
- which <c>listen()</c> has previously been called.</item>
- <item><c>conp</c> is a pointer to an
- <c>ErlConnect</c> struct, described as follows:</item>
- </list>
- <code type="none"><![CDATA[
-typedef struct {
- char ipadr[4];
- char nodename[MAXNODELEN];
-} ErlConnect;
- ]]></code>
- <p>On success, <c>conp</c> is filled in with the address and
- node name of the connecting client and a file descriptor is
- returned. On failure, <c>ERL_ERROR</c> is returned and
- <c>erl_errno</c> is set to <c>EIO</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_close_connection(fd)</nametext></name>
- <fsummary>Close a connection to an Erlang node.</fsummary>
- <type>
- <v>int fd;</v>
- </type>
- <desc>
- <p>Closes an open connection to an Erlang node.</p>
- <p><c>Fd</c> is a file descriptor obtained from
- <c>erl_connect()</c> or
- <c>erl_xconnect()</c>.</p>
- <p>Returns <c>0</c> on success. If the call fails, a non-zero value
- is returned, and the reason for the error can be obtained with the
- appropriate platform-dependent call.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_connect(node)</nametext></name>
- <name since=""><ret>int</ret><nametext>erl_xconnect(addr, alive)</nametext></name>
- <fsummary>Establish a connection to an Erlang node.</fsummary>
- <type>
- <v>char *node, *alive;</v>
- <v>struct in_addr *addr;</v>
- </type>
- <desc>
- <p>Sets up a connection to an Erlang node.</p>
- <p><c>erl_xconnect()</c> requires the IP address of the
- remote host and the alivename of the remote node to be
- specified. <c>erl_connect()</c> provides an alternative
- interface, and determines the information from the node name
- provided.</p>
- <list type="bulleted">
- <item><c>addr</c> is the 32-bit IP address of the remote
- host.</item>
- <item><c>alive</c> is the alivename of the remote node.
- </item>
- <item><c>node</c> is the name of the remote node.</item>
- </list>
- <p>Returns an open file descriptor on success, otherwise a negative
- value. In the latter case <c>erl_errno</c> is set to one
- of:</p>
- <taglist>
- <tag><c>EHOSTUNREACH</c></tag>
- <item>The remote host <c>node</c> is unreachable.</item>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- <p>Also, <c>errno</c> values from
- <c>socket</c><em>(2)</em> and
- <c>connect</c><em>(2)</em>
- system calls can be propagated into <c>erl_errno</c>.</p>
- <p><em>Example:</em></p>
- <code type="none"><![CDATA[
-#define NODE "madonna@chivas.du.etx.ericsson.se"
-#define ALIVE "madonna"
-#define IP_ADDR "150.236.14.75"
-
-/*** Variant 1 ***/
-erl_connect( NODE );
-
-/*** Variant 2 ***/
-struct in_addr addr;
-addr = inet_addr(IP_ADDR);
-erl_xconnect( &addr , ALIVE );
- ]]></code>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_connect_init(number, cookie, creation)</nametext></name>
- <name since=""><ret>int</ret><nametext>erl_connect_xinit(host, alive, node, addr, cookie, creation)</nametext></name>
- <fsummary>Initialize communication.</fsummary>
- <type>
- <v>int number;</v>
- <v>char *cookie;</v>
- <v>short creation;</v>
- <v>char *host,*alive,*node;</v>
- <v>struct in_addr *addr;</v>
- </type>
- <desc>
- <p>Initializes the <c>erl_connect</c> module.
- In particular, these functions are used to identify the name of the
- C-node from which they are called. One of these functions must
- be called before any of the other functions in the <c>erl_connect</c>
- module are used.</p>
- <p><c>erl_connect_xinit()</c> stores for later use
- information about:</p>
- <list type="bulleted">
- <item>Hostname of the node, <c>host</c></item>
- <item>Alivename, <c>alive</c></item>
- <item>Node name, <c>node</c></item>
- <item>IP address, <c>addr</c></item>
- <item>Cookie, <c>cookie</c></item>
- <item>Creation number, <c>creation</c></item>
- </list>
- <p><c>erl_connect_init()</c>
- provides an alternative interface that does not require as much
- information from the caller. Instead,
- <c>erl_connect_init()</c>
- uses <c>gethostbyname()</c> to obtain default values.</p>
- <p>If you use <c>erl_connect_init()</c>, your node will
- have a short name, that is, it will not be fully qualified. If you
- need to use fully qualified (long) names, use
- <c>erl_connect_xinit()</c> instead.</p>
- <list type="bulleted">
- <item>
- <p><c>host</c> is the name of the host on which the node
- is running.</p>
- </item>
- <item>
- <p><c>alive</c> is the alivename of the node.</p>
- </item>
- <item>
- <p><c>node</c> is the node name. It is to
- be of the form <em>alivename@hostname</em>.</p>
- </item>
- <item>
- <p><c>addr</c> is the 32-bit IP address of
- <c>host</c>.</p>
- </item>
- <item>
- <p><c>cookie</c> is the authorization string required
- for access to the remote node. If <c>NULL</c>, the user
- <c>HOME</c> directory is searched for a cookie file
- <c>.erlang.cookie</c>. The path to
- the home directory is retrieved from environment variable
- <c>HOME</c> on Unix and from the
- <c>HOMEDRIVE</c> and
- <c>HOMEPATH</c> variables on Windows. For more
- details, see the <seealso marker="kernel:auth">
- <c>auth</c></seealso> module in Kernel.</p>
- </item>
- <item>
- <p><c>creation</c> helps identifying a particular
- instance of a C-node. In particular, it can help prevent us from
- receiving messages sent to an earlier process with the same
- registered name.</p>
- </item>
- </list>
- <p>A C-node acting as a server is assigned a creation number
- when it calls <c>erl_publish()</c>.</p>
- <p><c>number</c> is used by
- <c>erl_connect_init()</c> to
- construct the actual node name. In Example 2
- below, <em>"c17@a.DNS.name"</em> is the resulting node name.</p>
- <p><em>Example 1:</em></p>
- <code type="none"><![CDATA[
-struct in_addr addr;
-addr = inet_addr("150.236.14.75");
-if (!erl_connect_xinit("chivas",
- "madonna",
- "madonna@chivas.du.etx.ericsson.se",
- &addr;
- "samplecookiestring..."),
- 0)
- erl_err_quit("<ERROR> when initializing !");
- ]]></code>
- <p><em>Example 2:</em></p>
- <code type="none"><![CDATA[
-if (!erl_connect_init(17, "samplecookiestring...", 0))
- erl_err_quit("<ERROR> when initializing !");
- ]]></code>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_publish(port)</nametext></name>
- <fsummary>Publish a node name.</fsummary>
- <type>
- <v>int port;</v>
- </type>
- <desc>
- <p>This function is used by a server process to register
- with the local name server EPMD, thereby allowing
- other processes to send messages by using the registered name.
- Before calling this function, the process should
- have called <c>bind()</c> and <c>listen()</c>
- on an open socket.</p>
- <p><c>port</c> is the local name to register, and is to be
- the same as the port number that was previously bound to the
- socket.</p>
- <p>To unregister with EPMD, simply close the returned descriptor.</p>
- <p>On success, a descriptor connecting the calling process to EPMD is
- returned. On failure, <c>-1</c> is returned and
- <c>erl_errno</c> is set to:</p>
- <taglist>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- <p>Also, <c>errno</c> values from
- <c>socket</c><em>(2)</em>
- and <c>connect</c><em>(2)</em> system calls can be
- propagated into <c>erl_errno</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_receive(fd, bufp, bufsize)</nametext></name>
- <fsummary>Receive a message.</fsummary>
- <type>
- <v>int fd;</v>
- <v>char *bufp;</v>
- <v>int bufsize;</v>
- </type>
- <desc>
- <p>Receives a message consisting of a sequence
- of bytes in the Erlang external format.</p>
- <list type="bulleted">
- <item><c>fd</c> is an open descriptor to an Erlang
- connection.</item>
- <item><c>bufp</c> is a buffer large enough to hold the
- expected message.</item>
- <item><c>bufsize</c> indicates the size of
- <c>bufp</c>.</item>
- </list>
- <p>If a <em>tick</em> occurs, that is, the Erlang node on the
- other end of the connection has polled this node to see if it
- is still alive, the function returns <c>ERL_TICK</c> and
- no message is placed in the buffer. Also,
- <c>erl_errno</c> is set to <c>EAGAIN</c>.</p>
- <p>On success, the message is placed in the specified buffer
- and the function returns the number of bytes actually read. On
- failure, the function returns a negative value and sets
- <c>erl_errno</c> to one of:</p>
- <taglist>
- <tag><c>EAGAIN</c></tag>
- <item>Temporary error: Try again.</item>
- <tag><c>EMSGSIZE</c></tag>
- <item>Buffer is too small.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_receive_msg(fd, bufp, bufsize, emsg)</nametext></name>
- <fsummary>Receive and decode a message.</fsummary>
- <type>
- <v>int fd;</v>
- <v>unsigned char *bufp;</v>
- <v>int bufsize;</v>
- <v>ErlMessage *emsg;</v>
- </type>
- <desc>
- <p>Receives the message into the specified buffer
- and decodes into <c>(ErlMessage *) emsg</c>.</p>
- <list type="bulleted">
- <item><c>fd</c> is an open descriptor to an Erlang
- connection.</item>
- <item><c>bufp</c> is a buffer large enough to hold the
- expected message.</item>
- <item><c>bufsize</c> indicates the size of
- <c>bufp</c>.</item>
- <item>><c>emsg</c> is a pointer to an
- <c>ErlMessage</c> structure
- into which the message will be decoded.
- <c>ErlMessage</c> is defined as follows:</item>
- </list>
- <code type="none"><![CDATA[
-typedef struct {
- int type;
- ETERM *msg;
- ETERM *to;
- ETERM *from;
- char to_name[MAXREGLEN];
-} ErlMessage;
- ]]></code>
- <note>
- <p>The definition of <c>ErlMessage</c> has changed since
- earlier versions of <c>Erl_Interface</c>.</p>
- </note>
- <p><c>type</c> identifies the type of message, one of the
- following:</p>
- <taglist>
- <tag><c>ERL_SEND</c></tag>
- <item>
- <p>An ordinary send operation has occurred and
- <c>emsg->to</c> contains the pid of the recipient.
- The message is in <c>emsg->msg</c>.</p>
- </item>
- <tag><c>ERL_REG_SEND</c></tag>
- <item>
- <p>A registered send operation has occurred and
- <c>emsg->from</c> contains the pid of the sender.
- The message is in <c>emsg->msg</c>.</p>
- </item>
- <tag><c>ERL_LINK</c> or <c>ERL_UNLINK</c>
- </tag>
- <item>
- <p><c>emsg->to</c> and <c>emsg->from</c>
- contain the pids of the sender and recipient of the link or
- unlink. <c>emsg->msg</c> is not used.</p>
- </item>
- <tag><c>ERL_EXIT</c></tag>
- <item>
- <p>A link is broken. <c>emsg->to</c> and
- <c>emsg->from</c> contain the pids of the linked
- processes, and <c>emsg->msg</c> contains the reason
- for the exit.</p>
- </item>
- </taglist>
- <note>
- <p>It is the caller's responsibility to release the
- memory pointed to by <c>emsg->msg</c>,
- <c>emsg->to</c>, and
- <c>emsg->from</c>.</p>
- </note>
- <p>If a <em>tick</em> occurs, that is, the Erlang node on the
- other end of the connection has polled this node to see if it
- is still alive, the function returns <c>ERL_TICK</c>
- indicating that the tick has been received and responded to,
- but no message is placed in the buffer. In this case you
- are to call <c>erl_receive_msg()</c> again.</p>
- <p>On success, the function returns <c>ERL_MSG</c> and the
- <c>Emsg</c> struct is initialized as described above, or
- <c>ERL_TICK</c>, in which case no message is returned. On
- failure, the function returns <c>ERL_ERROR</c> and sets
- <c>erl_errno</c> to one of:</p>
- <taglist>
- <tag><c>EMSGSIZE</c></tag>
- <item>Buffer is too small.</item>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_reg_send(fd, to, msg)</nametext></name>
- <fsummary>Send a message to a registered name.</fsummary>
- <type>
- <v>int fd;</v>
- <v>char *to;</v>
- <v>ETERM *msg;</v>
- </type>
- <desc>
- <p>Sends an Erlang term to a registered process.</p>
- <list type="bulleted">
- <item><c>fd</c> is an open descriptor to an Erlang
- connection.</item>
- <item><c>to</c> is a string containing the registered name
- of the intended recipient of the message.</item>
- <item><c>msg</c> is the Erlang term to be sent.</item>
- </list>
- <p>Returns <c>1</c> on success, otherwise <c>0</c>. In
- the latter case <c>erl_errno</c> is set to one of:</p>
- <taglist>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_rpc(fd, mod, fun, args)</nametext></name>
- <name since=""><ret>int</ret><nametext>erl_rpc_from(fd, timeout, emsg)</nametext></name>
- <name since=""><ret>int</ret><nametext>erl_rpc_to(fd, mod, fun, args)</nametext></name>
- <fsummary>Remote Procedure Call.</fsummary>
- <type>
- <v>int fd, timeout;</v>
- <v>char *mod, *fun;</v>
- <v>ETERM *args;</v>
- <v>ErlMessage *emsg;</v>
- </type>
- <desc>
- <p>Supports calling Erlang functions on remote nodes.
- <c>erl_rpc_to()</c> sends an RPC request to a remote node
- and <c>erl_rpc_from()</c> receives the results of such a
- call. <c>erl_rpc()</c> combines the functionality of
- these two functions by sending an RPC request and waiting for the
- results. See also <seealso marker="kernel:rpc#call/4">
- <c>rpc:call/4</c></seealso> in <c>Kernel</c>.</p>
- <list type="bulleted">
- <item><c>fd</c> is an open descriptor to an Erlang
- connection.</item>
- <item><c>timeout</c> is the maximum time (in milliseconds)
- to wait for
- results. To wait forever, specify <c>ERL_NO_TIMEOUT</c>.
- When <c>erl_rpc()</c> calls <c>erl_rpc_from()</c>, the call will
- never timeout.</item>
- <item><c>mod</c> is the name of the module containing the
- function to be run on the remote node.</item>
- <item><c>fun</c> is the name of the function to run.
- </item>
- <item><c>args</c> is an Erlang list, containing the
- arguments to be passed to the function.</item>
- <item><c>emsg</c> is a message containing the result of
- the function call.</item>
- </list>
- <p>The actual message returned by the RPC server
- is a 2-tuple <c>{rex,Reply}</c>. If you use
- <c>erl_rpc_from()</c> in your code, this is the message
- you will need to parse. If you use <c>erl_rpc()</c>, the
- tuple itself is parsed for you, and the message returned to your
- program is the Erlang term containing <c>Reply</c> only.
- Replies to RPC requests are always <c>ERL_SEND</c> messages.</p>
- <note>
- <p>It is the caller's responsibility to free the returned
- <c>ETERM</c> structure and the memory pointed to by
- <c>emsg->msg</c> and <c>emsg->to</c>.</p>
- </note>
- <p><c>erl_rpc()</c> returns the remote function's return
- value on success, otherwise <c>NULL</c>.</p>
- <p><c>erl_rpc_to()</c> returns <c>0</c> on
- success, otherwise a negative number.</p>
- <p><c>erl_rcp_from()</c> returns <c>ERL_MSG</c>
- on success (with <c>Emsg</c> now
- containing the reply tuple), otherwise one of
- <c>ERL_TICK</c>, <c>ERL_TIMEOUT</c>, or
- <c>ERL_ERROR</c>.</p>
- <p>When failing,
- all three functions set <c>erl_errno</c> to one of:</p>
- <taglist>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- <tag><c>ETIMEDOUT</c></tag>
- <item>Timeout has expired.</item>
- <tag><c>EAGAIN</c></tag>
- <item>Temporary error: Try again.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_send(fd, to, msg)</nametext></name>
- <fsummary>Send a message.</fsummary>
- <type>
- <v>int fd;</v>
- <v>ETERM *to, *msg;</v>
- </type>
- <desc>
- <p>Sends an Erlang term to a process.</p>
- <list type="bulleted">
- <item><c>fd</c> is an open descriptor to an Erlang
- connection.</item>
- <item><c>to</c> is an Erlang term containing the pid of
- the intended recipient of the message.</item>
- <item>><c>msg</c> is the Erlang term to be sent.</item>
- </list>
- <p>Returns <c>1</c> on success, otherwise <c>0</c>. In
- the latter case <c>erl_errno</c> is set to one of:</p>
- <taglist>
- <tag><c>EINVAL</c></tag>
- <item>Invalid argument: <c>to</c> is not a valid Erlang
- pid.</item>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>const char *</ret><nametext>erl_thisalivename()</nametext></name>
- <name since=""><ret>const char *</ret><nametext>erl_thiscookie()</nametext></name>
- <name since=""><ret>short</ret><nametext>erl_thiscreation()</nametext></name>
- <name since=""><ret>const char *</ret><nametext>erl_thishostname()</nametext></name>
- <name since=""><ret>const char *</ret><nametext>erl_thisnodename()</nametext></name>
- <fsummary>Retrieve some values.</fsummary>
- <desc>
- <p>Retrieves information about
- the C-node. These values are initially set with
- <c>erl_connect_init()</c> or
- <c>erl_connect_xinit()</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_unpublish(alive)</nametext></name>
- <fsummary>Forcefully unpublish a node name.</fsummary>
- <type>
- <v>char *alive;</v>
- </type>
- <desc>
- <p>This function can be called by a process to unregister a
- specified node from EPMD on the local host. This is, however, usually
- not allowed, unless EPMD was started with flag
- <c>-relaxed_command_check</c>, which it normally is not.</p>
- <p>To unregister a node you have published, you should instead
- close the descriptor that was returned by
- <c>ei_publish()</c>.</p>
- <warning>
- <p>This function is deprecated and will be removed in a future
- release.</p>
- </warning>
- <p><c>alive</c> is the name of the node to unregister, that
- is, the first component of the node name, without
- <c>@hostname</c>.</p>
- <p>If the node was successfully unregistered from EPMD, <c>0</c> is
- returned, otherwise <c>-1</c> is returned and
- <c>erl_errno</c> is set to <c>EIO</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_xreceive_msg(fd, bufpp, bufsizep, emsg)</nametext></name>
- <fsummary>Receive and decode a message.</fsummary>
- <type>
- <v>int fd;</v>
- <v>unsigned char **bufpp;</v>
- <v>int *bufsizep;</v>
- <v>ErlMessage *emsg;</v>
- </type>
- <desc>
- <p>Similar to <c>erl_receive_msg</c>. The difference is
- that <c>erl_xreceive_msg</c> expects the buffer to
- have been allocated by <c>malloc</c>, and reallocates it
- if the received
- message does not fit into the original buffer. Therefore
- both buffer and buffer length are given as pointers; their values
- can change by the call.</p>
- <p>On success, the function returns <c>ERL_MSG</c> and the
- <c>Emsg</c> struct is initialized as described above, or
- <c>ERL_TICK</c>, in which case no message is returned. On
- failure, the function returns <c>ERL_ERROR</c> and sets
- <c>erl_errno</c> to one of:</p>
- <taglist>
- <tag><c>EMSGSIZE</c></tag>
- <item>Buffer is too small.</item>
- <tag><c>ENOMEM</c></tag>
- <item>No more memory is available.</item>
- <tag><c>EIO</c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyaddr(addr, length, type)</nametext></name>
- <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, h_errnop)</nametext></name>
- <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyname(name)</nametext></name>
- <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyname_r(name, hostp, buffer, buflen, h_errnop)</nametext></name>
-
- <fsummary>Name lookup functions.</fsummary>
- <type>
- <v>const char *name;</v>
- <v>const char *addr;</v>
- <v>int length;</v>
- <v>int type;</v>
- <v>struct hostent *hostp;</v>
- <v>char *buffer;</v>
- <v>int buflen;</v>
- <v>int *h_errnop;</v>
- </type>
- <desc>
- <p>Convenience functions for some common name lookup functions.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>Debug Information</title>
- <p>If a connection attempt fails, the following can be checked:</p>
-
- <list type="bulleted">
- <item><c>erl_errno</c></item>
- <item>That the correct cookie was used</item>
- <item>That EPMD is running</item>
- <item>That the remote Erlang node on the other side is running the same
- version of Erlang as the <c>erl_interface</c> library</item>
- </list>
- </section>
-</cref>
diff --git a/lib/erl_interface/doc/src/erl_error.xml b/lib/erl_interface/doc/src/erl_error.xml
deleted file mode 100644
index 6fac94e442..0000000000
--- a/lib/erl_interface/doc/src/erl_error.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_error</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1996-10-14</date>
- <rev>A</rev>
- <file>erl_error.xml</file>
- </header>
- <lib>erl_error</lib>
- <libsummary>Error print routines.</libsummary>
- <description>
- <p>This module contains some error printing routines taken
- from "Advanced Programming in the UNIX Environment"
- by W. Richard Stevens.</p>
-
- <p>These functions are all called in the same manner as
- <c>printf()</c>, that is, with a string containing format
- specifiers followed by a list of corresponding arguments. All output from
- these functions is to <c>stderr</c>.</p>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>void</ret><nametext>erl_err_msg(FormatStr, ... )</nametext></name>
- <fsummary>Non-fatal error, and not system call error.</fsummary>
- <type>
- <v>const char *FormatStr;</v>
- </type>
- <desc>
- <p>The message provided by the caller is printed. This
- function is simply a wrapper for <c>fprintf()</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_err_quit(FormatStr, ... )</nametext></name>
- <fsummary>Fatal error, but not system call error.</fsummary>
- <type>
- <v>const char *FormatStr;</v>
- </type>
- <desc>
- <p>Use this function when a fatal error has occurred that
- is not because of a system call. The message provided by the
- caller is printed and the process terminates with exit
- value <c>1</c>. This function does not return.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_err_ret(FormatStr, ... )</nametext></name>
- <fsummary>Non-fatal system call error.</fsummary>
- <type>
- <v>const char *FormatStr;</v>
- </type>
- <desc>
- <p>Use this function after a failed system call. The message
- provided by the caller is printed followed by a string
- describing the reason for failure.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_err_sys(FormatStr, ... )</nametext></name>
- <fsummary>Fatal system call error.</fsummary>
- <type>
- <v>const char *FormatStr;</v>
- </type>
- <desc>
- <p>Use this function after a failed system call. The message
- provided by the caller is printed followed by a string
- describing the reason for failure, and the process
- terminates with exit value <c>1</c>. This function does not
- return.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>Error Reporting</title>
- <p>Most functions in <c>Erl_Interface</c> report failures to the caller by
- returning some otherwise meaningless value (typically
- <c>NULL</c>
- or a negative number). As this only tells you that things did not
- go well, examine the error code in <c>erl_errno</c> if you
- want to find out more about the failure.</p>
- </section>
-
- <funcs>
- <func>
- <name since=""><ret>volatile int</ret><nametext>erl_errno</nametext></name>
- <fsummary>Variable <c>erl_errno</c> contains the
- Erl_Interface error number. You can change the value if you wish.
- </fsummary>
- <desc>
- <p><c>erl_errno</c> is initially (at program startup) zero
- and is then set by many <c>Erl_Interface</c> functions on failure to
- a non-zero error code to indicate what kind of error it
- encountered. A successful function call can change
- <c>erl_errno</c> (by calling some other function that
- fails), but no function does never set it to zero. This means
- that you cannot use <c>erl_errno</c> to see <em>if</em> a
- function call failed. Instead, each function reports failure
- in its own way (usually by returning a negative number or
- <c>NULL</c>), in which case you can examine
- <c>erl_errno</c> for details.</p>
- <p><c>erl_errno</c> uses the error codes defined in your
- system's <c>&lt;errno.h&gt;</c>.</p>
- <note>
- <p><c>erl_errno</c> is a "modifiable lvalue" (just
- like ISO C defines <c>errno</c> to be) rather than a
- variable. This means it can be implemented as a macro
- (expanding to, for example, <c>*_erl_errno()</c>).
- For reasons of thread safety (or task safety), this is exactly what
- we do on most platforms.</p>
- </note>
- </desc>
- </func>
- </funcs>
-</cref>
diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml
deleted file mode 100644
index 295760b4e6..0000000000
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ /dev/null
@@ -1,776 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_eterm</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1998-07-03</date>
- <rev>A</rev>
- <file>erl_eterm.xml</file>
- </header>
- <lib>erl_eterm</lib>
- <libsummary>Functions for Erlang term construction.</libsummary>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
-
- <p>This module provides functions for creating and manipulating
- Erlang terms.</p>
-
- <p>An Erlang term is represented by a C structure of type
- <c>ETERM</c>. Applications should not reference any fields
- in this structure directly, as it can be changed in future releases
- to provide faster and more compact term storage. Instead,
- applications should use the macros and functions provided.</p>
-
- <p>Each of the following macros takes a single <c>ETERM</c> pointer as an
- argument. The macros return a non-zero value if the test is true,
- otherwise <c>0</c>.</p>
-
- <taglist>
- <tag><c>ERL_IS_INTEGER(t)</c></tag>
- <item>True if <c>t</c> is an integer.</item>
- <tag><c>ERL_IS_UNSIGNED_INTEGER(t)</c></tag>
- <item>True if <c>t</c> is an integer.</item>
- <tag><c>ERL_IS_FLOAT(t)</c></tag>
- <item>True if <c>t</c> is a floating point number.</item>
- <tag><c>ERL_IS_ATOM(t)</c></tag>
- <item>True if <c>t</c> is an atom.</item>
- <tag><c>ERL_IS_PID(t)</c></tag>
- <item>True if <c>t</c> is a pid (process identifier).</item>
- <tag><c>ERL_IS_PORT(t)</c></tag>
- <item>True if <c>t</c> is a port.</item>
- <tag><c>ERL_IS_REF(t)</c></tag>
- <item>True if <c>t</c> is a reference.</item>
- <tag><c>ERL_IS_TUPLE(t)</c></tag>
- <item>True if <c>t</c> is a tuple.</item>
- <tag><c>ERL_IS_BINARY(t)</c></tag>
- <item>True if <c>t</c> is a binary.</item>
- <tag><c>ERL_IS_LIST(t)</c></tag>
- <item>True if <c>t</c> is a list with zero or more
- elements.</item>
- <tag><c>ERL_IS_EMPTY_LIST(t)</c></tag>
- <item>True if <c>t</c> is an empty list.</item>
- <tag><c>ERL_IS_CONS(t)</c></tag>
- <item>True if <c>t</c> is a list with at least one
- element.</item>
- </taglist>
-
- <p>The following macros can be used for retrieving parts of Erlang
- terms. None of these do any type checking. Results are undefined
- if you pass an <c>ETERM*</c> containing the wrong type. For example,
- passing a tuple to <c>ERL_ATOM_PTR()</c> likely results in garbage.</p>
-
- <taglist>
- <tag><c>char *ERL_ATOM_PTR(t)</c></tag>
- <item></item>
- <tag><c>char *ERL_ATOM_PTR_UTF8(t)</c></tag>
- <item>A string representing atom <c>t</c>.</item>
- <tag><c>int ERL_ATOM_SIZE(t)</c></tag>
- <item></item>
- <tag><c>int ERL_ATOM_SIZE_UTF8(t)</c></tag>
- <item>The length (in bytes) of atom <c>t</c>.</item>
- <tag><c>void *ERL_BIN_PTR(t)</c></tag>
- <item>A pointer to the contents of <c>t</c>.</item>
- <tag><c>int ERL_BIN_SIZE(t)</c></tag>
- <item>The length (in bytes) of binary object <c>t</c>.</item>
- <tag><c>int ERL_INT_VALUE(t)</c></tag>
- <item>The integer of <c>t</c>.</item>
- <tag><c>unsigned int ERL_INT_UVALUE(t)</c></tag>
- <item>The unsigned integer value of <c>t</c>.</item>
- <tag><c>double ERL_FLOAT_VALUE(t)</c></tag>
- <item>The floating point value of <c>t</c>.</item>
- <tag><c>ETERM *ERL_PID_NODE(t)</c></tag>
- <item></item>
- <tag><c>ETERM *ERL_PID_NODE_UTF8(t)</c></tag>
- <item>The node in pid <c>t</c>.</item>
- <tag><c>int ERL_PID_NUMBER(t)</c></tag>
- <item>The sequence number in pid <c>t</c>.</item>
- <tag><c>int ERL_PID_SERIAL(t)</c></tag>
- <item>The serial number in pid <c>t</c>.</item>
- <tag><c>int ERL_PID_CREATION(t)</c></tag>
- <item>The creation number in pid <c>t</c>.</item>
- <tag><c>int ERL_PORT_NUMBER(t)</c></tag>
- <item>The sequence number in port <c>t</c>.</item>
- <tag><c>int ERL_PORT_CREATION(t)</c></tag>
- <item>The creation number in port <c>t</c>.</item>
- <tag><c>ETERM *ERL_PORT_NODE(t)</c></tag>
- <item></item>
- <tag><c>ETERM *ERL_PORT_NODE_UTF8(t)</c></tag>
- <item>The node in port <c>t</c>.</item>
- <tag><c>int ERL_REF_NUMBER(t)</c></tag>
- <item>The first part of the reference number in ref <c>t</c>.
- Use only for compatibility.</item>
- <tag><c>int ERL_REF_NUMBERS(t)</c></tag>
- <item>Pointer to the array of reference numbers in ref
- <c>t</c>.</item>
- <tag><c>int ERL_REF_LEN(t)</c></tag>
- <item>The number of used reference numbers in ref
- <c>t</c>.</item>
- <tag><c>int ERL_REF_CREATION(t)</c></tag>
- <item>The creation number in ref <c>t</c>.</item>
- <tag><c>int ERL_TUPLE_SIZE(t)</c></tag>
- <item>The number of elements in tuple <c>t</c>.</item>
- <tag><c>ETERM *ERL_CONS_HEAD(t)</c></tag>
- <item>The head element of list <c>t</c>.</item>
- <tag><c>ETERM *ERL_CONS_TAIL(t)</c></tag>
- <item>A list representing the tail elements of list
- <c>t</c>.</item>
- </taglist>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_cons(head, tail)</nametext></name>
- <fsummary>Prepend a term to the head of a list.</fsummary>
- <type>
- <v>ETERM *head;</v>
- <v>ETERM *tail;</v>
- </type>
- <desc>
- <p>Concatenates two Erlang terms, prepending <c>head</c>
- onto <c>tail</c> and thereby creating a
- <c>cons</c> cell.
- To make a proper list, <c>tail</c> is always to be a list
- or an empty list. Notice that <c>NULL</c> is not a valid list.</p>
- <list type="bulleted">
- <item><c>head</c> is the new term to be added.</item>
- <item><c>tail</c> is the existing list to which
- <c>head</c> is concatenated.</item>
- </list>
- <p>The function returns a new list.</p>
- <p><c>ERL_CONS_HEAD(list)</c> and
- <c>ERL_CONS_TAIL(list)</c>
- can be used to retrieve the head and tail components
- from the list. <c>erl_hd(list)</c> and
- <c>erl_tl(list)</c> do
- the same thing, but check that the argument really is a list.</p>
- <p><em>Example:</em></p>
- <code type="none"><![CDATA[
-ETERM *list,*anAtom,*anInt;
-anAtom = erl_mk_atom("madonna");
-anInt = erl_mk_int(21);
-list = erl_mk_empty_list();
-list = erl_cons(anAtom, list);
-list = erl_cons(anInt, list);
- ... /* do some work */
-erl_free_compound(list);
- ]]></code>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_copy_term(term)</nametext></name>
- <fsummary>Create a copy of an Erlang term.</fsummary>
- <type>
- <v>ETERM *term;</v>
- </type>
- <desc>
- <p>Creates and returns a copy of the Erlang term
- <c>term</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_element(position, tuple)</nametext></name>
- <fsummary>Extract an element from an Erlang tuple.</fsummary>
- <type>
- <v>int position;</v>
- <v>ETERM *tuple;</v>
- </type>
- <desc>
- <p>Extracts a specified element from an Erlang tuple.</p>
- <list type="bulleted">
- <item><c>position</c> specifies which element to retrieve
- from <c>tuple</c>. The elements are numbered starting
- from 1.</item>
- <item><c>tuple</c> is an Erlang term containing at least
- <c>position</c> elements.</item>
- </list>
- <p>Returns a new Erlang term corresponding to the requested element, or
- <c>NULL</c> if <c>position</c> was greater
- than the arity of <c>tuple</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_hd(list)</nametext></name>
- <fsummary>Extract the first element from a list.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Extracts the first element from a list.</p>
- <p><c>list</c> is an Erlang term containing a list.</p>
- <p>Returns an Erlang term corresponding to the head
- head element in the list, or a <c>NULL</c> pointer if
- <c>list</c> was not a list.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_init(NULL, 0)</nametext></name>
- <fsummary>Initialization routine.</fsummary>
- <type>
- <v>void *NULL;</v>
- <v>int 0;</v>
- </type>
- <desc>
- <p>This function must be called before any of the others in the
- <c>Erl_Interface</c> library to initialize the
- library functions. The arguments must be specified as
- <c>erl_init(NULL,0)</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_iolist_length(list)</nametext></name>
- <fsummary>Return the length of an I/O list.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Returns the length of an I/O list.</p>
- <p><c>list</c> is an Erlang term containing an I/O list.</p>
- <p>Returns the length of <c>list</c>, or
- <c>-1</c> if <c>list</c> is not an I/O list.</p>
- <p>For the definition of an I/O list, see
- <seealso marker="#erl_iolist_to_binary">
- <c>erl_iolist_to_binary</c></seealso>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_iolist_to_binary(term)</nametext></name>
- <fsummary>Convert an I/O list to a binary.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Converts an I/O list to a binary term.</p>
- <p><c>list</c> is an Erlang term containing a list.</p>
- <p>Returns an Erlang binary term, or <c>NULL</c> if
- <c>list</c> was not an I/O list.</p>
- <p>Informally, an I/O list is a deep list of characters and
- binaries that can be sent to an Erlang port. In BNF, an I/O
- list is formally defined as follows:</p>
- <code type="none"><![CDATA[
-iolist ::= []
- | Binary
- | [iohead | iolist]
- ;
-iohead ::= Binary
- | Byte (integer in the range [0..255])
- | iolist
- ;
- ]]></code>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>char *</ret><nametext>erl_iolist_to_string(list)</nametext></name>
- <fsummary>Convert an I/O list to a <c>NULL</c>-terminated string.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Converts an I/O list to a <c>NULL</c>-terminated C string.</p>
- <p><c>list</c> is an Erlang term containing an I/O list.
- The I/O list must not contain the integer 0, as C strings may not
- contain this value except as a terminating marker.</p>
- <p>Returns a pointer to a dynamically allocated
- buffer containing a string. If <c>list</c> is not an I/O
- list, or if <c>list</c> contains the integer 0,
- <c>NULL</c> is returned. It
- is the caller's responsibility to free the allocated buffer
- with <c>erl_free()</c>.</p>
- <p>For the definition of an I/O list, see
- <seealso marker="#erl_iolist_to_binary">
- <c>erl_iolist_to_binary</c></seealso>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_length(list)</nametext></name>
- <fsummary>Determine the length of a list.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Determines the length of a proper list.</p>
- <p><c>list</c> is an Erlang term containing a proper list.
- In a proper list, all tails except the last point to another list
- cell, and the last tail points to an empty list.</p>
- <p>Returns <c>-1</c> if <c>list</c> is not a proper
- list.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_atom(string)</nametext></name>
- <fsummary>Create an atom.</fsummary>
- <type>
- <v>const char *string;</v>
- </type>
- <desc>
- <p>Creates an atom.</p>
- <p><c>string</c> is the sequence of characters that will be
- used to create the atom.</p>
- <p>Returns an Erlang term containing an atom. Notice that it is
- the caller's responsibility to ensure that <c>string</c>
- contains a valid name for an atom.</p>
- <p><c>ERL_ATOM_PTR(atom)</c> and
- <c>ERL_ATOM_PTR_UTF8(atom)</c>
- can be used to retrieve the atom name (as a <c>NULL</c>-terminated string).
- <c>ERL_ATOM_SIZE(atom)</c>
- and <c>ERL_ATOM_SIZE_UTF8(atom)</c> return the length
- of the atom name.</p>
- <note>
- <p>The UTF-8 variants were introduced in Erlang/OTP R16 and the
- string returned by <c>ERL_ATOM_PTR(atom)</c> was not
- <c>NULL</c>-terminated on older releases.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_binary(bptr, size)</nametext></name>
- <fsummary>Create a binary object.</fsummary>
- <type>
- <v>char *bptr;</v>
- <v>int size;</v>
- </type>
- <desc>
- <p>Produces an Erlang binary object from a
- buffer containing a sequence of bytes.</p>
- <list type="bulleted">
- <item><c>bptr</c> is a pointer to a buffer containing
- data to be converted.</item>
- <item><c>size</c> indicates the length of
- <c>bptr</c>.</item>
- </list>
- <p>Returns an Erlang binary object.</p>
- <p><c>ERL_BIN_PTR(bin)</c> retrieves a pointer to
- the binary data. <c>ERL_BIN_SIZE(bin)</c> retrieves the
- size.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_empty_list()</nametext></name>
- <fsummary>Create an empty Erlang list.</fsummary>
- <desc>
- <p>Creates and returns an empty Erlang list.
- Notice that <c>NULL</c> is not used to represent an empty list;
- Use this function instead.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_estring(string, len)</nametext></name>
- <fsummary>Create an Erlang string.</fsummary>
- <type>
- <v>char *string;</v>
- <v>int len;</v>
- </type>
- <desc>
- <p>Creates a list from a sequence of bytes.</p>
- <list type="bulleted">
- <item><c>string</c> is a buffer containing a sequence of
- bytes. The buffer does not need to be <c>NULL</c>-terminated.</item>
- <item><c>len</c> is the length of
- <c>string</c>.</item>
- </list>
- <p>Returns an Erlang list object corresponding to
- the character sequence in <c>string</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_float(f)</nametext></name>
- <fsummary>Create an Erlang float.</fsummary>
- <type>
- <v>double f;</v>
- </type>
- <desc>
- <p>Creates an Erlang float.</p>
- <p><c>f</c> is a value to be converted to an Erlang
- float.</p>
- <p>Returns an Erlang float object with the value
- specified in <c>f</c> or <c>NULL</c> if
- <c>f</c> is not finite.</p>
- <p><c>ERL_FLOAT_VALUE(t)</c> can be used to retrieve the
- value from an Erlang float.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_int(n)</nametext></name>
- <fsummary>Create an Erlang integer.</fsummary>
- <type>
- <v>int n;</v>
- </type>
- <desc>
- <p>Creates an Erlang integer.</p>
- <p><c>n</c> is a value to be converted to an Erlang
- integer.</p>
- <p>Returns an Erlang integer object with the
- value specified in <c>n</c>.</p>
- <p><c>ERL_INT_VALUE(t)</c> can be used to retrieve the
- value from an Erlang integer.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_list(array, arrsize)</nametext></name>
- <fsummary>Create a list from an array.</fsummary>
- <type>
- <v>ETERM **array;</v>
- <v>int arrsize;</v>
- </type>
- <desc>
- <p>Creates an Erlang list from an array of Erlang terms, such
- that each element in the list corresponds to one element in
- the array.</p>
- <list type="bulleted">
- <item><c>array</c> is an array of Erlang terms.</item>
- <item><c>arrsize</c> is the number of elements in
- <c>array</c>.</item>
- </list>
- <p>The function creates an Erlang list object, whose length
- <c>arrsize</c> and whose elements are taken from the
- terms in <c>array</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_long_ref(node, n1, n2, n3, creation)</nametext></name>
- <fsummary>Create an Erlang reference.</fsummary>
- <type>
- <v>const char *node;</v>
- <v>unsigned int n1, n2, n3;</v>
- <v>unsigned int creation;</v>
- </type>
- <desc>
- <p>Creates an Erlang reference, with 82 bits.</p>
- <list type="bulleted">
- <item><c>node</c> is the name of the C-node.</item>
- <item><c>n1</c>, <c>n2</c>, and
- <c>n3</c> can be seen as one big number
- <c>n1*2^64+n2*2^32+n3</c>, which is to be chosen
- uniquely for each reference created for a given C-node.</item>
- <item><c>creation</c> is an arbitrary number.</item>
- </list>
- <p>Notice that <c>n3</c> and <c>creation</c>
- are limited in precision, so only the low 18 and 2 bits of these
- numbers are used.</p>
- <p>Returns an Erlang reference object.</p>
- <p><c>ERL_REF_NODE(ref)</c>,
- <c>ERL_REF_NUMBERS(ref)</c>,
- <c>ERL_REF_LEN(ref)</c>, and
- <c>ERL_REF_CREATION(ref)</c> can be used to retrieve the
- values used to create the reference.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_pid(node, number, serial, creation)</nametext></name>
- <fsummary>Create a process identifier.</fsummary>
- <type>
- <v>const char *node;</v>
- <v>unsigned int number;</v>
- <v>unsigned int serial;</v>
- <v>unsigned int creation;</v>
- </type>
- <desc>
- <p>Creates an Erlang process identifier (pid). The
- resulting pid can be used by Erlang processes wishing to
- communicate with the C-node.</p>
- <list type="bulleted">
- <item><c>node</c> is the name of the C-node.</item>
- <item><c>number</c>, <c>serial</c>, and
- <c>creation</c> are
- arbitrary numbers. Notice that these are limited in
- precision, so only the low 15, 3, and 2 bits of these numbers
- are used.</item>
- </list>
- <p>Returns an Erlang pid object.</p>
- <p><c>ERL_PID_NODE(pid)</c>,
- <c>ERL_PID_NUMBER(pid)</c>,
- <c>ERL_PID_SERIAL(pid)</c>, and
- <c>ERL_PID_CREATION(pid)</c>
- can be used to retrieve the four values used to create the pid.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_port(node, number, creation)</nametext></name>
- <fsummary>Create a port identifier.</fsummary>
- <type>
- <v>const char *node;</v>
- <v>unsigned int number;</v>
- <v>unsigned int creation;</v>
- </type>
- <desc>
- <p>Creates an Erlang port identifier.</p>
- <list type="bulleted">
- <item><c>node</c> is the name of the C-node.</item>
- <item><c>number</c> and <c>creation</c> are
- arbitrary numbers. Notice that these are limited in
- precision, so only the low 18 and 2 bits of these numbers
- are used.</item>
- </list>
- <p>Returns an Erlang port object.</p>
- <p><c>ERL_PORT_NODE(port)</c>,
- <c>ERL_PORT_NUMBER(port)</c>,
- and <c>ERL_PORT_CREATION</c> can be used to retrieve the
- three values used to create the port.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_ref(node, number, creation)</nametext></name>
- <fsummary>Create an old Erlang reference.</fsummary>
- <type>
- <v>const char *node;</v>
- <v>unsigned int number;</v>
- <v>unsigned int creation;</v>
- </type>
- <desc>
- <p>Creates an old Erlang reference, with
- only 18 bits - use <c>erl_mk_long_ref</c> instead.</p>
- <list type="bulleted">
- <item><c>node</c> is the name of the C-node.</item>
- <item><c>number</c> is to be chosen uniquely for each
- reference created for a given C-node.</item>
- <item><c>creation</c> is an arbitrary number.</item>
- </list>
- <p>Notice that <c>number</c> and <c>creation</c>
- are limited in precision, so only the low 18 and 2 bits of these
- numbers are used.</p>
- <p>Returns an Erlang reference object.</p>
- <p><c>ERL_REF_NODE(ref)</c>,
- <c>ERL_REF_NUMBER(ref)</c>, and
- <c>ERL_REF_CREATION(ref)</c> can be used to retrieve the
- three values used to create the reference.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_string(string)</nametext></name>
- <fsummary>Create a string.</fsummary>
- <type>
- <v>char *string;</v>
- </type>
- <desc>
- <p>Creates a list from a <c>NULL</c>-terminated string.</p>
- <p><c>string</c> is a <c>NULL</c>-terminated sequence of
- characters
- (that is, a C string) from which the list will be created.</p>
- <p>Returns an Erlang list.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_tuple(array, arrsize)</nametext></name>
- <fsummary>Create an Erlang tuple from an array.</fsummary>
- <type>
- <v>ETERM **array;</v>
- <v>int arrsize;</v>
- </type>
- <desc>
- <p>Creates an Erlang tuple from an array of Erlang terms.</p>
- <list type="bulleted">
- <item><c>array</c> is an array of Erlang terms.</item>
- <item><c>arrsize</c> is the number of elements in
- <c>array</c>.</item>
- </list>
- <p>The function creates an Erlang tuple, whose arity is
- <c>size</c> and whose elements are taken from the terms
- in <c>array</c>.</p>
- <p>To retrieve the size of a tuple, either use function
- <c>erl_size</c> (which checks the type of the
- checked term and works for a binary as well as for a tuple) or
- <c>ERL_TUPLE_SIZE(tuple)</c> returns the arity of a tuple.
- <c>erl_size()</c> does the same thing, but it checks
- that the argument is a tuple.
- <c>erl_element(index,tuple)</c> returns the element
- corresponding to a given position in the tuple.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_uint(n)</nametext></name>
- <fsummary>Create an unsigned integer.</fsummary>
- <type>
- <v>unsigned int n;</v>
- </type>
- <desc>
- <p>Creates an Erlang unsigned integer.</p>
- <p><c>n</c> is a value to be converted to an Erlang
- unsigned integer.</p>
- <p>Returns an Erlang unsigned integer object with
- the value specified in <c>n</c>.</p>
- <p><c>ERL_INT_UVALUE(t)</c> can be used to retrieve the
- value from an Erlang unsigned integer.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_mk_var(name)</nametext></name>
- <fsummary>Create an Erlang variable.</fsummary>
- <type>
- <v>char *name;</v>
- </type>
- <desc>
- <p>Creates an unbound Erlang variable. The variable can later be bound
- through pattern matching or assignment.</p>
- <p><c>name</c> specifies a name for the variable.</p>
- <p>Returns an Erlang variable object with the
- name <c>name</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_print_term(stream, term)</nametext></name>
- <fsummary>Print an Erlang term.</fsummary>
- <type>
- <v>FILE *stream;</v>
- <v>ETERM *term;</v>
- </type>
- <desc>
- <p>Prints the specified Erlang term to the specified output stream.</p>
- <list type="bulleted">
- <item><c>stream</c> indicates where the function is to
- send its output.</item>
- <item><c>term</c> is the Erlang term to print.</item>
- </list>
- <p>Returns the number of characters written on success, otherwise a
- negative value.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_set_compat_rel(release_number)</nametext></name>
- <fsummary>Set the Erl_Interface library in compatibility mode.</fsummary>
- <type>
- <v>unsigned release_number;</v>
- </type>
- <desc>
- <p>By default, the <c>Erl_Interface</c> library is only
- guaranteed to be compatible with other Erlang/OTP components from the
- same release as the <c>Erl_Interface</c> library itself.
- For example, <c>Erl_Interface</c> from Erlang/OTP R10
- is not compatible
- with an Erlang emulator from Erlang/OTP R9 by default.</p>
- <p>A call to <c>erl_set_compat_rel(release_number)</c> sets
- the <c>Erl_Interface</c> library in compatibility mode of
- release <c>release_number</c>. Valid range of
- <c>release_number</c>
- is [7, current release]. This makes it possible to
- communicate with Erlang/OTP components from earlier releases.</p>
- <note>
- <p>If this function is called, it may only be called once
- directly after the call to function
- <seealso marker="#erl_init">erl_init()</seealso>.</p>
- </note>
- <warning>
- <p>You may run into trouble if this feature is used
- carelessly. Always ensure that all communicating
- components are either from the same Erlang/OTP release, or
- from release X and release Y where all components
- from release Y are in compatibility mode of release X.</p>
- </warning>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_size(term)</nametext></name>
- <fsummary>Return the arity of a tuple or binary.</fsummary>
- <type>
- <v>ETERM *term;</v>
- </type>
- <desc>
- <p>Returns either the arity of an Erlang tuple or the
- number of bytes in an Erlang binary object.</p>
- <p><c>term</c> is an Erlang tuple or an Erlang binary
- object.</p>
- <p>Returns the size of <c>term</c> as described
- above, or <c>-1</c> if <c>term</c> is not one of the two
- supported types.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_tl(list)</nametext></name>
- <fsummary>Extract the tail from a list.</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Extracts the tail from a list.</p>
- <p><c>list</c> is an Erlang term containing a list.</p>
- <p>Returns an Erlang list corresponding to the
- original list minus the first element, or <c>NULL</c> pointer if
- <c>list</c> was not a list.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_var_content(term, name)</nametext></name>
- <fsummary>Extract the content of a variable.</fsummary>
- <type>
- <v>ETERM *term;</v>
- <v>char *name;</v>
- </type>
- <desc>
- <p>Returns the contents of the specified variable in an Erlang term.</p>
- <list type="bulleted">
- <item><c>term</c> is an Erlang term. In order for this
- function to succeed,
- <c>term</c> must either be an Erlang variable with
- the specified name, or it must be an Erlang list or tuple
- containing a variable with the specified name. Other Erlang
- types cannot contain variables.</item>
- <item><c>name</c> is the name of an Erlang variable.
- </item>
- </list>
- <p>Returns the Erlang object corresponding to the value of
- <c>name</c> in <c>term</c>. If no variable
- with the name <c>name</c> is found in
- <c>term</c>, or if <c>term</c> is
- not a valid Erlang term, <c>NULL</c> is returned.</p>
- </desc>
- </func>
- </funcs>
-</cref>
diff --git a/lib/erl_interface/doc/src/erl_format.xml b/lib/erl_interface/doc/src/erl_format.xml
deleted file mode 100644
index b5e895c720..0000000000
--- a/lib/erl_interface/doc/src/erl_format.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_format</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1996-10-16</date>
- <rev>A</rev>
- <file>erl_format.xml</file>
- </header>
- <lib>erl_format</lib>
- <libsummary>Create and match Erlang terms.</libsummary>
- <description>
- <p>This module contains two routines: one general function for
- creating Erlang terms and one for pattern matching Erlang terms.</p>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_format(FormatStr, ...)</nametext></name>
- <fsummary>Create an Erlang term.</fsummary>
- <type>
- <v>char *FormatStr;</v>
- </type>
- <desc>
- <p>A general function for creating Erlang terms using
- a format specifier and a corresponding set of arguments, much
- in the way <c>printf()</c> works.</p>
- <p><c>FormatStr</c> is a format specification string.
- The valid format specifiers are as follows:</p>
- <list type="bulleted">
- <item><c>~i</c> - Integer</item>
- <item><c>~f</c> - Floating point</item>
- <item><c>~a</c> - Atom</item>
- <item><c>~s</c> - String</item>
- <item><c>~w</c> - Arbitrary Erlang term</item>
- </list>
- <p>For each format specifier included in <c>FormatStr</c>,
- there must be a corresponding argument following
- <c>FormatStr</c>. An Erlang term is built according to
- <c>FormatStr</c> with values and Erlang terms substituted
- from the corresponding arguments, and according to the individual
- format specifiers. For example:</p>
- <code type="none"><![CDATA[
-erl_format("[{name,~a},{age,~i},{data,~w}]",
- "madonna",
- 21,
- erl_format("[{adr,~s,~i}]","E-street",42));
- ]]></code>
- <p>This creates an <c>(ETERM *)</c> structure corresponding
- to the Erlang term
- <c>[{name,madonna},{age,21},{data,[{adr,"E-street",42}]}]</c></p>
- <p>The function returns an Erlang term, or <c>NULL</c> if
- <c>FormatStr</c> does not describe a valid Erlang
- term.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_match(Pattern, Term)</nametext></name>
- <fsummary>Perform pattern matching.</fsummary>
- <type>
- <v>ETERM *Pattern,*Term;</v>
- </type>
- <desc>
- <p>This function is used to perform pattern matching similar
- to that done in Erlang. For matching rules and more examples, see
- section <seealso marker="doc/reference_manual:patterns">
- Pattern Matching</seealso> in the Erlang Reference Manual.</p>
- <list type="bulleted">
- <item><c>Pattern</c> is an Erlang term, possibly
- containing unbound variables.</item>
- <item><c>Term</c> is an Erlang term that we wish to match
- against <c>Pattern</c>.</item>
- </list>
- <p><c>Term</c> and <c>Pattern</c> are compared
- and any unbound variables in <c>Pattern</c> are bound to
- corresponding values in <c>Term</c>.</p>
- <p>If <c>Term</c> and <c>Pattern</c> can be
- matched, the function returns a non-zero value and binds any unbound
- variables in <c>Pattern</c>. If <c>Term</c>
- and <c>Pattern</c> do
- not match, <c>0</c> is returned. For example:</p>
- <code type="none"><![CDATA[
-ETERM *term, *pattern, *pattern2;
-term1 = erl_format("{14,21}");
-term2 = erl_format("{19,19}");
-pattern1 = erl_format("{A,B}");
-pattern2 = erl_format("{F,F}");
-if (erl_match(pattern1, term1)) {
- /* match succeeds:
- * A gets bound to 14,
- * B gets bound to 21
- */
- ...
-}
-if (erl_match(pattern2, term1)) {
- /* match fails because F cannot be
- * bound to two separate values, 14 and 21
- */
- ...
-}
-if (erl_match(pattern2, term2)) {
- /* match succeeds and F gets bound to 19 */
- ...
-}
- ]]></code>
- <p><c>erl_var_content()</c> can be used to retrieve the
- content of any variables bound as a result of a call to
- <c>erl_match()</c>.</p>
- </desc>
- </func>
- </funcs>
-</cref>
diff --git a/lib/erl_interface/doc/src/erl_interface.xml b/lib/erl_interface/doc/src/erl_interface.xml
deleted file mode 100644
index decd66046a..0000000000
--- a/lib/erl_interface/doc/src/erl_interface.xml
+++ /dev/null
@@ -1,637 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>The Erl_Interface Library</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>K.Lundin</checked>
- <date>990113</date>
- <rev>A</rev>
- <file>erl_interface.sgml</file>
- </header>
- <p>The Erl_Interface library contains functions. which help you
- integrate programs written in C and Erlang. The functions in
- Erl_Interface support the following:</p>
- <list type="bulleted">
- <item>manipulation of data represented as Erlang data types</item>
- <item>conversion of data between C and Erlang formats</item>
- <item>encoding and decoding of Erlang data types for transmission or storage</item>
- <item>communication between C nodes and Erlang processes</item>
- <item>backup and restore of C node state to and from Mnesia</item>
- </list>
- <p>In the following sections, these topics are described:</p>
- <list type="bulleted">
- <item>compiling your code for use with Erl_Interface</item>
- <item>initializing Erl_Interface</item>
- <item>encoding, decoding, and sending Erlang terms</item>
- <item>building terms and patterns</item>
- <item>pattern matching</item>
- <item>connecting to a distributed Erlang node</item>
- <item>using EPMD</item>
- <item>sending and receiving Erlang messages</item>
- <item>remote procedure calls</item>
- <item>global names</item>
- <item>the registry</item>
- </list>
-
- <section>
- <title>Deprecation and Removal</title>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
- </section>
-
- <section>
- <title>Compiling and Linking Your Code</title>
- <p>In order to use any of the Erl_Interface functions, include the
- following lines in your code:</p>
- <code type="none"><![CDATA[
-#include "erl_interface.h"
-#include "ei.h" ]]></code>
- <p>Determine where the top directory of your OTP installation is. You
- can find this out by starting Erlang and entering the following
- command at the Eshell prompt:</p>
- <code type="none"><![CDATA[
-Eshell V4.7.4 (abort with ^G)
-1> code:root_dir().
-/usr/local/otp ]]></code>
- <p>To compile your code, make sure that your C compiler knows where
- to find <c>erl_interface.h</c> by specifying an appropriate <c>-I</c>
- argument on the command line, or by adding it to the <c>CFLAGS</c>
- definition in your <c>Makefile</c>. The correct value for this path is
- <c>$OTPROOT/lib/erl_interface</c><em>Vsn</em><c>/include</c>, where <c>$OTPROOT</c> is the path
- reported by <c>code:root_dir/0</c> in the above example, and <em>Vsn</em> is
- the version of the Erl_interface application, for example
- <c>erl_interface-3.2.3</c></p>
- <code type="none"><![CDATA[
-$ cc -c -I/usr/local/otp/lib/erl_interface-3.2.3/include myprog.c ]]></code>
- <p>When linking, you will need to specify the path to
- <c>liberl_interface.a</c> and <c>libei.a</c> with
- <c>-L$OTPROOT/lib/erl_interface-3.2.3/lib</c>, and you will need to specify the
- name of the libraries with <c>-lerl_interface -lei</c>. You can do
- this on the command line or by adding the flags to the <c>LDFLAGS</c>
- definition in your <c>Makefile</c>.</p>
- <code type="none"><![CDATA[
-$ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
- lib myprog.o -lerl_interface -lei -o myprog ]]></code>
- <p>Also, on some systems it may be necessary to link with some
- additional libraries (e.g. <c>libnsl.a</c> and <c>libsocket.a</c> on
- Solaris, or <c>wsock32.lib</c> on Windows) in order to use the
- communication facilities of Erl_Interface.</p>
- <p>If you are using Erl_Interface functions in a threaded
- application based on POSIX threads or Solaris threads, then
- Erl_Interface needs access to some of the synchronization
- facilities in your threads package, and you will need to specify
- additional compiler flags in order to indicate which of the packages
- you are using. Define <c>_REENTRANT</c> and either <c>STHREADS</c> or
- <c>PTHREADS</c>. The default is to use POSIX threads if
- <c>_REENTRANT</c> is specified.</p>
- <p>Note that both single threaded and default versions of the Erl_interface
- and Ei libraries are provided. (The single threaded versions are named
- <c>liberl_interface_st</c> and <c>libei_st</c>). Whether the default
- versions of the libraries have support for threads or not is determined by if
- the platform in question has support for POSIX or Solaris threads. To check this,
- have a look in the <c>eidefs.mk</c> file in the erl_interface src directory.</p>
- </section>
-
- <section>
- <title>Initializing the erl_interface Library</title>
- <p>Before calling any of the other Erl_Interface functions, you
- must call <c>erl_init()</c> exactly once to initialize the library.
- <c>erl_init()</c> takes two arguments, however the arguments are no
- longer used by Erl_Interface, and should therefore be specified
- as <c>erl_init(NULL,0)</c>.</p>
- </section>
-
- <section>
- <title>Encoding, Decoding and Sending Erlang Terms</title>
- <p>Data sent between distributed Erlang nodes is encoded in the
- Erlang external format. Consequently, you have to encode and decode
- Erlang terms into byte streams if you want to use the distribution
- protocol to communicate between a C program and Erlang. </p>
- <p>The Erl_Interface library supports this activity. It has a
- number of C functions which create and manipulate Erlang data
- structures. The library also contains an encode and a decode function.
- The example below shows how to create and encode an Erlang tuple
- <c>{tobbe,3928}</c>:</p>
- <code type="none"><![CDATA[
-
-ETERM *arr[2], *tuple;
-char buf[BUFSIZ];
-int i;
-
-arr[0] = erl_mk_atom("tobbe");
-arr[1] = erl_mk_integer(3928);
-tuple = erl_mk_tuple(arr, 2);
-i = erl_encode(tuple, buf); ]]></code>
- <p>Alternatively, you can use <c>erl_send()</c> and
- <c>erl_receive_msg</c>, which handle the encoding and decoding of
- messages transparently.</p>
- <p>Refer to the Reference Manual for a complete description of the
- following modules:</p>
- <list type="bulleted">
- <item>the <c>erl_eterm</c> module for creating Erlang terms</item>
- <item>the <c>erl_marshal</c> module for encoding and decoding routines.</item>
- </list>
- </section>
-
- <section>
- <title>Building Terms and Patterns</title>
- <p>The previous example can be simplified by using
- <c>erl_format()</c> to create an Erlang term.</p>
- <code type="none"><![CDATA[
-
-ETERM *ep;
-ep = erl_format("{~a,~i}", "tobbe", 3928); ]]></code>
- <p>Refer to the Reference Manual, the <c>erl_format</c> module, for a
- full description of the different format directives. The following
- example is more complex:</p>
- <code type="none"><![CDATA[
-
-ETERM *ep;
-ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
- "madonna",
- 21,
- erl_format("[{adr,~s,~i}]", "E-street", 42));
-erl_free_compound(ep); ]]></code>
- <p>As in previous examples, it is your responsibility to free the
- memory allocated for Erlang terms. In this example,
- <c>erl_free_compound()</c> ensures that the complete term pointed to
- by <c>ep</c> is released. This is necessary, because the pointer from
- the second call to <c>erl_format()</c> is lost. </p>
- <p>The following
- example shows a slightly different solution:</p>
- <code type="none"><![CDATA[
-
-ETERM *ep,*ep2;
-ep2 = erl_format("[{adr,~s,~i}]","E-street",42);
-ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
- "madonna", 21, ep2);
-erl_free_term(ep);
-erl_free_term(ep2); ]]></code>
- <p>In this case, you free the two terms independently. The order in
- which you free the terms <c>ep</c> and <c>ep2</c> is not important,
- because the Erl_Interface library uses reference counting to
- determine when it is safe to actually remove objects. </p>
- <p>If you are not sure whether you have freed the terms properly, you
- can use the following function to see the status of the fixed term
- allocator:</p>
- <code type="none"><![CDATA[
-long allocated, freed;
-
-erl_eterm_statistics(&allocated,&freed);
-printf("currently allocated blocks: %ld\
-",allocated);
-printf("length of freelist: %ld\
-",freed);
-
-/* really free the freelist */
-erl_eterm_release();
- ]]></code>
- <p>Refer to the Reference Manual, the <c>erl_malloc</c> module for more
- information.</p>
- </section>
-
- <section>
- <title>Pattern Matching</title>
- <p>An Erlang pattern is a term that may contain unbound variables or
- <c>"do not care"</c> symbols. Such a pattern can be matched against a
- term and, if the match is successful, any unbound variables in the
- pattern will be bound as a side effect. The content of a bound
- variable can then be retrieved.</p>
- <code type="none"><![CDATA[
-
-ETERM *pattern;
-pattern = erl_format("{madonna,Age,_}"); ]]></code>
- <p><c>erl_match()</c> is used to perform pattern matching. It takes a
- pattern and a term and tries to match them. As a side effect any unbound
- variables in the pattern will be bound. In the following example, we
- create a pattern with a variable <em>Age</em> which appears at two
- positions in the tuple. The pattern match is performed as follows:</p>
- <list type="ordered">
- <item><c>erl_match()</c> will bind the contents of
- <em>Age</em> to <em>21</em> the first time it reaches the variable</item>
- <item>the second occurrence of <em>Age</em> will cause a test for
- equality between the terms since <em>Age</em> is already bound to
- <em>21</em>. Since <em>Age</em> is bound to 21, the equality test will
- succeed and the match continues until the end of the pattern.</item>
- <item>if the end of the pattern is reached, the match succeeds and you
- can retrieve the contents of the variable</item>
- </list>
- <code type="none"><![CDATA[
-ETERM *pattern,*term;
-pattern = erl_format("{madonna,Age,Age}");
-term = erl_format("{madonna,21,21}");
-if (erl_match(pattern, term)) {
- fprintf(stderr, "Yes, they matched: Age = ");
- ep = erl_var_content(pattern, "Age");
- erl_print_term(stderr, ep);
- fprintf(stderr,"\
-");
- erl_free_term(ep);
-}
-erl_free_term(pattern);
-erl_free_term(term); ]]></code>
- <p>Refer to the Reference Manual, the <c>erl_match()</c> function for
- more information.</p>
- </section>
-
- <section>
- <title>Connecting to a Distributed Erlang Node</title>
- <p>In order to connect to a distributed Erlang node you need to first
- initialize the connection routine with <c>erl_connect_init()</c>,
- which stores information such as the host name, node name, and IP
- address for later use:</p>
- <code type="none"><![CDATA[
-int identification_number = 99;
-int creation=1;
-char *cookie="a secret cookie string"; /* An example */
-erl_connect_init(identification_number, cookie, creation); ]]></code>
- <p>Refer to the Reference Manual, the <c>erl_connect</c> module for more information.</p>
- <p>After initialization, you set up the connection to the Erlang node.
- Use <c>erl_connect()</c> to specify the Erlang node you want to
- connect to. The following example sets up the connection and should
- result in a valid socket file descriptor:</p>
- <code type="none"><![CDATA[
-int sockfd;
-char *nodename="xyz@chivas.du.etx.ericsson.se"; /* An example */
-if ((sockfd = erl_connect(nodename)) < 0)
- erl_err_quit("ERROR: erl_connect failed"); ]]></code>
- <p><c>erl_err_quit()</c> prints the specified string and terminates
- the program. Refer to the Reference Manual, the <c>erl_error()</c>
- function for more information.</p>
- </section>
-
- <section>
- <title>Using EPMD</title>
- <p><c>Epmd</c> is the Erlang Port Mapper Daemon. Distributed Erlang nodes
- register with <c>epmd</c> on the localhost to indicate to other nodes that
- they exist and can accept connections. <c>Epmd</c> maintains a register of
- node and port number information, and when a node wishes to connect to
- another node, it first contacts <c>epmd</c> in order to find out the correct
- port number to connect to.</p>
- <p>When you use <c>erl_connect()</c> to connect to an Erlang node, a
- connection is first made to <c>epmd</c> and, if the node is known, a
- connection is then made to the Erlang node.</p>
- <p>C nodes can also register themselves with <c>epmd</c> if they want other
- nodes in the system to be able to find and connect to them.</p>
- <p>Before registering with <c>epmd</c>, you need to first create a listen socket
- and bind it to a port. Then:</p>
- <code type="none"><![CDATA[
-int pub;
-
-pub = erl_publish(port); ]]></code>
- <p><c>pub</c> is a file descriptor now connected to <c>epmd</c>. <c>Epmd</c>
- monitors the other end of the connection, and if it detects that the
- connection has been closed, the node will be unregistered. So, if you
- explicitly close the descriptor or if your node fails, it will be
- unregistered from <c>epmd</c>.</p>
- <p>Be aware that on some systems (such as VxWorks), a failed node will
- not be detected by this mechanism since the operating system does not
- automatically close descriptors that were left open when the node
- failed. If a node has failed in this way, <c>epmd</c> will prevent you from
- registering a new node with the old name, since it thinks that the old
- name is still in use. In this case, you must unregister the name
- explicitly:</p>
- <code type="none"><![CDATA[
-erl_unpublish(node); ]]></code>
- <p>This will cause <c>epmd</c> to close the connection from the far end. Note
- that if the name was in fact still in use by a node, the results of
- this operation are unpredictable. Also, doing this does not cause the
- local end of the connection to close, so resources may be consumed.</p>
- </section>
-
- <section>
- <title>Sending and Receiving Erlang Messages</title>
- <p>Use one of the following two functions to send messages:</p>
- <list type="bulleted">
- <item><c>erl_send()</c></item>
- <item><c>erl_reg_send()</c></item>
- </list>
- <p>As in Erlang, it is possible to send messages to a
- <em>Pid</em> or to a registered name. It is easier to send a
- message to a registered name because it avoids the problem of finding
- a suitable <em>Pid</em>.</p>
- <p>Use one of the following two functions to receive messages:</p>
- <list type="bulleted">
- <item><c>erl_receive()</c></item>
- <item><c>erl_receive_msg()</c></item>
- </list>
- <p><c>erl_receive()</c> receives the message into a buffer, while
- <c>erl_receive_msg()</c> decodes the message into an Erlang term. </p>
-
- <section>
- <title>Example of Sending Messages</title>
- <p>In the following example, <c>{Pid, hello_world}</c> is
- sent to a registered process <c>my_server</c>. The message is encoded
- by <c>erl_send()</c>:</p>
- <code type="none"><![CDATA[
-extern const char *erl_thisnodename(void);
-extern short erl_thiscreation(void);
-#define SELF(fd) erl_mk_pid(erl_thisnodename(),fd,0,erl_thiscreation())
-ETERM *arr[2], *emsg;
-int sockfd, creation=1;
-
-arr[0] = SELF(sockfd);
-arr[1] = erl_mk_atom("Hello world");
-emsg = erl_mk_tuple(arr, 2);
-
-erl_reg_send(sockfd, "my_server", emsg);
-erl_free_term(emsg); ]]></code>
- <p>The first element of the tuple that is sent is your own
- <em>Pid</em>. This enables <c>my_server</c> to reply. Refer to the
- Reference Manual, the <c>erl_connect</c> module for more information
- about send primitives.</p>
- </section>
-
- <section>
- <title>Example of Receiving Messages</title>
- <p>In this example <c>{Pid, Something}</c> is received. The
- received Pid is then used to return <c>{goodbye,Pid}</c></p>
- <code type="none"><![CDATA[
-ETERM *arr[2], *answer;
-int sockfd,rc;
-char buf[BUFSIZE];
-ErlMessage emsg;
-
-if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
- arr[0] = erl_mk_atom("goodbye");
- arr[1] = erl_element(1, emsg.msg);
- answer = erl_mk_tuple(arr, 2);
- erl_send(sockfd, arr[1], answer);
- erl_free_term(answer);
- erl_free_term(emsg.msg);
- erl_free_term(emsg.to);
-}
-} ]]></code>
- <p>In order to provide robustness, a distributed Erlang node
- occasionally polls all its connected neighbours in an attempt to
- detect failed nodes or communication links. A node which receives such
- a message is expected to respond immediately with an <c>ERL_TICK</c> message.
- This is done automatically by <c>erl_receive()</c>, however when this
- has occurred <c>erl_receive</c> returns <c>ERL_TICK</c> to the caller
- without storing a message into the <c>ErlMessage</c> structure.</p>
- <p>When a message has been received, it is the caller's responsibility
- to free the received message <c>emsg.msg</c> as well as <c>emsg.to</c>
- or <c>emsg.from</c>, depending on the type of message received.</p>
- <p>Refer to the Reference Manual for additional information about the
- following modules:</p>
- <list type="bulleted">
- <item><c>erl_connect</c></item>
- <item><c>erl_eterm</c>.</item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Remote Procedure Calls</title>
- <p>An Erlang node acting as a client to another Erlang node
- typically sends a request and waits for a reply. Such a request is
- included in a function call at a remote node and is called a remote
- procedure call. The following example shows how the
- Erl_Interface library supports remote procedure calls:</p>
- <code type="none"><![CDATA[
-
-char modname[]=THE_MODNAME;
-ETERM *reply,*ep;
-ep = erl_format("[~a,[]]", modname);
-if (!(reply = erl_rpc(fd, "c", "c", ep)))
- erl_err_msg("<ERROR> when compiling file: %s.erl !\
-", modname);
-erl_free_term(ep);
-ep = erl_format("{ok,_}");
-if (!erl_match(ep, reply))
- erl_err_msg("<ERROR> compiler errors !\
-");
-erl_free_term(ep);
-erl_free_term(reply); ]]></code>
- <p><c>c:c/1</c> is called to compile the specified module on the
- remote node. <c>erl_match()</c> checks that the compilation was
- successful by testing for the expected <c>ok</c>.</p>
- <p>Refer to the Reference Manual, the <c>erl_connect</c> module for
- more information about <c>erl_rpc()</c>, and its companions
- <c>erl_rpc_to()</c> and <c>erl_rpc_from()</c>.</p>
- </section>
-
- <section>
- <title>Using Global Names</title>
- <p>A C node has access to names registered through the Erlang Global
- module. Names can be looked up, allowing the C node to send messages
- to named Erlang services. C nodes can also register global names,
- allowing them to provide named services to Erlang processes or other C
- nodes. </p>
- <p>Erl_Interface does not provide a native implementation of the global
- service. Instead it uses the global services provided by a "nearby"
- Erlang node. In order to use the services described in this section,
- it is necessary to first open a connection to an Erlang node.</p>
- <p>To see what names there are:</p>
- <code type="none"><![CDATA[
-char **names;
-int count;
-int i;
-
-names = erl_global_names(fd,&count);
-
-if (names)
- for (i=0; i<count; i++)
- printf("%s\
-",names[i]);
-
-free(names); ]]></code>
- <p><c>erl_global_names()</c> allocates and returns a buffer containing
- all the names known to global. <c>count</c> will be initialized to
- indicate how many names are in the array. The array of strings in
- names is terminated by a NULL pointer, so it is not necessary to use
- <c>count</c> to determine when the last name is reached.</p>
- <p>It is the caller's responsibility to free the array.
- <c>erl_global_names()</c> allocates the array and all of the strings
- using a single call to <c>malloc()</c>, so <c>free(names)</c> is all
- that is necessary.</p>
- <p>To look up one of the names:</p>
- <code type="none"><![CDATA[
-ETERM *pid;
-char node[256];
-
-pid = erl_global_whereis(fd,"schedule",node); ]]></code>
- <p>If <c>"schedule"</c> is known to global, an Erlang pid is returned
- that can be used to send messages to the schedule service.
- Additionally, <c>node</c> will be initialized to contain the name of
- the node where the service is registered, so that you can make a
- connection to it by simply passing the variable to <c>erl_connect()</c>.</p>
- <p>Before registering a name, you should already have registered your
- port number with <c>epmd</c>. This is not strictly necessary, but if you
- neglect to do so, then other nodes wishing to communicate with your
- service will be unable to find or connect to your process.</p>
- <p>Create a pid that Erlang processes can use to communicate with your
- service:</p>
- <code type="none"><![CDATA[
-ETERM *pid;
-
-pid = erl_mk_pid(thisnode,14,0,0);
-erl_global_register(fd,servicename,pid); ]]></code>
- <p>After registering the name, you should use <c>erl_accept()</c> to wait for
- incoming connections.</p>
- <p>Do not forget to free <c>pid</c> later with <c>erl_free_term()</c>!</p>
- <p>To unregister a name:</p>
- <code type="none"><![CDATA[
-erl_global_unregister(fd,servicename); ]]></code>
- </section>
-
- <section>
- <title>The Registry</title>
- <p>This section describes the use of the registry, a simple mechanism
- for storing key-value pairs in a C-node, as well as backing them up or
- restoring them from a Mnesia table on an Erlang node. More detailed
- information about the individual API functions can be found in the
- Reference Manual.</p>
- <p>Keys are strings, i.e. <c>NULL</c>-terminated arrays of characters, and values
- are arbitrary objects. Although integers and floating point numbers
- are treated specially by the registry, you can store strings or binary
- objects of any type as pointers.</p>
- <p>To start, you need to open a registry:</p>
- <code type="none"><![CDATA[
-ei_reg *reg;
-
-reg = ei_reg_open(45); ]]></code>
- <p>The number 45 in the example indicates the approximate number of
- objects that you expect to store in the registry. Internally the
- registry uses hash tables with collision chaining, so there is no
- absolute upper limit on the number of objects that the registry can
- contain, but if performance or memory usage are important, then you
- should choose a number accordingly. The registry can be resized later.</p>
- <p>You can open as many registries as you like (if memory permits).</p>
- <p>Objects are stored and retrieved through set and get functions. In
- the following examples you see how to store integers, floats, strings
- and arbitrary binary objects:</p>
- <code type="none"><![CDATA[
-struct bonk *b = malloc(sizeof(*b));
-char *name = malloc(7);
-
-ei_reg_setival(reg,"age",29);
-ei_reg_setfval(reg,"height",1.85);
-
-strcpy(name,"Martin");
-ei_reg_setsval(reg,"name",name);
-
-b->l = 42;
-b->m = 12;
-ei_reg_setpval(reg,"jox",b,sizeof(*b)); ]]></code>
- <p>If you attempt to store an object in the registry and there is an
- existing object with the same key, the new value will replace the old
- one. This is done regardless of whether the new object and the old one
- have the same type, so you can, for example, replace a string with an
- integer. If the existing value is a string or binary, it will be freed
- before the new value is assigned.</p>
- <p>Stored values are retrieved from the registry as follows:</p>
- <code type="none"><![CDATA[
-long i;
-double f;
-char *s;
-struct bonk *b;
-int size;
-
-i = ei_reg_getival(reg,"age");
-f = ei_reg_getfval(reg,"height");
-s = ei_reg_getsval(reg,"name");
-b = ei_reg_getpval(reg,"jox",&size); ]]></code>
- <p>In all of the above examples, the object must exist and it must be of
- the right type for the specified operation. If you do not know the
- type of a given object, you can ask:</p>
- <code type="none"><![CDATA[
-struct ei_reg_stat buf;
-
-ei_reg_stat(reg,"name",&buf); ]]></code>
- <p>Buf will be initialized to contain object attributes.</p>
- <p>Objects can be removed from the registry:</p>
- <code type="none"><![CDATA[
-ei_reg_delete(reg,"name"); ]]></code>
- <p>When you are finished with a registry, close it to remove all the
- objects and free the memory back to the system:</p>
- <code type="none"><![CDATA[
-ei_reg_close(reg); ]]></code>
-
- <section>
- <title>Backing Up the Registry to Mnesia</title>
- <p>The contents of a registry can be backed up to Mnesia on a "nearby"
- Erlang node. You need to provide an open connection to the Erlang node
- (see <c>erl_connect()</c>). Also, Mnesia 3.0 or later must be running
- on the Erlang node before the backup is initiated:</p>
- <code type="none"><![CDATA[
-ei_reg_dump(fd, reg, "mtab", dumpflags); ]]></code>
- <p>The example above will backup the contents of the registry to the
- specified Mnesia table <c>"mtab"</c>. Once a registry has been backed
- up to Mnesia in this manner, additional backups will only affect
- objects that have been modified since the most recent backup, i.e.
- objects that have been created, changed or deleted. The backup
- operation is done as a single atomic transaction, so that the entire
- backup will be performed or none of it will.</p>
- <p>In the same manner, a registry can be restored from a Mnesia table:</p>
- <code type="none"><![CDATA[
-ei_reg_restore(fd, reg, "mtab"); ]]></code>
- <p>This will read the entire contents of <c>"mtab"</c> into the specified
- registry. After the restore, all of the objects in the registry will
- be marked as unmodified, so a subsequent backup will only affect
- objects that you have modified since the restore.</p>
- <p>Note that if you restore to a non-empty registry, objects in the
- table will overwrite objects in the registry with the same keys. Also,
- the <em>entire</em> contents of the registry is marked as unmodified
- after the restore, including any modified objects that were not
- overwritten by the restore operation. This may not be your intention.</p>
- </section>
-
- <section>
- <title>Storing Strings and Binaries</title>
- <p>When string or binary objects are stored in the registry it is
- important that a number of simple guidelines are followed. </p>
- <p>Most importantly, the object must have been created with a single call
- to <c>malloc()</c> (or similar), so that it can later be removed by a
- single call to <c>free()</c>. Objects will be freed by the registry
- when it is closed, or when you assign a new value to an object that
- previously contained a string or binary.</p>
- <p>You should also be aware that if you store binary objects that are
- context-dependent (e.g. containing pointers or open file descriptors),
- they will lose their meaning if they are backed up to a Mnesia table
- and subsequently restored in a different context.</p>
- <p>When you retrieve a stored string or binary value from the registry,
- the registry maintains a pointer to the object and you are passed a
- copy of that pointer. You should never free an object retrieved in
- this manner because when the registry later attempts to free it, a
- runtime error will occur that will likely cause the C-node to crash.</p>
- <p>You are free to modify the contents of an object retrieved this way.
- However when you do so, the registry will not be aware of the changes
- you make, possibly causing it to be missed the next time you make a
- Mnesia backup of the registry contents. This can be avoided if you
- mark the object as dirty after any such changes with
- <c>ei_reg_markdirty()</c>, or pass appropriate flags to
- <c>ei_reg_dump()</c>.</p>
- </section>
- </section>
-</chapter>
diff --git a/lib/erl_interface/doc/src/erl_malloc.xml b/lib/erl_interface/doc/src/erl_malloc.xml
deleted file mode 100644
index 6650620064..0000000000
--- a/lib/erl_interface/doc/src/erl_malloc.xml
+++ /dev/null
@@ -1,212 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_malloc</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1998-07-03</date>
- <rev>A</rev>
- <file>erl_malloc.xml</file>
- </header>
- <lib>erl_malloc</lib>
- <libsummary>Memory allocation functions.</libsummary>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
- <p>This module provides functions for allocating and deallocating
- memory.</p>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_alloc_eterm(etype)</nametext></name>
- <fsummary>Allocate an ETERM structure.</fsummary>
- <type>
- <v>unsigned char etype;</v>
- </type>
- <desc>
- <p>Allocates an <c>(ETERM)</c> structure.</p>
- <p>Specify <c>etype</c> as one of the following
- constants:</p>
- <list type="bulleted">
- <item><c>ERL_INTEGER</c>
- </item>
- <item><c>ERL_U_INTEGER</c> (unsigned integer)
- </item>
- <item><c>ERL_ATOM</c>
- </item>
- <item><c>ERL_PID</c> (Erlang process identifier)
- </item>
- <item><c>ERL_PORT</c>
- </item>
- <item><c>ERL_REF</c> (Erlang reference)
- </item>
- <item><c>ERL_LIST</c>
- </item>
- <item><c>ERL_EMPTY_LIST</c>
- </item>
- <item><c>ERL_TUPLE</c>
- </item>
- <item><c>ERL_BINARY</c>
- </item>
- <item><c>ERL_FLOAT</c>
- </item>
- <item><c>ERL_VARIABLE</c>
- </item>
- <item><c>ERL_SMALL_BIG</c> (bignum)
- </item>
- <item><c>ERL_U_SMALL_BIG</c> (bignum)
- </item>
- </list>
- <p><c>ERL_SMALL_BIG</c> and
- <c>ERL_U_SMALL_BIG</c> are for
- creating Erlang <c>bignums</c>, which can contain integers
- of any size. The size of an integer in Erlang is machine-dependent,
- but any integer &gt; 2^28 requires a bignum.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_eterm_release(void)</nametext></name>
- <fsummary>Clear the ETERM freelist.</fsummary>
- <desc>
- <p>Clears the freelist, where blocks are placed when they are
- released by <c>erl_free_term()</c> and
- <c>erl_free_compound()</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_eterm_statistics(allocated, freed)</nametext></name>
- <fsummary>Report term allocation statistics.</fsummary>
- <type>
- <v>long *allocated;</v>
- <v>long *freed;</v>
- </type>
- <desc>
- <p>Reports term allocation statistics.</p>
- <p><c>allocated</c> and <c>freed</c> are
- initialized to
- contain information about the fix-allocator used to allocate
- <c>ETERM</c> components.</p>
- <list type="bulleted">
- <item>
- <p><c>allocated</c> is the number of blocks currently
- allocated to <c>ETERM</c> objects.</p>
- </item>
- <item>
- <p><c>freed</c> is the length of the freelist, where
- blocks are placed when they are
- released by <c>erl_free_term()</c> and
- <c>erl_free_compound()</c>.</p>
- </item>
- </list>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_free(ptr)</nametext></name>
- <fsummary>Free some memory.</fsummary>
- <type>
- <v>void *ptr;</v>
- </type>
- <desc>
- <p>Calls the standard
- <c>free()</c> function.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_free_array(array, size)</nametext></name>
- <fsummary>Free an array of ETERM structures.</fsummary>
- <type>
- <v>ETERM **array;</v>
- <v>int size;</v>
- </type>
- <desc>
- <p>Frees an array of Erlang terms.</p>
- <list type="bulleted">
- <item><c>array</c> is an array of ETERM* objects.</item>
- <item><c>size</c> is the number of terms in the array.
- </item>
- </list>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_free_compound(t)</nametext></name>
- <fsummary>Free an array of ETERM structures.</fsummary>
- <type>
- <v>ETERM *t;</v>
- </type>
- <desc>
- <p>Normally it is the programmer's responsibility to free each
- Erlang term that has been returned from any of the
- <c>Erl_Interface</c> functions. However, as many of the
- functions that build new Erlang terms in fact share objects
- with other existing terms, it can be difficult for the
- programmer to maintain pointers to all such terms to
- free them individually.</p>
- <p><c>erl_free_compound()</c> recursively frees all of the
- subterms associated with a specified Erlang term, regardless of
- whether we are still holding pointers to the subterms.</p>
- <p>For an example, see section
- <seealso marker="ei_users_guide#building_terms_and_patterns">Building Terms and Patterns</seealso>
- in the User's Guide.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_free_term(t)</nametext></name>
- <fsummary>Free an ETERM structure.</fsummary>
- <type>
- <v>ETERM *t;</v>
- </type>
- <desc>
- <p>Frees an Erlang term.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>void</ret><nametext>erl_malloc(size)</nametext></name>
- <fsummary>Allocate some memory.</fsummary>
- <type>
- <v>long size;</v>
- </type>
- <desc>
- <p>Calls the standard
- <c>malloc()</c> function.</p>
- </desc>
- </func>
- </funcs>
-</cref>
diff --git a/lib/erl_interface/doc/src/erl_marshal.xml b/lib/erl_interface/doc/src/erl_marshal.xml
deleted file mode 100644
index 33d359d871..0000000000
--- a/lib/erl_interface/doc/src/erl_marshal.xml
+++ /dev/null
@@ -1,276 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>erl_marshal</title>
- <prepared>Torbj&ouml;rn T&ouml;rnkvist</prepared>
- <responsible>Torbj&ouml;rn T&ouml;rnkvist</responsible>
- <docno></docno>
- <approved>Bjarne D&auml;cker</approved>
- <checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>1998-07-03</date>
- <rev>A</rev>
- <file>erl_marshal.xml</file>
- </header>
- <lib>erl_marshal</lib>
- <libsummary>Encoding and decoding of Erlang terms.</libsummary>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
- <p>This module contains functions for encoding Erlang terms into
- a sequence of bytes, and for decoding Erlang terms from a
- sequence of bytes.</p>
- </description>
-
- <funcs>
- <func>
- <name since=""><ret>int</ret><nametext>erl_compare_ext(bufp1, bufp2)</nametext></name>
- <fsummary>Compare encoded byte sequences.</fsummary>
- <type>
- <v>unsigned char *bufp1,*bufp2;</v>
- </type>
- <desc>
- <p>Compares two encoded terms.</p>
- <list type="bulleted">
- <item><c>bufp1</c> is a buffer containing an encoded
- Erlang term term1.</item>
- <item><c>bufp2</c> is a buffer containing an encoded
- Erlang term term2.</item>
- </list>
- <p>Returns <c>0</c> if the terms are equal, <c>-1</c> if
- <c>term1</c> &lt; <c>term2</c>, or <c>1</c> if <c>term2</c> &lt;
- <c>term1</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>ETERM *</ret><nametext>erl_decode(bufp)</nametext></name>
- <name since=""><ret>ETERM *</ret><nametext>erl_decode_buf(bufpp)</nametext></name>
- <fsummary>Convert a term from Erlang external format.</fsummary>
- <type>
- <v>unsigned char *bufp;</v>
- <v>unsigned char **bufpp;</v>
- </type>
- <desc>
- <p><c>erl_decode()</c> and
- <c>erl_decode_buf()</c> decode
- the contents of a buffer and return the corresponding
- Erlang term. <c>erl_decode_buf()</c> provides a simple
- mechanism for dealing with several encoded terms stored
- consecutively in the buffer.</p>
- <list type="bulleted">
- <item>
- <p><c>bufp</c> is a pointer to a buffer containing one
- or more encoded Erlang terms.</p>
- </item>
- <item>
- <p><c>bufpp</c> is the address of a buffer pointer. The
- buffer contains one or more consecutively encoded Erlang terms.
- Following a successful call to
- <c>erl_decode_buf()</c>, <c>bufpp</c> is
- updated so that it points to the next encoded term.</p>
- </item>
- </list>
- <p><c>erl_decode()</c> returns an Erlang term
- corresponding to the contents of <c>bufp</c> on success,
- otherwise <c>NULL</c>. <c>erl_decode_buf()</c>
- returns an Erlang
- term corresponding to the first of the consecutive terms in
- <c>bufpp</c> and moves <c>bufpp</c> forward
- to point to the
- next term in the buffer. On failure, each of the functions
- return <c>NULL</c>.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_encode(term, bufp)</nametext></name>
- <name since=""><ret>int</ret><nametext>erl_encode_buf(term, bufpp)</nametext></name>
- <fsummary>Convert a term into Erlang external format.</fsummary>
- <type>
- <v>ETERM *term;</v>
- <v>unsigned char *bufp;</v>
- <v>unsigned char **bufpp;</v>
- </type>
- <desc>
- <p><c>erl_encode()</c> and
- <c>erl_encode_buf()</c> encode
- Erlang terms into external format for storage or transmission.
- <c>erl_encode_buf()</c> provides a simple mechanism for
- encoding several terms consecutively in the same buffer.</p>
- <list type="bulleted">
- <item>
- <p><c>term</c> is an Erlang term to be encoded.</p>
- </item>
- <item>
- <p><c>bufp</c> is a pointer to a buffer containing one or
- more encoded Erlang terms.</p>
- </item>
- <item>
- <p><c>bufpp</c> is a pointer to a pointer to a buffer
- containing one or more consecutively encoded Erlang terms.
- Following a successful call to
- <c>erl_encode_buf()</c>, <c>bufpp</c> is updated so
- that it points to the
- position for the next encoded term.</p>
- </item>
- </list>
- <p>These functions return the number of bytes written to buffer
- on success, otherwise <c>0</c>.</p>
- <p>Notice that no bounds checking is done on the buffer. It is
- the caller's responsibility to ensure that the buffer is
- large enough to hold the encoded terms. You can either use a
- static buffer that is large enough to hold the terms you expect
- to need in your program, or use <c>erl_term_len()</c>
- to determine the exact requirements for a given term.</p>
- <p>The following can help you estimate the buffer
- requirements for a term. Notice that this information is
- implementation-specific, and can change in future versions.
- If you are unsure, use <c>erl_term_len()</c>.</p>
- <p>Erlang terms are encoded with a 1 byte tag that
- identifies the type of object, a 2- or 4-byte length field,
- and then the data itself. Specifically:</p>
- <taglist>
- <tag><c>Tuples</c></tag>
- <item>Need 5 bytes, plus the space for each element.</item>
- <tag><c>Lists</c></tag>
- <item>Need 5 bytes, plus the space for each element, and 1
- more byte for the empty list at the end.</item>
- <tag><c>Strings and atoms</c></tag>
- <item>Need 3 bytes, plus 1 byte for each character (the
- terminating 0 is not encoded). Really long strings (more
- than 64k characters) are encoded as lists. Atoms cannot
- contain more than 256 characters.</item>
- <tag><c>Integers</c></tag>
- <item>Need 5 bytes.</item>
- <tag><c>Characters</c></tag>
- <item>(Integers &lt; 256) need 2 bytes.</item>
- <tag><c>Floating point numbers</c></tag>
- <item>Need 32 bytes.</item>
- <tag><c>Pids</c></tag>
- <item>Need 10 bytes, plus the space for the node name, which
- is an atom.</item>
- <tag><c>Ports and Refs</c></tag>
- <item>Need 6 bytes, plus the space for the node name, which
- is an atom.</item>
- </taglist>
- <p>The total space required is the result calculated
- from the information above, plus 1 more byte for a
- version identifier.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_ext_size(bufp)</nametext></name>
- <fsummary>Count elements in encoded term.</fsummary>
- <type>
- <v>unsigned char *bufp;</v>
- </type>
- <desc>
- <p>Returns the number of elements in an encoded term.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>unsigned char</ret><nametext>erl_ext_type(bufp)</nametext></name>
- <fsummary>Determine type of an encoded byte sequence.</fsummary>
- <type>
- <v>unsigned char *bufp;</v>
- </type>
- <desc>
- <p>Identifies and returns the type of Erlang term encoded
- in a buffer. It skips a trailing <em>magic</em> identifier.</p>
- <p>Returns <c>0</c> if the type cannot be determined or
- one of:</p>
- <list type="bulleted">
- <item><c>ERL_INTEGER</c>
- </item>
- <item><c>ERL_ATOM</c>
- </item>
- <item><c>ERL_PID</c> (Erlang process identifier)
- </item>
- <item><c>ERL_PORT</c>
- </item>
- <item><c>ERL_REF</c> (Erlang reference)
- </item>
- <item><c>ERL_EMPTY_LIST</c>
- </item>
- <item><c>ERL_LIST</c>
- </item>
- <item><c>ERL_TUPLE</c>
- </item>
- <item><c>ERL_FLOAT</c>
- </item>
- <item><c>ERL_BINARY</c>
- </item>
- <item><c>ERL_FUNCTION</c>
- </item>
- </list>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>unsigned char *</ret><nametext>erl_peek_ext(bufp, pos)</nametext></name>
- <fsummary>Step over encoded term.</fsummary>
- <type>
- <v>unsigned char *bufp;</v>
- <v>int pos;</v>
- </type>
- <desc>
- <p>This function is used for stepping over one or more
- encoded terms in a buffer, to directly access later term.</p>
- <list type="bulleted">
- <item><c>bufp</c> is a pointer to a buffer containing one
- or more encoded Erlang terms.</item>
- <item><c>pos</c> indicates how many terms to step over in
- the buffer.</item>
- </list>
- <p>Returns a pointer to a subterm that can be
- used in a later call to <c>erl_decode()</c> to retrieve
- the term at that position. If there is no term, or
- <c>pos</c> would exceed the size of the terms in the
- buffer, <c>NULL</c> is returned.</p>
- </desc>
- </func>
-
- <func>
- <name since=""><ret>int</ret><nametext>erl_term_len(t)</nametext></name>
- <fsummary>Determine encoded size of term.</fsummary>
- <type>
- <v>ETERM *t;</v>
- </type>
- <desc>
- <p>Determines the buffer space that would be
- needed by <c>t</c> if it were encoded into Erlang external
- format by <c>erl_encode()</c>.</p>
- <p>Returns the size in bytes.</p>
- </desc>
- </func>
- </funcs>
-</cref>
diff --git a/lib/erl_interface/doc/src/part_erl_interface.xml b/lib/erl_interface/doc/src/part_erl_interface.xml
deleted file mode 100644
index e256cfa193..0000000000
--- a/lib/erl_interface/doc/src/part_erl_interface.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Erl_Interface User's Guide</title>
- <prepared>Gordon Beaton</prepared>
- <docno></docno>
- <date>1998-11-30</date>
- <rev>1.2</rev>
- <file>part_erl_interface.sgml</file>
- </header>
- <xi:include href="erl_interface.xml"/>
-</part>
diff --git a/lib/erl_interface/doc/src/ref_man.xml b/lib/erl_interface/doc/src/ref_man.xml
index a4f947c79f..d00868562f 100644
--- a/lib/erl_interface/doc/src/ref_man.xml
+++ b/lib/erl_interface/doc/src/ref_man.xml
@@ -31,22 +31,10 @@
<description>
<note><p>The support for VxWorks is deprecated as of OTP 22, and
will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
</description>
<xi:include href="ei.xml"/>
<xi:include href="ei_connect.xml"/>
<xi:include href="registry.xml"/>
- <xi:include href="erl_connect.xml"/>
- <xi:include href="erl_error.xml"/>
- <xi:include href="erl_eterm.xml"/>
- <xi:include href="erl_format.xml"/>
- <xi:include href="erl_global.xml"/>
- <xi:include href="erl_malloc.xml"/>
- <xi:include href="erl_marshal.xml"/>
- <xi:include href="erl_call.xml"/>
+ <xi:include href="ei_global.xml"/>
+ <xi:include href="erl_call_cmd.xml"/>
</application>
diff --git a/lib/erl_interface/doc/src/ref_man_ei.xml b/lib/erl_interface/doc/src/ref_man_ei.xml
deleted file mode 100644
index d8d1deaea1..0000000000
--- a/lib/erl_interface/doc/src/ref_man_ei.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE application SYSTEM "application.dtd">
-
-<application xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2002</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>EI Library Reference</title>
- <prepared>Gordon Beaton</prepared>
- <docno></docno>
- <date>1998-11-30</date>
- <rev>1.2</rev>
- <file>ref_man_ei.xml</file>
- </header>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
- <note>
- <p>By default, the <c>ei</c> library is only guaranteed
- to be compatible with other Erlang/OTP components from the same
- release as the <c>ei</c> library itself. See the documentation of the
- <seealso marker="ei#ei_set_compat_rel">ei_set_compat_rel()</seealso>
- function on how to communicate with Erlang/OTP components from earlier
- releases.</p>
- </note>
- </description>
- <xi:include href="ei.xml"/>
- <xi:include href="ei_connect.xml"/>
- <xi:include href="registry.xml"/>
-</application>
diff --git a/lib/erl_interface/doc/src/ref_man_erl_interface.xml b/lib/erl_interface/doc/src/ref_man_erl_interface.xml
deleted file mode 100644
index 2b69d0fa74..0000000000
--- a/lib/erl_interface/doc/src/ref_man_erl_interface.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE application SYSTEM "application.dtd">
-
-<application xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>1996</year><year>2016</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- </legalnotice>
-
- <title>Erl_Interface Library Reference</title>
- <prepared>Gordon Beaton</prepared>
- <docno></docno>
- <date>1998-11-30</date>
- <rev>1.2</rev>
- <file>ref_man_erl_interface.xml</file>
- </header>
- <description>
- <note><p>The support for VxWorks is deprecated as of OTP 22, and
- will be removed in OTP 23.</p></note>
- <note><p>The old legacy <c>erl_interface</c> library (functions
- with prefix <c>erl_</c>) is deprecated as of OTP 22, and will be
- removed in OTP 23. This does not apply to the <c>ei</c>
- library. Reasonably new <c>gcc</c> compilers will issue deprecation
- warnings. In order to disable these warnings, define the macro
- <c>EI_NO_DEPR_WARN</c>.</p></note>
- <p>The <c>erl_interface</c> library is a <c>C</c> interface library
- for communication with <c>Erlang</c>.</p>
- <note>
- <p>By default, the <c>erl_interface</c> library is only guaranteed
- to be compatible with other Erlang/OTP components from the same
- release as the <c>erl_interface</c> library. See the documentation
- of the
- <seealso marker="erl_eterm#erl_set_compat_rel">erl_set_compat_rel()</seealso>
- function on how to communicate with Erlang/OTP components from earlier
- releases.</p>
- </note>
- </description>
- <xi:include href="erl_connect.xml"/>
- <xi:include href="erl_error.xml"/>
- <xi:include href="erl_eterm.xml"/>
- <xi:include href="erl_format.xml"/>
- <xi:include href="erl_global.xml"/>
- <xi:include href="erl_malloc.xml"/>
- <xi:include href="erl_marshal.xml"/>
-</application>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index b138118f04..7d96dac560 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -43,7 +43,7 @@ typedef LONG_PTR ssize_t; /* Sigh... */
#include <stdio.h> /* Need type FILE */
#include <errno.h> /* Need EHOSTUNREACH, ENOMEM, ... */
-#if !(defined(__WIN32__) || defined(_WIN32)) && !defined(VXWORKS) || (defined(VXWORKS) && defined(HAVE_SENS))
+#if !(defined(__WIN32__) || defined(_WIN32))
# include <netdb.h>
#endif
@@ -188,20 +188,20 @@ extern "C" {
* the 'ei' interface as well.... :-(
*/
-#if defined(_REENTRANT) || defined(VXWORKS) || defined(__WIN32__)
+#if defined(_REENTRANT) || defined(__WIN32__)
/* 'erl_errno' as a function return value */
volatile int* __erl_errno_place(void) __attribute__ ((__const__));
#define erl_errno (*__erl_errno_place ())
-#else /* !_REENTRANT && !VXWORKS && !__WIN32__ */
+#else /* !_REENTRANT && !__WIN32__ */
extern volatile int __erl_errno;
#define erl_errno __erl_errno
-#endif /* !_REENTRANT && !VXWORKS && !__WIN32__ */
+#endif /* !_REENTRANT && !__WIN32__ */
/* -------------------------------------------------------------------- */
@@ -323,13 +323,24 @@ typedef struct {
#define EI_SCLBK_FLG_FULL_IMPL (1 << 0)
+/*
+ * HACK: AIX defines many socket functions like accept to be naccept, which
+ * pollutes the global namespace. Set up an ugly ifdef for consumers of this
+ * API here so they get a mangled name for AIX and the sane name elsewhere.
+ */
+#ifdef _AIX
+#define EI_ACCEPT_NAME accept_ei
+#else
+#define EI_ACCEPT_NAME accept
+#endif
+
typedef struct {
int flags;
int (*socket)(void **ctx, void *setup_ctx);
int (*close)(void *ctx);
int (*listen)(void *ctx, void *addr, int *len, int backlog);
- int (*accept)(void **ctx, void *addr, int *len, unsigned tmo);
+ int (*EI_ACCEPT_NAME)(void **ctx, void *addr, int *len, unsigned tmo);
int (*connect)(void *ctx, void *addr, int len, unsigned tmo);
int (*writev)(void *ctx, const void *iov, int iovcnt, ssize_t *len, unsigned tmo);
int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo);
@@ -351,7 +362,7 @@ typedef struct ei_cnode_s {
/* Currently this_ipaddr isn't used */
/* struct in_addr this_ipaddr; */
char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1];
- short creation;
+ unsigned int creation;
erlang_pid self;
ei_socket_callbacks *cbs;
void *setup_context;
@@ -393,8 +404,12 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname,
int ei_connect(ei_cnode* ec, char *nodename);
int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms);
+int ei_connect_host_port(ei_cnode* ec, char *hostname, int port);
+int ei_connect_host_port_tmo(ei_cnode* ec, char *hostname, int port, unsigned ms);
int ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename);
int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned ms);
+int ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr adr, int port);
+int ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr adr, int port, unsigned ms);
int ei_receive(int fd, unsigned char *bufp, int bufsize);
int ei_receive_tmo(int fd, unsigned char *bufp, int bufsize, unsigned ms);
@@ -444,41 +459,6 @@ int ei_get_tracelevel(void);
* We have erl_gethost*() so we include ei versions as well.
*/
-#if defined(VXWORKS)
-
-extern int h_errno;
-
-/*
- * We need these definitions - if the user has SENS then he gets them
- * from netdb.h, otherwise we define them ourselves.
- *
- * If you are getting "multiple definition" errors here,
- * make sure you have included <netdb.h> BEFORE "erl_interface.h"
- * or define HAVE_SENS in your CFLAGS.
- */
-
-#if !defined(HAVE_SENS) && !defined(HOST_NOT_FOUND) /* just in case */
-
-struct hostent {
- char *h_name; /* official name of host */
- char **h_aliases; /* alias list */
- int h_addrtype; /* host address type */
- int h_length; /* length of address */
- char **h_addr_list; /* list of addresses from name server */
-#define h_addr h_addr_list[0] /* address, for backward compatiblity */
- unsigned int unused; /* SENS defines this as ttl */
-};
-
-#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */
-#define TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */
-#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
-#define NO_DATA 4 /* Valid name, no data record of requested type */
-#define NO_ADDRESS NO_DATA /* no address, look for MX record */
-
-#endif /* !HAVE_SENS && !HOST_NOT_FOUND */
-#endif /* VXWORKS */
-
-
struct hostent *ei_gethostbyname(const char *name);
struct hostent *ei_gethostbyaddr(const char *addr, int len, int type);
struct hostent *ei_gethostbyname_r(const char *name,
@@ -537,8 +517,6 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p);
int ei_x_encode_port(ei_x_buff* x, const erlang_port *p);
int ei_encode_ref(char *buf, int *index, const erlang_ref *p);
int ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p);
-int ei_encode_term(char *buf, int *index, void *t) EI_DEPRECATED_ATTR;
-int ei_x_encode_term(ei_x_buff* x, void* t) EI_DEPRECATED_ATTR;
int ei_encode_trace(char *buf, int *index, const erlang_trace *p);
int ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p);
int ei_encode_tuple_header(char *buf, int *index, int arity);
@@ -586,7 +564,6 @@ void free_fun(erlang_fun* f);
int ei_decode_pid(const char *buf, int *index, erlang_pid *p);
int ei_decode_port(const char *buf, int *index, erlang_port *p);
int ei_decode_ref(const char *buf, int *index, erlang_ref *p);
-int ei_decode_term(const char *buf, int *index, void *t) EI_DEPRECATED_ATTR;
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);
@@ -802,6 +779,13 @@ int ei_reg_dump(int fd, ei_reg *reg, const char *mntab, int flags);
int ei_reg_restore(int fd, ei_reg *reg, const char *mntab);
int ei_reg_purge(ei_reg *reg);
+/* -------------------------------------------------------------------- */
+/* The ei_global functions */
+/* -------------------------------------------------------------------- */
+char **ei_global_names(ei_cnode *ec, int fd, int *count);
+int ei_global_whereis(ei_cnode *ec, int fd, const char *name, erlang_pid* pid, char *node);
+int ei_global_register(int fd, const char *name, erlang_pid *self);
+int ei_global_unregister(ei_cnode *ec, int fd, const char *name);
/* -------------------------------------------------------------------- */
/* Encoding/decoding bugnums to GNU MP format */
@@ -832,14 +816,12 @@ int ei_x_encode_bignum(ei_x_buff *x, mpz_t obj);
#define EI_ULONGLONG unsigned long long
#endif
-#ifndef VXWORKS
int ei_decode_longlong(const char *buf, int *index, EI_LONGLONG *p);
int ei_decode_ulonglong(const char *buf, int *index, EI_ULONGLONG *p);
int ei_encode_longlong(char *buf, int *index, EI_LONGLONG p);
int ei_encode_ulonglong(char *buf, int *index, EI_ULONGLONG p);
int ei_x_encode_longlong(ei_x_buff* x, EI_LONGLONG n);
int ei_x_encode_ulonglong(ei_x_buff* x, EI_ULONGLONG n);
-#endif
#ifdef USE_EI_UNDOCUMENTED
diff --git a/lib/erl_interface/src/legacy/erl_global.h b/lib/erl_interface/include/ei_global.h
index d2eec08b35..b85a31217d 100644
--- a/lib/erl_interface/src/legacy/erl_global.h
+++ b/lib/erl_interface/include/ei_global.h
@@ -17,12 +17,13 @@
*
* %CopyrightEnd%
*/
-#ifndef _ERL_GLOBAL_H
-#define _ERL_GLOBAL_H
+#ifndef _EI_GLOBAL_H
+#define _EI_GLOBAL_H
+#include "ei.h"
-char **erl_global_names(int fd, int *count);
-ETERM *erl_global_whereis(int fd, const char *name, char *node);
-int erl_global_register(int fd, const char *name, ETERM *pid);
-int erl_global_unregister(int fd, const char *name);
+char **ei_global_names(ei_cnode *ec, int fd, int *count);
+int ei_global_whereis(ei_cnode *ec, int fd, const char *name, erlang_pid* pid, char *node);
+int ei_global_register(int fd, const char *name, erlang_pid *self);
+int ei_global_unregister(ei_cnode *ec, int fd, const char *name);
-#endif /* _ERL_GLOBAL_H */
+#endif /* _EI_GLOBAL_H */
diff --git a/lib/erl_interface/include/erl_interface.h b/lib/erl_interface/include/erl_interface.h
deleted file mode 100644
index 7c87223a38..0000000000
--- a/lib/erl_interface/include/erl_interface.h
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_INTERFACE_H
-#define _ERL_INTERFACE_H
-
-/************************************************************************/
-/* This file defines the complete interface to erl_interface */
-/* Note: the 'ei' interface is the prefered C API. */
-/************************************************************************/
-
-#include "ei.h" /* ei is the base */
-
-/* -------------------------------------------------------------------- */
-/* Public defines */
-/* -------------------------------------------------------------------- */
-
-#define ERL_COMPOUND (1 << 7)
-
-#define ERL_UNDEF 0
-#define ERL_INTEGER 1
-#define ERL_U_INTEGER 2 /* unsigned int */
-#define ERL_ATOM 3
-#define ERL_PID 4
-#define ERL_PORT 5
-#define ERL_REF 6
-#define ERL_CONS (7 | ERL_COMPOUND)
-#define ERL_LIST ERL_CONS
-#define ERL_NIL 8
-#define ERL_EMPTY_LIST ERL_NIL
-#define ERL_TUPLE (9 | ERL_COMPOUND)
-#define ERL_BINARY 10
-#define ERL_FLOAT 11
-#define ERL_VARIABLE (12 | ERL_COMPOUND) /* used in patterns */
-#define ERL_SMALL_BIG 13
-#define ERL_U_SMALL_BIG 14
-#define ERL_FUNCTION (15 | ERL_COMPOUND)
-#define ERL_BIG 16
-#define ERL_LONGLONG 17
-#define ERL_U_LONGLONG 18
-
-
-#define ERL_TYPE(x) (ERL_HEADER(x)->type)
-
-/* FIXME some macros left in erl_eterm.h should probably be documented */
-
-#define ERL_IS_INTEGER(x) (ERL_TYPE(x) == ERL_INTEGER)
-#define ERL_IS_UNSIGNED_INTEGER(x) (ERL_TYPE(x) == ERL_U_INTEGER)
-#define ERL_IS_LONGLONG(x) (ERL_TYPE(x) == ERL_LONGLONG)
-#define ERL_IS_UNSIGNED_LONGLONG(x) (ERL_TYPE(x) == ERL_U_LONGLONG)
-#define ERL_IS_FLOAT(x) (ERL_TYPE(x) == ERL_FLOAT)
-#define ERL_IS_ATOM(x) (ERL_TYPE(x) == ERL_ATOM)
-#define ERL_IS_PID(x) (ERL_TYPE(x) == ERL_PID)
-#define ERL_IS_PORT(x) (ERL_TYPE(x) == ERL_PORT)
-#define ERL_IS_REF(x) (ERL_TYPE(x) == ERL_REF)
-#define ERL_IS_TUPLE(x) (ERL_TYPE(x) == ERL_TUPLE)
-#define ERL_IS_BINARY(x) (ERL_TYPE(x) == ERL_BINARY)
-#define ERL_IS_NIL(x) (ERL_TYPE(x) == ERL_NIL)
-#define ERL_IS_EMPTY_LIST(x) ERL_IS_NIL(x)
-#define ERL_IS_CONS(x) (ERL_TYPE(x) == ERL_CONS)
-#define ERL_IS_LIST(x) (ERL_IS_CONS(x) || ERL_IS_EMPTY_LIST(x))
-
-/*
- * Macros used for XXXX
- */
-
-#define ERL_HEADER(x) ((Erl_Header *)x)
-#define ERL_COUNT(x) (ERL_HEADER(x)->count)
-
-/*
- * Macros used for retrieving values from Erlang terms.
- */
-
-#define ERL_INT_VALUE(x) ((x)->uval.ival.i)
-#define ERL_INT_UVALUE(x) ((x)->uval.uival.u)
-#define ERL_LL_VALUE(x) ((x)->uval.llval.i)
-#define ERL_LL_UVALUE(x) ((x)->uval.ullval.u)
-
-#define ERL_FLOAT_VALUE(x) ((x)->uval.fval.f)
-
-#define ERL_ATOM_PTR(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.aval.d)
-#define ERL_ATOM_PTR_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.aval.d)
-#define ERL_ATOM_SIZE(x) erl_atom_size_latin1((Erl_Atom_data*) &(x)->uval.aval.d)
-#define ERL_ATOM_SIZE_UTF8(x) erl_atom_size_utf8((Erl_Atom_data*) &(x)->uval.aval.d)
-
-#define ERL_PID_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.pidval.node)
-#define ERL_PID_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.pidval.node)
-#define ERL_PID_NUMBER(x) ((x)->uval.pidval.number)
-#define ERL_PID_SERIAL(x) ((x)->uval.pidval.serial)
-#define ERL_PID_CREATION(x) ((x)->uval.pidval.creation)
-
-#define ERL_PORT_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.portval.node)
-#define ERL_PORT_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.portval.node)
-#define ERL_PORT_NUMBER(x) ((x)->uval.portval.number)
-#define ERL_PORT_CREATION(x) ((x)->uval.portval.creation)
-
-#define ERL_REF_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.refval.node)
-#define ERL_REF_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.refval.node)
-#define ERL_REF_NUMBER(x) ((x)->uval.refval.n[0])
-#define ERL_REF_NUMBERS(x) ((x)->uval.refval.n)
-#define ERL_REF_LEN(x) ((x)->uval.refval.len)
-#define ERL_REF_CREATION(x) ((x)->uval.refval.creation)
-
-#define ERL_TUPLE_SIZE(x) ((x)->uval.tval.size)
-
-/* NOTE!!! This is 0-based!! (first item is number 0)
- * Note too that element/2 (in Erlang) and
- * erl_element() are both 1-based.
- */
-#define ERL_TUPLE_ELEMS(x) ((x)->uval.tval.elems)
-#define ERL_TUPLE_ELEMENT(x, i) (ERL_TUPLE_ELEMS(x)[(i)])
-
-#define ERL_BIN_SIZE(x) ((x)->uval.bval.size)
-#define ERL_BIN_PTR(x) ((x)->uval.bval.b)
-
-#define ERL_CONS_HEAD(x) ((x)->uval.lval.head)
-#define ERL_CONS_TAIL(x) ((x)->uval.lval.tail)
-
-#define ERL_VAR_LEN(x) ((x)->uval.vval.len)
-#define ERL_VAR_NAME(x) ((x)->uval.vval.name)
-#define ERL_VAR_VALUE(x) ((x)->uval.vval.v)
-
-#define ERL_CLOSURE_SIZE(x) ((x)->uval.funcval.size)
-#define ERL_FUN_CREATOR(x) ((x)->uval.funcval.creator)
-#define ERL_FUN_MODULE(x) ((x)->uval.funcval.module)
-#define ERL_FUN_UNIQ(x) ((x)->uval.funcval.uniq)
-#define ERL_FUN_INDEX(x) ((x)->uval.funcval.index)
-#define ERL_FUN_ARITY(x) ((x)->uval.funcval.arity)
-#define ERL_FUN_NEW_INDEX(x) ((x)->uval.funcval.new_index)
-#define ERL_FUN_MD5(x) ((x)->uval.funcval.md5)
-#define ERL_CLOSURE(x) ((x)->uval.funcval.closure)
-#define ERL_CLOSURE_ELEMENT(x,i) (ERL_CLOSURE(x)[(i)])
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* -------------------------------------------------------------------- */
-/* Type definitions of Erlang terms in C */
-/* -------------------------------------------------------------------- */
-
-typedef struct {
- unsigned int count:24; /* reference counter */
- unsigned int type:8; /* type of Erlang term */
-} Erl_Header;
-
-typedef struct {
- Erl_Header h;
- int i;
-} Erl_Integer;
-
-typedef struct {
- Erl_Header h;
- unsigned int u;
-} Erl_Uinteger;
-
-typedef struct {
- Erl_Header h;
- long long i;
-} Erl_LLInteger;
-
-typedef struct {
- Erl_Header h;
- unsigned long long u;
-} Erl_ULLInteger;
-
-typedef struct {
- Erl_Header h;
- double f;
-} Erl_Float;
-
-typedef struct {
- char *utf8;
- int lenU;
- char *latin1;
- int lenL;
-} Erl_Atom_data;
-
-char* erl_atom_ptr_latin1(Erl_Atom_data*) EI_DEPRECATED_ATTR;
-char* erl_atom_ptr_utf8(Erl_Atom_data*) EI_DEPRECATED_ATTR;
-int erl_atom_size_latin1(Erl_Atom_data*) EI_DEPRECATED_ATTR;
-int erl_atom_size_utf8(Erl_Atom_data*) EI_DEPRECATED_ATTR;
-char* erl_atom_init_latin1(Erl_Atom_data*, const char*) EI_DEPRECATED_ATTR;
-
-typedef struct {
- Erl_Header h;
- Erl_Atom_data d;
-} Erl_Atom;
-
-typedef struct {
- Erl_Header h;
- Erl_Atom_data node;
- unsigned int number;
- unsigned int serial;
- unsigned int creation;
-} Erl_Pid;
-
-typedef struct {
- Erl_Header h;
- Erl_Atom_data node;
- unsigned int number;
- unsigned int creation;
-} Erl_Port;
-
-typedef struct {
- Erl_Header h;
- Erl_Atom_data node;
- int len;
- unsigned int n[3];
- unsigned int creation;
-} Erl_Ref;
-
-typedef struct {
- Erl_Header h;
- int arity;
- int is_neg;
- unsigned short *digits;
-} Erl_Big;
-
-struct _eterm; /* forward */
-
-typedef struct {
- Erl_Header h;
- struct _eterm *head;
- struct _eterm *tail;
-} Erl_List;
-
-typedef struct {
- Erl_Header h;
-} Erl_EmptyList;
-
-typedef struct {
- Erl_Header h;
- int size;
- struct _eterm **elems;
-} Erl_Tuple;
-
-typedef struct {
- Erl_Header h;
- int size;
- unsigned char *b;
-} Erl_Binary;
-
-/* Variables may only exist in patterns.
- * Note: identical variable names in a pattern
- * denotes the same value.
- */
-typedef struct {
- Erl_Header h;
- int len;
- char *name;
- struct _eterm *v;
-} Erl_Variable;
-
-
-typedef struct {
- Erl_Header h;
- int size; /* size of closure */
- int arity; /* arity for new (post R7) external funs */
- unsigned char md5[16]; /* md5 for new funs */
- int new_index; /* new funs */
- struct _eterm* creator; /* pid */
- struct _eterm* module; /* module */
- struct _eterm* index;
- struct _eterm* uniq;
- struct _eterm** closure;
-} Erl_Function;
-
-typedef struct _eterm {
- union {
- Erl_Integer ival;
- Erl_Uinteger uival;
- Erl_LLInteger llval;
- Erl_ULLInteger ullval;
- Erl_Float fval;
- Erl_Atom aval;
- Erl_Pid pidval;
- Erl_Port portval;
- Erl_Ref refval;
- Erl_List lval;
- Erl_EmptyList nval;
- Erl_Tuple tval;
- Erl_Binary bval;
- Erl_Variable vval;
- Erl_Function funcval;
- Erl_Big bigval;
- } uval;
-} ETERM;
-
-
-#define MAXREGLEN (255*4) /* max length of registered (atom) name */
-
-typedef struct {
- int type; /* one of the message type constants in eiext.h */
- ETERM *msg; /* the actual message */
- ETERM *from;
- ETERM *to;
- char to_name[MAXREGLEN+1];
-} ErlMessage;
-
-typedef unsigned char Erl_Heap;
-
-
-/* -------------------------------------------------------------------- */
-/* The functions */
-/* -------------------------------------------------------------------- */
-
-void erl_init(void *x, long y) EI_DEPRECATED_ATTR;
-void erl_set_compat_rel(unsigned) EI_DEPRECATED_ATTR;
-int erl_connect_init(int, char*,short) EI_DEPRECATED_ATTR;
-int erl_connect_xinit(char*,char*,char*,struct in_addr*,char*,short) EI_DEPRECATED_ATTR;
-int erl_connect(char*) EI_DEPRECATED_ATTR;
-int erl_xconnect(struct in_addr*,char *) EI_DEPRECATED_ATTR;
-int erl_close_connection(int) EI_DEPRECATED_ATTR;
-int erl_receive(int, unsigned char*, int) EI_DEPRECATED_ATTR;
-int erl_receive_msg(int, unsigned char*, int, ErlMessage*) EI_DEPRECATED_ATTR;
-int erl_xreceive_msg(int, unsigned char**, int*, ErlMessage*) EI_DEPRECATED_ATTR;
-int erl_send(int, ETERM*, ETERM*) EI_DEPRECATED_ATTR;
-int erl_reg_send(int, char*, ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_rpc(int,char*,char*,ETERM*) EI_DEPRECATED_ATTR;
-int erl_rpc_to(int,char*,char*,ETERM*) EI_DEPRECATED_ATTR;
-int erl_rpc_from(int,int,ErlMessage*) EI_DEPRECATED_ATTR;
-
-/* erl_publish returns open descriptor on success, or -1 */
-int erl_publish(int port) EI_DEPRECATED_ATTR;
-int erl_accept(int,ErlConnect*) EI_DEPRECATED_ATTR;
-
-const char *erl_thiscookie(void) EI_DEPRECATED_ATTR;
-const char *erl_thisnodename(void) EI_DEPRECATED_ATTR;
-const char *erl_thishostname(void) EI_DEPRECATED_ATTR;
-const char *erl_thisalivename(void) EI_DEPRECATED_ATTR;
-short erl_thiscreation(void) EI_DEPRECATED_ATTR;
-
-/* returns 0 on success, -1 if node not known to epmd or epmd not reached */
-int erl_unpublish(const char *alive) EI_DEPRECATED_ATTR;
-
-#ifdef EI_HAVE_DEPRECATED_ATTR__
-#define EI_DEPR_ATTR_EXTRA , EI_DEPRECATED_ATTR_NAME
-#else
-#define EI_DEPR_ATTR_EXTRA
-#endif
-
-
-/* Report generic error to stderr. */
-void erl_err_msg(const char * __template, ...)
- __attribute__ ((__format__ (printf, 1, 2) EI_DEPR_ATTR_EXTRA)) ;
-/* Report generic error to stderr and die. */
-void erl_err_quit(const char * __template, ...)
- __attribute__ ((__format__ (printf, 1, 2), __noreturn__ EI_DEPR_ATTR_EXTRA));
-/* Report system/libc error to stderr. */
-void erl_err_ret(const char * __template, ...)
- __attribute__ ((__format__ (printf, 1, 2) EI_DEPR_ATTR_EXTRA));
-/* Report system/libc error to stderr and die. */
-void erl_err_sys(const char * __template, ...)
- __attribute__ ((__format__ (printf, 1, 2), __noreturn__ EI_DEPR_ATTR_EXTRA));
-
-ETERM *erl_cons(ETERM*,ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_copy_term(const ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_element(int,const ETERM*) EI_DEPRECATED_ATTR;
-
-ETERM *erl_hd(const ETERM*) EI_DEPRECATED_ATTR;
-ETERM* erl_iolist_to_binary(const ETERM* term) EI_DEPRECATED_ATTR;
-char* erl_iolist_to_string(const ETERM* term) EI_DEPRECATED_ATTR;
-int erl_iolist_length(const ETERM*) EI_DEPRECATED_ATTR;
-int erl_length(const ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_atom(const char*) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_binary(const char*,int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_empty_list(void) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_estring(const char*, int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_float(double) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_int(int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_longlong(long long) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_list(ETERM**,int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_pid(const char*,unsigned int,unsigned int,unsigned char) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_port(const char*,unsigned int,unsigned char) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_ref(const char*,unsigned int,unsigned char) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_long_ref(const char*,unsigned int,unsigned int,
- unsigned int,unsigned char) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_string(const char*) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_tuple(ETERM**,int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_uint(unsigned int) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_ulonglong(unsigned long long) EI_DEPRECATED_ATTR;
-ETERM *erl_mk_var(const char*) EI_DEPRECATED_ATTR;
-int erl_print_term(FILE*,const ETERM*) EI_DEPRECATED_ATTR;
-/* int erl_sprint_term(char*,const ETERM*) EI_DEPRECATED_ATTR; */
-int erl_size(const ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_tl(const ETERM*) EI_DEPRECATED_ATTR;
-ETERM *erl_var_content(const ETERM*, const char*) EI_DEPRECATED_ATTR;
-
-ETERM *erl_format(char*, ... ) EI_DEPRECATED_ATTR;
-int erl_match(ETERM*, ETERM*) EI_DEPRECATED_ATTR;
-
-char **erl_global_names(int fd, int *count) EI_DEPRECATED_ATTR;
-int erl_global_register(int fd, const char *name, ETERM *pid) EI_DEPRECATED_ATTR;
-int erl_global_unregister(int fd, const char *name) EI_DEPRECATED_ATTR;
-ETERM *erl_global_whereis(int fd, const char *name, char *node) EI_DEPRECATED_ATTR;
-
-void erl_init_malloc(Erl_Heap*,long) EI_DEPRECATED_ATTR;
-ETERM *erl_alloc_eterm(unsigned char) EI_DEPRECATED_ATTR;
-void erl_eterm_release(void) EI_DEPRECATED_ATTR;
-void erl_eterm_statistics(unsigned long*,unsigned long*) EI_DEPRECATED_ATTR;
-void erl_free_array(ETERM**,int) EI_DEPRECATED_ATTR;
-void erl_free_term(ETERM*) EI_DEPRECATED_ATTR;
-void erl_free_compound(ETERM*) EI_DEPRECATED_ATTR;
-void *erl_malloc(long) EI_DEPRECATED_ATTR;
-void erl_free(void*) EI_DEPRECATED_ATTR;
-
-int erl_compare_ext(unsigned char*, unsigned char*) EI_DEPRECATED_ATTR;
-ETERM *erl_decode(unsigned char*) EI_DEPRECATED_ATTR;
-ETERM *erl_decode_buf(unsigned char**) EI_DEPRECATED_ATTR;
-int erl_encode(ETERM*,unsigned char*t) EI_DEPRECATED_ATTR;
-int erl_encode_buf(ETERM*,unsigned char**) EI_DEPRECATED_ATTR;
-int erl_ext_size(unsigned char*) EI_DEPRECATED_ATTR;
-unsigned char erl_ext_type(unsigned char*) EI_DEPRECATED_ATTR; /* Note: returned 'char' before R9C */
-unsigned char *erl_peek_ext(unsigned char*,int) EI_DEPRECATED_ATTR;
-int erl_term_len(ETERM*) EI_DEPRECATED_ATTR;
-
-int cmp_latin1_vs_utf8(const char* sL, int lenL, const char* sU, int lenU) EI_DEPRECATED_ATTR;
-
-/* -------------------------------------------------------------------- */
-/* Wrappers around ei functions */
-/* -------------------------------------------------------------------- */
-
-/*
- * Undocumented before R9C, included for compatibility with old code
- */
-
-struct hostent *erl_gethostbyname(const char *name) EI_DEPRECATED_ATTR;
-struct hostent *erl_gethostbyaddr(const char *addr, int len, int type) EI_DEPRECATED_ATTR;
-struct hostent *erl_gethostbyname_r(const char *name,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop) EI_DEPRECATED_ATTR;
-struct hostent *erl_gethostbyaddr_r(const char *addr,
- int length,
- int type,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop) EI_DEPRECATED_ATTR;
-
-/*
- * Undocumented, included for compatibility with old code
- */
-
-void erl_init_resolve(void) EI_DEPRECATED_ATTR;
-int erl_distversion(int fd) EI_DEPRECATED_ATTR;
-int erl_epmd_connect(struct in_addr *inaddr) EI_DEPRECATED_ATTR;
-int erl_epmd_port(struct in_addr *inaddr, const char *alive, int *dist) EI_DEPRECATED_ATTR;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index 6e0d3476c7..122cc560cc 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -52,9 +52,7 @@ APPUP_FILE= erl_interface.appup
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBINDIR)/$(APPUP_FILE)
-USING_MINGW=@MIXED_CYGWIN_MINGW@
-USING_MSYS_VC==@MIXED_MSYS_VC@
-USING_CYGWIN_VC==@MIXED_MSYS_VC@
+USING_MINGW=@MIXED_MINGW@
USING_VC=@MIXED_VC@
ifdef TESTROOT
@@ -130,12 +128,7 @@ endif
WARNFLAGS += -DEI_NO_DEPR_WARN
CFLAGS = @LIB_CFLAGS@ $(WARNFLAGS) $(INCFLAGS) $(TYPE_FLAGS)
-PROG_CFLAGS = @CFLAGS@ $(WARNFLAGS) $(INCFLAGS) $(TYPE_FLAGS) -Ilegacy
-
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-PROG_CFLAGS += -nostartfiles -Wl,-r,-d
-endif
-
+PROG_CFLAGS = @CFLAGS@ $(WARNFLAGS) $(INCFLAGS) $(TYPE_FLAGS) -Iglobal
INSTALL = @INSTALL@
INSTALL_DIR = @INSTALL_DIR@
@@ -161,7 +154,7 @@ BINDIR = $(ERL_TOP)/lib/erl_interface/bin/$(TARGET)
# -Wno-char-subscripts
# -Wshadow
-vpath %.c connect:encode:decode:misc:epmd:legacy:registry
+vpath %.c connect:encode:decode:misc:epmd:global:registry
###########################################################################
# List targets
@@ -193,17 +186,12 @@ ERL_CALL = $(BINDIR)/erl_call$(EXE)
ifdef THR_DEFS
ST_EILIB = $(OBJDIR)/$(LIBPRE)ei_st$(LIBEXT)
-ST_ERLLIB = $(OBJDIR)/$(LIBPRE)erl_interface_st$(LIBEXT)
MT_EILIB = $(OBJDIR)/$(LIBPRE)ei$(LIBEXT)
-MT_ERLLIB = $(OBJDIR)/$(LIBPRE)erl_interface$(LIBEXT)
else
ST_EILIB = $(OBJDIR)/$(LIBPRE)ei$(LIBEXT)
-ST_ERLLIB = $(OBJDIR)/$(LIBPRE)erl_interface$(LIBEXT)
endif
MD_EILIB = $(OBJDIR)/$(LIBPRE)ei_md$(LIBEXT)
MDD_EILIB = $(OBJDIR)/$(LIBPRE)ei_mdd$(LIBEXT)
-MD_ERLLIB = $(OBJDIR)/$(LIBPRE)erl_interface_md$(LIBEXT)
-MDD_ERLLIB = $(OBJDIR)/$(LIBPRE)erl_interface_mdd$(LIBEXT)
###########################################################################
# Specify targets to build
@@ -225,10 +213,7 @@ TARGETS = \
OBJ_TARGETS = \
$(MT_EILIB) \
$(MD_EILIB) \
- $(MDD_EILIB) \
- $(MT_ERLLIB) \
- $(MD_ERLLIB) \
- $(MDD_ERLLIB)
+ $(MDD_EILIB)
FAKE_TARGETS = \
$(OBJDIR)/erl_fake_prog_mt$(EXE) \
@@ -254,8 +239,7 @@ TARGETS = \
$(APPUP_TARGET)
OBJ_TARGETS = \
- $(MD_EILIB) \
- $(MD_ERLLIB)
+ $(MD_EILIB)
FAKE_TARGETS = \
$(OBJDIR)/erl_fake_prog_md$(EXE) \
@@ -275,9 +259,7 @@ TARGETS = \
OBJ_TARGETS = \
$(ST_EILIB) \
- $(ST_ERLLIB) \
- $(MT_EILIB) \
- $(MT_ERLLIB)
+ $(MT_EILIB)
FAKE_TARGETS = \
$(ST_OBJDIR)/erl_fake_prog_st$(EXE) \
@@ -298,8 +280,7 @@ TARGETS = \
$(APPUP_TARGET)
OBJ_TARGETS = \
- $(ST_EILIB) \
- $(ST_ERLLIB)
+ $(ST_EILIB)
FAKE_TARGETS = \
$(ST_OBJDIR)/erl_fake_prog_st$(EXE) \
@@ -321,8 +302,7 @@ endif
HEADERS = \
../include/ei.h \
../include/ei_connect.h \
- ../include/eicode.h \
- ../include/erl_interface.h
+ ../include/eicode.h
EISOURCES = \
$(CONNECTSRC) \
@@ -330,7 +310,8 @@ EISOURCES = \
$(ENCODESRC) \
$(EPMDSRC) \
$(MISCSRC) \
- $(REGISTRYSRC)
+ $(REGISTRYSRC) \
+ $(GLOBALSOURCES)
CONNECTSRC = \
connect/ei_connect.c \
@@ -363,14 +344,9 @@ DECODESRC = \
decode/decode_version.c \
$(DECODESRC_LONGLONG)
-ifneq ($(findstring vxworks,$(TARGET)),vxworks)
DECODESRC_LONGLONG = \
decode/decode_longlong.c \
decode/decode_ulonglong.c
-else
-DECODESRC_LONGLONG =
-endif
-
ENCODESRC = \
encode/encode_atom.c \
@@ -393,13 +369,9 @@ ENCODESRC = \
encode/encode_version.c \
$(ENCODESRC_LONGLONG)
-ifneq ($(findstring vxworks,$(TARGET)),vxworks)
ENCODESRC_LONGLONG = \
encode/encode_longlong.c \
encode/encode_ulonglong.c
-else
-ENCODESRC_LONGLONG =
-endif
EPMDSRC = \
@@ -457,24 +429,13 @@ REGISTRYSRC = \
registry/reg_stat.c \
registry/reg_tabstat.c
-ERLSOURCES = \
- legacy/decode_term.c \
- legacy/encode_term.c \
- legacy/erl_connect.c \
- legacy/erl_error.c \
- legacy/erl_eterm.c \
- legacy/erl_fix_alloc.c \
- legacy/erl_format.c \
- legacy/erl_malloc.c \
- legacy/erl_marshal.c \
- legacy/erl_resolve.c \
- legacy/erl_timeout.c \
- legacy/global_names.c \
- legacy/global_register.c \
- legacy/global_unregister.c \
- legacy/global_whereis.c
-
-SOURCES = $(EISOURCES) $(ERLSOURCES)
+GLOBALSOURCES = \
+ global/global_names.c \
+ global/global_register.c \
+ global/global_unregister.c \
+ global/global_whereis.c
+
+SOURCES = $(EISOURCES)
NEVERUSED = \
whereis.c \
@@ -491,13 +452,9 @@ ERLCALL = \
# located in the erl_interface library, not ei library.
ST_EIOBJECTS = $(addprefix $(ST_OBJDIR)/,$(notdir $(EISOURCES:.c=.o)))
-ST_ERLOBJECTS = $(addprefix $(ST_OBJDIR)/,$(notdir $(ERLSOURCES:.c=.o)))
MT_EIOBJECTS = $(addprefix $(MT_OBJDIR)/,$(notdir $(EISOURCES:.c=.o)))
-MT_ERLOBJECTS = $(addprefix $(MT_OBJDIR)/,$(notdir $(ERLSOURCES:.c=.o)))
MD_EIOBJECTS = $(addprefix $(MD_OBJDIR)/,$(notdir $(EISOURCES:.c=.o)))
-MD_ERLOBJECTS = $(addprefix $(MD_OBJDIR)/,$(notdir $(ERLSOURCES:.c=.o)))
MDD_EIOBJECTS = $(addprefix $(MDD_OBJDIR)/,$(notdir $(EISOURCES:.c=.o)))
-MDD_ERLOBJECTS = $(addprefix $(MDD_OBJDIR)/,$(notdir $(ERLSOURCES:.c=.o)))
###########################################################################
# Main targets
@@ -513,10 +470,10 @@ docs:
tests:
clean:
- rm -f $(ST_EIOBJECTS) $(ST_ERLOBJECTS) $(ST_EILIB) $(ST_ERLLIB)
- rm -f $(MT_EIOBJECTS) $(MT_ERLOBJECTS) $(MT_EILIB) $(MT_ERLLIB)
- rm -f $(MD_EIOBJECTS) $(MD_ERLOBJECTS) $(MD_EILIB) $(MD_ERLLIB)
- rm -f $(MDD_EIOBJECTS) $(MDD_ERLOBJECTS) $(MDD_EILIB) $(MDD_ERLLIB)
+ rm -f $(ST_EIOBJECTS) $(ST_EILIB)
+ rm -f $(MT_EIOBJECTS) $(MT_EILIB)
+ rm -f $(MD_EIOBJECTS) $(MD_EILIB)
+ rm -f $(MDD_EIOBJECTS) $(MDD_EILIB)
rm -f $(ERL_CALL)
rm -f $(FAKE_TARGETS)
rm -f $(APP_TARGET)
@@ -527,20 +484,6 @@ distclean: clean
###########################################################################
-# FIXME move this VxWorks stuff to configure or something
-###########################################################################
-
-# FIXME depend on $(TARGET)/Makefile ???
-
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-$(TARGET)/config.h:
- $(gen_verbose)
- $(V_at)echo "/* Generated by Makefile */" > $@
- $(V_at)echo "#define HAVE_STRERROR 1" >> $@
- $(V_at)echo "#define HAVE_SOCKLEN_T 1" >> $@
-endif
-
-###########################################################################
# Default rules, normal and threaded
###########################################################################
@@ -582,34 +525,18 @@ $(ST_EILIB) : $(ST_EIOBJECTS)
$(V_AR) -out:$@ $(ST_EIOBJECTS)
$(V_RANLIB) $@
-$(ST_ERLLIB) : $(ST_ERLOBJECTS)
- $(V_AR) -out:$@ $(ST_ERLOBJECTS)
- $(V_RANLIB) $@
-
$(MT_EILIB) : $(MT_EIOBJECTS)
$(V_AR) -out:$@ $(MT_EIOBJECTS)
$(V_RANLIB) $@
-$(MT_ERLLIB) : $(MT_ERLOBJECTS)
- $(V_AR) -out:$@ $(MT_ERLOBJECTS)
- $(V_RANLIB) $@
-
$(MD_EILIB) : $(MD_EIOBJECTS)
$(V_AR) -out:$@ $(MD_EIOBJECTS)
$(V_RANLIB) $@
-$(MD_ERLLIB) : $(MD_ERLOBJECTS)
- $(V_AR) -out:$@ $(MD_ERLOBJECTS)
- $(V_RANLIB) $@
-
$(MDD_EILIB) : $(MDD_EIOBJECTS)
$(V_AR) -out:$@ $(MDD_EIOBJECTS)
$(V_RANLIB) $@
-$(MDD_ERLLIB) : $(MDD_ERLOBJECTS)
- $(V_AR) -out:$@ $(MDD_ERLOBJECTS)
- $(V_RANLIB) $@
-
else
# Unix archive creation
@@ -621,12 +548,6 @@ ifdef RANLIB
$(V_RANLIB) $@
endif
-$(ST_ERLLIB) : $(ST_ERLOBJECTS)
- $(V_at)rm -f $@
- $(V_AR) $(AR_FLAGS) $@ $(ST_ERLOBJECTS)
-ifdef RANLIB
- $(V_RANLIB) $@
-endif
$(MT_EILIB) : $(MT_EIOBJECTS)
$(V_at)rm -f $@
@@ -635,13 +556,6 @@ ifdef RANLIB
$(V_RANLIB) $@
endif
-$(MT_ERLLIB) : $(MT_ERLOBJECTS)
- $(V_at)rm -f $@
- $(V_AR) $(AR_FLAGS) $@ $(MT_ERLOBJECTS)
-ifdef RANLIB
- $(V_RANLIB) $@
-endif
-
endif
###########################################################################
@@ -653,17 +567,6 @@ $(ERL_CALL): $(ERLCALL) ../include/ei.h $(MD_EILIB)
$(ld_verbose)$(PURIFY) $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $(ERLCALL) \
-L$(OBJDIR) -lei_md $(THR_LIBS) $(LIBS) -lsocket
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-$(ERL_CALL): $(ST_OBJDIR)/erl_call.o $(ST_OBJDIR)/erl_start.o ../include/ei.h $(ST_EILIB)
- $(V_LD) -r -d -o $@ $(ST_OBJDIR)/erl_call.o $(ST_OBJDIR)/erl_start.o -L$(OBJDIR) -lei $(LIBS)
-
-$(ST_OBJDIR)/erl_call.o: prog/erl_call.c
- $(V_CC) $(CFLAGS) -c $< -o $@
-
-$(ST_OBJDIR)/erl_start.o: prog/erl_start.c
- $(V_CC) $(CFLAGS) -c $< -o $@
-
-else
ifdef THR_DEFS
$(ERL_CALL): $(ERLCALL) ../include/ei.h $(MT_EILIB)
$(ld_verbose)$(PURIFY) $(CC) $(PROG_CFLAGS) $(THR_DEFS) $(LDFLAGS) -o $@ $(ERLCALL) \
@@ -674,7 +577,6 @@ $(ERL_CALL): $(ERLCALL) ../include/ei.h $(ST_EILIB)
-L$(OBJDIR) -lei $(LIBS)
endif
endif
-endif
###########################################################################
# Fake application targets used to test header files and linking
@@ -683,7 +585,7 @@ endif
check: $(FAKE_TARGETS)
ifndef THR_DEFS
-$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_ERLLIB) $(ST_EILIB)
+$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface -lei \
$(LIBS)
@@ -691,7 +593,7 @@ $(ST_OBJDIR)/ei_fake_prog_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei $(LIBS)
$(ST_OBJDIR)/erl_fake_prog_cxx_st$(EXE): prog/erl_fake_prog.c \
- $(ST_ERLLIB) $(ST_EILIB)
+ $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
-lerl_interface -lei $(LIBS)
@@ -700,7 +602,7 @@ $(ST_OBJDIR)/ei_fake_prog_cxx_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
else
-$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_ERLLIB) $(ST_EILIB)
+$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface_st -lei_st \
$(LIBS)
@@ -708,7 +610,7 @@ $(ST_OBJDIR)/ei_fake_prog_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei_st $(LIBS)
$(ST_OBJDIR)/erl_fake_prog_cxx_st$(EXE): prog/erl_fake_prog.c \
- $(ST_ERLLIB) $(ST_EILIB)
+ $(ST_EILIB)
$(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
-lerl_interface_st -lei_st $(LIBS)
@@ -720,7 +622,7 @@ endif
####
$(MT_OBJDIR)/erl_fake_prog_mt$(EXE): prog/erl_fake_prog.c \
- $(MT_ERLLIB) $(MT_EILIB)
+ $(MT_EILIB)
$(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface -lei $(THR_LIBS) $(LIBS)
@@ -729,7 +631,7 @@ $(MT_OBJDIR)/ei_fake_prog_mt$(EXE): prog/ei_fake_prog.c $(MT_EILIB)
-L$(OBJDIR) -lei $(THR_LIBS) $(LIBS)
$(MT_OBJDIR)/erl_fake_prog_mt_cxx$(EXE): prog/erl_fake_prog.c \
- $(MT_ERLLIB) $(MT_EILIB)
+ $(MT_EILIB)
$(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface -lei \
$(THR_LIBS) $(LIBS)
@@ -741,7 +643,7 @@ $(MT_OBJDIR)/ei_fake_prog_mt_cxx$(EXE): prog/ei_fake_prog.c $(MT_EILIB)
####
$(MD_OBJDIR)/erl_fake_prog_md$(EXE): prog/erl_fake_prog.c \
- $(MD_ERLLIB) $(MD_EILIB)
+ $(MD_EILIB)
$(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface_r -lei_r $(THR_LIBS) $(LIBS)
@@ -750,7 +652,7 @@ $(MD_OBJDIR)/ei_fake_prog_md$(EXE): prog/ei_fake_prog.c $(MD_EILIB)
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
$(MD_OBJDIR)/erl_fake_prog_md_cxx$(EXE): prog/erl_fake_prog.c \
- $(MD_ERLLIB) $(MD_EILIB)
+ $(MD_EILIB)
$(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface_r -lei_r \
$(THR_LIBS) $(LIBS)
@@ -762,7 +664,7 @@ $(MD_OBJDIR)/ei_fake_prog_md_cxx$(EXE): prog/ei_fake_prog.c $(MD_EILIB)
####
$(MDD_OBJDIR)/erl_fake_prog_mdd$(EXE): prog/erl_fake_prog.c \
- $(MDD_ERLLIB) $(MDD_EILIB)
+ $(MDD_EILIB)
$(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface_r -lei_r $(THR_LIBS) $(LIBS)
@@ -771,7 +673,7 @@ $(MDD_OBJDIR)/ei_fake_prog_mdd$(EXE): prog/ei_fake_prog.c $(MDD_EILIB)
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
$(MDD_OBJDIR)/erl_fake_prog_mdd_cxx$(EXE): prog/erl_fake_prog.c \
- $(MDD_ERLLIB) $(MDD_EILIB)
+ $(MDD_EILIB)
$(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface_r -lei_r \
$(THR_LIBS) $(LIBS)
@@ -784,32 +686,30 @@ $(MDD_OBJDIR)/ei_fake_prog_mdd_cxx$(EXE): prog/ei_fake_prog.c $(MDD_EILIB)
# Create dependency file using gcc -MM
###########################################################################
+ifneq ($(ERTS_SKIP_DEPEND),true)
depend: $(TARGET)/depend.mk
$(TARGET)/depend.mk: $(TARGET)/config.h
$(gen_verbose)
$(V_colon)echo "Generating dependency file depend.mk..."
@echo "# Generated dependency rules" > $@
- $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
- sed 's&$(TARGET)&\$$\(TARGET\)&g' | \
- sed 's/^.*:/\$$\(ST_OBJDIR\)\/&/' >> $@
- @echo >> $@
- $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
- sed 's&$(TARGET)&\$$\(TARGET\)&g' | \
- sed 's/^.*:/\$$\(MT_OBJDIR\)\/&/' >> $@
- @echo >> $@
- $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
- sed 's&$(TARGET)&\$$\(TARGET\)&g' | \
- sed 's/^.*:/\$$\(MD_OBJDIR\)\/&/' >> $@
- @echo >> $@
- $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
- sed 's&$(TARGET)&\$$\(TARGET\)&g' | \
- sed 's/^.*:/\$$\(MDD_OBJDIR\)\/&/' >> $@
- @echo >> $@
+ $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
+ sed 's&$(TARGET)&\$$\(TARGET\)&g' > $@.$$$$; \
+ sed 's/^.*:/\$$\(ST_OBJDIR\)\/&/' < $@.$$$$ >> $@; \
+ echo >> $@; \
+ sed 's/^.*:/\$$\(MT_OBJDIR\)\/&/' < $@.$$$$ >> $@; \
+ echo >> $@; \
+ sed 's/^.*:/\$$\(MD_OBJDIR\)\/&/' < $@.$$$$ >> $@; \
+ echo >> $@; \
+ sed 's/^.*:/\$$\(MDD_OBJDIR\)\/&/' < $@.$$$$ >> $@; \
+ echo >> $@; \
+ rm -f $@.$$$$
# For some reason this has to be after 'opt' target
-include $(TARGET)/depend.mk
-
+else
+depend:
+endif
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -832,7 +732,7 @@ release: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src/decode"
$(INSTALL_DIR) "$(RELSYSDIR)/src/encode"
$(INSTALL_DIR) "$(RELSYSDIR)/src/epmd"
- $(INSTALL_DIR) "$(RELSYSDIR)/src/legacy"
+ $(INSTALL_DIR) "$(RELSYSDIR)/src/global"
$(INSTALL_DIR) "$(RELSYSDIR)/src/misc"
$(INSTALL_DIR) "$(RELSYSDIR)/src/prog"
$(INSTALL_DIR) "$(RELSYSDIR)/src/registry"
@@ -854,7 +754,7 @@ endif
$(INSTALL_DATA) epmd/*.[ch] "$(RELSYSDIR)/src/epmd"
$(INSTALL_DATA) misc/*.[ch] "$(RELSYSDIR)/src/misc"
$(INSTALL_DATA) registry/*.[ch] "$(RELSYSDIR)/src/registry"
- $(INSTALL_DATA) legacy/*.[ch] "$(RELSYSDIR)/src/legacy"
+ $(INSTALL_DATA) global/*.[ch] "$(RELSYSDIR)/src/global"
$(INSTALL_DATA) prog/*.[ch] "$(RELSYSDIR)/src/prog"
release_docs:
diff --git a/lib/erl_interface/src/README b/lib/erl_interface/src/README
index 7591615f78..823575c6b7 100644
--- a/lib/erl_interface/src/README
+++ b/lib/erl_interface/src/README
@@ -21,7 +21,7 @@ The cause of an assertion can be either errors in the application
program (for instance, passing NULL pointers to any function that
expects an ETERM pointer) or in erl_interface itself.
-If you encounter any assertion failures which think you originate in
+If you encounter any assertion failures which you think originate in
erl_interface, I'll need the following information to track it down:
1a) The printout from the assertion, especially filename and line number.
@@ -35,7 +35,7 @@ erl_interface, I'll need the following information to track it down:
Changes in this version
-----------------------
-There is now *one* representation for an empty list, not two as is used
+There is now *one* representation for an empty list, not two as it used
to be. An empty list is represented by the Erl_EmptyList structure.
There are new macros, ERL_IS_EMPTY_LIST(ep), to test for an empty list,
and ERL_IS_CONS(ep) to test for a cons cell (more on that below).
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 96823b4ee7..d97b77d3e8 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -32,26 +32,7 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <selectLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-
-#include <unistd.h>
-#include <sys/times.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <timers.h>
-
-#define getpid() taskIdSelf()
-
-#else /* some other unix */
+#else /* some unix */
#include <unistd.h>
#include <sys/times.h>
@@ -106,7 +87,8 @@ int ei_tracelevel = 0;
(offsetof(ei_socket_callbacks, get_fd) \
+ sizeof(int (*)(void *)))
-/* FIXME why not macro? */
+typedef EI_ULONGLONG DistFlags;
+
static char *null_cookie = "";
static int get_cookie(char *buf, int len);
@@ -120,15 +102,17 @@ static int send_status(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, char *status, unsigned ms);
static int recv_status(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned ms);
-static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned challenge,
- unsigned version, unsigned ms);
+static int send_challenge(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned challenge,
+ DistFlags version, unsigned ms);
static int recv_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
unsigned *challenge, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms);
+ DistFlags *flags, char *namebuf, unsigned ms);
static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned char digest[16],
unsigned challenge, unsigned ms);
+static int recv_complement(ei_socket_callbacks *cbs, void *ctx,
+ int pkt_sz, unsigned ms);
static int recv_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned our_challenge,
char cookie[],
@@ -139,12 +123,20 @@ static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned our_challenge,
char cookie[], unsigned ms);
-static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned version, unsigned ms);
-
+static int send_name(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned version, unsigned ms);
+static int send_complement(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned epmd_says_version, DistFlags her_flags,
+ unsigned ms);
static int recv_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- unsigned *version, unsigned *flags, char *namebuf,
- unsigned ms);
+ char* send_name_tag, DistFlags *flags,
+ char *namebuf, unsigned ms);
+static int ei_connect_helper(ei_cnode* ec,
+ Erl_IpAddr ip_addr,
+ char *alivename,
+ unsigned ms,
+ int rport,
+ int epmd_says_version);
static struct hostent*
dyn_gethostbyname_r(const char *name, struct hostent *hostp, char **buffer_p,
@@ -659,7 +651,7 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname,
return ERL_ERROR;
}
- ec->creation = creation & 0x3; /* 2 bits */
+ ec->creation = creation;
if (cookie) {
if (strlen(cookie) >= sizeof(ec->ei_connect_cookie)) {
@@ -698,7 +690,7 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname,
strcpy(ec->self.node,thisnodename);
ec->self.num = 0;
ec->self.serial = 0;
- ec->self.creation = creation & 0x3; /* 2 bits */
+ ec->self.creation = creation;
ec->cbs = cbs;
ec->setup_context = setup_context;
@@ -874,78 +866,57 @@ struct hostent *dyn_gethostbyname_r(const char *name,
#endif
}
- /*
- * Set up a connection to a given Node, and
- * interchange hand shake messages with it.
- * Returns a valid file descriptor at success,
- * otherwise a negative error code.
-*/
-int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
+/* Finds the the IP address for hostname and saves that IP address at
+ the location that ip_wb points to. Returns a negative error code if
+ the IP address cannot be found for the hostname. */
+static int ip_address_from_hostname(char* hostname,
+ char** buffer_p,
+ size_t buffer_size,
+ Erl_IpAddr* ip_wb)
{
- char *hostname, alivename[BUFSIZ];
struct hostent *hp;
-#if !defined (__WIN32__)
+#ifndef __WIN32__
/* these are needed for the call to gethostbyname_r */
struct hostent host;
- char buffer[1024];
- char *buf = buffer;
int ei_h_errno;
-#endif /* !win32 */
- int res;
-
- if (strlen(nodename) > MAXNODELEN) {
- EI_TRACE_ERR0("ei_connect","Too long nodename");
- return ERL_ERROR;
- }
-
- /* extract the host and alive parts from nodename */
- if (!(hostname = strchr(nodename,'@'))) {
- EI_TRACE_ERR0("ei_connect","Node name has no @ in name");
- return ERL_ERROR;
- } else {
- strncpy(alivename, nodename, hostname - nodename);
- alivename[hostname - nodename] = 0x0;
- hostname++;
- }
-
-#ifndef __WIN32__
- hp = dyn_gethostbyname_r(hostname,&host,&buf,sizeof(buffer),&ei_h_errno);
+ hp = dyn_gethostbyname_r(hostname,&host,buffer_p,buffer_size,&ei_h_errno);
if (hp == NULL) {
char thishostname[EI_MAXHOSTNAMELEN+1];
/* gethostname requies len to be max(hostname) + 1*/
if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
- EI_TRACE_ERR0("ei_connect_tmo",
+ EI_TRACE_ERR0("ip_address_from_hostname",
"Failed to get name of this host");
erl_errno = EHOSTUNREACH;
return ERL_ERROR;
} else {
char *ct;
- /* We use a short node name */
+ /* We use a short node name */
if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
}
if (strcmp(hostname,thishostname) == 0)
/* Both nodes on same standalone host, use loopback */
- hp = dyn_gethostbyname_r("localhost",&host,&buf,sizeof(buffer),&ei_h_errno);
+ hp = dyn_gethostbyname_r("localhost",&host,buffer_p,buffer_size,&ei_h_errno);
if (hp == NULL) {
EI_TRACE_ERR2("ei_connect",
- "Can't find host for %s: %d\n",nodename,ei_h_errno);
+ "Can't find host for %s: %d\n",hostname,ei_h_errno);
erl_errno = EHOSTUNREACH;
return ERL_ERROR;
}
}
+ *ip_wb = (Erl_IpAddr) *hp->h_addr_list;
#else /* __WIN32__ */
if ((hp = ei_gethostbyname(hostname)) == NULL) {
char thishostname[EI_MAXHOSTNAMELEN+1];
/* gethostname requires len to be max(hostname) + 1 */
if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
- EI_TRACE_ERR1("ei_connect_tmo",
- "Failed to get name of this host: %d",
+ EI_TRACE_ERR1("ip_address_from_hostname",
+ "Failed to get name of this host: %d",
WSAGetLastError());
erl_errno = EHOSTUNREACH;
return ERL_ERROR;
} else {
char *ct;
- /* We use a short node name */
+ /* We use a short node name */
if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
}
if (strcmp(hostname,thishostname) == 0)
@@ -955,42 +926,29 @@ int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
char reason[1024];
win32_error(reason,sizeof(reason));
EI_TRACE_ERR2("ei_connect",
- "Can't find host for %s: %s",nodename,reason);
+ "Can't find host for %s: %s",hostname,reason);
erl_errno = EHOSTUNREACH;
return ERL_ERROR;
}
}
+ *ip_wb = (Erl_IpAddr) *hp->h_addr_list;
#endif /* win32 */
-
- res = ei_xconnect_tmo(ec, (Erl_IpAddr) *hp->h_addr_list, alivename, ms);
-
-#ifndef __WIN32__
- if (buf != buffer)
- free(buf);
-#endif
- return res;
-} /* ei_connect */
-
-int ei_connect(ei_cnode* ec, char *nodename)
-{
- return ei_connect_tmo(ec, nodename, 0);
+ return 0;
}
-
- /* ip_addr is now in network byte order
- *
- * first we have to get hold of the portnumber to
- * the node through epmd at that host
- *
-*/
-int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned ms)
+/* Helper function for ei_connect family of functions */
+static int ei_connect_helper(ei_cnode* ec,
+ Erl_IpAddr ip_addr, /* network byte order */
+ char *alivename,
+ unsigned ms,
+ int rport,
+ int epmd_says_version)
{
ei_socket_callbacks *cbs = ec->cbs;
void *ctx;
- int rport = 0; /*uint16 rport = 0;*/
int sockd;
- int dist = 0;
- unsigned her_flags, her_version;
+ unsigned her_version;
+ DistFlags her_flags;
unsigned our_challenge, her_challenge;
unsigned char our_digest[16];
int err;
@@ -999,18 +957,18 @@ int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned
unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
erl_errno = EIO; /* Default error code */
-
- EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to %s",
- alivename);
-
- if ((rport = ei_epmd_port_tmo(ip_addr,alivename,&dist, tmo)) < 0) {
- EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port");
- /* ei_epmd_port_tmo() has set erl_errno */
- return ERL_NO_PORT;
+
+ if (alivename != NULL) {
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to %s",
+ alivename);
+ } else {
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to port %d",
+ rport);
}
- if (dist <= 4) {
- EI_TRACE_ERR0("ei_xconnect","-> CONNECT remote version not compatible");
+ if (epmd_says_version < EI_DIST_LOW) {
+ EI_TRACE_ERR1("ei_xconnect","-> CONNECT remote version %d not compatible",
+ epmd_says_version);
return ERL_ERROR;
}
@@ -1050,21 +1008,24 @@ int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned
goto error;
}
- if (send_name(cbs, ctx, pkt_sz, ec->thisnodename, (unsigned) dist, tmo))
+ if (send_name(ec, ctx, pkt_sz, epmd_says_version, tmo))
goto error;
if (recv_status(cbs, ctx, pkt_sz, tmo))
goto error;
- if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge,
- &her_version, &her_flags, NULL, tmo))
+ if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge, &her_version,
+ &her_flags, NULL, tmo))
goto error;
+ her_version = (her_flags & DFLAG_HANDSHAKE_23) ? EI_DIST_6 : EI_DIST_5;
our_challenge = gen_challenge();
gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
+ if (send_complement(ec, ctx, pkt_sz, epmd_says_version, her_flags, tmo))
+ goto error;
if (send_challenge_reply(cbs, ctx, pkt_sz, our_digest, our_challenge, tmo))
goto error;
if (recv_challenge_ack(cbs, ctx, pkt_sz, our_challenge,
ec->ei_connect_cookie, tmo))
goto error;
- if (put_ei_socket_info(sockd, dist, null_cookie, ec, cbs, ctx) != 0)
+ if (put_ei_socket_info(sockd, her_version, null_cookie, ec, cbs, ctx) != 0)
goto error;
if (cbs->connect_handshake_complete) {
@@ -1077,8 +1038,12 @@ int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned
return ERL_ERROR;
}
}
-
- EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename);
+
+ if (alivename != NULL) {
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename);
+ } else {
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote port = %d",rport);
+ }
erl_errno = 0;
return sockd;
@@ -1089,11 +1054,103 @@ error:
return ERL_ERROR;
} /* ei_xconnect */
+ /*
+ * Set up a connection to a given Node, and
+ * interchange hand shake messages with it.
+ * Returns a valid file descriptor at success,
+ * otherwise a negative error code.
+*/
+int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
+{
+ char *hostname, alivename[BUFSIZ];
+ Erl_IpAddr ip;
+ int res;
+ char buffer[1024];
+ char* buf = buffer;
+
+ if (strlen(nodename) > MAXNODELEN) {
+ EI_TRACE_ERR0("ei_connect","Too long nodename");
+ return ERL_ERROR;
+ }
+
+ /* extract the host and alive parts from nodename */
+ if (!(hostname = strchr(nodename,'@'))) {
+ EI_TRACE_ERR0("ei_connect","Node name has no @ in name");
+ return ERL_ERROR;
+ } else {
+ strncpy(alivename, nodename, hostname - nodename);
+ alivename[hostname - nodename] = 0x0;
+ hostname++;
+ }
+
+ res = ip_address_from_hostname(hostname, &buf, sizeof(buffer), &ip);
+
+ if (res < 0) {
+ return res;
+ }
+
+ res = ei_xconnect_tmo(ec, ip, alivename, ms);
+
+ if(buf != buffer) {
+ free(buf);
+ }
+
+ return res;
+} /* ei_connect */
+
+int ei_connect(ei_cnode* ec, char *nodename)
+{
+ return ei_connect_tmo(ec, nodename, 0);
+}
+
+int ei_connect_host_port_tmo(ei_cnode* ec, char *host, int port, unsigned ms)
+{
+ Erl_IpAddr ip;
+ char buffer[1024];
+ char* buf = buffer;
+ int res = ip_address_from_hostname(host, &buf, sizeof(buffer), &ip);
+ if (res < 0) {
+ return res;
+ }
+ if(buf != buffer) {
+ free(buf);
+ }
+ return ei_xconnect_host_port_tmo(ec, ip, port, ms);
+}
+
+int ei_connect_host_port(ei_cnode* ec, char *host, int port)
+{
+ return ei_connect_host_port_tmo(ec, host, port, 0);
+}
+
+int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned ms)
+{
+ int epmd_says_version = 0;
+ int port;
+ unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
+ if ((port = ei_epmd_port_tmo(ip_addr,alivename,&epmd_says_version, tmo)) < 0) {
+ EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port");
+ /* ei_epmd_port_tmo() has set erl_errno */
+ return ERL_NO_PORT;
+ }
+ return ei_connect_helper(ec, ip_addr, alivename, ms, port, epmd_says_version);
+}
+
int ei_xconnect(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename)
{
return ei_xconnect_tmo(ec, ip_addr, alivename, 0);
}
+int ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, int port, unsigned ms)
+{
+ return ei_connect_helper(ec, ip_addr, NULL, ms, port, EI_DIST_LOW);
+}
+
+int ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr ip_addr, int port)
+{
+ return ei_xconnect_host_port_tmo(ec, ip_addr, port, 0);
+}
+
int ei_listen(ei_cnode *ec, int *port, int backlog)
{
struct in_addr ip_addr;
@@ -1209,8 +1266,9 @@ int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp)
int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
{
int fd;
- unsigned her_version, her_flags;
+ DistFlags her_flags;
char tmp_nodename[MAXNODELEN+1];
+ char send_name_tag;
char *her_name;
int pkt_sz, err;
struct sockaddr_in addr;
@@ -1235,6 +1293,10 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
ctx = EI_FD_AS_CTX__(lfd);
}
+ if (ec->cbs != cbs) {
+ EI_CONN_SAVE_ERRNO__(EINVAL);
+ return ERL_ERROR;
+ }
EI_TRACE_CONN0("ei_accept","<- ACCEPT waiting for connection");
@@ -1281,16 +1343,14 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
EI_TRACE_CONN0("ei_accept","<- ACCEPT connected to remote");
- if (recv_name(cbs, ctx, pkt_sz, &her_version, &her_flags, her_name, tmo)) {
+ if (recv_name(cbs, ctx, pkt_sz, &send_name_tag, &her_flags,
+ her_name, tmo)) {
EI_TRACE_ERR0("ei_accept","<- ACCEPT initial ident failed");
goto error;
}
- if (her_version <= 4) {
- EI_TRACE_ERR0("ei_accept","<- ACCEPT remote version not compatible");
- goto error;
- }
- else {
+ {
+ unsigned her_version = (her_flags & DFLAG_HANDSHAKE_23) ? 6 : 5;
unsigned our_challenge;
unsigned her_challenge;
unsigned char our_digest[16];
@@ -1298,9 +1358,12 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
if (send_status(cbs, ctx, pkt_sz, "ok", tmo))
goto error;
our_challenge = gen_challenge();
- if (send_challenge(cbs, ctx, pkt_sz, ec->thisnodename,
- our_challenge, her_version, tmo))
+ if (send_challenge(ec, ctx, pkt_sz, our_challenge, her_flags, tmo))
goto error;
+ if (send_name_tag == 'n' && (her_flags & DFLAG_HANDSHAKE_23)) {
+ if (recv_complement(cbs, ctx, pkt_sz, tmo))
+ goto error;
+ }
if (recv_challenge_reply(cbs, ctx, pkt_sz, our_challenge,
ec->ei_connect_cookie, &her_challenge, tmo))
goto error;
@@ -1629,21 +1692,6 @@ unsigned int gen_challenge(void)
return md_32((char*) &s, sizeof(s));
}
-#elif defined(VXWORKS)
-
-static unsigned int gen_challenge(void)
-{
- struct {
- struct timespec tv;
- clock_t cpu;
- int pid;
- } s;
- s.cpu = clock();
- clock_gettime(CLOCK_REALTIME, &s.tv);
- s.pid = getpid();
- return md_32((char*) &s, sizeof(s));
-}
-
#else /* some unix */
static unsigned int gen_challenge(void)
@@ -1846,26 +1894,50 @@ error:
return -1;
}
-static int send_name_or_challenge(ei_socket_callbacks *cbs,
- void *ctx,
- int pkt_sz,
- char *nodename,
- int f_chall,
- unsigned challenge,
- unsigned version,
- unsigned ms)
+static DistFlags preferred_flags(void)
+{
+ DistFlags flags =
+ DFLAG_EXTENDED_REFERENCES
+ | DFLAG_DIST_MONITOR
+ | DFLAG_EXTENDED_PIDS_PORTS
+ | DFLAG_FUN_TAGS
+ | DFLAG_NEW_FUN_TAGS
+ | DFLAG_NEW_FLOATS
+ | DFLAG_SMALL_ATOM_TAGS
+ | DFLAG_UTF8_ATOMS
+ | DFLAG_MAP_TAG
+ | DFLAG_BIG_CREATION
+ | DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES
+ | DFLAG_HANDSHAKE_23;
+ if (ei_internal_use_21_bitstr_expfun()) {
+ flags &= ~(DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES);
+ }
+ return flags;
+}
+
+static int send_name(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned version,
+ unsigned ms)
{
char *buf;
unsigned char *s;
char dbuf[DEFBUF_SIZ];
- int siz = pkt_sz + 1 + 2 + 4 + strlen(nodename);
- const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"};
+ const unsigned int nodename_len = strlen(ec->thisnodename);
+ int siz;
int err;
ssize_t len;
- unsigned int flags;
+ DistFlags flags;
+ const char tag = (version == EI_DIST_5) ? 'n' : 'N';
+
+ if (tag == 'n')
+ siz = pkt_sz + 1 + 2 + 4 + nodename_len;
+ else
+ siz = pkt_sz + 1 + 8 + 4 + 2 + nodename_len;
- if (f_chall)
- siz += 4;
buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
if (!buf) {
erl_errno = ENOMEM;
@@ -1882,35 +1954,95 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
default:
return -1;
}
- put8(s, 'n');
- put16be(s, version);
- flags = (DFLAG_EXTENDED_REFERENCES
- | DFLAG_DIST_MONITOR
- | DFLAG_EXTENDED_PIDS_PORTS
- | DFLAG_FUN_TAGS
- | DFLAG_NEW_FUN_TAGS
- | DFLAG_NEW_FLOATS
- | DFLAG_SMALL_ATOM_TAGS
- | DFLAG_UTF8_ATOMS
- | DFLAG_MAP_TAG
- | DFLAG_BIG_CREATION
- | DFLAG_EXPORT_PTR_TAG
- | DFLAG_BIT_BINARIES);
- if (ei_internal_use_21_bitstr_expfun()) {
- flags &= ~(DFLAG_EXPORT_PTR_TAG
- | DFLAG_BIT_BINARIES);
+ flags = preferred_flags();
+
+ put8(s, tag);
+ if (tag == 'n') {
+ put16be(s, EI_DIST_5); /* some impl (jinterface) demand ver==5 */
+ put32be(s, flags);
+ }
+ else { /* tag == 'N' */
+ put64be(s, flags);
+ put32be(s, ec->creation);
+ put16be(s, nodename_len);
}
- put32be(s, flags);
- if (f_chall)
- put32be(s, challenge);
- memcpy(s, nodename, strlen(nodename));
+ memcpy(s, ec->thisnodename, nodename_len);
len = (ssize_t) siz;
- err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms);
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
if (!err && len != (ssize_t) siz)
err = EIO;
if (err) {
- EI_TRACE_ERR1("send_name_or_challenge",
- "-> %s socket write failed", function[f_chall]);
+ EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
+ if (buf != dbuf)
+ free(buf);
+ EI_CONN_SAVE_ERRNO__(err);
+ return -1;
+ }
+
+ if (buf != dbuf)
+ free(buf);
+ return 0;
+}
+
+static int send_challenge(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned challenge,
+ DistFlags her_flags,
+ unsigned ms)
+{
+ char *buf;
+ unsigned char *s;
+ char dbuf[DEFBUF_SIZ];
+ const unsigned int nodename_len = strlen(ec->thisnodename);
+ int siz;
+ int err;
+ ssize_t len;
+ DistFlags flags;
+ const char tag = (her_flags & DFLAG_HANDSHAKE_23) ? 'N' : 'n';
+
+ if (tag == 'n')
+ siz = pkt_sz + 1 + 2 + 4 + 4 + nodename_len;
+ else
+ siz = pkt_sz + 1 + 8 + 4 + 4 + 2 + nodename_len;
+
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = (unsigned char *)buf;
+ switch (pkt_sz) {
+ case 2:
+ put16be(s,siz - 2);
+ break;
+ case 4:
+ put32be(s,siz - 4);
+ break;
+ default:
+ return -1;
+ }
+
+ flags = preferred_flags();
+ put8(s, tag);
+ if (tag == 'n') {
+ put16be(s, EI_DIST_5); /* choosen version */
+ put32be(s, flags);
+ put32be(s, challenge);
+ }
+ else {
+ put64be(s, flags);
+ put32be(s, challenge);
+ put32be(s, ec->creation);
+ put16be(s, nodename_len);
+ }
+ memcpy(s, ec->thisnodename, nodename_len);
+ len = (ssize_t) siz;
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
+ if (!err && len != (ssize_t) siz)
+ err = EIO;
+ if (err) {
+ EI_TRACE_ERR0("send_challenge", "-> SEND_CHALLENGE socket write failed");
if (buf != dbuf)
free(buf);
EI_CONN_SAVE_ERRNO__(err);
@@ -1924,13 +2056,13 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned *challenge, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms)
+ DistFlags *flags, char *namebuf, unsigned ms)
{
char dbuf[DEFBUF_SIZ];
char *buf = dbuf;
int is_static = 1;
int buflen = DEFBUF_SIZ;
- int rlen;
+ int rlen, nodename_len;
char *s;
char tag;
char tmp_nodename[MAXNODELEN+1];
@@ -1943,21 +2075,57 @@ static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
"<- RECV_CHALLENGE socket read failed (%d)",rlen);
goto error;
}
- if ((rlen - 11) > MAXNODELEN) {
- EI_TRACE_ERR1("recv_challenge",
- "<- RECV_CHALLENGE nodename too long (%d)",rlen - 11);
- goto error;
- }
s = buf;
- if ((tag = get8(s)) != 'n') {
+ tag = get8(s);
+ if (tag != 'n' && tag != 'N') {
EI_TRACE_ERR2("recv_challenge",
"<- RECV_CHALLENGE incorrect tag, "
- "expected 'n' got '%c' (%u)",tag,tag);
+ "expected 'n' or 'N', got '%c' (%u)",tag,tag);
goto error;
}
- *version = get16be(s);
- *flags = get32be(s);
- *challenge = get32be(s);
+ if (tag == 'n') { /* OLD */
+ unsigned int version;
+ if (rlen < 1+2+4+4) {
+ EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'n' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+
+ version = get16be(s);
+ if (version != EI_DIST_5) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE 'n' incorrect version=%d",
+ version);
+ goto error;
+ }
+ *flags = get32be(s);
+ *challenge = get32be(s);
+ nodename_len = (buf + rlen) - s;
+ }
+ else { /* NEW */
+ if (rlen < 1+8+4+4+2) {
+ EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'N' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ *version = EI_DIST_6;
+ *flags = get64be(s);
+ *challenge = get32be(s);
+ s += 4; /* ignore peer 'creation' */
+ nodename_len = get16be(s);
+ if (nodename_len > (buf + rlen) - s) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE 'N' nodename too long (%d)",
+ nodename_len);
+ goto error;
+ }
+ }
+
+ if (nodename_len > MAXNODELEN) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE nodename too long (%d)", nodename_len);
+ goto error;
+ }
if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
@@ -1981,8 +2149,8 @@ static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
if (!namebuf)
namebuf = &tmp_nodename[0];
- memcpy(namebuf, s, rlen - 11);
- namebuf[rlen - 11] = '\0';
+ memcpy(namebuf, s, nodename_len);
+ namebuf[nodename_len] = '\0';
if (!is_static)
free(buf);
@@ -2003,6 +2171,63 @@ error:
return -1;
}
+static int send_complement(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned epmd_says_version,
+ DistFlags her_flags,
+ unsigned ms)
+{
+ if (epmd_says_version == EI_DIST_5 && (her_flags & DFLAG_HANDSHAKE_23)) {
+ char *buf;
+ unsigned char *s;
+ char dbuf[DEFBUF_SIZ];
+ int err;
+ ssize_t len;
+ unsigned int flagsHigh;
+ const int siz = pkt_sz + 1 + 4 + 4;
+
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = (unsigned char *)buf;
+ switch (pkt_sz) {
+ case 2:
+ put16be(s,siz - 2);
+ break;
+ case 4:
+ put32be(s,siz - 4);
+ break;
+ default:
+ return -1;
+ }
+ flagsHigh = preferred_flags() >> 32;
+
+ put8(s, 'c');
+ put32be(s, flagsHigh);
+ put32be(s, ec->creation);
+
+ len = (ssize_t) siz;
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
+ if (!err && len != (ssize_t) siz)
+ err = EIO;
+ if (err) {
+ EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
+ if (buf != dbuf)
+ free(buf);
+ EI_CONN_SAVE_ERRNO__(err);
+ return -1;
+ }
+
+ if (buf != dbuf)
+ free(buf);
+ }
+ return 0;
+}
+
+
static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned char digest[16],
unsigned challenge, unsigned ms)
@@ -2049,6 +2274,54 @@ static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
return 0;
}
+static int recv_complement(ei_socket_callbacks *cbs,
+ void *ctx,
+ int pkt_sz,
+ unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ char tag;
+ unsigned int creation;
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 21) {
+ EI_TRACE_ERR1("recv_complement",
+ "<- RECV_COMPLEMENT socket read failed (%d)",rlen);
+ goto error;
+ }
+
+ s = buf;
+ if ((tag = get8(s)) != 'c') {
+ EI_TRACE_ERR2("recv_complement",
+ "<- RECV_COMPLEMENT incorrect tag, "
+ "expected 'c' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ creation = get32be(s);
+ if (!is_static)
+ free(buf);
+
+ if (ei_tracelevel >= 3) {
+ EI_TRACE_CONN1("recv_complement",
+ "<- RECV_COMPLEMENT (ok) creation = %u",
+ creation);
+ }
+ /* We don't have any use for 'creation' of other node, so we drop it */
+ erl_errno = 0;
+ return 0;
+
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
static int recv_challenge_reply(ei_socket_callbacks *cbs,
void *ctx,
int pkt_sz,
@@ -2204,30 +2477,16 @@ error:
return -1;
}
-static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned version, unsigned ms)
-{
- return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 0,
- 0, version, ms);
-}
-
-static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned challenge, unsigned version,
- unsigned ms)
-{
- return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 1,
- challenge, version, ms);
-}
-
static int recv_name(ei_socket_callbacks *cbs, void *ctx,
- int pkt_sz, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms)
+ int pkt_sz, char *send_name_tag,
+ DistFlags *flags, char *namebuf, unsigned ms)
{
char dbuf[DEFBUF_SIZ];
char *buf = dbuf;
int is_static = 1;
int buflen = DEFBUF_SIZ;
int rlen;
+ unsigned int namelen;
char *s;
char tmp_nodename[MAXNODELEN+1];
char tag;
@@ -2239,19 +2498,40 @@ static int recv_name(ei_socket_callbacks *cbs, void *ctx,
EI_TRACE_ERR1("recv_name","<- RECV_NAME socket read failed (%d)",rlen);
goto error;
}
- if ((rlen - 7) > MAXNODELEN) {
- EI_TRACE_ERR1("recv_name","<- RECV_NAME nodename too long (%d)",rlen-7);
- goto error;
- }
s = buf;
tag = get8(s);
- if (tag != 'n') {
+ *send_name_tag = tag;
+ if (tag != 'n' && tag != 'N') {
EI_TRACE_ERR2("recv_name","<- RECV_NAME incorrect tag, "
- "expected 'n' got '%c' (%u)",tag,tag);
+ "expected 'n' or 'N', got '%c' (%u)",tag,tag);
goto error;
}
- *version = get16be(s);
- *flags = get32be(s);
+ if (tag == 'n') {
+ unsigned int version;
+ if (rlen < 1+2+4) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ version = get16be(s);
+ if (version < EI_DIST_5) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' invalid version=%d",
+ version)
+ goto error;
+ }
+ *flags = get32be(s);
+ namelen = rlen - (1+2+4);
+ }
+ else { /* tag == 'N' */
+ if (rlen < 1+8+4+2) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'N' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ *flags = get64be(s);
+ s += 4; /* ignore peer 'creation' */
+ namelen = get16be(s);
+ }
if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot handle"
@@ -2269,14 +2549,20 @@ static int recv_name(ei_socket_callbacks *cbs, void *ctx,
if (!namebuf)
namebuf = &tmp_nodename[0];
- memcpy(namebuf, s, rlen - 7);
- namebuf[rlen - 7] = '\0';
+ if (namelen > MAXNODELEN || s+namelen > buf+rlen) {
+ EI_TRACE_ERR2("recv_name","<- RECV_NAME '%c' nodename too long (%d)",
+ tag, namelen);
+ goto error;
+ }
+
+ memcpy(namebuf, s, namelen);
+ namebuf[namelen] = '\0';
if (!is_static)
free(buf);
EI_TRACE_CONN3("recv_name",
- "<- RECV_NAME (ok) node = %s, version = %u, flags = %u",
- namebuf,*version,*flags);
+ "<- RECV_NAME (ok) node = %s, tag = %c, flags = %u",
+ namebuf,tag,*flags);
erl_errno = 0;
return 0;
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index b41a5f2b23..05cec3c824 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -38,30 +38,7 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <selectLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-#include <ioLib.h>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/times.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <timers.h>
-
-#define getpid() taskIdSelf()
-extern int h_errno;
-
-#else /* some other unix */
+#else /* some unix */
#include <unistd.h>
#include <sys/types.h>
#include <sys/times.h>
@@ -109,6 +86,8 @@ extern int h_errno;
#define DFLAG_UTF8_ATOMS 0x10000
#define DFLAG_MAP_TAG 0x20000
#define DFLAG_BIG_CREATION 0x40000
+#define DFLAG_HANDSHAKE_23 0x1000000
+#define DFLAG_HANDSHAKE_XX 0xfe000000 /* bits reserved for handshake changes */
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 225fddc784..f6182ccaf0 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -21,19 +21,7 @@
* Interface functions to different versions of gethostbyname
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <stdio.h>
-#include <semLib.h>
-#include <hostLib.h>
-#include <resolvLib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <symLib.h>
-#include <sysSymTbl.h>
-
-#elif __WIN32__
+#ifdef __WIN32__
#include <winsock2.h>
#include <windows.h>
#include <winbase.h>
@@ -55,6 +43,16 @@
#include "ei_resolve.h"
#include "ei_locking.h"
+/* AIX has a totally different signature (allegedly shared with some other
+ * Unices) that isn't compatible. It turns out that the _r version isn't
+ * thread-safe according to curl - but bizarrely, since AIX 4.3, libc
+ * is thread-safe in a manner that makes the normal gethostbyname OK
+ * for re-entrant use.
+ */
+#ifdef _AIX
+#undef HAVE_GETHOSTBYNAME_R
+#endif
+
#ifdef HAVE_GETHOSTBYNAME_R
int ei_init_resolve(void)
@@ -75,7 +73,7 @@ int ei_init_resolve(void)
static ei_mutex_t *ei_gethost_sem = NULL;
#endif /* _REENTRANT */
static int ei_resolve_initialized = 0;
-#ifndef __WIN32__
+#if !defined(__WIN32__) && !defined(_AIX)
int h_errno;
#endif
@@ -85,18 +83,6 @@ int h_errno;
#define DEBUGF(X) /* Nothing */
#endif
-#ifdef VXWORKS
-/* FIXME problem for threaded ? */
-static struct hostent *(*sens_gethostbyname)(const char *name,
- char *, int) = NULL;
-static struct hostent *(*sens_gethostbyaddr)(const char *addr,
- char *, int) = NULL;
-#endif
-
-#ifdef VXWORKS
-static int verify_dns_configuration(void);
-#endif
-
/*
* If we find SENS resolver, use the functions found there, i.e.
* resolvGetHostByName() and resolvGetHostByAddr(). Otherwise we use
@@ -106,32 +92,6 @@ static int verify_dns_configuration(void);
int ei_init_resolve(void)
{
-#ifdef VXWORKS
- void *sym;
- SYM_TYPE symtype;
-
- if (symFindByName(sysSymTbl,"resolvGetHostByName",
- (char **)&sym,&symtype) == OK &&
- verify_dns_configuration()) {
- sens_gethostbyname = sym;
- DEBUGF((stderr,"found SENS resolver - using it for gethostbyname()\n"));
- if (symFindByName(sysSymTbl,"resolvGetHostByAddr",
- (char **)&sym,&symtype) == OK) {
- sens_gethostbyaddr = sym;
- DEBUGF((stderr,"found SENS resolver - "
- "using it for gethostbyaddr()\n"));
- }
- else {
- DEBUGF((stderr,"SENS resolver not found - "
- "using default gethostbyaddr()\n"));
- }
- }
- else {
- DEBUGF((stderr,"SENS resolver not found - "
- "using default gethostbyname()\n"));
- }
-#endif /* VXWORKS */
-
#ifdef _REENTRANT
ei_gethost_sem = ei_mutex_create();
if (!ei_gethost_sem)
@@ -142,42 +102,7 @@ int ei_init_resolve(void)
return 0;
}
-#ifdef VXWORKS
-/*
-** Function to verify the DNS configuration on VwXorks SENS.
-** Actually configures to a default value if unconfigured...
-*/
-static int verify_dns_configuration(void)
-{
- /* FIXME problem for threaded ? */
- static char resolv_params[sizeof(RESOLV_PARAMS_S)];
- void (*rpg)(char *);
- STATUS (*rps)(char *);
- SYM_TYPE dummy;
- int get_result, set_result;
-
- get_result = symFindByName(sysSymTbl,"resolvParamsGet", (char **) &rpg, &dummy);
- set_result = symFindByName(sysSymTbl,"resolvParamsSet", (char **) &rps, &dummy);
-
- if (!(get_result == OK &&
- set_result == OK))
- return -1;
- (*rpg)(resolv_params);
- if (*resolv_params == '\0') {
- /* It exists, but is not configured, ei_connect would fail
- if we left it this way... The best we can do is to configure
- it to use the local host database on the card, as a fallback */
- *resolv_params = (char) 1;
- fprintf(stderr,"Trying to fix up DNS configuration.\n");
- if (((*rps)(resolv_params)) != OK)
- return -1;
- }
- return 0;
-}
-
-#endif
-
-#if defined(VXWORKS) || _REENTRANT
+#if _REENTRANT
/*
* Copy the contents of one struct hostent to another, i.e. don't just
@@ -365,9 +290,9 @@ static struct hostent *my_gethostbyname_r(const char *name,
return rval;
}
-#endif /* defined(VXWORKS) || _REENTRANT */
+#endif /* _REENTRANT */
-#if defined(VXWORKS) || EI_THREADS != false
+#if EI_THREADS != false
static struct hostent *my_gethostbyaddr_r(const char *addr,
int length,
@@ -433,7 +358,7 @@ static struct hostent *my_gethostbyaddr_r(const char *addr,
return rval;
}
-#endif /* defined(VXWORKS) || EI_THREADS != false */
+#endif /* EI_THREADS != false */
#endif /* !HAVE_GETHOSTBYNAME_R */
@@ -449,154 +374,6 @@ struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
return gethostbyaddr(addr, len, type);
}
-#elif VXWORKS
-
-
-/* these are a couple of substitutes for the real thing when we run on
- * stock vxworks (i.e. no sens).
- *
- * len and type are ignored, but we make up some reasonable values and
- * insert them
- */
-static struct hostent *my_gethostbyname(const char *name)
-{
- /* FIXME problem for threaded ? */
- static struct hostent h;
- static char hostname[EI_MAXHOSTNAMELEN+1];
- static char *aliases[1] = {NULL};
- static char *addrp[2] = {NULL,NULL};
- static unsigned long addr = 0;
-
- strcpy(hostname,name);
- if ((addr = (unsigned long)hostGetByName(hostname)) == ERROR) {
- h_errno = HOST_NOT_FOUND;
- return NULL;
- }
-
- h_errno = 0;
- h.h_name = hostname;
- h.h_aliases = aliases;
- h.h_length = 4;
- h.h_addrtype = AF_INET;
- addrp[0] = (char *)&addr;
- h.h_addr_list = addrp;
-
- return &h;
-}
-
-static struct hostent *my_gethostbyaddr(const char *addr, int len, int type)
-{
- /* FIXME problem for threaded ? */
- static struct hostent h;
- static char hostname[EI_MAXHOSTNAMELEN+1];
- static char *aliases[1] = { NULL };
- static unsigned long inaddr;
- static char *addrp[2] = {(char *)&inaddr, NULL};
-
- memmove(&inaddr,addr,sizeof(inaddr));
-
- if ((hostGetByAddr(inaddr,hostname)) == ERROR) {
- h_errno = HOST_NOT_FOUND;
- return NULL;
- }
-
- h_errno = 0;
- h.h_name = hostname;
- h.h_aliases = aliases;
- h.h_length = 4;
- h.h_addrtype = AF_INET;
- h.h_addr_list = addrp;
-
- return &h;
-}
-
-/* use sens functions for these, if found. */
-struct hostent *ei_gethostbyname(const char *name)
-{
- struct hostent *h = NULL;
-
- if (!sens_gethostbyname) {
- h = my_gethostbyname(name);
- }
- else {
- /* FIXME problem for threaded ? */
- static char buf[1024];
- h = sens_gethostbyname(name,buf,1024);
- }
-
- return h;
-}
-
-struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
-{
- struct hostent *h = NULL;
-
- if (!sens_gethostbyaddr) {
- h = my_gethostbyaddr(addr,len,type);
- }
- else {
- /* FIXME problem for threaded ? */
- static char buf[1024];
- h = sens_gethostbyaddr(addr,buf,1024);
- }
-
- return h;
-}
-
-struct hostent *ei_gethostbyaddr_r(const char *addr,
- int length,
- int type,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop)
-{
- struct hostent *h = NULL;
-
- /* use own func if sens function not available */
- if (!sens_gethostbyaddr) {
- h = my_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
- }
- else {
- if (!(h = sens_gethostbyaddr(addr,buffer,buflen))) {
- /* sens returns status via errno */
- *h_errnop = errno;
- }
- else {
- *hostp = *h;
- *h_errnop = 0;
- }
- }
-
- return h;
-}
-
-struct hostent *ei_gethostbyname_r(const char *name,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop)
-{
- struct hostent *h = NULL;
-
- /* use own func if sens function not available */
- if (!sens_gethostbyname) {
- h = my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
- }
- else {
- if (!(h = sens_gethostbyname(name,buffer,buflen))) {
- /* sens returns status via errno */
- *h_errnop = errno;
- }
- else {
- *hostp = *h;
- *h_errnop = 0;
- }
- }
-
- return h;
-}
-
#else /* unix of some kind */
struct hostent *ei_gethostbyname(const char *name)
@@ -667,5 +444,5 @@ struct hostent *ei_gethostbyname_r(const char *name,
#endif
}
-#endif /* vxworks, win, unix */
+#endif /* win, unix */
diff --git a/lib/erl_interface/src/connect/send.c b/lib/erl_interface/src/connect/send.c
index d97532d123..8535b2a206 100644
--- a/lib/erl_interface/src/connect/send.c
+++ b/lib/erl_interface/src/connect/send.c
@@ -24,13 +24,6 @@
# include <windows.h>
# include <winbase.h>
-#elif VXWORKS
-
-# include <sys/types.h>
-# include <unistd.h>
-# include <sysLib.h>
-# include <tickLib.h>
-
#else /* unix */
# include <sys/types.h>
diff --git a/lib/erl_interface/src/connect/send_reg.c b/lib/erl_interface/src/connect/send_reg.c
index 80d61e57b5..b34432fb6e 100644
--- a/lib/erl_interface/src/connect/send_reg.c
+++ b/lib/erl_interface/src/connect/send_reg.c
@@ -22,10 +22,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <sys/types.h>
-#include <unistd.h>
-
#else /* unix */
#include <sys/types.h>
#include <unistd.h>
diff --git a/lib/erl_interface/src/decode/decode_big.c b/lib/erl_interface/src/decode/decode_big.c
index cbbbd3f0b7..bd2d6662a5 100644
--- a/lib/erl_interface/src/decode/decode_big.c
+++ b/lib/erl_interface/src/decode/decode_big.c
@@ -144,13 +144,6 @@ int ei_big_comp(erlang_big *x, erlang_big *y)
* Handling of floating point exceptions.
*/
-#if defined(VXWORKS) && CPU == PPC860
-#undef NO_FPE_SIGNALS
-#define NO_FPE_SIGNALS 1
-#undef INLINED_FP_CONVERSION
-#define INLINED_FP_CONVERSION 1
-#endif
-
#ifdef NO_FPE_SIGNALS
# define ERTS_FP_CHECK_INIT() do {} while (0)
# define ERTS_FP_ERROR(f, Action) if (!isfinite(f)) { Action; } else {}
diff --git a/lib/erl_interface/src/encode/encode_pid.c b/lib/erl_interface/src/encode/encode_pid.c
index d14746b40f..0dfdb16372 100644
--- a/lib/erl_interface/src/encode/encode_pid.c
+++ b/lib/erl_interface/src/encode/encode_pid.c
@@ -25,7 +25,6 @@
int ei_encode_pid(char *buf, int *index, const erlang_pid *p)
{
char* s = buf + *index;
- const char tag = (p->creation > 3) ? ERL_NEW_PID_EXT : ERL_PID_EXT;
++(*index); /* skip ERL_PID_EXT */
if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node),
@@ -33,21 +32,17 @@ int ei_encode_pid(char *buf, int *index, const erlang_pid *p)
return -1;
if (buf) {
- put8(s, tag);
+ put8(s, ERL_NEW_PID_EXT);
s = buf + *index;
/* now the integers */
put32be(s,p->num & 0x7fff); /* 15 bits */
put32be(s,p->serial & 0x1fff); /* 13 bits */
- if (tag == ERL_PID_EXT) {
- put8(s,(p->creation & 0x03)); /* 2 bits */
- } else {
- put32be(s, p->creation); /* 32 bits */
- }
+ put32be(s, p->creation); /* 32 bits */
}
- *index += 4 + 4 + (tag == ERL_PID_EXT ? 1 : 4);
+ *index += 4 + 4 + 4;
return 0;
}
diff --git a/lib/erl_interface/src/encode/encode_port.c b/lib/erl_interface/src/encode/encode_port.c
index eb464380c0..0fb4018db1 100644
--- a/lib/erl_interface/src/encode/encode_port.c
+++ b/lib/erl_interface/src/encode/encode_port.c
@@ -25,7 +25,6 @@
int ei_encode_port(char *buf, int *index, const erlang_port *p)
{
char *s = buf + *index;
- const char tag = p->creation > 3 ? ERL_NEW_PORT_EXT : ERL_PORT_EXT;
++(*index); /* skip ERL_PORT_EXT */
if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8,
@@ -33,19 +32,15 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p)
return -1;
}
if (buf) {
- put8(s, tag);
+ put8(s, ERL_NEW_PORT_EXT);
s = buf + *index;
/* now the integers */
put32be(s,p->id & 0x0fffffff /* 28 bits */);
- if (tag == ERL_PORT_EXT) {
- put8(s,(p->creation & 0x03));
- } else {
- put32be(s, p->creation);
- }
+ put32be(s, p->creation);
}
- *index += 4 + (tag == ERL_PORT_EXT ? 1 : 4);
+ *index += 4 + 4;
return 0;
}
diff --git a/lib/erl_interface/src/encode/encode_ref.c b/lib/erl_interface/src/encode/encode_ref.c
index 5ccfc32c6d..8c2e0a25f7 100644
--- a/lib/erl_interface/src/encode/encode_ref.c
+++ b/lib/erl_interface/src/encode/encode_ref.c
@@ -24,7 +24,6 @@
int ei_encode_ref(char *buf, int *index, const erlang_ref *p)
{
- const char tag = (p->creation > 3) ? ERL_NEWER_REFERENCE_EXT : ERL_NEW_REFERENCE_EXT;
char *s = buf + *index;
int i;
@@ -37,7 +36,7 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p)
/* Always encode as an extended reference; all participating parties
are now expected to be able to decode extended references. */
if (buf) {
- put8(s, tag);
+ put8(s, ERL_NEWER_REFERENCE_EXT);
/* first, number of integers */
put16be(s, p->len);
@@ -46,15 +45,12 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p)
s = buf + *index;
/* now the integers */
- if (tag == ERL_NEW_REFERENCE_EXT)
- put8(s,(p->creation & 0x03));
- else
- put32be(s, p->creation);
+ put32be(s, p->creation);
for (i = 0; i < p->len; i++)
put32be(s,p->n[i]);
}
- *index += p->len*4 + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4);
+ *index += p->len*4 + 4;
return 0;
}
diff --git a/lib/erl_interface/src/epmd/ei_epmd.h b/lib/erl_interface/src/epmd/ei_epmd.h
index ac153b6e66..e3cb041dc9 100644
--- a/lib/erl_interface/src/epmd/ei_epmd.h
+++ b/lib/erl_interface/src/epmd/ei_epmd.h
@@ -24,9 +24,12 @@
#define INADDR_LOOPBACK ((u_long) 0x7F000001)
#endif
+#define EI_DIST_5 5 /* OTP R4 - 22 */
+#define EI_DIST_6 6 /* OTP 23 and later */
+
#ifndef EI_DIST_HIGH
-#define EI_DIST_HIGH 5 /* R4 and later */
-#define EI_DIST_LOW 1 /* R3 and earlier */
+#define EI_DIST_HIGH EI_DIST_6
+#define EI_DIST_LOW EI_DIST_5
#endif
#ifndef EPMD_PORT
@@ -45,6 +48,7 @@
#ifndef EI_EPMD_ALIVE2_REQ
#define EI_EPMD_ALIVE2_REQ 120
#define EI_EPMD_ALIVE2_RESP 121
+#define EI_EPMD_ALIVE2_X_RESP 118
#define EI_EPMD_PORT2_REQ 122
#define EI_EPMD_PORT2_RESP 119
#define EI_EPMD_STOP_REQ 's'
diff --git a/lib/erl_interface/src/epmd/epmd_port.c b/lib/erl_interface/src/epmd/epmd_port.c
index 492c3fb3aa..1ea2f7b9df 100644
--- a/lib/erl_interface/src/epmd/epmd_port.c
+++ b/lib/erl_interface/src/epmd/epmd_port.c
@@ -25,16 +25,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <inetLib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
#else
#include <unistd.h>
#include <sys/types.h>
@@ -114,9 +104,6 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive,
int err;
ssize_t dlen;
unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
-#if defined(VXWORKS)
- char ntoabuf[32];
-#endif
if (len > sizeof(buf) - 3)
{
@@ -144,17 +131,8 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive,
return -1;
}
-#ifdef VXWORKS
- /* FIXME use union/macro for level. Correct level? */
- if (ei_tracelevel > 2) {
- inet_ntoa_b(*addr,ntoabuf);
- EI_TRACE_CONN2("ei_epmd_r4_port",
- "-> PORT2_REQ alive=%s ip=%s",alive,ntoabuf);
- }
-#else
EI_TRACE_CONN2("ei_epmd_r4_port",
"-> PORT2_REQ alive=%s ip=%s",alive,inet_ntoa(*addr));
-#endif
dlen = (ssize_t) 2;
err = ei_read_fill_t__(fd, buf, &dlen, tmo);
diff --git a/lib/erl_interface/src/epmd/epmd_publish.c b/lib/erl_interface/src/epmd/epmd_publish.c
index 20b8e867e8..f2c7abbd1a 100644
--- a/lib/erl_interface/src/epmd/epmd_publish.c
+++ b/lib/erl_interface/src/epmd/epmd_publish.c
@@ -25,16 +25,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <inetLib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
#else
#include <unistd.h>
#include <sys/types.h>
@@ -68,7 +58,8 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms)
int nlen = strlen(alive);
int len = elen + nlen + 13; /* hard coded: be careful! */
int n;
- int err, res, creation;
+ int err, response, res;
+ unsigned creation;
ssize_t dlen;
unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
@@ -124,8 +115,10 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms)
/* Don't close fd here! It keeps us registered with epmd */
s = buf;
- if (((res=get8(s)) != EI_EPMD_ALIVE2_RESP)) { /* response */
- EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",res);
+ response = get8(s);
+ if (response != EI_EPMD_ALIVE2_RESP &&
+ response != EI_EPMD_ALIVE2_X_RESP) {
+ EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",response);
EI_TRACE_ERR0("ei_epmd_r4_publish","-> CLOSE");
ei_close__(fd);
erl_errno = EIO;
@@ -141,18 +134,21 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms)
return -1;
}
- creation = get16be(s);
+ if (response == EI_EPMD_ALIVE2_RESP)
+ creation = get16be(s);
+ else /* EI_EPMD_ALIVE2_X_RESP */
+ creation = get32be(s);
EI_TRACE_CONN2("ei_epmd_r4_publish",
- " result=%d (ok) creation=%d",res,creation);
-
- /* probably should save fd so we can close it later... */
- /* epmd_saveconn(OPEN,fd,alive); */
+ " result=%d (ok) creation=%u",res,creation);
- /* return the creation number, for no good reason */
- /* return creation;*/
+ /*
+ * Would be nice to somehow use the nice "unique" creation value
+ * received here from epmd instead of using the crappy one
+ * passed (already) to ei_connect_init.
+ */
- /* no - return the descriptor */
+ /* return the descriptor */
return fd;
}
diff --git a/lib/erl_interface/src/epmd/epmd_unpublish.c b/lib/erl_interface/src/epmd/epmd_unpublish.c
index c112f74147..592cc0371e 100644
--- a/lib/erl_interface/src/epmd/epmd_unpublish.c
+++ b/lib/erl_interface/src/epmd/epmd_unpublish.c
@@ -22,16 +22,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <inetLib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
#else
#include <unistd.h>
#include <sys/types.h>
diff --git a/lib/erl_interface/src/legacy/global_names.c b/lib/erl_interface/src/global/global_names.c
index ee808620fb..bcbe740223 100644
--- a/lib/erl_interface/src/legacy/global_names.c
+++ b/lib/erl_interface/src/global/global_names.c
@@ -24,8 +24,8 @@
#include "eisend.h"
#include "eirecv.h"
#include "ei_connect_int.h"
-#include "erl_interface.h"
-#include "erl_connect.h"
+#include "ei.h"
+#include "ei_connect.h"
#define GLOBALNAMEBUF (16*1024) /* not very small actually */
@@ -36,14 +36,14 @@
* caller can make one call to free().
*/
/* global:registered_names() -> [name1,name2,...] */
-char **erl_global_names(int fd, int *count)
+char **ei_global_names(ei_cnode *ec, int fd, int *count)
{
char buf[GLOBALNAMEBUF];
char *bufp=buf;
char tmpbuf[64];
int size = 0;
int index = 0;
- erlang_pid *self = erl_self();
+ erlang_pid *self = ei_self(ec);
erlang_msg msg;
int i;
int version;
diff --git a/lib/erl_interface/src/legacy/global_register.c b/lib/erl_interface/src/global/global_register.c
index 4cb6d8071f..c260ce091c 100644
--- a/lib/erl_interface/src/legacy/global_register.c
+++ b/lib/erl_interface/src/global/global_register.c
@@ -22,15 +22,14 @@
#include "eiext.h"
#include "eisend.h"
#include "eirecv.h"
-#include "erl_interface.h"
+#include "ei.h"
-int erl_global_register(int fd, const char *name, ETERM *pid)
+int ei_global_register(int fd, const char *name, erlang_pid *self)
{
char buf[EISMALLBUF];
char *bufp=buf;
char tmpbuf[64];
int index = 0;
- erlang_pid self;
erlang_msg msg;
int needlink, needatom, needmonitor;
int arity;
@@ -38,24 +37,19 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
int msglen;
int i;
- /* get that pid into a better format */
- if (!erl_encode(pid,(unsigned char*)buf)) return -1;
- if (ei_decode_version(buf,&index,&version)
- || ei_decode_pid(buf,&index,&self)) return -1;
-
/* set up rpc arguments */
/* { PidFrom, { call, Mod, Fun, Args, user }} */
index = 0;
ei_encode_version(buf,&index);
ei_encode_tuple_header(buf,&index,2);
- ei_encode_pid(buf,&index,&self); /* PidFrom */
+ ei_encode_pid(buf,&index,self); /* PidFrom */
ei_encode_tuple_header(buf,&index,5);
ei_encode_atom(buf,&index,"call"); /* call */
ei_encode_atom(buf,&index,"global"); /* Mod */
ei_encode_atom(buf,&index,"register_name_external"); /* Fun */
ei_encode_list_header(buf,&index,3); /* Args: [ name, self(), cnode ] */
ei_encode_atom(buf,&index,name);
- ei_encode_pid(buf,&index,&self);
+ ei_encode_pid(buf,&index,self);
ei_encode_tuple_header(buf,&index,2);
ei_encode_atom(buf,&index,"global"); /* special "resolve" treatment */
ei_encode_atom(buf,&index,"cnode"); /* i.e. we get a SEND when conflict */
@@ -63,7 +57,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
ei_encode_atom(buf,&index,"user"); /* user */
/* make the rpc call */
- if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1;
+ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1;
/* get the reply: expect link and an atom, or just an atom */
needlink = needatom = needmonitor = 1;
diff --git a/lib/erl_interface/src/legacy/global_unregister.c b/lib/erl_interface/src/global/global_unregister.c
index 27f68670ca..ee785a2abd 100644
--- a/lib/erl_interface/src/legacy/global_unregister.c
+++ b/lib/erl_interface/src/global/global_unregister.c
@@ -23,18 +23,18 @@
#include "eisend.h"
#include "eirecv.h"
#include "ei_connect_int.h"
-#include "erl_interface.h"
-#include "erl_connect.h"
+#include "ei_connect.h"
+#include "ei.h"
/* remove the association between name and its pid */
/* global:unregister_name(name) -> ok */
-int erl_global_unregister(int fd, const char *name)
+int ei_global_unregister(ei_cnode *ec, int fd, const char *name)
{
char buf[EISMALLBUF];
char *bufp=buf;
char tmpbuf[64];
int index = 0;
- erlang_pid *self = erl_self();
+ erlang_pid *self = ei_self(ec);
erlang_msg msg;
int i;
int version,arity,msglen;
diff --git a/lib/erl_interface/src/legacy/global_whereis.c b/lib/erl_interface/src/global/global_whereis.c
index 13c4c93ca7..afedc98030 100644
--- a/lib/erl_interface/src/legacy/global_whereis.c
+++ b/lib/erl_interface/src/global/global_whereis.c
@@ -24,23 +24,22 @@
#include "eisend.h"
#include "eirecv.h"
#include "ei_connect_int.h"
-#include "erl_interface.h"
-#include "erl_connect.h"
+#include "ei.h"
+#include "ei_connect.h"
/* return the ETERM pid corresponding to name. If caller
* provides non-NULL node, nodename will be returned there
*/
/* global:whereis_name(name) -> pid */
-ETERM *erl_global_whereis(int fd, const char *name, char *node)
+int ei_global_whereis(ei_cnode *ec, int fd, const char *name, erlang_pid* pid, char *node)
{
char buf[EISMALLBUF];
char *bufp=buf;
char tmpbuf[64];
int index = 0;
- erlang_pid *self = erl_self();
+ erlang_pid *self = ei_self(ec);
erlang_pid epid;
- ETERM *opid;
erlang_msg msg;
int i;
int version,arity,msglen;
@@ -60,7 +59,7 @@ ETERM *erl_global_whereis(int fd, const char *name, char *node)
ei_encode_atom(buf,&index,"user"); /* user */
/* make the rpc call */
- if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return NULL;
+ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1;
while (1) {
index = EISMALLBUF;
@@ -68,7 +67,7 @@ ETERM *erl_global_whereis(int fd, const char *name, char *node)
else break;
}
- if (i != ERL_SEND) return NULL;
+ if (i != ERL_SEND) return -1;
/* expecting { rex, pid } */
index = 0;
@@ -78,24 +77,18 @@ ETERM *erl_global_whereis(int fd, const char *name, char *node)
|| ei_decode_atom(buf,&index,tmpbuf)
|| strcmp(tmpbuf,"rex")
|| ei_decode_pid(buf,&index,&epid))
- return NULL; /* bad response from other side */
-
- /* put the pid into a format for the caller */
- index = 0;
- ei_encode_pid(buf,&index,&epid);
- opid = erl_decode((unsigned char*)buf);
+ return -1; /* bad response from other side */
/* extract the nodename for the caller */
if (node) {
- char* node_str = ERL_PID_NODE(opid);
+ char* node_str = epid.node;
if (node_str) {
strcpy(node, node_str);
}
else {
- erl_free_term(opid);
- return NULL;
+ return -1;
}
}
-
- return opid;
+ *pid = epid;
+ return 0;
}
diff --git a/lib/erl_interface/src/legacy/decode_term.c b/lib/erl_interface/src/legacy/decode_term.c
deleted file mode 100644
index 72bacc3123..0000000000
--- a/lib/erl_interface/src/legacy/decode_term.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#include "eidef.h"
-#include "eiext.h"
-#include "putget.h"
-#include "erl_interface.h"
-
-/*
- * This file is actually part of the erl_interface library,
- * not the newer 'ei' library. The header file is still in "ei.h"
- */
-
-/* FIXME: is this to be completed? */
-
-#if (0)
-int ei_decode_term(const char *buf, int *index, void *t)
-{
- const char *s = buf + *index;
- const char *s0 = s;
-
- if (t) {
- ETERM *tmp;
-
- /* this decodes and advances s */
- if (!(tmp = erl_decode_buf((unsigned char **)&s))) return -1;
-
- *(ETERM **)t = tmp;
- *index += s - s0;
-
- return 0;
- }
- else {
- int tmpindex = *index;
- long ttype;
- int arity;
- int i;
-
- /* these are all the external types */
- switch ((ttype = get8(s))) {
- case ERL_SMALL_INTEGER_EXT:
- case ERL_INTEGER_EXT:
- case ERL_SMALL_BIG_EXT:
- return ei_decode_long(buf,index,NULL);
-
- case ERL_FLOAT_EXT:
- case NEW_FLOAT_EXT:
- return ei_decode_double(buf,index,NULL);
-
- case ERL_ATOM_EXT:
- return ei_decode_atom(buf,index,NULL);
-
- case ERL_REFERENCE_EXT:
- case ERL_NEW_REFERENCE_EXT:
- return ei_decode_ref(buf,index,NULL);
-
- case ERL_PORT_EXT:
- return ei_decode_port(buf,index,NULL);
-
- case ERL_PID_EXT:
- return ei_decode_pid(buf,index,NULL);
-
- case ERL_SMALL_TUPLE_EXT:
- case ERL_LARGE_TUPLE_EXT:
- if (ei_decode_tuple_header(buf,index,&arity) < 0)
- return -1;
-
- for (i=0; i<arity; i++) {
- if (ei_decode_term(buf,index,NULL)) {
- /* restore possibly changed index before returning */
- *index = tmpindex;
- return -1;
- }
- }
- return 0;
-
- case ERL_STRING_EXT:
- return ei_decode_string(buf,index,NULL);
-
- case ERL_LIST_EXT:
- case ERL_NIL_EXT:
- if (ei_decode_list_header(buf,index,&arity) < 0)
- return -1;
-
- if (arity) {
- for (i=0; i<arity; i++) {
- if (ei_decode_term(buf,index,NULL) < 0) {
- /* restore possibly changed index before returning */
- *index = tmpindex;
- return -1;
- }
- }
- if (ei_decode_list_header(buf,index,&arity) < 0) {
- *index = tmpindex;
- return -1;
- }
- }
- return 0;
-
- case ERL_BINARY_EXT:
- return ei_decode_binary(buf,index,NULL,NULL);
-
- case ERL_LARGE_BIG_EXT:
- default:
- break;
- }
- }
-
- return -1;
-}
-#else
-int ei_decode_term(const char *buf, int *index, void *t)
-{
- const char *s = buf + *index;
- const char *s0 = s;
- ETERM *tmp;
-
- /* this decodes and advances s */
- if (!(tmp = erl_decode_buf((unsigned char **)&s))) return -1;
-
- if (t) *(ETERM **)t = tmp;
- else erl_free_term(tmp);
-
- *index += s - s0;
-
- return 0;
-}
-#endif
diff --git a/lib/erl_interface/src/legacy/encode_term.c b/lib/erl_interface/src/legacy/encode_term.c
deleted file mode 100644
index df740ab487..0000000000
--- a/lib/erl_interface/src/legacy/encode_term.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#include "eidef.h"
-#include "eiext.h"
-#include "putget.h"
-#include "ei_x_encode.h"
-#include "erl_interface.h"
-#include "erl_marshal.h"
-
-/* FIXME: depends on old erl_interface */
-
-int ei_x_encode_term(ei_x_buff* x, void* t)
-{
- int i = x->index;
- ei_encode_term(NULL, &i, t);
- if (!x_fix_buff(x, i))
- return -1;
- return ei_encode_term(x->buff, &x->index, t);
-}
-
-int ei_encode_term(char *buf, int *index, void *t)
-{
- char *s = buf + *index;
- char *s0 = s;
-
- if (!buf) s += erl_term_len(t) -1; /* -1 for version */
- else {
- /* this encodes all but the version at the start */
- /* and it will move s forward the right number of bytes */
- if (erl_encode_it(t,(unsigned char **)&s, 5)) return -1;
- }
-
- *index += s - s0;
-
- return 0;
-}
-
diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c
deleted file mode 100644
index e2fd4611c0..0000000000
--- a/lib/erl_interface/src/legacy/erl_connect.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Connect to any node at any host.
- */
-
-/***************************************************************************
- *
- * 'erl_interface' node connection handling is to use 'ei' for all
- * operations without access to the internal structure of saved data,
- * e.i. it should use the public interface functions. The connection
- * handling can be seen as a restricted node interface where only one
- * node can be used in one operating system process.
- *
- ***************************************************************************/
-
-#include "eidef.h"
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <fcntl.h>
-
-#ifdef __WIN32__
-#include <winsock2.h>
-#include <windows.h>
-#include <winbase.h>
-
-#elif VXWORKS
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <selectLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/times.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <timers.h>
-
-#include "erl_error.h"
-
-#else /* some other unix */
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/times.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/utsname.h> /* for gen_challenge (NEED FIX?) */
-#endif
-
-/* common includes */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-/* FIXME include less */
-#include "erl_interface.h"
-#include "erl_connect.h"
-#include "erl_eterm.h"
-#include "erl_malloc.h"
-#include "putget.h"
-#include "ei.h"
-#include "ei_connect_int.h"
-#include "ei_locking.h"
-#include "ei_epmd.h"
-#include "ei_internal.h"
-
-/* rpc_from() uses a buffer this size */
-#ifndef MAX_RECEIVE_BUF
-#define MAX_RECEIVE_BUF 32*1024
-#endif
-
-/* This is the global state of the old erl_* API */
-
-static ei_cnode erl_if_ec;
-
-/***************************************************************************
- *
- * API: erl_connect_init()
- * API: erl_connect_xinit()
- *
- * Returns 1 on success and 0 on failure.
- * Not documented to set erl_errno.
- *
- ***************************************************************************/
-
-int erl_connect_init(int this_node_number, char *cookie, short creation)
-{
- char nn[MAXATOMLEN];
-
- sprintf(nn, "c%d", this_node_number);
-
- return ei_connect_init(&erl_if_ec, nn, cookie, creation) == 0;
-}
-
-/* FIXME documented to use struct in_addr as addr */
-
-int erl_connect_xinit(char *thishostname,
- char *thisalivename,
- char *thisnodename,
- struct in_addr *thisipaddr,
- char *cookie,
- short creation)
-{
- return ei_connect_xinit(&erl_if_ec, thishostname, thisalivename,
- thisnodename, thisipaddr, cookie, creation) >= 0;
-}
-
-/***************************************************************************
- *
- * API: erl_connect()
- * API: erl_xconnect()
- *
- * Set up a connection to a given Node, and interchange hand shake
- * messages with it.
- *
- * Returns valid file descriptor on success and < 0 on failure.
- * Set erl_errno to EHOSTUNREACH, ENOMEM, EIO or errno from socket(2)
- * or connect(2).
- *
- ***************************************************************************/
-
-int erl_connect(char *nodename)
-{
- int res = ei_connect(&erl_if_ec, nodename);
- if (res < 0) erl_errno = EIO;
- return res;
-}
-
-/* FIXME documented to use struct in_addr as addr */
-
-int erl_xconnect(Erl_IpAddr addr, char *alivename)
-{
- return ei_xconnect(&erl_if_ec, addr, alivename);
-}
-
-
-/***************************************************************************
- *
- * API: erl_close_connection()
- *
- * Returns 0 on success and -1 on failure.
- *
- ***************************************************************************/
-
-int erl_close_connection(int fd)
-{
- return ei_close_connection(fd);
-}
-
-/*
- * Accept and initiate a connection from another
- * Erlang node. Return a file descriptor at success,
- * otherwise -1;
- */
-int erl_accept(int lfd, ErlConnect *conp)
-{
- return ei_accept(&erl_if_ec, lfd, conp);
-}
-
-
-/* Receives a message from an Erlang socket.
- * If the message was a TICK it is immediately
- * answered. Returns: ERL_ERROR, ERL_TICK or
- * the number of bytes read.
- */
-int erl_receive(int s, unsigned char *bufp, int bufsize)
-{
- return ei_receive(s, bufp, bufsize);
-}
-
-/*
- * Send an Erlang message to a registered process
- * at the Erlang node, connected with a socket.
- */
-int erl_reg_send(int fd, char *server_name, ETERM *msg)
-{
- ei_x_buff x;
- int r;
-
- if (ei_x_new_with_version(&x) < 0) {
- erl_errno = ENOMEM;
- return 0;
- }
- if (ei_x_encode_term(&x, msg) < 0) {
- erl_errno = EINVAL;
- r = 0;
- } else {
- r = ei_reg_send(&erl_if_ec, fd, server_name, x.buff, x.index);
- }
- ei_x_free(&x);
- return r == 0;
-}
-
-/*
- * Sends an Erlang message to a process at an Erlang node
- */
-int erl_send(int fd, ETERM *to ,ETERM *msg)
-{
- erlang_pid topid;
- ei_x_buff x;
- int r;
-
- ei_x_new_with_version(&x);
- ei_x_encode_term(&x, msg);
- /* make the to-pid */
- if (!ERL_IS_PID(to)) {
- ei_x_free(&x);
- erl_errno = EINVAL;
- return -1;
- }
-
- if (to->uval.pidval.node.latin1) {
- strcpy(topid.node, to->uval.pidval.node.latin1);
- }
- else {
- strcpy(topid.node, to->uval.pidval.node.utf8);
- }
- topid.num = ERL_PID_NUMBER(to);
- topid.serial = ERL_PID_SERIAL(to);
- topid.creation = ERL_PID_CREATION(to);
- r = ei_send(fd, &topid, x.buff, x.index);
- ei_x_free(&x);
- return r == 0;
-}
-
-static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg)
-{
- erlang_msg msg;
-
- int r;
- msg.from.node[0] = msg.to.node[0] = msg.toname[0] = '\0';
- r = ei_do_receive_msg(fd, 0, &msg, x, 0);
-
- if (r == ERL_MSG) {
- int index = 0;
- emsg->type = msg.msgtype;
-
- /*
- We can't call ei_decode_term for cases where there are no
- data following the type information. If there are other
- types added later where there are data this case has to be
- extended.
- */
-
- switch (msg.msgtype) {
- case ERL_SEND:
- case ERL_REG_SEND:
- case ERL_EXIT:
- case ERL_EXIT2:
- if (ei_decode_term(x->buff, &index, &emsg->msg) < 0)
- r = ERL_ERROR;
- break;
- default:
- emsg->msg = NULL; /* Not needed but may avoid problems for unsafe caller */
- break;
- }
- } else
- emsg->msg = NULL;
- if (msg.from.node[0] != '\0')
- emsg->from = erl_mk_pid(msg.from.node, msg.from.num, msg.from.serial, msg.from.creation);
- else
- emsg->from = NULL;
- if (msg.to.node[0] != '\0')
- emsg->to = erl_mk_pid(msg.to.node, msg.to.num, msg.to.serial, msg.to.creation);
- else
- emsg->to = NULL;
- strcpy(emsg->to_name, msg.toname);
- return r;
-}
-
-int erl_receive_msg(int fd, unsigned char *buf, int bufsize, ErlMessage *emsg)
-{
- ei_x_buff x;
- int r;
-
- ei_x_new(&x);
- r = erl_do_receive_msg(fd, &x, emsg);
- /* FIXME what is this about? */
- if (bufsize > x.index)
- bufsize = x.index;
- memcpy(buf, x.buff, bufsize);
- ei_x_free(&x);
- return r;
-}
-
-int erl_xreceive_msg(int fd, unsigned char **buf, int *bufsize,
- ErlMessage *emsg)
-{
- ei_x_buff x;
- int r;
-
- ei_x_new(&x);
- r = erl_do_receive_msg(fd, &x, emsg);
- if (*bufsize < x.index)
- *buf = erl_realloc(*buf, x.index);
- *bufsize = x.index;
- memcpy(*buf, x.buff, *bufsize);
- ei_x_free(&x);
- return r;
-}
-
-/*
- * The RPC consists of two parts, send and receive.
- * Here is the send part !
- * { PidFrom, { call, Mod, Fun, Args, user }}
- */
-/*
- * Now returns non-negative number for success, negative for failure.
- */
-int erl_rpc_to(int fd, char *mod, char *fun, ETERM *args)
-{
- int r;
- ei_x_buff x;
-
- ei_x_new(&x);
- ei_x_encode_term(&x, args);
- r = ei_rpc_to(&erl_if_ec, fd, mod, fun, x.buff, x.index);
- ei_x_free(&x);
- return r;
-} /* rpc_to */
-
- /*
- * And here is the rpc receiving part. A negative
- * timeout means 'infinity'. Returns either of: ERL_MSG,
- * ERL_TICK, ERL_ERROR or ERL_TIMEOUT.
-*/
-int erl_rpc_from(int fd, int timeout, ErlMessage *emsg)
-{
- fd_set readmask;
- struct timeval tv;
- struct timeval *t = NULL;
- unsigned char rbuf[MAX_RECEIVE_BUF];
-
- if (timeout >= 0) {
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
- t = &tv;
- }
-
- FD_ZERO(&readmask);
- FD_SET(fd,&readmask);
-
- switch (select(fd+1, &readmask, NULL, NULL, t)) {
- case -1:
- erl_errno = EIO;
- return ERL_ERROR;
- case 0:
- erl_errno = ETIMEDOUT;
- return ERL_TIMEOUT;
- default:
- if (FD_ISSET(fd, &readmask))
- return erl_receive_msg(fd, rbuf, MAX_RECEIVE_BUF, emsg);
- else {
- erl_errno = EIO;
- return ERL_ERROR;
- }
- }
-} /* rpc_from */
-
-/*
- * A true RPC. It return a NULL pointer
- * in case of failure, otherwise a valid
- * (ETERM *) pointer containing the reply
- */
-ETERM *erl_rpc(int fd, char *mod, char *fun, ETERM *args)
-{
- int i;
- ETERM *ep;
- ErlMessage emsg;
-
- if (erl_rpc_to(fd, mod, fun, args) < 0) {
- return NULL; }
- while ((i=erl_rpc_from(fd, ERL_NO_TIMEOUT, &emsg)) == ERL_TICK);
-
- if (i == ERL_ERROR) return NULL;
-
- ep = erl_element(2,emsg.msg); /* {RPC_Tag, RPC_Reply} */
- erl_free_term(emsg.msg);
- erl_free_term(emsg.to);
- return ep;
-} /* rpc */
-
-
-/*
- ** Handshake
- */
-
-int erl_publish(int port)
-{
- return ei_publish(&erl_if_ec, port);
-}
-
-int erl_unpublish(const char *alive)
-{
- return ei_unpublish_tmo(alive,0);
-}
-
-erlang_pid *erl_self(void)
-{
- return ei_self(&erl_if_ec);
-}
-
-const char *erl_thisnodename(void)
-{
- return ei_thisnodename(&erl_if_ec);
-}
-
-const char *erl_thishostname(void)
-{
- return ei_thishostname(&erl_if_ec);
-}
-
-const char *erl_thisalivename(void)
-{
- return ei_thisalivename(&erl_if_ec);
-}
-
-const char *erl_thiscookie(void)
-{
- return ei_thiscookie(&erl_if_ec);
-}
-
-short erl_thiscreation(void)
-{
- return ei_thiscreation(&erl_if_ec);
-}
diff --git a/lib/erl_interface/src/legacy/erl_error.c b/lib/erl_interface/src/legacy/erl_error.c
deleted file mode 100644
index a3bbfbc58f..0000000000
--- a/lib/erl_interface/src/legacy/erl_error.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Function: Some nice error routines taken from:
- * "Advanced Programming in the UNIX Environment",
- * by W.Richard Stevens
- *
- * void erl_err_sys(const char *fmt, ... ) fatal, sys-error
- * void erl_err_ret(const char *fmt, ... ) non-fatal, sys-error
- * void erl_err_quit(const char *fmt, ...) fatal, non-sys-error
- * void erl_err_msg(const char *fmt, ... ) non-fatal, non-sys-error
- */
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef VRTX /* What's VRIX? [sverkerw] */
-#define __READY_EXTENSIONS__
-#endif
-#include <errno.h>
-
-#if defined(VXWORKS)
-#include <taskLib.h>
-#include <taskVarLib.h>
-#endif
-
-#include "eidef.h"
-#include "erl_interface.h"
-#include "erl_error.h"
-
-/* Forward */
-static void err_doit(int, const char*, va_list);
-/* __attribute__ ((format (printf, 2, 0)))*/
-
-/*
- * Some thoughts on flushing stdout/stderr:
- *
- * The defaults are reasonable (linebuffered stdout, unbuffered
- * stderr). If they are in effect (the user neither knows nor cares),
- * there's no need to flush.
- *
- * If the user changes these defaults (and knows what he's doing, so
- * he knows and cares) we shouldn't surprise him by
- * second-guessing. So there's a need to not flush.
- *
- * If the user doesn't know what he's doing, he's hosed anyway.
- */
-
-/* Fatal error related to a system call.
- * Print a message and terminate.
- */
-void erl_err_sys(const char *fmt, ... )
-{
- va_list ap;
-
- va_start(ap, fmt);
- err_doit(1, fmt, ap);
- va_end(ap);
- exit(1);
-} /* erl_err_sys */
-
-/* Nonfatal error related to a system call.
- * Print a message and return
- */
-void erl_err_ret(const char *fmt, ... )
-{
- va_list ap;
-
- va_start(ap, fmt);
- err_doit(1, fmt, ap);
- va_end(ap);
- return;
-} /* erl_err_ret */
-
-/* Nonfatal error unrelated to a system call.
- * Print a message and return
- */
-void erl_err_msg(const char *fmt, ... )
-{
- va_list ap;
-
- va_start(ap, fmt);
- err_doit(0, fmt, ap);
- va_end(ap);
- return;
-} /* erl_err_msg */
-
-/* Fatal error unrelated to a system call.
- * Print a message and terminate
- */
-void erl_err_quit(const char *fmt, ... )
-{
- va_list ap;
-
- va_start(ap, fmt);
- err_doit(0, fmt, ap);
- va_end(ap);
- exit(1);
-} /* erl_err_quit */
-
-
-
-/*
- * For example on SunOS we don't have the ANSI C strerror.
- *
- * maybe move to a convenince lib [sverkerw]
- */
-#ifndef HAVE_STRERROR
-
-/* FIXME: move to configure */
-/* CONFIG: probe for sys_nerr/_sys_nerr */
-extern int sys_nerr;
-
-/* CONFIG: probe for sys_errlist/_sys_errlist and maybe for const-ness */
-#ifdef FREEBSD
-extern const char * const sys_errlist[];
-#else
-extern char * sys_errlist[];
-#endif
-
-/* Should be in string.h */
-/* Is supposed to return 'char *' (no const-ness in ANSI's prototype),
- but if you rewrite the returned string in place you deserve to
- lose. */
-static const char *strerror(int errnum)
-{
- if (errnum >= 0 && errnum < sys_nerr) {
- return sys_errlist[errnum];
- } else {
- /* Enough buffer for 64 bits of error. It should last a while. */
- /* FIXME problem for threaded ? */
- static char b[] = "(error -9223372036854775808)";
- sprintf(b, "(error %d)", errnum);
- buf[sizeof(b)-1] = '\0';
- return b;
- }
-}
-#endif /* !HAVE_STRERROR */
-
-
-/* Print a message and return to caller.
- * Caller specifies "errnoflag".
- */
-static void err_doit(int errnoflag, const char *fmt, va_list ap)
-{
-#ifndef NO_ERR_MSG
- int errno_save;
-
- errno_save = errno;
-
- vfprintf(stderr, fmt, ap);
- if (errnoflag)
- {
- fputs(": ", stderr);
- fputs(strerror(errno_save), stderr);
- }
- fputs("\n", stderr);
-#endif
-
- return;
-} /* err_doit */
-
diff --git a/lib/erl_interface/src/legacy/erl_error.h b/lib/erl_interface/src/legacy/erl_error.h
deleted file mode 100644
index 0cce083ae7..0000000000
--- a/lib/erl_interface/src/legacy/erl_error.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_ERROR_H
-#define _ERL_ERROR_H
-
-/* Initialize thread/task-safe erl_errno handling */
-void erl_init_errno(void);
-
-#endif /* _ERL_ERROR_H */
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
deleted file mode 100644
index 7ecea83b1a..0000000000
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ /dev/null
@@ -1,1413 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Representation of Erlang terms.
- */
-
-#include "eidef.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#if defined(HAVE_ISFINITE)
-#include <math.h>
-#endif
-
-#include "ei_locking.h"
-#include "ei_resolve.h"
-#include "erl_interface.h"
-#include "erl_eterm.h"
-#include "erl_malloc.h"
-#include "erl_marshal.h"
-#include "erl_error.h"
-#include "erl_internal.h"
-#include "ei_internal.h"
-#include "putget.h"
-
-#define ERL_IS_BYTE(x) (ERL_IS_INTEGER(x) && (ERL_INT_VALUE(x) & ~0xFF) == 0)
-
-/* FIXME use unsigned char, or uint8 for buffers, cast (int) really needed? */
-
-static void iolist_to_buf(const ETERM* term, char** bufp);
-static char* strsave(const char *src);
-
-/***************************************************************************
- *
- * API: erl_init()
- *
- * Not documented to set erl_errno.
- *
- ***************************************************************************/
-
-/* all initialisation of erl_interface modules should be called from here */
-/* order is important: erl_malloc and erl_resolve depend on ei_locking */
-/* NOTE: don't call this directly - please use erl_init() macro defined
- in ei_locking.h! */
-void erl_init(void *hp,long heap_size)
-{
- erl_init_malloc(hp, heap_size);
- erl_init_marshal();
- (void) ei_init();
-}
-
-void erl_set_compat_rel(unsigned rel)
-{
- ei_set_compat_rel(rel);
-}
-
-/*
- * Create an INTEGER. Depending on its value it
- * may end up as a BigNum.
- */
-ETERM *erl_mk_int (int i)
-{
- ETERM *ep;
-
- ep = erl_alloc_eterm(ERL_INTEGER);
- ERL_COUNT(ep) = 1;
- ERL_INT_VALUE(ep) = i;
- return ep;
-}
-
-ETERM *erl_mk_longlong (long long i)
-{
- ETERM *ep;
-
- ep = erl_alloc_eterm(ERL_LONGLONG);
- ERL_COUNT(ep) = 1;
- ERL_LL_VALUE(ep) = i;
- return ep;
-}
-
-/*
- * Create an UNSIGNED INTEGER. Depending on its
- * value it may end up as a BigNum.
- */
-
-ETERM *erl_mk_uint (unsigned int u)
-{
- ETERM *ep;
-
- ep = erl_alloc_eterm(ERL_U_INTEGER);
- ERL_COUNT(ep) = 1;
- ERL_INT_UVALUE(ep) = u;
- return ep;
-}
-
-ETERM *erl_mk_ulonglong (unsigned long long i)
-{
- ETERM *ep;
-
- ep = erl_alloc_eterm(ERL_U_LONGLONG);
- ERL_COUNT(ep) = 1;
- ERL_LL_UVALUE(ep) = i;
- return ep;
-}
-
-/*
- * Create a FLOAT.
- */
-ETERM *erl_mk_float (double d)
-{
- ETERM *ep;
-
-#if defined(HAVE_ISFINITE)
- /* Erlang does not handle Inf and NaN, so we return an error
- * rather than letting the Erlang VM complain about a bad external
- * term. */
- if(!isfinite(d)) {
- return NULL;
- }
-#endif
-
- ep = erl_alloc_eterm(ERL_FLOAT);
- ERL_COUNT(ep) = 1;
- ERL_FLOAT_VALUE(ep) = d;
- return ep;
-}
-
-/*
- * Create an ATOM
- */
-ETERM *erl_mk_atom (const char *s)
-{
- ETERM *ep;
-
- /* ASSERT(s != NULL); */
- if (!s) return NULL;
-
- ep = erl_alloc_eterm(ERL_ATOM);
- ERL_COUNT(ep) = 1;
- if (erl_atom_init_latin1(&ep->uval.aval.d, s) == NULL) {
- erl_free_term(ep);
- erl_errno = ENOMEM;
- return NULL;
- }
- return ep;
-}
-
-char* erl_atom_ptr_latin1(Erl_Atom_data* a)
-{
- if (a->latin1 == NULL) {
- erlang_char_encoding enc;
- a->lenL = utf8_to_latin1(NULL, a->utf8, a->lenU, a->lenU, &enc);
- if (a->lenL < 0) {
- a->lenL = 0;
- return NULL;
- }
- if (enc == ERLANG_ASCII) {
- a->latin1 = a->utf8;
- }
- else {
- a->latin1 = malloc(a->lenL+1);
- utf8_to_latin1(a->latin1, a->utf8, a->lenU, a->lenL, NULL);
- a->latin1[a->lenL] = '\0';
- }
- }
- return a->latin1;
-}
-
-char* erl_atom_ptr_utf8(Erl_Atom_data* a)
-{
- if (a->utf8 == NULL) {
- erlang_char_encoding enc;
- a->lenU = latin1_to_utf8(NULL, a->latin1, a->lenL, a->lenL*2, &enc);
- if (enc == ERLANG_ASCII) {
- a->utf8 = a->latin1;
- }
- else {
- a->utf8 = malloc(a->lenU + 1);
- latin1_to_utf8(a->utf8, a->latin1, a->lenL, a->lenU, NULL);
- a->utf8[a->lenU] = '\0';
- }
- }
- return a->utf8;
-}
-
-int erl_atom_size_latin1(Erl_Atom_data* a)
-{
- if (a->latin1 == NULL) {
- erl_atom_ptr_latin1(a);
- }
- return a->lenL;
-}
-int erl_atom_size_utf8(Erl_Atom_data* a)
-{
- if (a->utf8 == NULL) {
- erl_atom_ptr_utf8(a);
- }
- return a->lenU;
-}
-char* erl_atom_init_latin1(Erl_Atom_data* a, const char* s)
-{
- a->lenL = strlen(s);
- if ((a->latin1 = strsave(s)) == NULL)
- {
- return NULL;
- }
- a->utf8 = NULL;
- a->lenU = 0;
- return a->latin1;
-}
-
-
-/*
- * Given a string as input, creates a list.
- */
-ETERM *erl_mk_string(const char *s)
-{
- /* ASSERT(s != NULL); */
- if (!s) return NULL;
-
- return erl_mk_estring(s, strlen(s));
-}
-
-ETERM *erl_mk_estring(const char *s, int len)
-{
- ETERM *ep;
- int i;
-
- if ((!s) || (len < 0)) return NULL;
-
- /*
- * ASSERT(s != NULL);
- * ASSERT(len >= 0);
- */
-
- ep = erl_mk_empty_list();
- for (i = len-1; i >= 0; i--) {
- ETERM* integer;
- ETERM* cons;
-
- integer = erl_alloc_eterm(ERL_INTEGER);
- ERL_COUNT(integer) = 1;
- ERL_INT_VALUE(integer) = (unsigned char)s[i];
-
- cons = erl_alloc_eterm(ERL_LIST);
- ERL_COUNT(cons) = 1;
- HEAD(cons) = integer;
- TAIL(cons) = ep;
- ep = cons;
- }
- return ep;
-}
-
-/*
- * Create a PID.
- */
-ETERM *erl_mk_pid(const char *node,
- unsigned int number,
- unsigned int serial,
- unsigned char creation)
-{
- ETERM *ep;
-
- if (!node) return NULL;
- /* ASSERT(node != NULL); */
-
- ep = erl_alloc_eterm(ERL_PID);
- ERL_COUNT(ep) = 1;
- if (erl_atom_init_latin1(&ep->uval.pidval.node, node) == NULL)
- {
- erl_free_term(ep);
- erl_errno = ENOMEM;
- return NULL;
- }
- erl_mk_pid_helper(ep, number, serial, creation & 0x03);
- return ep;
-}
-
-void erl_mk_pid_helper(ETERM *ep, unsigned int number,
- unsigned int serial, unsigned int creation)
-{
- ERL_PID_NUMBER(ep) = number & 0x7fff; /* 15 bits */
- ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */
- ERL_PID_CREATION(ep) = creation; /* 32 bits */
-}
-
-/*
- * Create a PORT.
- */
-ETERM *erl_mk_port(const char *node,
- unsigned int number,
- unsigned char creation)
-{
- ETERM *ep;
-
- if (!node) return NULL;
- /* ASSERT(node != NULL); */
-
- ep = erl_alloc_eterm(ERL_PORT);
- ERL_COUNT(ep) = 1;
- if (erl_atom_init_latin1(&ep->uval.portval.node, node) == NULL)
- {
- erl_free_term(ep);
- erl_errno = ENOMEM;
- return NULL;
- }
- erl_mk_port_helper(ep, number, creation);
- return ep;
-}
-
-void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned int creation)
-{
- ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */
- ERL_PORT_CREATION(ep) = creation; /* 32 bits */
-}
-
-/*
- * Create any kind of reference.
- */
-ETERM *__erl_mk_reference (ETERM* t,
- const char *node,
- size_t len,
- unsigned int n[],
- unsigned int creation)
-{
- if (t == NULL) {
- if (node == NULL) return NULL;
-
- t = erl_alloc_eterm(ERL_REF);
- ERL_COUNT(t) = 1;
-
- if (erl_atom_init_latin1(&t->uval.refval.node, node) == NULL)
- {
- erl_free_term(t);
- erl_errno = ENOMEM;
- return NULL;
- }
- }
- ERL_REF_LEN(t) = len;
- ERL_REF_NUMBERS(t)[0] = n[0] & 0x3ffff; /* 18 bits */
- ERL_REF_NUMBERS(t)[1] = n[1];
- ERL_REF_NUMBERS(t)[2] = n[2];
- ERL_REF_CREATION(t) = creation; /* 32 bits */
-
- return t;
-}
-
-/*
- * Create a REFERENCE.
- */
-ETERM *erl_mk_ref (const char *node,
- unsigned int number,
- unsigned char creation)
-{
- unsigned int n[3] = {0, 0, 0};
- n[0] = number;
- return __erl_mk_reference(NULL, node, 1, n, creation);
-}
-
-/*
- * Create a long REFERENCE.
- */
-ETERM *
-erl_mk_long_ref (const char *node,
- unsigned int n1, unsigned int n2, unsigned int n3,
- unsigned char creation)
-{
- unsigned int n[3] = {0, 0, 0};
- n[0] = n3; n[1] = n2; n[2] = n1;
- return __erl_mk_reference(NULL, node, 3, n, creation);
-}
-
-/*
- * Create a BINARY.
- */
-ETERM *erl_mk_binary (const char *b, int size)
-{
- ETERM *ep;
-
- if ((!b) || (size < 0)) return NULL;
- /* ASSERT(b != NULL); */
-
- ep = erl_alloc_eterm(ERL_BINARY);
- ERL_COUNT(ep) = 1;
- ERL_BIN_SIZE(ep) = size;
- ERL_BIN_PTR(ep) = (unsigned char *) erl_malloc(size);
- memcpy(ERL_BIN_PTR(ep), b, size);
- return ep;
-}
-
-/*
- * Create a TUPLE. For each element in the tuple
- * bump its reference counter.
- */
-ETERM *erl_mk_tuple (ETERM **arr,int size)
-{
- ETERM *ep;
- int i;
-
- if ((!arr) || (size < 0)) return NULL;
- for (i=0; i<size; i++) if (!arr[i]) return NULL;
- /* ASSERT(arr != NULL); */
-
- ep = erl_alloc_eterm(ERL_TUPLE);
- ERL_COUNT(ep) = 1;
- ERL_TUPLE_SIZE(ep) = size;
- ERL_TUPLE_ELEMS(ep) = (ETERM**) erl_malloc((size) * (sizeof(ETERM*)));
- for (i = 0; i < size; i++) {
- /* ASSERT(arr[i] != NULL); */
- ERL_COUNT(arr[i])++;
- ERL_TUPLE_ELEMENT(ep, i) = arr[i];
- }
- return ep;
-}
-
-/*
- * SET an ELEMENT in a TUPLE. Free the old element
- * and bump the reference counter of the new one.
- * Return 1 on success, otherwise 0.
- */
-#if 0
-int erl_setelement (int ix, ETERM *ep, ETERM *vp)
-{
- if ((!ep) || (!vp)) return 0;
- /* ASSERT(ep != NULL);
- * ASSERT(vp != NULL);
- */
-
- if ((ERL_TYPE(ep) == ERL_TUPLE) && (ix <= ERL_TUPLE_SIZE(ep))) {
- erl_free_term(ERL_TUPLE_ELEMENT(ep, ix-1));
- ERL_TUPLE_ELEMENT(ep, ix-1) = vp;
- ERL_COUNT(vp)++;
- return 1;
- }
- erl_err_msg("<ERROR> erl_setelement: Bad type to setelement or out of range \n");
- return 0;
-}
-#endif
-
-/*
- * Extract an ELEMENT from a TUPLE. Bump the
- * reference counter on the extracted object.
- */
-ETERM *erl_element (int ix, const ETERM *ep)
-{
- if ((!ep) || (ix < 0)) return NULL;
- /*
- * ASSERT(ep != NULL);
- * ASSERT(ix >= 0);
- */
-
- if ((ERL_TYPE(ep) == ERL_TUPLE) && (ix <= ERL_TUPLE_SIZE(ep))) {
- ERL_COUNT(ERL_TUPLE_ELEMENT(ep, ix-1))++;
- return ERL_TUPLE_ELEMENT(ep, ix-1);
- }
- else
- return NULL;
-} /* erl_element */
-
-ETERM *erl_mk_empty_list(void)
-{
- ETERM *ep;
-
- ep = erl_alloc_eterm(ERL_EMPTY_LIST);
- ERL_COUNT(ep) = 1;
- return ep;
-}
-
-/*
- * Construct a new list by CONS'ing a HEAD on
- * to the TAIL. Bump the reference counter on
- * the head and tail object. Note that we allow
- * non-well formed lists to be created.
- */
-ETERM *erl_cons(ETERM *hd, ETERM *tl)
-{
- ETERM *ep;
-
- if ((!hd) || (!tl)) return NULL;
-
- /*
- * ASSERT(hd != NULL);
- * ASSERT(tl != NULL);
- */
-
- ep = erl_alloc_eterm(ERL_LIST);
- ERL_COUNT(ep) = 1;
- HEAD(ep) = hd;
- TAIL(ep) = tl;
- ERL_COUNT(hd)++;
- ERL_COUNT(tl)++;
- return ep;
-}
-
-/*
- * Extract the HEAD of a LIST. Bump the reference
- * counter on the head object.
- */
-ETERM *erl_hd (const ETERM *ep)
-{
- if (!ep) return NULL;
- /* ASSERT(ep != NULL); */
-
- if (ERL_TYPE(ep) != ERL_LIST) {
- return (ETERM *) NULL;
- }
- ERL_COUNT(ERL_CONS_HEAD(ep))++;
- return ERL_CONS_HEAD(ep);
-}
-
-/*
- * Extract the TAIL of a LIST. Bump the reference
- * counter on the tail object.
- */
-ETERM *erl_tl (const ETERM *ep)
-{
- ETERM *tl;
-
- if (!ep) return NULL;
- /* ASSERT(ep != NULL); */
-
- if (ERL_TYPE(ep) != ERL_LIST) {
- return (ETERM *) NULL;
- }
-
- tl = TAIL(ep);
- ERL_COUNT(tl)++;
- return tl;
-}
-
-/*
- * Create a LIST from an array of elements. Note that
- * we create it from the last element in the array to
- * the first. Also, note that we decrement the reference
- * counter for each member in the list but the first one.
- * This is done because of the use of erl_cons.
- */
-
-ETERM *erl_mk_list (ETERM **arr, int size)
-{
- ETERM *ep;
- int i;
-
- if ((!arr) || (size < 0)) return NULL;
- for (i=0; i<size; i++) if (!arr[i]) return NULL;
-
- /* ASSERT(arr != NULL); */
- ep = erl_mk_empty_list();
- if (size > 0) {
- ERL_COUNT(ep)--;
- }
-
- for (i = size-1; i >= 0; i--) {
- /* ASSERT(arr[i] != NULL); */
- ep = erl_cons(arr[i], ep);
- if (i > 0)
- ERL_COUNT(ep)--; /* Internal reference */
- }
- return ep;
-}
-
-/*
- * Create an empty VARIABLE.
- */
-ETERM *erl_mk_var(const char *s)
-{
- ETERM *ep;
-
- if (!s) return NULL;
-
- /* ASSERT(s != NULL); */
-
- ep = erl_alloc_eterm(ERL_VARIABLE);
- ERL_COUNT(ep) = 1;
- ERL_VAR_LEN(ep) = strlen(s);
- if ((ERL_VAR_NAME(ep) = strsave(s)) == NULL)
- {
- erl_free_term(ep);
- erl_errno = ENOMEM;
- return NULL;
- }
- ERL_VAR_VALUE(ep) = (ETERM *) NULL;
- return ep;
-}
-
-/*
- * Return the CONTENT of a VARIABLE with NAME.
- * If the content is non-nil then bump its
- * reference counter.
- */
-ETERM *erl_var_content (const ETERM *ep, const char *name)
-{
- int i;
- ETERM *vp;
-
- if ((!ep) || (!name)) return NULL;
-
- /* ASSERT(ep != NULL); */
-
- switch(ERL_TYPE(ep))
- {
- case ERL_VARIABLE:
- if (strcmp(ERL_VAR_NAME(ep), name) == 0) {
- if ((vp = ERL_VAR_VALUE(ep)) != NULL) {
- ERL_COUNT(vp)++;
- return vp;
- }
- }
- break;
-
- case ERL_LIST:
- while (ep && (ERL_TYPE(ep) != ERL_EMPTY_LIST)) {
- if ((vp = erl_var_content(HEAD(ep), name))) return vp;
- ep = TAIL(ep);
- }
- break;
-
- case ERL_TUPLE:
- for (i=0; i < ERL_TUPLE_SIZE(ep); i++)
- if ((vp = erl_var_content(ERL_TUPLE_ELEMENT(ep, i), name)))
- {
- return vp;
- }
- break;
-
- default:
- /* variables can't occur in other types */
- break;
- }
-
- /* nothing found ! */
- return NULL;
-}
-
-/*
- * Return the SIZE of a TUPLE or a BINARY.
- * At failure -1 is returned.
- */
-int erl_size (const ETERM *ep)
-{
- if (!ep) return -1;
-
- /* ASSERT(ep != NULL); */
-
- switch (ERL_TYPE(ep)) {
- case ERL_TUPLE:
- return ERL_TUPLE_SIZE(ep);
-
- case ERL_BINARY:
- return ERL_BIN_SIZE(ep);
-
- default:
- return -1;
-
- }
-}
-
-/*
- * Return the LENGTH of a LIST.
- * At failure -1 is returned (this include non-proper lists like [a|b]).
- */
-int erl_length(const ETERM *ep)
-{
- int n = 0;
-
- if (!ep) return -1;
- /* ASSERT(ep != NULL); */
-
- while (ERL_TYPE(ep) == ERL_LIST) {
- n++;
- ep = TAIL(ep);
- }
-
- if (!ERL_IS_EMPTY_LIST(ep)) return -1;
-
- return n;
-}
-
-
-/***********************************************************************
- * I o l i s t f u n c t i o n s
- *
- * The following functions handles I/O lists.
- *
- * Informally, an I/O list is a deep list of characters and binaries,
- * which can be sent to an Erlang port.
- *
- * Formally, in BNF, an I/O list is defined as:
- *
- * iolist ::= []
- * | Binary
- * | [iohead | iolist]
- * ;
- *
- * iohead ::= Binary
- * | Byte (integer in the range [0..255])
- * | iolist
- * ;
- *
- * Note that versions of Erlang/OTP prior to R2 had a slightly more
- * restricted definition of I/O lists, in that the tail of a an I/O list
- * was not allowed to be a binary. The erl_interface functions
- * for I/O lists follows the more liberal rules described by the BNF
- * description above.
- ***********************************************************************/
-
-/*
- * This function converts an I/O list to a '\0' terminated C string.
- * The I/O list must not contain any occurrences of the integer 0.
- *
- * The string will be in memory allocated by erl_malloc(). It is the
- * responsibility of the caller to eventually call erl_free() to free
- * the memory.
- *
- * Returns: NULL if the list was not an I/O list or contained
- * the integer 0, otherwise a pointer to '\0' terminated string.
- */
-
-char* erl_iolist_to_string(const ETERM* term)
-{
- ETERM* bin;
-
- if ((bin = erl_iolist_to_binary(term)) == NULL) {
- return NULL;
- } else {
- char* result = NULL;
-
- if (memchr(ERL_BIN_PTR(bin), '\0', ERL_BIN_SIZE(bin)) == NULL) {
- result = (char *) erl_malloc(ERL_BIN_SIZE(bin)+1);
- memcpy(result, ERL_BIN_PTR(bin), ERL_BIN_SIZE(bin));
- result[ERL_BIN_SIZE(bin)] = '\0';
- }
- erl_free_term(bin);
- return result;
- }
-}
-
-/*
- * This function converts an I/O list to a binary term.
- *
- * Returns: NULL if the list was not an I/O list, otherwise
- * an ETERM pointer pointing to a binary term.
- */
-
-ETERM *erl_iolist_to_binary (const ETERM* term)
-{
- ETERM *dest;
- int size;
- char* ptr;
-
- if (!term) return NULL;
- /* ASSERT(term != NULL); */
-
- /*
- * Verify that the term is an I/O list and get its length.
- */
-
- size = erl_iolist_length(term);
- if (size == -1) {
- return NULL;
- }
-
- /*
- * Allocate the binary and copy the contents of the I/O list into it.
- */
-
- dest = erl_alloc_eterm(ERL_BINARY);
- ERL_COUNT(dest) = 1;
- ERL_BIN_SIZE(dest) = size;
- ptr = (char *)erl_malloc(size);
- ERL_BIN_PTR(dest) = (unsigned char *)ptr;
- iolist_to_buf(term, &ptr);
-
- /*
- * If ptr doesn't point exactly one byte beyond the end of the
- * binary, something must be seriously wrong.
- */
-
- if (ERL_BIN_PTR(dest) + size != (unsigned char *) ptr) return NULL;
- /* ASSERT(ERL_BIN_PTR(dest) + size == (unsigned char *) ptr); */
-
- return dest;
-}
-
-/*
- * Returns the length of an I/O list.
- *
- * Returns: -1 if the term if the given term is not a I/O list,
- * or the length otherwise.
- */
-
-int erl_iolist_length (const ETERM* term)
-{
- int len = 0;
-
- while (ERL_IS_CONS(term)) {
- ETERM* obj = HEAD(term);
-
- if (ERL_IS_BYTE(obj)) {
- len++;
- } else if (ERL_IS_CONS(obj)) {
- int i;
- if ((i = erl_iolist_length(obj)) < 0)
- return i;
- len += i;
- } else if (ERL_IS_BINARY(obj)) {
- len += ERL_BIN_SIZE(obj);
- } else if (!ERL_IS_EMPTY_LIST(obj)) {
- return(-1);
- }
- term = TAIL(term);
- }
- if (ERL_IS_EMPTY_LIST(term))
- return len;
- else if (ERL_IS_BINARY(term))
- return len + ERL_BIN_SIZE(term);
- else
- return -1;
-}
-
-static int erl_atom_copy(Erl_Atom_data* dst, const Erl_Atom_data* src)
-{
- if (src->latin1 == src->utf8) {
- dst->latin1 = dst->utf8 = strsave(src->latin1);
- dst->lenL = dst->lenU = strlen(src->latin1);
- }
- else if (src->latin1) {
- dst->latin1 = strsave(src->latin1);
- dst->lenL = strlen(src->latin1);
- dst->utf8 = NULL;
- dst->lenU = 0;
- }
- else {
- dst->utf8 = strsave(src->utf8);
- dst->lenU = strlen(src->utf8);
- dst->latin1 = NULL;
- dst->lenL = 0;
- }
- return (dst->latin1 != NULL || dst->utf8 == NULL);
-}
-
-
-/*
- * Return a brand NEW COPY of an ETERM.
- */
-/*
- * FIXME: Deep (the whole tree) or shallow (just the top term) copy?
- * The documentation never says, but the code as written below will
- * make a deep copy. This should be documented.
- */
-ETERM *erl_copy_term(const ETERM *ep)
-{
- int i;
- ETERM *cp;
-
- if (!ep) return NULL;
- /* ASSERT(ep != NULL); */
-
- cp = erl_alloc_eterm(ERL_TYPE(ep));
- ERL_COUNT(cp) = 1;
-
- switch(ERL_TYPE(cp)) {
- case ERL_INTEGER:
- case ERL_SMALL_BIG:
- ERL_INT_VALUE(cp) = ERL_INT_VALUE(ep);
- break;
- case ERL_U_INTEGER:
- case ERL_U_SMALL_BIG:
- ERL_INT_UVALUE(cp) = ERL_INT_UVALUE(ep);
- break;
- case ERL_LONGLONG:
- ERL_LL_VALUE(cp) = ERL_LL_VALUE(ep);
- break;
- case ERL_U_LONGLONG:
- ERL_LL_UVALUE(cp) = ERL_LL_UVALUE(ep);
- break;
- case ERL_FLOAT:
- ERL_FLOAT_VALUE(cp) = ERL_FLOAT_VALUE(ep);
- break;
- case ERL_ATOM:
- if (!erl_atom_copy(&cp->uval.aval.d, &ep->uval.aval.d))
- {
- erl_free_term(cp);
- erl_errno = ENOMEM;
- return NULL;
- }
- break;
- case ERL_PID:
- /* FIXME: First copy the bit pattern, then duplicate the node
- name and plug in. Somewhat ugly (also done with port and
- ref below). */
- memcpy(&cp->uval.pidval, &ep->uval.pidval, sizeof(Erl_Pid));
- erl_atom_copy(&cp->uval.pidval.node, &ep->uval.pidval.node);
- ERL_COUNT(cp) = 1;
- break;
- case ERL_PORT:
- memcpy(&cp->uval.portval, &ep->uval.portval, sizeof(Erl_Port));
- erl_atom_copy(&cp->uval.portval.node, &ep->uval.portval.node);
- ERL_COUNT(cp) = 1;
- break;
- case ERL_REF:
- memcpy(&cp->uval.refval, &ep->uval.refval, sizeof(Erl_Ref));
- erl_atom_copy(&cp->uval.refval.node, &ep->uval.refval.node);
- ERL_COUNT(cp) = 1;
- break;
- case ERL_LIST:
- HEAD(cp) = erl_copy_term(HEAD(ep));
- TAIL(cp) = erl_copy_term(TAIL(ep));
- break;
- case ERL_EMPTY_LIST:
- break;
- case ERL_TUPLE:
- i = ERL_TUPLE_SIZE(cp) = ERL_TUPLE_SIZE(ep);
- ERL_TUPLE_ELEMS(cp) = (ETERM**) erl_malloc(i * sizeof(ETERM*));
- for(i=0; i < ERL_TUPLE_SIZE(ep); i++)
- ERL_TUPLE_ELEMENT(cp,i) = erl_copy_term(ERL_TUPLE_ELEMENT(ep, i));
- break;
- case ERL_BINARY:
- ERL_BIN_SIZE(cp) = ERL_BIN_SIZE(ep);
- ERL_BIN_PTR(cp) = (unsigned char *) erl_malloc(ERL_BIN_SIZE(ep));
- memcpy(ERL_BIN_PTR(cp), ERL_BIN_PTR(ep), ERL_BIN_SIZE(ep));
- break;
- case ERL_FUNCTION:
- i = ERL_CLOSURE_SIZE(cp) = ERL_CLOSURE_SIZE(ep);
- ERL_FUN_ARITY(cp) = ERL_FUN_ARITY(ep);
- ERL_FUN_NEW_INDEX(cp) = ERL_FUN_NEW_INDEX(ep);
- ERL_FUN_INDEX(cp) = erl_copy_term(ERL_FUN_INDEX(ep));
- ERL_FUN_UNIQ(cp) = erl_copy_term(ERL_FUN_UNIQ(ep));
- ERL_FUN_CREATOR(cp) = erl_copy_term(ERL_FUN_CREATOR(ep));
- ERL_FUN_MODULE(cp) = erl_copy_term(ERL_FUN_MODULE(ep));
- memcpy(ERL_FUN_MD5(cp), ERL_FUN_MD5(ep), sizeof(ERL_FUN_MD5(ep)));
- ERL_CLOSURE(cp) = (ETERM**) erl_malloc(i * sizeof(ETERM*));
- for(i=0; i < ERL_CLOSURE_SIZE(ep); i++)
- ERL_CLOSURE_ELEMENT(cp,i) =
- erl_copy_term(ERL_CLOSURE_ELEMENT(ep, i));
- break;
- default:
- erl_err_msg("<ERROR> erl_copy_term: wrong type encountered !");
- erl_free_term(cp);
- return (ETERM *) NULL;
- }
-
- return cp;
-}
-
-#ifndef SILENT
-
-static int print_string(FILE* fp, const ETERM* ep);
-static int is_printable_list(const ETERM* term);
-
-/*
- * PRINT out an ETERM.
- */
-
-int erl_print_term(FILE *fp, const ETERM *ep)
-{
- int j,i,doquote;
- int ch_written = 0; /* counter of written chars */
-
- if ((!fp) || (!ep)) return 0;
- /* ASSERT(ep != NULL); */
-
- j = i = doquote = 0;
- switch(ERL_TYPE(ep))
- {
- case ERL_ATOM: {
- char* adata = ERL_ATOM_PTR(ep);
- /* FIXME: what if some weird locale is in use? */
- if (!islower(adata[0]))
- doquote = 1;
-
- for (i = 0; !doquote && i < ERL_ATOM_SIZE(ep); i++)
- {
- doquote = !(isalnum(adata[i]) || (adata[i] == '_'));
- }
-
- if (doquote) {
- putc('\'', fp);
- ch_written++;
- }
- fputs(adata, fp);
- ch_written += ERL_ATOM_SIZE(ep);
- if (doquote) {
- putc('\'', fp);
- ch_written++;
- }
- break;
- }
- case ERL_VARIABLE:
- if (!isupper((int)ERL_VAR_NAME(ep)[0])) {
- doquote = 1;
- putc('\'', fp);
- ch_written++;
- }
-
- fputs(ERL_VAR_NAME(ep), fp);
- ch_written += ERL_VAR_LEN(ep);
-
- if (doquote) {
- putc('\'', fp);
- ch_written++;
- }
- break;
-
- case ERL_PID:
- ch_written += fprintf(fp, "<%s.%d.%d>",
- ERL_PID_NODE(ep),
- ERL_PID_NUMBER(ep), ERL_PID_SERIAL(ep));
- break;
- case ERL_PORT:
- ch_written += fprintf(fp, "#Port");
- break;
- case ERL_REF:
- ch_written += fprintf(fp, "#Ref");
- break;
- case ERL_EMPTY_LIST:
- ch_written += fprintf(fp, "[]");
- break;
- case ERL_LIST:
- if (is_printable_list(ep)) {
- ch_written += print_string(fp, ep);
- } else {
- putc('[', fp);
- ch_written++;
- while (ERL_IS_CONS(ep)) {
- ch_written += erl_print_term(fp, HEAD(ep));
- ep = TAIL(ep);
- if (ERL_IS_CONS(ep)) {
- putc(',', fp);
- ch_written++;
- }
- }
- if (!ERL_IS_EMPTY_LIST(ep)) {
- putc('|', fp);
- ch_written++;
- ch_written += erl_print_term(fp, ep);
- }
- putc(']', fp);
- ch_written++;
- }
- break;
- case ERL_TUPLE:
- putc('{', fp);
- ch_written++;
- for (i=0; i < ERL_TUPLE_SIZE(ep); i++) {
- ch_written += erl_print_term(fp, ERL_TUPLE_ELEMENT(ep, j++) );
- if (i != ERL_TUPLE_SIZE(ep)-1) {
- putc(',', fp);
- ch_written++;
- }
- }
- putc('}', fp);
- ch_written++;
- break;
- case ERL_BINARY: {
- int sz = (ERL_BIN_SIZE(ep) > 20) ? 20 : ERL_BIN_SIZE(ep);
- unsigned char *ptr = ERL_BIN_PTR(ep);
- ch_written += fprintf(fp, "#Bin<");
- for (i = 0; i < sz; i++) {
- putc(ptr[i], fp); ch_written++;
- }
- if (sz == 20) ch_written += fprintf(fp, "(%d)....>", ERL_BIN_SIZE(ep)-20);
- else ch_written += fprintf(fp, ">");
- break;
- }
- case ERL_INTEGER:
- case ERL_SMALL_BIG:
- ch_written += fprintf(fp, "%d", ERL_INT_VALUE(ep));
- break;
- case ERL_U_INTEGER:
- case ERL_U_SMALL_BIG:
- ch_written += fprintf(fp, "%d", ERL_INT_UVALUE(ep));
- break;
- case ERL_LONGLONG:
- case ERL_U_LONGLONG:
- ch_written += fprintf(fp, "%lld", ERL_LL_UVALUE(ep));
- break;
- case ERL_FLOAT:
- ch_written += fprintf(fp, "%f", ERL_FLOAT_VALUE(ep));
- break;
- case ERL_FUNCTION:
- ch_written += fprintf(fp, "#Fun<");
- ch_written += erl_print_term(fp, ERL_FUN_MODULE(ep));
- putc('.', fp);
- ch_written++;
- ch_written += erl_print_term(fp, ERL_FUN_INDEX(ep));
- putc('.', fp);
- ch_written++;
- ch_written += erl_print_term(fp, ERL_FUN_UNIQ(ep));
- putc('>', fp);
- ch_written++;
- break;
- default:
- ch_written = -10000;
- erl_err_msg("<ERROR> erl_print_term: Bad type of term !");
- }
- return ch_written;
-}
-
-/*
- * FIXME not done yet....
- */
-
-#if 0
-
-int erl_sprint_term(char *buf, const ETERM *ep)
-{
- int j,i,doquote;
- int ch_written = 0; /* counter of written chars */
-
- if ((!buf) || (!ep)) return 0;
- /* ASSERT(ep != NULL); */
-
- j = i = doquote = 0;
- switch(ERL_TYPE(ep))
- {
- case ERL_ATOM:
- /* FIXME: what if some weird locale is in use? */
- if (!islower((int)ERL_ATOM_PTR(ep)[0]))
- doquote = 1;
-
- for (i = 0; !doquote && i < ERL_ATOM_SIZE(ep); i++)
- {
- doquote = !(isalnum((int)ERL_ATOM_PTR(ep)[i])
- || (ERL_ATOM_PTR(ep)[i] == '_'));
- }
-
- if (doquote) {
- *buf++ = '\'';
- ch_written++;
- }
- {
- int len = ERL_ATOM_SIZE(ep);
- strncpy(buf, ERL_ATOM_PTR(ep), len);
- buf += len;
- ch_written += len;
- }
- if (doquote) {
- *buf++ = '\'';
- ch_written++;
- }
- break;
-
- case ERL_VARIABLE:
- if (!isupper((int)ERL_VAR_NAME(ep)[0])) {
- doquote = 1;
- *buf++ = '\'';
- ch_written++;
- }
- len = ERL_VAR_LEN(ep);
- strncpy(buf, ERL_VAR_NAME(ep), len);
- buf += len;
- ch_written += len;
-
- if (doquote) {
- *buf++ = '\'';
- ch_written++;
- }
- break;
-
- case ERL_PID:
- len = sprintf(buf, "<%s.%d.%d>",
- ERL_PID_NODE(ep),
- ERL_PID_NUMBER(ep), ERL_PID_SERIAL(ep));
- buf += len;
- ch_written += len;
- break;
- case ERL_PORT:
- len = sprintf(buf , "#Port");
- buf += len;
- ch_written += len;
- break;
- case ERL_REF:
- len = sprintf(buf , "#Ref");
- buf += len;
- ch_written += len;
- break;
- case ERL_EMPTY_LIST:
- len = sprintf(buf , "[]");
- buf += len;
- ch_written += len;
- break;
- case ERL_LIST:
- if (is_printable_list(ep)) {
- ch_written += print_string(fp, ep);
- } else {
- putc('[', fp);
- ch_written++;
- while (ERL_IS_CONS(ep)) {
- ch_written += erl_sprint_term(fp, HEAD(ep));
- ep = TAIL(ep);
- if (ERL_IS_CONS(ep)) {
- putc(',', fp);
- ch_written++;
- }
- }
- if (!ERL_IS_EMPTY_LIST(ep)) {
- putc('|', fp);
- ch_written++;
- ch_written += erl_sprint_term(fp, ep);
- }
- putc(']', fp);
- ch_written++;
- }
- break;
- case ERL_TUPLE:
- putc('{', fp);
- ch_written++;
- for (i=0; i < ERL_TUPLE_SIZE(ep); i++) {
- ch_written += erl_sprint_term(fp, ERL_TUPLE_ELEMENT(ep, j++) );
- if (i != ERL_TUPLE_SIZE(ep)-1) {
- putc(',', fp);
- ch_written++;
- }
- }
- putc('}', fp);
- ch_written++;
- break;
- case ERL_BINARY:
- len = sprintf(buf , "#Bin");
- buf += len;
- ch_written += len;
- break;
- case ERL_INTEGER:
- case ERL_SMALL_BIG:
- len = sprintf(buf , "%d", ERL_INT_VALUE(ep));
- buf += len;
- ch_written += len;
- break;
- case ERL_U_INTEGER:
- case ERL_U_SMALL_BIG:
- len = sprintf(buf , "%d", ERL_INT_UVALUE(ep));
- buf += len;
- ch_written += len;
- break;
- case ERL_FLOAT:
- len = sprintf(buf , "%f", ERL_FLOAT_VALUE(ep));
- buf += len;
- ch_written += len;
- break;
- case ERL_FUNCTION:
- len = sprintf(buf , "#Fun<");
- buf += len;
- ch_written += len;
- ch_written += erl_sprint_term(fp, ERL_FUN_MODULE(ep));
- putc('.', fp);
- ch_written++;
- ch_written += erl_sprint_term(fp, ERL_FUN_INDEX(ep));
- putc('.', fp);
- ch_written++;
- ch_written += erl_sprint_term(fp, ERL_FUN_UNIQ(ep));
- putc('>', fp);
- ch_written++;
- break;
- default:
- ch_written = -10000;
- erl_err_msg("<ERROR> erl_sprint_term: Bad type of term !");
- }
- return ch_written;
-}
-#endif
-
-static int print_string(FILE* fp, const ETERM* ep)
-{
- int ch_written = 0; /* counter of written chars */
-
- putc('"', fp);
- ch_written++;
- while (ERL_IS_CONS(ep)) {
- int c = ERL_INT_VALUE(HEAD(ep));
-
- if (c >= ' ') {
- putc(c, fp);
- ch_written++;
- }
- else {
- switch (c) {
- case '\n': fputs("\\n", fp); ch_written += 2; break;
- case '\r': fputs("\\r", fp); ch_written += 2; break;
- case '\t': fputs("\\t", fp); ch_written += 2; break;
- case '\v': fputs("\\v", fp); ch_written += 2; break;
- case '\b': fputs("\\b", fp); ch_written += 2; break;
- case '\f': fputs("\\f", fp); ch_written += 2; break;
- break;
- default:
- ch_written += fprintf(fp, "\\%o", c);
- break;
- }
- }
- ep = TAIL(ep);
- }
- putc('"', fp);
- ch_written++;
- return ch_written;
-}
-
-/*
- * Returns 1 if term is a list of printable character, otherwise 0.
- */
-
-static int is_printable_list(const ETERM* term)
-{
- while (ERL_TYPE(term) == ERL_LIST) {
- ETERM* head = HEAD(term);
-
- if (!ERL_IS_BYTE(head)) {
- return 0;
- }
- if (ERL_INT_VALUE(head) < ' ') {
- switch (ERL_INT_VALUE(head)) {
- case '\n':
- case '\r':
- case '\t':
- case '\v':
- case '\b':
- case '\f':
- break;
- default:
- return 0;
- }
- }
- term = TAIL(term);
- }
-
- return ERL_IS_EMPTY_LIST(term);
-}
-
-#endif
-
-/*
- * Retrieves the bytes from an I/O list and copy into a buffer.
- *
- * NOTE! It is the responsibility of the caller to ensure that
- * that the buffer is big enough (typically by calling
- * erl_iolist_length()), and that the term is an I/O list.
- *
- * ETERM* term; Term to convert to bytes.
- * char** bufp; Pointer to pointer to buffer
- * where the bytes should be stored.
- * On return, the pointer will point beyond
- * the last byte stored.
- */
-
-static void iolist_to_buf(const ETERM* term, char** bufp)
-{
- char* dest = *bufp;
-
- while (ERL_IS_CONS(term)) {
- ETERM* obj = HEAD(term);
-
- if (ERL_IS_BYTE(obj)) {
- *dest++ = ERL_INT_VALUE(obj);
- } else if (ERL_IS_CONS(obj)) {
- iolist_to_buf(obj, &dest);
- } else if (ERL_IS_BINARY(obj)) {
- memcpy(dest, ERL_BIN_PTR(obj), ERL_BIN_SIZE(obj));
- dest += ERL_BIN_SIZE(obj);
- } else {
- /*
- * Types have been checked by caller.
- */
- if (!ERL_IS_EMPTY_LIST(obj)) return;
- /* ASSERT(ERL_IS_EMPTY_LIST(obj)); */
- }
- term = TAIL(term);
- }
- if (ERL_IS_BINARY(term)) {
- memcpy(dest, ERL_BIN_PTR(term), ERL_BIN_SIZE(term));
- dest += ERL_BIN_SIZE(term);
- } else {
- /*
- * Types have been checked by caller.
- */
- if (!ERL_IS_EMPTY_LIST(term)) return;
- /* ASSERT(ERL_IS_EMPTY_LIST(term));*/
- }
- *bufp = dest;
-}
-
-static char* strsave(const char *src)
-{
- char * dest = malloc(strlen(src)+1);
-
- if (dest != NULL)
- strcpy(dest, src);
- return dest;
-}
-
-
-/*
- * Local Variables:
- * compile-command: "cd ..; ERL_TOP=/clearcase/otp/erts make -k"
- * End:
- */
diff --git a/lib/erl_interface/src/legacy/erl_eterm.h b/lib/erl_interface/src/legacy/erl_eterm.h
deleted file mode 100644
index e2f3a90531..0000000000
--- a/lib/erl_interface/src/legacy/erl_eterm.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_ETERM_H
-#define _ERL_ETERM_H
-
-#ifndef SILENT
-#include <stdio.h>
-#endif
-
-#include "portability.h"
-
-#define ERL_MAX_COUNT 0xffffff
-#define ERL_MAX ((1 << 27)-1)
-#define ERL_MIN -(1 << 27)
-
-/* FIXME should this be documented and in erl_interface.h ??? */
-#define ERL_BIG_ARITY(x) ((x)->uval.bigval.arity)
-#define ERL_BIG_IS_NEG(x) ((x)->uval.bigval.is_neg)
-#define ERL_BIG_DIGITS(x) ((x)->uval.bigval.digits)
-#define ERL_BIG_DIGIT(x,i) (ERL_BIG_DIGITS(x)[(i)])
-
-/*
- * Typing checking macros.
- */
-
-/* FIXME should this be documented and in erl_interface.h ??? */
-#define ERL_IS_DEFINED(x) (ERL_TYPE(x) != 0)
-#define ERL_IS_COMPOUND(x) (ERL_TYPE(x) & ERL_COMPOUND)
-#define ERL_IS_FUNCTION(x) (ERL_TYPE(x) == ERL_FUNCTION)
-#define ERL_IS_BIG(x) (ERL_TYPE(x) == ERL_BIG)
-
-
-typedef struct _heapmark {
- unsigned long mark; /* id */
- int size; /* size of buffer */
- Erl_Heap *base; /* points to start of buffer */
- Erl_Heap *cur; /* points into buffer */
- struct _heapmark *prev; /* previous heapmark */
-} Erl_HeapMark;
-
-
-void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned int creation);
-void erl_mk_pid_helper(ETERM*, unsigned,unsigned, unsigned int);
-ETERM * __erl_mk_reference(ETERM*, const char *, size_t, unsigned int n[], unsigned int);
-int erl_current_fix_desc(void);
-
-#endif /* _ERL_ETERM_H */
diff --git a/lib/erl_interface/src/legacy/erl_fix_alloc.c b/lib/erl_interface/src/legacy/erl_fix_alloc.c
deleted file mode 100644
index 890a9ce291..0000000000
--- a/lib/erl_interface/src/legacy/erl_fix_alloc.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Function: General purpose Memory allocator for fixed block
- * size objects. This allocater is at least an order of
- * magnitude faster than malloc().
- */
-#include "eidef.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "ei_locking.h"
-#include "erl_interface.h"
-#include "erl_error.h"
-#include "erl_malloc.h"
-#include "erl_fix_alloc.h"
-#include "erl_eterm.h"
-
-#define WIPE_CHAR ((char)0xaa) /* 10101010 */
-
-/* the freelist is a singly linked list of these */
-/* i.e. the user structure and a link pointer */
-struct fix_block {
- ETERM term;
- struct fix_block *next;
- int free;
-};
-
-/* this is a struct just to keep namespace pollution low on VxWorks */
-struct eterm_stateinfo {
- struct fix_block *freelist;
- unsigned long freed;
- unsigned long allocated;
-#ifdef _REENTRANT
- ei_mutex_t *lock;
-#endif /* _REENTRANT */
-};
-/* FIXME problem for threaded ? */
-static struct eterm_stateinfo *erl_eterm_state=NULL;
-
-
-int erl_init_eterm_alloc (void)
-{
-#if defined(PURIFY) && defined (DEBUG)
- fprintf(stderr,"erl_fix_alloc() compiled for Purify - using \"real\" malloc()");
-#endif
-
- erl_eterm_state = malloc(sizeof(*erl_eterm_state));
- if (erl_eterm_state == NULL) goto err1;
-
- erl_eterm_state->freelist = NULL;
- erl_eterm_state->freed = 0;
- erl_eterm_state->allocated = 0;
-#ifdef _REENTRANT
- erl_eterm_state->lock = ei_mutex_create();
- if (erl_eterm_state->lock == NULL) goto err2;
-#endif /* _REENTRANT */
-
- return 1;
-
- /* Error cleanup */
-#ifdef _REENTRANT
- err2:
- /* FIXME ENOMEM is not what went wrong... */
- free(erl_eterm_state);
-#endif /* _REENTRANT */
- err1:
- erl_errno = ENOMEM;
- return 0;
-}
-
-/* get an eterm, from the freelist if possible or from malloc() */
-void *erl_eterm_alloc (void)
-{
-#ifdef PURIFY
- ETERM *p;
-
- if ((p = malloc(sizeof(*p)))) {
- memset(p, WIPE_CHAR, sizeof(*p));
- }
- return p;
-#else
- struct fix_block *b;
-
-#ifdef _REENTRANT
- ei_mutex_lock(erl_eterm_state->lock, 0);
-#endif /* _REENTRANT */
-
- /* try to pop block from head of freelist */
- if ((b = erl_eterm_state->freelist) != NULL) {
- erl_eterm_state->freelist = b->next;
- erl_eterm_state->freed--;
- } else if ((b = malloc(sizeof(*b))) == NULL) {
- erl_errno = ENOMEM;
-#ifdef _REENTRANT
- ei_mutex_unlock(erl_eterm_state->lock);
-#endif /* _REENTRANT */
- return NULL;
- }
- erl_eterm_state->allocated++;
- b->free = 0;
- b->next = NULL;
-#ifdef _REENTRANT
- ei_mutex_unlock(erl_eterm_state->lock);
-#endif /* _REENTRANT */
- return (void *) &b->term;
-#endif /* !PURIFY */
-}
-
-/* free an eterm back to the freelist */
-void erl_eterm_free(void *p)
-{
-#ifdef PURIFY
- if (p) {
- memset(p, WIPE_CHAR, sizeof(ETERM));
- }
- free(p);
-#else
- struct fix_block *b = p;
-
- if (b) {
- if (b->free) {
-#ifdef DEBUG
- fprintf(stderr,"erl_eterm_free: attempt to free already freed block %p\n",b);
-#endif
- return;
- }
-
-#ifdef _REENTRANT
- ei_mutex_lock(erl_eterm_state->lock,0);
-#endif /* _REENTRANT */
- b->free = 1;
- b->next = erl_eterm_state->freelist;
- erl_eterm_state->freelist = b;
- erl_eterm_state->freed++;
- erl_eterm_state->allocated--;
-#ifdef _REENTRANT
- ei_mutex_unlock(erl_eterm_state->lock);
-#endif /* _REENTRANT */
- }
-#endif /* !PURIFY */
-}
-
-/* really free the freelist */
-void erl_eterm_release (void)
-{
-#if !defined(PURIFY)
- struct fix_block *b;
-
-#ifdef _REENTRANT
- ei_mutex_lock(erl_eterm_state->lock,0);
-#endif /* _REENTRANT */
- {
- while (erl_eterm_state->freelist != NULL) {
- b = erl_eterm_state->freelist;
- erl_eterm_state->freelist = b->next;
- free(b);
- erl_eterm_state->freed--;
- }
- }
-#ifdef _REENTRANT
- ei_mutex_unlock(erl_eterm_state->lock);
-#endif /* _REENTRANT */
-#endif /* !PURIFY */
-}
-
-void erl_eterm_statistics (unsigned long *allocd, unsigned long *freed)
-{
- if (allocd) *allocd = erl_eterm_state->allocated;
- if (freed) *freed = erl_eterm_state->freed;
-
- return;
-}
-
-
-/*
- * Local Variables:
- * compile-command: "cd ..; ERL_TOP=/clearcase/otp/erts make -k"
- * End:
- */
diff --git a/lib/erl_interface/src/legacy/erl_fix_alloc.h b/lib/erl_interface/src/legacy/erl_fix_alloc.h
deleted file mode 100644
index 50d1368e34..0000000000
--- a/lib/erl_interface/src/legacy/erl_fix_alloc.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_FIX_ALLOC_H
-#define _ERL_FIX_ALLOC_H
-
-int erl_init_eterm_alloc(void);
-void erl_eterm_free(void*);
-void *erl_eterm_alloc(void);
-
-#endif /* _ERL_FIX_ALLOC_H */
diff --git a/lib/erl_interface/src/legacy/erl_format.c b/lib/erl_interface/src/legacy/erl_format.c
deleted file mode 100644
index 45f5489e54..0000000000
--- a/lib/erl_interface/src/legacy/erl_format.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Function: Provides two primitives: erl_format to build
- * Erlang terms in an easy way, and erl_match to perform
- * pattern match similar to what is done in Erlang.
- *
- */
-
-#include "eidef.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-
-#ifdef VRTX
-#define __READY_EXTENSIONS__
-#include <errno.h>
-#endif
-#include "erl_interface.h"
-#include "erl_eterm.h"
-#include "erl_malloc.h"
-#include "erl_error.h"
-#include "erl_internal.h"
-
-#define ERL_TRUE 1
-#define ERL_FALSE 0
-#define ERL_OK 0
-#define ERL_FORMAT_ERROR -1
-
-#define ERL_MAX_ENTRIES 255 /* Max entries in a tuple/list term */
-#define ERL_MAX_NAME_LENGTH 255 /* Max length of variable names */
-
-#define PRINT(t) \
-{ \
- print_term(stderr,t); \
- fprintf(stderr,"\n"); \
- }
-
-
-typedef struct lvar {
- ETERM *var;
- struct lvar *next;
-} lvar;
-
-
-/* Forward */
-static ETERM *eformat(char**, va_list*);
-static int ematch(ETERM*, ETERM*);
-
-/* FIXME not thread safe */
-struct _ef {
- lvar *chain; /* Chain of local variables */
- lvar *idle; /* Idle list of lvar's */
-} ef;
-
-/* Find local variable in term.
- */
-static ETERM *find_lvar(char *name)
-{
- lvar *tmp=ef.chain;
-
- while (tmp != NULL) {
- if (strcmp(tmp->var->uval.vval.name,name) == 0)
- return tmp->var->uval.vval.v;
- tmp = tmp->next;
- }
- return (ETERM *) NULL;
-
-} /* find_lvar */
-
-static void lvar_free(lvar *lv)
-{
- lvar *tmp=ef.chain;
-
- /* Link in the chain into the idle list */
- if (ef.idle == NULL)
- ef.idle = lv;
- else {
- tmp = ef.idle;
- while (tmp->next != NULL)
- tmp = tmp->next;
- tmp->next = lv;
- }
-
-
- /* Clear out the variable information */
- tmp = lv;
- while (tmp != NULL) {
- tmp->var = (ETERM *) NULL;
- tmp = tmp->next;
- }
-
-} /* lvar_free */
-
-static lvar *lvar_alloc(void)
-{
- lvar *tmp;
-
- if ((tmp = ef.idle) == NULL) {
- tmp = (lvar *) erl_malloc(sizeof(lvar));
- }
- else {
- tmp = ef.idle;
- ef.idle = tmp->next;
- }
- return tmp;
-
-} /* lvar_alloc */
-
-static void undo_bindings(void)
-{
- lvar *tmp=ef.chain;
-
- while (tmp != NULL) {
- erl_free_term(tmp->var->uval.vval.v);
- tmp->var->uval.vval.v = (ETERM *) NULL;
- tmp = tmp->next;
- }
-
-} /* undo_bindings */
-
-static void release_chain(void)
-{
-
- lvar_free(ef.chain);
- ef.chain = (lvar *) NULL;
-
-} /* release_chain */
-
-static void add_lvar(ETERM *t)
-{
- lvar *lv;
-
- lv = lvar_alloc();
- lv->var = t;
- lv->next = ef.chain;
- ef.chain = lv;
-
-} /* add_lvar */
-
-static char *pvariable(char **fmt, char *buf)
-{
- char *start=*fmt;
- char c;
- int len;
-
- while (1) {
- c = *(*fmt)++;
- if (isalnum((int) c) || (c == '_'))
- continue;
- else
- break;
- }
- (*fmt)--;
- len = *fmt - start;
- memcpy(buf, start, len);
- buf[len] = 0;
-
- return buf;
-
-} /* pvariable */
-
-static char *patom(char **fmt, char *buf)
-{
- char *start=*fmt;
- char c;
- int len;
-
- while (1) {
- c = *(*fmt)++;
- if (isalnum((int) c) || (c == '_') || (c == '@'))
- continue;
- else
- break;
- }
- (*fmt)--;
- len = *fmt - start;
- memcpy(buf, start, len);
- buf[len] = 0;
-
- return buf;
-
-} /* patom */
-
-/* Check if integer or float
- */
-static char *pdigit(char **fmt, char *buf)
-{
- char *start=*fmt;
- char c;
- int len,dotp=0;
-
- while (1) {
- c = *(*fmt)++;
- if (isdigit((int) c))
- continue;
- else if (!dotp && (c == '.')) {
- dotp = 1;
- continue;
- }
- else
- break;
- }
- (*fmt)--;
- len = *fmt - start;
- memcpy(buf, start, len);
- buf[len] = 0;
-
- return buf;
-
-} /* pdigit */
-
-static char *pstring(char **fmt, char *buf)
-{
- char *start=++(*fmt); /* skip first quote */
- char c;
- int len;
-
- while (1) {
- c = *(*fmt)++;
- if (c == '"') {
- if (*((*fmt)-1) == '\\')
- continue;
- else
- break;
- } else
- continue;
- }
- len = *fmt - 1 - start; /* skip last quote */
- memcpy(buf, start, len);
- buf[len] = 0;
-
- return buf;
-
-} /* pstring */
-
-static char *pquotedatom(char **fmt, char *buf)
-{
- char *start=++(*fmt); /* skip first quote */
- char c;
- int len;
-
- while (1) {
- c = *(*fmt)++;
- if (c == '\'') {
- if (*((*fmt)-1) == '\\')
- continue;
- else
- break;
- } else
- continue;
- }
- len = *fmt - 1 - start; /* skip last quote */
- memcpy(buf, start, len);
- buf[len] = 0;
-
- return buf;
-
-} /* pquotedatom */
-
-
-/*
- * The format letters are:
- * w - Any Erlang term
- * a - An Atom
- * b - A Binary
- * s - A String
- * i - An Integer
- * f - A Float (double)
- */
-static int pformat(char **fmt, va_list *pap, ETERM *v[], int size)
-{
- int rc=ERL_OK;
-
- /* this next section hacked to remove the va_arg calls */
- switch (*(*fmt)++) {
-
- case 'w':
- v[size] = va_arg(*pap, ETERM*);
- ERL_COUNT(v[size])++;
- break;
-
- case 'a':
- v[size] = erl_mk_atom(va_arg(*pap, char *));
- break;
-
- case 's':
- v[size] = erl_mk_string(va_arg(*pap, char *));
- break;
-
- case 'i':
- v[size] = erl_mk_int(va_arg(*pap, int));
- break;
-
- case 'f':
- v[size] = erl_mk_float(va_arg(*pap, double));
- break;
-
- case 'b': {
- char *sarg = va_arg(*pap, char *);
- v[size] = erl_mk_binary(sarg, strlen(sarg));
- break;
- }
-
- default:
- rc = ERL_FORMAT_ERROR;
- break;
- }
-
- return rc;
-
-} /* pformat */
-
-static int ptuple(char **fmt, va_list *pap, ETERM *v[], int size)
-{
- int res=ERL_FORMAT_ERROR;
-
- switch (*(*fmt)++) {
-
- case '}':
- res = size;
- break;
-
- case ',':
- res = ptuple(fmt, pap, v, size);
- break;
-
- case '~':
-
- if (pformat(fmt, pap, v, size) == ERL_OK)
- res = ptuple(fmt, pap, v, ++size);
- else
- erl_err_msg("ptuple(1): Wrong format sequence !");
- break;
-
- case ' ':
- return ptuple(fmt, pap, v, size);
- break;
-
- default: {
- (*fmt)--;
- if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL)
- res = ptuple(fmt, pap, v, size);
- break;
-
- /*
- if (isupper(**fmt)) {
- v[size++] = erl_mk_var(pvariable(fmt, wbuf));
- res = ptuple(fmt, pap, v, size);
- }
- else if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL)
- res = ptuple(fmt, pap, v, size);
- break;
- */
- }
-
- } /* switch */
-
- return res;
-
-} /* ptuple */
-
-
-static int plist(char **fmt, va_list *pap, ETERM *v[], int size)
-{
- int res=ERL_FORMAT_ERROR;
-
- switch (*(*fmt)++) {
-
- case ']':
- res = size;
- break;
-
- case ',':
- res = plist(fmt, pap, v, size);
- break;
-
- case '~':
-
- if (pformat(fmt, pap, v, size) == ERL_OK)
- res = plist(fmt, pap, v, ++size);
- else
- erl_err_msg("plist(1): Wrong format sequence !");
- break;
-
- case ' ':
- return plist(fmt, pap, v, size);
- break;
-
- default: {
- (*fmt)--;
- if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL)
- res = plist(fmt, pap, v, size);
- break;
-
- /*
- if (isupper(**fmt)) {
- v[size++] = erl_mk_var(pvariable(fmt, wbuf));
- res = plist(fmt, pap, v, size);
- }
- else if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL)
- res = plist(fmt, pap, v, size);
- break;
- */
- }
-
- } /* switch */
-
- return res;
-
-} /* plist */
-
-
-static ETERM *eformat(char **fmt, va_list *pap)
-{
- int size;
- ETERM *v[ERL_MAX_ENTRIES],*ep;
-
- switch (*(*fmt)++) {
- case '{':
- if ((size = ptuple(fmt, pap , v, 0)) != ERL_FORMAT_ERROR) {
- ep = erl_mk_tuple(v, size);
- erl_free_array(v, size);
- return ep;
- }
- else
- return (ETERM *) NULL;
- break;
-
- case '[':
- if (**fmt == ']') {
- (*fmt)++;
- return erl_mk_empty_list();
- } else if ((size = plist(fmt, pap , v, 0)) != ERL_FORMAT_ERROR) {
- ep = erl_mk_list(v, size);
- erl_free_array(v, size);
- return ep;
- } else
- return (ETERM *) NULL;
- break;
-
- case '$': /* char-value? */
- return erl_mk_int((int)(*(*fmt)++));
- break;
-
- case '~':
- if (pformat(fmt, pap, v, 0) == ERL_OK) {
- ep = erl_copy_term(v[0]);
- erl_free_term(v[0]);
- return ep;
- }
- break;
-
- case ' ':
- return eformat(fmt, pap);
- break;
-
- /* handle negative numbers too...
- * case '-':
- * {
- * ETERM *tmp;
- *
- * tmp = eformat(fmt,pap);
- * if (ERL_IS_INTEGER(tmp)) ERL_INT_VALUE(tmp) = -(ERL_INT_VALUE(tmp));
- * return tmp;
- * }
- *
- *
- * break;
- */
-
- default:
- {
- char wbuf[BUFSIZ]; /* now local to this function for reentrancy */
-
- (*fmt)--;
- if (islower((int)**fmt)) { /* atom ? */
- char *atom=patom(fmt, wbuf);
- return erl_mk_atom(atom);
- }
- else if (isupper((int)**fmt) || (**fmt == '_')) {
- char *var=pvariable(fmt, wbuf);
- return erl_mk_var(var);
- }
- else if (isdigit((int)**fmt)) { /* integer/float ? */
- char *digit=pdigit(fmt, wbuf);
- if (strchr(digit,(int) '.') == NULL)
- return erl_mk_int(atoi((const char *) digit));
- else
- return erl_mk_float(atof((const char *) digit));
- }
- else if (**fmt == '"') { /* string ? */
- char *string=pstring(fmt, wbuf);
- return erl_mk_string(string);
- }
- else if (**fmt == '\'') { /* quoted atom ? */
- char *qatom=pquotedatom(fmt, wbuf);
- return erl_mk_atom(qatom);
- }
- }
- break;
-
- }
-
- erl_err_msg("<ERROR> Syntax error in eformat, char was: %c !", **fmt);
- return (ETERM *) NULL;
-
-} /* eformat */
-
-
-ETERM *erl_format(char *fmt, ... )
-{
- ETERM *res=NULL;
- va_list ap;
-
- va_start(ap, fmt);
- res = eformat(&fmt, &ap);
- va_end(ap);
-
- return res;
-} /* erl_format */
-
-
-/*
- * Perform a pattern match between a pattern p and a term t.
- * As a side effect bind any unbound variables in p.
- * Return true or false.
- */
-static int ematch(ETERM *p, ETERM *t)
-{
- unsigned int type_p;
- unsigned int type_t;
- ETERM *tmp;
-
- /* two NULLs are equal, one is not... */
- if (!p && !t) return ERL_TRUE;
- if (!p || !t) return ERL_FALSE;
- /*
- * ASSERT(p != NULL);
- * ASSERT(t != NULL);
- */
-
- type_p = ERL_TYPE(p);
- type_t = ERL_TYPE(t);
-
- if (type_t == ERL_VARIABLE) {
- if (t->uval.vval.v == NULL)
- return ERL_FALSE; /* Can't have an unbound variable here ! */
- else
- t = t->uval.vval.v;
- }
-
- if (type_p != ERL_VARIABLE && type_p != type_t)
- return ERL_FALSE;
-
- switch (type_p) {
-
- case ERL_ATOM: {
- Erl_Atom_data* pa = &p->uval.aval.d;
- Erl_Atom_data* ta = &t->uval.aval.d;
- if (pa->utf8 && ta->utf8) {
- return pa->lenU == ta->lenU && memcmp(pa->utf8, ta->utf8, pa->lenU)==0;
- }
- else if (pa->latin1 && ta->latin1) {
- return pa->lenL == ta->lenL && memcmp(pa->latin1, ta->latin1, pa->lenL)==0;
- }
- else if (pa->latin1) {
- return cmp_latin1_vs_utf8(pa->latin1, pa->lenL, ta->utf8, ta->lenU)==0;
- }
- else {
- return cmp_latin1_vs_utf8(ta->latin1, ta->lenL, pa->utf8, pa->lenU)==0;
- }
- }
- case ERL_VARIABLE:
- if (strcmp(p->uval.vval.name, "_") == 0) /* anon. variable */
- return ERL_TRUE;
- else if ((tmp = find_lvar(p->uval.vval.name)) != (ETERM *) NULL) {
- /* v points to NULL in cases like erl_format("{X,X}") for the
- second variable */
- if (p->uval.vval.v == NULL)
- p->uval.vval.v = erl_copy_term(tmp);
- return ematch(p->uval.vval.v, t);
- }
- else {
- /* check if the variable is bound already */
- if (p->uval.vval.v != NULL) {
- if (ematch(p->uval.vval.v, t) == ERL_TRUE ){
- add_lvar(p);
- return ERL_TRUE;
- }
- else
- return ERL_FALSE;
- }
- else {
- p->uval.vval.v = erl_copy_term(t);
- add_lvar(p);
- return ERL_TRUE;
- }
- }
- break;
-
- case ERL_PID:
- if ((strcmp(ERL_PID_NODE(p), ERL_PID_NODE(t)) == 0) &&
- (ERL_PID_NUMBER(p) == ERL_PID_NUMBER(t)) &&
- (ERL_PID_SERIAL(p) == ERL_PID_SERIAL(t)) &&
- (ERL_PID_CREATION(p) == ERL_PID_CREATION(t)))
- return ERL_TRUE;
- else
- return ERL_FALSE;
- break;
-
- case ERL_PORT:
- if ((strcmp(ERL_PORT_NODE(p), ERL_PORT_NODE(t)) == 0) &&
- (ERL_PORT_NUMBER(p) == ERL_PORT_NUMBER(t)) &&
- (ERL_PORT_CREATION(p) == ERL_PORT_CREATION(t)))
- return ERL_TRUE;
- else
- return ERL_FALSE;
- break;
-
- case ERL_REF: {
- int i, len;
-
- if (strcmp(ERL_REF_NODE(p), ERL_REF_NODE(t)) != 0 ||
- ERL_REF_CREATION(p) != ERL_REF_CREATION(t))
- return ERL_FALSE;
-
- /* FIXME: {len=1, n={42}} and {len=3, n={42, 17, 13}} tests equal. */
- len = ERL_REF_LEN(p);
- if (len > ERL_REF_LEN(t))
- len = ERL_REF_LEN(t);
-
- for (i = 0; i < len; i++)
- if (ERL_REF_NUMBERS(p)[i] != ERL_REF_NUMBERS(t)[i])
- return ERL_FALSE;
-
- return ERL_TRUE;
- break;
- }
-
- case ERL_EMPTY_LIST:
- return ERL_TRUE;
-
- case ERL_LIST:
- while (ERL_IS_CONS(p) && ERL_IS_CONS(t)) {
- if (ematch(p->uval.lval.head, t->uval.lval.head) == ERL_FALSE)
- return ERL_FALSE;
- p = p->uval.lval.tail;
- t = t ->uval.lval.tail;
- }
- return ematch(p, t);
-
- case ERL_TUPLE:
- {
- int i;
- if (erl_size(p) != erl_size(t))
- return ERL_FALSE;
- else {
- for(i=0; i<erl_size(p); i++)
- if (ematch(p->uval.tval.elems[i],t->uval.tval.elems[i]) == ERL_FALSE)
- return ERL_FALSE;
- return ERL_TRUE;
- }
- }
- break;
-
- case ERL_BINARY:
- {
- int i;
- if ((i = p->uval.bval.size) != t->uval.bval.size)
- return ERL_FALSE;
- else
- return (memcmp(p->uval.bval.b,t->uval.bval.b,i)==0) ? ERL_TRUE : ERL_FALSE;
- }
- break;
-
- case ERL_INTEGER:
- return (p->uval.ival.i == t->uval.ival.i) ? ERL_TRUE : ERL_FALSE;
- break;
-
- case ERL_SMALL_BIG:
- case ERL_U_SMALL_BIG:
- /* This case can't happend since it is impossible
- * to create a bignum from the C code.
- */
- return ERL_FALSE;
- break;
-
- case ERL_FLOAT:
-#if defined(VXWORKS) && CPU == PPC860
- {
- return (erl_fp_compare((unsigned *)&(p->uval.fval.f),
- (unsigned *)&(t->uval.fval.f)) == 0)
- ? ERL_TRUE : ERL_FALSE;
- }
-#else
- return (p->uval.fval.f == t->uval.fval.f) ? ERL_TRUE : ERL_FALSE;
-#endif
- break;
- default:
- return ERL_FALSE;
- break;
- }
-
- /* erl_err_msg("ematch: Unknown type == %c\n", type_p); */
- return ERL_FALSE;
-
-} /* ematch */
-
-
-int erl_match(ETERM *p, ETERM *t)
-{
- int i;
-
- if ((i = ematch(p, t)) == ERL_FALSE)
- undo_bindings();
- release_chain();
- return i;
-
-} /* erl_match */
-
-
diff --git a/lib/erl_interface/src/legacy/erl_format.h b/lib/erl_interface/src/legacy/erl_format.h
deleted file mode 100644
index 92fa068206..0000000000
--- a/lib/erl_interface/src/legacy/erl_format.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_FORMAT_H
-#define _ERL_FORMAT_H
-
-#endif /* _ERL_FORMAT_H */
diff --git a/lib/erl_interface/src/legacy/erl_malloc.c b/lib/erl_interface/src/legacy/erl_malloc.c
deleted file mode 100644
index 27ef8c4b32..0000000000
--- a/lib/erl_interface/src/legacy/erl_malloc.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#include "eidef.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "erl_interface.h"
-#include "erl_fix_alloc.h"
-#include "erl_malloc.h"
-#include "erl_internal.h"
-#include "erl_eterm.h"
-#include "ei_malloc.h"
-
-void erl_init_malloc(Erl_Heap *hp, long heap_size)
-{
- erl_init_eterm_alloc();
-} /* erl_init_malloc */
-
-ETERM *erl_alloc_eterm(unsigned char type)
-{
- ETERM *e;
-
- /* Use fix size allocator */
- if (!(e = (ETERM *) erl_eterm_alloc()))
- erl_err_sys("<ERROR> erl_alloc_eterm: Failed to allocate more memory\n");
-
- ERL_HEADER(e)->count = 0;
- ERL_HEADER(e)->type = type;
- return e;
-
-} /* erl_alloc_eterm */
-
-#define EXTERNAL 1
-#define INTERNAL 0
-#define COMPOUND 1
-#define NOT_COMPOUND 0
-
-static void _erl_free_term (ETERM *ep, int external, int compound);
-
-/*
- * Free a term, but don't deallocate it until
- * the reference counter triggers.
- */
-void erl_free_term(ETERM *ep)
-{
- _erl_free_term(ep, EXTERNAL, NOT_COMPOUND);
-} /* erl_free_term */
-
-/*
- * Free a term regardless of its reference
- * counter value. Use this when you have
- * built compound terms such as lists or tuples.
- */
-
-/*
- * FIXME is this true?!
- * Tearing down term structures no-matter-what is a horrible idea if
- * any term happens to be shared (with some other structure or even
- * with yourself).
- */
-
-void erl_free_compound (ETERM *ep)
-{
- _erl_free_term(ep, EXTERNAL, COMPOUND);
-} /* erl_free_compound */
-
-
-/*
-** The actual free'ing is done here in _erl_free_term.
-** It is by nature recursive, but does not recurse
-** on the CDR of a list, which makes it usable for large lists.
-*/
-
-/*
-** Convenience macro, called for variables and lists,
-** avoids deep recursions.
-*/
-#define RESTART(Eterm, External, Compound) \
-do { \
- ETERM *sep; \
- sep = (Eterm); \
- external = (External); \
- compound = (Compound); \
- /* Clear header info */ \
- ERL_TYPE(ep) = ERL_UNDEF; \
- erl_eterm_free((unsigned int *) ep); \
- ep = sep; \
- goto restart; \
-} while(0)
-
-#define FREE_AND_CLEAR(ptr) \
-do { \
- erl_free(ptr); \
- (ptr) = NULL; \
-} while (0)
-
-static void erl_atom_free(Erl_Atom_data* p)
-{
- erl_free(p->latin1);
- if (p->utf8 != p->latin1) {
- erl_free(p->utf8);
- }
- p->latin1 = NULL;
- p->utf8 = NULL;
- p->lenL = 0;
- p->lenU = 0;
-}
-
-static void _erl_free_term (ETERM *ep, int external, int compound)
-{
-restart:
- if (ep == NULL)
- return;
- if (compound || ERL_NO_REF(ep)) {
- /* Yes, it's time to *really* free this one ! */
- switch(ERL_TYPE(ep))
- {
- case ERL_ATOM:
- erl_atom_free(&ep->uval.aval.d);
- break;
- case ERL_VARIABLE:
- FREE_AND_CLEAR(ERL_VAR_NAME(ep));
- /* Note: It may be unbound ! */
- if (ERL_VAR_VALUE(ep) != NULL) {
- ERL_COUNT(ERL_VAR_VALUE(ep))--;
- /* Cleanup and Restart with the actual value */
- RESTART(ERL_VAR_VALUE(ep), INTERNAL, compound);
- }
- break;
- case ERL_LIST:
- if (HEAD(ep)) {
- ERL_COUNT(HEAD(ep))--;
- /* FIXME added cast, is this correct? */
- _erl_free_term((ETERM *)HEAD(ep), INTERNAL, compound);
- }
- if (TAIL(ep)) {
- ERL_COUNT(TAIL(ep))--;
- /* Clean up and walk on to CDR in list */
- RESTART(TAIL(ep), INTERNAL, compound);
- }
- break;
- case ERL_TUPLE:
- {
- int i;
- for (i=0; i < ERL_TUPLE_SIZE(ep); i++)
- if (ERL_TUPLE_ELEMENT(ep, i)) {
- ERL_COUNT(ERL_TUPLE_ELEMENT(ep, i))--;
- _erl_free_term(ERL_TUPLE_ELEMENT(ep, i),
- INTERNAL, compound);
- }
- FREE_AND_CLEAR(ERL_TUPLE_ELEMS(ep));
- }
- break;
- case ERL_BINARY:
- FREE_AND_CLEAR(ERL_BIN_PTR(ep));
- break;
- case ERL_PID:
- erl_atom_free(&ep->uval.pidval.node);
- break;
- case ERL_PORT:
- erl_atom_free(&ep->uval.portval.node);
- break;
- case ERL_REF:
- erl_atom_free(&ep->uval.refval.node);
- break;
- case ERL_EMPTY_LIST:
- case ERL_INTEGER:
- case ERL_SMALL_BIG:
- case ERL_U_SMALL_BIG:
- case ERL_FLOAT:
- break;
- case ERL_FUNCTION:
- {
- int i;
-
- _erl_free_term(ERL_FUN_INDEX(ep), INTERNAL, compound);
- _erl_free_term(ERL_FUN_UNIQ(ep), INTERNAL, compound);
- _erl_free_term(ERL_FUN_CREATOR(ep), INTERNAL, compound);
- _erl_free_term(ERL_FUN_MODULE(ep), INTERNAL, compound);
- if (ERL_CLOSURE(ep) != NULL) {
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- _erl_free_term(ERL_CLOSURE_ELEMENT(ep,i),
- INTERNAL, compound);
- }
- }
- break;
- } /* switch */
-
- /* Clear header info for those cases where we are done */
- ERL_TYPE(ep) = ERL_UNDEF;
- erl_eterm_free(ep);
- } else if (external) {
- ERL_COUNT(ep)--;
- external = INTERNAL;
- goto restart;
- }
-} /* _erl_free_term */
-#undef RESTART
-#undef FREE_AND_CLEAR
-
-void erl_free_array(ETERM **arr, int size)
-{
- int i;
-
- for (i=0; i<size; i++)
- erl_free_term(arr[i]);
-
-} /* erl_free_array */
-
-
-void* erl_malloc (long size)
-{
- void *res;
-
- if ((res = ei_malloc(size)) == NULL)
- erl_err_sys("<ERROR> erl_malloc: Failed to allocate more memory");
-
- return res;
-}
-
-void* erl_realloc(void* orig, long size)
-{
- void *res;
-
- if ((res = ei_realloc(orig, size)) == NULL)
- erl_err_sys("<ERROR> erl_realloc: Failed to allocate more memory");
- return res;
-}
-
-void erl_free (void *ptr)
-{
- ei_free(ptr);
-}
diff --git a/lib/erl_interface/src/legacy/erl_malloc.h b/lib/erl_interface/src/legacy/erl_malloc.h
deleted file mode 100644
index 6cbc01faba..0000000000
--- a/lib/erl_interface/src/legacy/erl_malloc.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_MALLOC_H
-#define _ERL_MALLOC_H
-
-/* FIXME: not documented */
-void *erl_realloc(void*, long);
-int erl_current_fix_desc(void);
-
-#endif /* _ERL_MALLOC_H */
diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c
deleted file mode 100644
index 932bba43bf..0000000000
--- a/lib/erl_interface/src/legacy/erl_marshal.c
+++ /dev/null
@@ -1,2267 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2018. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Decoding and encoding Erlang terms.
- */
-#include "eidef.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <string.h>
-#include <limits.h>
-
-#include "erl_interface.h"
-#include "erl_marshal.h"
-#include "erl_eterm.h"
-#include "erl_malloc.h"
-#include "erl_error.h"
-#include "erl_internal.h"
-
-#include "eiext.h" /* replaces external.h */
-#include "putget.h"
-
-static int is_string(ETERM* term);
-#if defined(VXWORKS) && CPU == PPC860
-int erl_fp_compare(unsigned *a, unsigned *b);
-static void erl_long_to_fp(long l, unsigned *d);
-#endif
-
-static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2);
-static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1, unsigned char* s2, int l2, unsigned char tag2);
-
-/* Used when comparing two encoded byte arrays */
-/* this global data is ok (from threading point of view) since it is
- * initialized once and never changed
- */
-
-#define CMP_ARRAY_SIZE 256
-/* FIXME problem for threaded ? */
-
-static enum
-{
- ERL_NUM_CMP=1, ERL_ATOM_CMP, ERL_REF_CMP, ERL_FUN_CMP, ERL_PORT_CMP,
- ERL_PID_CMP, ERL_TUPLE_CMP, ERL_NIL_CMP, ERL_LIST_CMP, ERL_BIN_CMP
-}cmp_array[CMP_ARRAY_SIZE];
-
-static int init_cmp_array_p=1; /* initialize array, the first time */
-
-#if defined(VXWORKS) && CPU == PPC860
-#include <limits.h>
-#endif
-
-#if defined(__GNUC__)
-# define INLINE __inline__
-#elif defined(__WIN32__)
-# define INLINE __inline
-#else
-# define INLINE
-#endif
-
-static int cmp_floats(double f1, double f2);
-static INLINE double to_float(long l);
-
-#define IS_ERL_NUM(t) (cmp_array[t]==ERL_NUM_CMP)
-#define IS_ERL_ATOM(t) (cmp_array[t]==ERL_ATOM_CMP)
-
-#define CMP_NUM_CLASS_SIZE 256
-static unsigned char cmp_num_class[CMP_NUM_CLASS_SIZE];
-static int init_cmp_num_class_p=1; /* initialize array, the first time */
-
-#define MK_CMP_NUM_CODE(x,y) (((x)<<2)|(y))
-#define CMP_NUM_CLASS(x) (cmp_num_class[x] & 0x03)
-#define CMP_NUM_CODE(x,y) (MK_CMP_NUM_CODE(CMP_NUM_CLASS(x),CMP_NUM_CLASS(y)))
-
-#define SMALL 1
-#define FLOAT 2
-#define BIG 3
-
-#define SMALL_SMALL MK_CMP_NUM_CODE(SMALL,SMALL)
-#define SMALL_FLOAT MK_CMP_NUM_CODE(SMALL,FLOAT)
-#define SMALL_BIG MK_CMP_NUM_CODE(SMALL,BIG)
-#define FLOAT_SMALL MK_CMP_NUM_CODE(FLOAT,SMALL)
-#define FLOAT_FLOAT MK_CMP_NUM_CODE(FLOAT,FLOAT)
-#define FLOAT_BIG MK_CMP_NUM_CODE(FLOAT,BIG)
-#define BIG_SMALL MK_CMP_NUM_CODE(BIG,SMALL)
-#define BIG_FLOAT MK_CMP_NUM_CODE(BIG,FLOAT)
-#define BIG_BIG MK_CMP_NUM_CODE(BIG,BIG)
-
-void erl_init_marshal(void)
-{
- if (init_cmp_array_p) {
- memset(cmp_array, 0, sizeof cmp_array);
- cmp_array[ERL_SMALL_INTEGER_EXT] = ERL_NUM_CMP;
- cmp_array[ERL_INTEGER_EXT] = ERL_NUM_CMP;
- cmp_array[ERL_FLOAT_EXT] = ERL_NUM_CMP;
- cmp_array[NEW_FLOAT_EXT] = ERL_NUM_CMP;
- cmp_array[ERL_SMALL_BIG_EXT] = ERL_NUM_CMP;
- cmp_array[ERL_LARGE_BIG_EXT] = ERL_NUM_CMP;
- cmp_array[ERL_ATOM_EXT] = ERL_ATOM_CMP;
- cmp_array[ERL_ATOM_UTF8_EXT] = ERL_ATOM_CMP;
- cmp_array[ERL_SMALL_ATOM_EXT] = ERL_ATOM_CMP;
- cmp_array[ERL_SMALL_ATOM_UTF8_EXT] = ERL_ATOM_CMP;
- cmp_array[ERL_REFERENCE_EXT] = ERL_REF_CMP;
- cmp_array[ERL_NEW_REFERENCE_EXT] = ERL_REF_CMP;
- cmp_array[ERL_NEWER_REFERENCE_EXT]=ERL_REF_CMP;
- cmp_array[ERL_FUN_EXT] = ERL_FUN_CMP;
- cmp_array[ERL_NEW_FUN_EXT] = ERL_FUN_CMP;
- cmp_array[ERL_PORT_EXT] = ERL_PORT_CMP;
- cmp_array[ERL_NEW_PORT_EXT] = ERL_PORT_CMP;
- cmp_array[ERL_PID_EXT] = ERL_PID_CMP;
- cmp_array[ERL_NEW_PID_EXT] = ERL_PID_CMP;
- cmp_array[ERL_SMALL_TUPLE_EXT] = ERL_TUPLE_CMP;
- cmp_array[ERL_LARGE_TUPLE_EXT] = ERL_TUPLE_CMP;
- cmp_array[ERL_NIL_EXT] = ERL_NIL_CMP;
- cmp_array[ERL_STRING_EXT] = ERL_LIST_CMP;
- cmp_array[ERL_LIST_EXT] = ERL_LIST_CMP;
- cmp_array[ERL_BINARY_EXT] = ERL_BIN_CMP;
- init_cmp_array_p = 0;
- }
- if (init_cmp_num_class_p) {
- memset(cmp_num_class, 0, CMP_NUM_CLASS_SIZE);
- cmp_num_class[ERL_SMALL_INTEGER_EXT] = SMALL;
- cmp_num_class[ERL_INTEGER_EXT] = SMALL;
- cmp_num_class[ERL_FLOAT_EXT] = FLOAT;
- cmp_num_class[NEW_FLOAT_EXT] = FLOAT;
- cmp_num_class[ERL_SMALL_BIG_EXT] = BIG;
- cmp_num_class[ERL_LARGE_BIG_EXT] = BIG;
- init_cmp_num_class_p = 0;
- }
-}
-
-/* The encoder calls length, if erl_length() should return */
-/* -1 for dotted pairs (why !!!!) we can't use erl_length() */
-/* from the encoder in erl_marshal.c */
-
-static int erl_length_x(const ETERM *ep) {
- int n = 0;
-
- if (!ep) return -1;
-
- while (ERL_TYPE(ep) == ERL_LIST) {
- n++;
- ep = TAIL(ep);
- }
-
- return n;
-}
-
-
-/*==============================================================
- * Marshalling routines.
- *==============================================================
- */
-
-static void encode_atom(Erl_Atom_data* a, unsigned char **ext)
-{
- int ix = 0;
- if (a->latin1) {
- ei_encode_atom_len_as((char*)*ext, &ix, a->latin1, a->lenL,
- ERLANG_LATIN1, ERLANG_UTF8);
- }
- else {
- ei_encode_atom_len_as((char*)*ext, &ix, a->utf8, a->lenU,
- ERLANG_UTF8, ERLANG_UTF8);
- }
- *ext += ix;
-}
-
-/*
- * The actual ENCODE engine.
- * Returns 0 on success, otherwise 1.
- */
-int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
-{
- int i;
- unsigned int u;
- long long l;
- unsigned long long ul;
-
- switch(ERL_TYPE(ep))
- {
- case ERL_ATOM:
- encode_atom(&ep->uval.aval.d, ext);
- return 0;
-
- case ERL_INTEGER:
- i = ep->uval.ival.i;
- /* SMALL_INTEGER */
- if ((i < 256) && (i >= 0)) {
- *(*ext)++ = ERL_SMALL_INTEGER_EXT;
- *(*ext)++ = i & 0xff;
- return 0;
- }
- /* R14B: Use all 32 bits of INTEGER_EXT */
- *(*ext)++ = ERL_INTEGER_EXT;
- *(*ext)++ = (i >> 24) & 0xff;
- *(*ext)++ = (i >> 16) & 0xff;
- *(*ext)++ = (i >> 8) & 0xff;
- *(*ext)++ = i & 0xff;
- return 0;
-
- case ERL_U_INTEGER:
- u = ep->uval.uival.u;
- /* ERL_U_SMALL_BIG */
- if ((int)u < 0) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 4; /* four bytes */
- *(*ext)++ = 0; /* sign byte */
- *(*ext)++ = u & 0xff; /* LSB first */
- *(*ext)++ = (u >> 8) & 0xff;
- *(*ext)++ = (u >> 16) & 0xff;
- *(*ext)++ = (u >> 24) & 0xff;
- return 0;
- }
- /* SMALL_INTEGER */
- if (u < 256) {
- *(*ext)++ = ERL_SMALL_INTEGER_EXT;
- *(*ext)++ = u & 0xff;
- return 0;
- }
- /* R14B: Use all 32 bits of INTEGER_EXT */
- *(*ext)++ = ERL_INTEGER_EXT;
- *(*ext)++ = (u >> 24) & 0xff;
- *(*ext)++ = (u >> 16) & 0xff;
- *(*ext)++ = (u >> 8) & 0xff;
- *(*ext)++ = u & 0xff;
- return 0;
- case ERL_LONGLONG:
- l = ep->uval.llval.i;
- /* ERL_SMALL_BIG */
- if (l > ((long long) INT_MAX) || l < ((long long) INT_MIN)) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 8;
- if ((*(*ext)++ = (l<0))) /* sign byte */
- l = -l;
- *(*ext)++ = l & 0xff; /* LSB first */
- *(*ext)++ = (l >> 8) & 0xff;
- *(*ext)++ = (l >> 16) & 0xff;
- *(*ext)++ = (l >> 24) & 0xff;
- *(*ext)++ = (l >> 32) & 0xff;
- *(*ext)++ = (l >> 40) & 0xff;
- *(*ext)++ = (l >> 48) & 0xff;
- *(*ext)++ = (l >> 56) & 0xff;
- return 0;
- }
- /* SMALL_INTEGER */
- if ((l < 256) && (l >= 0)) {
- *(*ext)++ = ERL_SMALL_INTEGER_EXT;
- *(*ext)++ = l & 0xff;
- return 0;
- }
- /* R14B: Use all 32 bits of INTEGER_EXT */
- *(*ext)++ = ERL_INTEGER_EXT;
- *(*ext)++ = (l >> 24) & 0xff;
- *(*ext)++ = (l >> 16) & 0xff;
- *(*ext)++ = (l >> 8) & 0xff;
- *(*ext)++ = l & 0xff;
- return 0;
-
- case ERL_U_LONGLONG:
- ul = ep->uval.ullval.u;
- /* ERL_U_SMALL_BIG */
- if (ul > ((unsigned long long) INT_MAX)) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 8; /* eight bytes */
- *(*ext)++ = 0; /* sign byte */
- *(*ext)++ = ul & 0xff; /* LSB first */
- *(*ext)++ = (ul >> 8) & 0xff;
- *(*ext)++ = (ul >> 16) & 0xff;
- *(*ext)++ = (ul >> 24) & 0xff;
- *(*ext)++ = (ul >> 32) & 0xff;
- *(*ext)++ = (ul >> 40) & 0xff;
- *(*ext)++ = (ul >> 48) & 0xff;
- *(*ext)++ = (ul >> 56) & 0xff;
- return 0;
- }
- /* SMALL_INTEGER */
- if (ul < 256) {
- *(*ext)++ = ERL_SMALL_INTEGER_EXT;
- *(*ext)++ = ul & 0xff;
- return 0;
- }
- /* R14B: Use all 32 bits of INTEGER_EXT */
- *(*ext)++ = ERL_INTEGER_EXT;
- *(*ext)++ = (ul >> 24) & 0xff;
- *(*ext)++ = (ul >> 16) & 0xff;
- *(*ext)++ = (ul >> 8) & 0xff;
- *(*ext)++ = ul & 0xff;
- return 0;
-
- case ERL_PID: {
- unsigned char* tagp = (*ext)++;
- /* First poke in node as an atom */
- encode_atom(&ep->uval.pidval.node, ext);
- /* And then fill in the integer fields */
- i = ERL_PID_NUMBER(ep);
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- i = ERL_PID_SERIAL(ep);
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
-
- i = ERL_PID_CREATION(ep);
- if ((unsigned int)i <= 3) {
- *tagp = ERL_PID_EXT;
- *(*ext)++ = i;
- } else {
- *tagp = ERL_NEW_PID_EXT;
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- }
- return 0;
- }
- case ERL_REF: {
- unsigned char* tagp = (*ext)++;
-
- int len, j;
-
- /* Always encode as an extended reference; all
- participating parties are now expected to be
- able to decode extended references. */
-
- i = strlen((char *)ERL_REF_NODE(ep));
- len = ERL_REF_LEN(ep);
- *(*ext)++ = (len >> 8) &0xff;
- *(*ext)++ = len &0xff;
-
- encode_atom(&ep->uval.refval.node, ext);
-
- i = ERL_REF_CREATION(ep);
- if ((unsigned int)i <= 3) {
- *tagp = ERL_NEW_REFERENCE_EXT;
- *(*ext)++ = i;
- } else {
- *tagp = ERL_NEWER_REFERENCE_EXT;
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- }
-
- /* Then the integer fields */
- for (j = 0; j < ERL_REF_LEN(ep); j++) {
- i = ERL_REF_NUMBERS(ep)[j];
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- }
- }
- return 0;
- case ERL_PORT: {
- unsigned char* tagp = (*ext)++;
- /* First poke in node as an atom */
- encode_atom(&ep->uval.portval.node, ext);
- /* Then the integer fields */
- i = ERL_PORT_NUMBER(ep);
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
-
- i = ERL_PORT_CREATION(ep);
- if ((unsigned int)i <= 3) {
- *tagp = ERL_PORT_EXT;
- *(*ext)++ = i;
- } else {
- *tagp = ERL_NEW_PORT_EXT;
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- }
- return 0;
- }
- case ERL_EMPTY_LIST:
- *(*ext)++ = ERL_NIL_EXT;
- break;
- case ERL_LIST:
- i = is_string(ep);
- if (0 < i && i < 0x10000) { /* String. */
- *(*ext)++ = ERL_STRING_EXT;
- *(*ext)++ = (i >>8) &0xff;
- *(*ext)++ = i &0xff;
- while (ERL_TYPE(ep) == ERL_LIST) {
- *(*ext)++ = HEAD(ep)->uval.ival.i;
- ep = TAIL(ep);
- }
- break;
- } else { /* List. */
- i = erl_length_x(ep);
- *(*ext)++ = ERL_LIST_EXT;
- *(*ext)++ = (i >> 24) &0xff;
- *(*ext)++ = (i >> 16) &0xff;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- while (ERL_TYPE(ep) == ERL_LIST) {
- if (erl_encode_it(HEAD(ep), ext, dist))
- return 1;
- ep = TAIL(ep);
- }
- i = erl_encode_it(ep, ext, dist);
- return i;
- }
- case ERL_TUPLE:
- i = ep->uval.tval.size;
- if (i <= 0xff) {
- *(*ext)++ = ERL_SMALL_TUPLE_EXT;
- *(*ext)++ = i & 0xff;
- }
- else {
- *(*ext)++ = ERL_LARGE_TUPLE_EXT;
- *(*ext)++ = (i >> 24) & 0xff;
- *(*ext)++ = (i >> 16) & 0xff;
- *(*ext)++ = (i >> 8) & 0xff;
- *(*ext)++ = i & 0xff;
- }
- for (i=0; i<ep->uval.tval.size; i++)
- if (erl_encode_it(ep->uval.tval.elems[i], ext, dist))
- return 1;
- break;
- case ERL_FLOAT:
- *(*ext)++ = ERL_FLOAT_EXT;
- memset(*ext, 0, 31);
- sprintf((char *) *ext, "%.20e", ep->uval.fval.f);
- *ext += 31;
- break;
- case ERL_BINARY:
- *(*ext)++ = ERL_BINARY_EXT;
- i = ep->uval.bval.size;
- *(*ext)++ = (i >> 24) & 0xff;
- *(*ext)++ = (i >> 16) & 0xff;
- *(*ext)++ = (i >> 8) & 0xff;
- *(*ext)++ = i & 0xff;
- memcpy((char *) *ext, (char*) ep->uval.bval.b, i);
- *ext += i;
- break;
- case ERL_FUNCTION:
- if (ERL_FUN_ARITY(ep) != -1) {
- unsigned char *size_p = *ext + 1;
- *(*ext)++ = ERL_NEW_FUN_EXT;
- *ext += 4;
- i = ERL_FUN_ARITY(ep);
- put8(*ext, i);
- memcpy(*ext, ERL_FUN_MD5(ep), 16);
- *ext += 16;
- i = ERL_FUN_NEW_INDEX(ep);
- put32be(*ext, i);
- i = ERL_CLOSURE_SIZE(ep);
- put32be(*ext, i);
- erl_encode_it(ERL_FUN_MODULE(ep), ext, dist);
- erl_encode_it(ERL_FUN_INDEX(ep), ext, dist);
- erl_encode_it(ERL_FUN_UNIQ(ep), ext, dist);
- erl_encode_it(ERL_FUN_CREATOR(ep), ext, dist);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- erl_encode_it(ep->uval.funcval.closure[i], ext, dist);
- if (size_p != NULL) {
- i = *ext - size_p;
- put32be(size_p, i);
- }
- } else {
- *(*ext)++ = ERL_FUN_EXT;
- i = ERL_CLOSURE_SIZE(ep);
- *(*ext)++ = (i >> 24) & 0xff;
- *(*ext)++ = (i >> 16) & 0xff;
- *(*ext)++ = (i >> 8) & 0xff;
- *(*ext)++ = i & 0xff;
- erl_encode_it(ERL_FUN_CREATOR(ep), ext, dist);
- erl_encode_it(ERL_FUN_MODULE(ep), ext, dist);
- erl_encode_it(ERL_FUN_INDEX(ep), ext, dist);
- erl_encode_it(ERL_FUN_UNIQ(ep), ext, dist);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- erl_encode_it(ep->uval.funcval.closure[i], ext, dist);
- }
- break;
- default:
- return 1;
- }
- return 0;
-}
-
-/*
- * ENCODE an ETERM into a BUFFER, assuming BUFFER is of
- * enough size. At success return number of bytes written
- * into it, otherwise return 0.
- */
-static int erl_encode3(ETERM *ep, unsigned char *t, int dist)
-{
- unsigned char *x = t;
-
- *x++ = ERL_VERSION_MAGIC;
- if (erl_encode_it(ep, &x, dist)) {
-#ifdef DEBUG
- erl_err_msg("<ERROR> erl_encode: Error while encoding");
-#endif
- return 0;
- }
- return (x - t);
-
-}
-
-/* API */
-
-int erl_encode(ETERM *ep, unsigned char *t)
-{
- return erl_encode3(ep, t, 4);
-}
-
-/* determine the buffer size that will be required for the eterm */
-static int erl_term_len_helper(ETERM *ep, int dist);
-
-/* FIXME hard coded dist version */
-int erl_term_len(ETERM *ep)
-{
- return 1+erl_term_len_helper(ep, 4);
-}
-
-static int atom_len_helper(Erl_Atom_data* a)
-{
- (void) erl_atom_ptr_utf8(a);
- return 1 + 1 + (a->lenU > 255) + a->lenU;
-}
-
-static int erl_term_len_helper(ETERM *ep, int dist)
-{
- int len = 0;
- int i;
- unsigned int u;
- long long l;
- unsigned long long ul;
-
- if (ep) {
- switch (ERL_TYPE(ep)) {
- case ERL_ATOM:
- len = atom_len_helper(&ep->uval.aval.d);
- break;
-
- case ERL_INTEGER:
- i = ep->uval.ival.i;
- if ((i < 256) && (i >= 0)) len = 2;
- else len = 5;
- break;
-
- case ERL_U_INTEGER:
- u = ep->uval.uival.u;
- if ((int)u < 0) len = 7;
- else if (u < 256) len = 2;
- else len = 5;
- break;
-
- case ERL_LONGLONG:
- l = ep->uval.llval.i;
- if ((l > ((long long) INT_MAX)) ||
- (l < ((long long) INT_MIN))) len = 11;
- else if ((l < 256) && (l >= 0)) len = 2;
- else len = 5;
- break;
-
- case ERL_U_LONGLONG:
- ul = ep->uval.ullval.u;
- if (ul > ((unsigned long long) INT_MAX)) len = 11;
- else if (ul < 256) len = 2;
- else len = 5;
- break;
-
- case ERL_PID:
- len = 1 + atom_len_helper(&ep->uval.pidval.node) + 4 + 4 + 1;
- break;
-
- case ERL_REF:
- len = 1 + 2 + atom_len_helper(&ep->uval.refval.node) + 1 + ERL_REF_LEN(ep) * 4;
- break;
-
- case ERL_PORT:
- len = 1 + atom_len_helper(&ep->uval.portval.node) + 4 + 1;
- break;
-
- case ERL_EMPTY_LIST:
- len = 1;
- break;
-
- case ERL_LIST:
- i = is_string(ep);
- if ((i > 0) && (i < 0x10000)) { /* string: 3 + strlen */
- for (len = 3; ERL_TYPE(ep) == ERL_LIST; ep = TAIL(ep)) {
- len++;
- }
- }
- else { /* list: 5 + len(elem1) + len(elem2) ... */
- for (len = 5; ERL_TYPE(ep) == ERL_LIST; ep = TAIL(ep)) {
- len += erl_term_len_helper(HEAD(ep), dist);
- }
- len += erl_term_len_helper(ep, dist); /* last element */
- }
- break;
-
- case ERL_TUPLE:
- /* (2 or 5) + len(elem1) + len(elem2) ... */
- i = ep->uval.tval.size;
- if (i <= 0xff) len = 2;
- else len = 5;
-
- for (i=0; i<ep->uval.tval.size; i++) {
- len += erl_term_len_helper(ep->uval.tval.elems[i], dist);
- }
- break;
-
- case ERL_FLOAT:
- len = 32;
- break;
-
- case ERL_BINARY:
- i = ep->uval.bval.size;
- len = 5 + i;
- break;
-
- case ERL_FUNCTION:
- if (ERL_FUN_ARITY(ep) == -1) {
- len = 1 + 4;
- len += erl_term_len_helper(ERL_FUN_CREATOR(ep),dist);
- len += erl_term_len_helper(ERL_FUN_MODULE(ep),dist);
- len += erl_term_len_helper(ERL_FUN_INDEX(ep),dist);
- len += erl_term_len_helper(ERL_FUN_UNIQ(ep),dist);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- len += erl_term_len_helper(ERL_CLOSURE_ELEMENT(ep,i), dist);
- } else {
- len = 1 + 4 + 16 + 4 + 4;
- len += erl_term_len_helper(ERL_FUN_MODULE(ep),dist);
- len += erl_term_len_helper(ERL_FUN_INDEX(ep),dist);
- len += erl_term_len_helper(ERL_FUN_UNIQ(ep),dist);
- len += erl_term_len_helper(ERL_FUN_CREATOR(ep),dist);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- len += erl_term_len_helper(ERL_CLOSURE_ELEMENT(ep,i), dist);
- }
- break;
-
- default:
-#ifdef DEBUG
- fprintf(stderr, "Shouldn't happen: erl_term_len, unknown term type: '%c'\n",ERL_TYPE(ep));
-#endif
- erl_errno = EINVAL;
- exit(1);
- }
- }
-
- return len;
-}
-
-/*
- * This one makes it easy to ENCODE several CONSECUTIVE
- * ETERM's into the same buffer.
- */
-int erl_encode_buf(ETERM *ep, unsigned char **ext)
-{
- unsigned char *start=*ext;
-
- *(*ext)++ = ERL_VERSION_MAGIC;
- if (erl_encode_it(ep, ext, 0)) {
-#ifdef DEBUG
- erl_err_msg("<ERROR> erl_encode_buf: Error while encoding\n");
-#endif
- return 0;
- }
- return (*ext - start);
-
-} /* erl_encode_buf */
-
-
-static int read_atom(unsigned char** ext, Erl_Atom_data* a)
-{
- char buf[MAXATOMLEN_UTF8];
- int offs = 0;
- erlang_char_encoding enc;
- int ret = ei_decode_atom_as((char*)*ext, &offs, buf, MAXATOMLEN_UTF8,
- ERLANG_LATIN1|ERLANG_UTF8, NULL, &enc);
- *ext += offs;
-
- if (ret == 0) {
- int i = strlen(buf);
- char* clone = erl_malloc(i+1);
- memcpy(clone, buf, i+1);
-
- a->latin1 = NULL;
- a->lenL = 0;
- a->utf8 = NULL;
- a->lenU = 0;
- if (enc & (ERLANG_LATIN1 | ERLANG_ASCII)) {
- a->latin1 = clone;
- a->lenL = i;
- }
- if (enc & (ERLANG_UTF8 | ERLANG_ASCII)) {
- a->utf8 = clone;
- a->lenU = i;
- }
- }
- return ret;
-}
-
-/*
- * The actual DECODE engine.
- * Returns NULL in case of failure.
- */
-static ETERM *erl_decode_it(unsigned char **ext)
-{
- char *cp;
- ETERM *ep,*tp,*np;
- unsigned int u,sign;
- int i,j,arity;
- double ff;
- unsigned char tag;
-
- /* Assume we are going to decode an integer */
- ep = erl_alloc_eterm(ERL_INTEGER);
- ERL_COUNT(ep) = 1;
-
- tag = *(*ext)++;
- switch (tag)
- {
- case ERL_INTEGER_EXT:
- i = (int) (**ext << 24) | ((*ext)[1] << 16) |
- ((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- ep->uval.ival.i = i;
- return ep;
-
- case ERL_SMALL_INTEGER_EXT:
- i = *(*ext)++;
- ep->uval.ival.i = i;
- return ep;
-
- /* NOTE: The arity below for bigs is not really the arity (= number of digits) */
- /* It is the byte count and this might cause problems in other parts... */
- case ERL_SMALL_BIG_EXT:
- arity = *(*ext)++;
- goto big_cont;
- case ERL_LARGE_BIG_EXT:
- arity = (**ext << 24) | ((*ext)[1])<< 16 |
- ((*ext)[2]) << 8 |((*ext)[3]);
- *ext += 4;
- big_cont:
-
-#ifdef _MSC_VER
-#define MAX_TO_NEGATE 0x8000000000000000Ui64
-#else
-#define MAX_TO_NEGATE 0x8000000000000000ULL
-#endif
-
- sign = *(*ext)++;
- if (arity > 8)
- goto big_truncate;
-
- if (arity == 4 && ((*ext)[3] & 0x80) && !sign) {
- /* It will fit into an unsigned int !! */
- u = (((*ext)[3] << 24)|((*ext)[2])<< 16|((*ext)[1]) << 8 |(**ext));
- ERL_TYPE(ep) = ERL_U_INTEGER;
- ep->uval.uival.u = u;
- /* *ext += i; */
- *ext += arity;
- return ep;
- } else if (arity == 4 && !((*ext)[3] & 0x80)) {
- /* It will fit into an int !!
- */
- i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 |
- ((*ext)[1]) << 8 | (**ext));
- if (sign) i = -i;
- ERL_TYPE(ep) = ERL_INTEGER;
- ep->uval.ival.i = i;
- *ext += arity;
- return ep;
- } else if (arity == 8 && ((*ext)[7] & 0x80) && !sign) {
- /* Fits in an unsigned long long */
- int x;
- unsigned long long ul = 0LL;
-
- for(x = 0 ; x < arity ; x++) {
- ul |= ((unsigned long long)(*ext)[x]) << ((unsigned long long)(8*x));
- }
-
- ERL_TYPE(ep) = ERL_U_LONGLONG;
- ep->uval.ullval.u = ul;
- *ext += arity;
- return ep;
- } else {
- /* Fits in a signed long long */
- int x;
- unsigned long long l = 0LL;
- long long sl;
-
- for(x = 0 ; x < arity ; x++) {
- l |= ((unsigned long long)(*ext)[x]) << ((unsigned long long)(8*x));
- }
-
- sl = (long long)l;
-
- if (sign && l != MAX_TO_NEGATE) {
- sl = -sl;
- if (sl > 0) goto big_truncate;
- }
-
- ERL_TYPE(ep) = ERL_LONGLONG;
- ep->uval.llval.i = sl;
- *ext += arity;
- return ep;
- }
-#undef MAX_TO_NEGATE
- big_truncate:
- /* truncate to: (+/-) 1 */
-#ifdef DEBUG
- erl_err_msg("<WARNING> erl_decode_it: Integer truncated...");
-#endif
- ERL_TYPE(ep) = ERL_INTEGER;
- ep->uval.ival.i = sign?-1:1;
- *ext += arity;
- return ep;
-
- case ERL_ATOM_EXT:
- case ERL_SMALL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
-
- ERL_TYPE(ep) = ERL_ATOM;
- --(*ext);
- if (read_atom(ext, &ep->uval.aval.d) < 0) return NULL;
- return ep;
-
- case ERL_PID_EXT:
- case ERL_NEW_PID_EXT:
- {
- unsigned int number, serial;
- unsigned int creation;
-
- ERL_TYPE(ep) = ERL_PID;
- if (read_atom(ext, &ep->uval.pidval.node) < 0) return NULL;
-
- /* get the integers */
- number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- serial = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- if (tag == ERL_PID_EXT)
- creation = *(*ext)++;
- else {
- creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- }
- erl_mk_pid_helper(ep, number, serial, creation);
- return ep;
- }
- case ERL_REFERENCE_EXT:
- {
- unsigned int n[3] = {0, 0, 0};
- unsigned char creation;
-
- ERL_TYPE(ep) = ERL_REF;
- if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;
-
- /* get the integers */
- n[0] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- creation = *(*ext)++;
- __erl_mk_reference(ep, NULL, 1, n, creation);
- return ep;
- }
-
- case ERL_NEW_REFERENCE_EXT:
- case ERL_NEWER_REFERENCE_EXT:
- {
- size_t cnt, i;
- unsigned int n[3];
- unsigned int creation;
-
- ERL_TYPE(ep) = ERL_REF;
- cnt = ((*ext)[0] << 8) | (*ext)[1];
- *ext += 2;
-
- if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;
-
- /* get the integers */
- if (tag == ERL_NEW_REFERENCE_EXT)
- creation = *(*ext)++;
- else {
- creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- }
- for(i = 0; i < cnt; i++)
- {
- n[i] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- }
- __erl_mk_reference(ep, NULL, cnt, n, creation);
- return ep;
- }
-
- case ERL_PORT_EXT:
- case ERL_NEW_PORT_EXT:
- {
- unsigned int number;
- unsigned int creation;
-
- ERL_TYPE(ep) = ERL_PORT;
- if (read_atom(ext, &ep->uval.portval.node) < 0) return NULL;
-
- /* get the integers */
- number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]);
- *ext += 4;
- if (tag == ERL_PORT_EXT)
- creation = *(*ext)++;
- else {
- creation = (((*ext)[0] << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]));
- *ext += 4;
- }
- erl_mk_port_helper(ep, number, creation);
- return ep;
- }
-
- case ERL_NIL_EXT:
- ERL_TYPE(ep) = ERL_EMPTY_LIST;
- return ep;
-
- case ERL_LIST_EXT:
- ERL_TYPE(ep) = ERL_LIST;
- i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- /* ASSERT(i != 0); */ /* Should be represented by ERL_NIL_EXT. */
- tp = ep;
- for (j = 0; j < i; j++)
- if ((HEAD(tp) = erl_decode_it(ext)) == NULL)
- goto failure;
- else if (j + 1 < i) {
- /* We have to watch out for how we allocates the
- * last tail element since we may encounter non-
- * well formed lists.
- */
- np = erl_alloc_eterm(ERL_LIST);
- ERL_COUNT(np) = 1;
- TAIL(np) = NULL; /* in case of failure */
- TAIL(tp) = np;
- tp = np;
- }
- if ((TAIL(tp) = erl_decode_it(ext)) == NULL)
- goto failure;
- return ep;
-
- case ERL_STRING_EXT:
- {
- unsigned char* s;
-
- ERL_TYPE(ep) = ERL_EMPTY_LIST;
- i = (**ext << 8) | ((*ext)[1]);
- *ext += 2;
- s = *ext+i;
-
- while (*ext < s) {
- ETERM* integer;
- ETERM* cons;
-
- integer = erl_alloc_eterm(ERL_INTEGER);
- ERL_COUNT(integer) = 1;
- integer->uval.ival.i = *--s;
-
- cons = erl_alloc_eterm(ERL_LIST);
- ERL_COUNT(cons) = 1;
- HEAD(cons) = integer;
- TAIL(cons) = ep;
- ep = cons;
- }
- *ext += i;
- return ep;
- }
-
- case ERL_SMALL_TUPLE_EXT:
- ERL_TYPE(ep) = ERL_TUPLE;
- i = *(*ext)++;
- goto decode_tuple;
-
- case ERL_LARGE_TUPLE_EXT:
- i = (**ext << 24) | ((*ext)[1]) << 16 |
- ((*ext)[2]) << 8 | ((*ext)[3]) ;
- *ext += 4;
- decode_tuple:
- ep->uval.tval.size = i;
- j = (i + 1) * sizeof(ETERM*);
- ep->uval.tval.elems = (ETERM**) erl_malloc(j);
- memset(ep->uval.tval.elems, 0, j); /* in case of failure below... */
- for (i=0; i<ep->uval.tval.size; i++)
- if ((tp = erl_decode_it(ext)) == NULL)
- goto failure;
- else
- ep->uval.tval.elems[i] = tp;
- return ep;
-
- case ERL_FLOAT_EXT:
- case NEW_FLOAT_EXT:
- ERL_TYPE(ep) = ERL_FLOAT;
- cp = (char *) *ext;
- i = -1;
- if (ei_decode_double(cp, &i, &ff) == -1)
- goto failure;
- *ext += i;
- ep->uval.fval.f = ff;
- return ep;
-
- case ERL_BINARY_EXT:
- ERL_TYPE(ep) = ERL_BINARY;
- i = (**ext << 24) | ((*ext)[1] << 16) |
- ((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- ep->uval.bval.size = i;
- ep->uval.bval.b = (unsigned char *) erl_malloc(i);
- memcpy(ep->uval.bval.b, *ext, i);
- *ext += i;
- return ep;
-
- case ERL_FUN_EXT: /* FIXME: error checking */
- ERL_TYPE(ep) = ERL_FUNCTION;
- i = get32be(*ext);
- /*i = *(**ext << 24) | ((*ext)[1] << 16) | ((*ext)[2] << 8) | (*ext)[3];
- *ext += 4; */
- ERL_FUN_ARITY(ep) = -1;
- ERL_CLOSURE_SIZE(ep) = i;
- ERL_FUN_CREATOR(ep) = erl_decode_it(ext);
- ERL_FUN_MODULE(ep) = erl_decode_it(ext);
- ERL_FUN_INDEX(ep) = erl_decode_it(ext);
- ERL_FUN_UNIQ(ep) = erl_decode_it(ext);
- j = i * sizeof(ETERM*);
- ERL_CLOSURE(ep) = (ETERM**) erl_malloc(j);
- memset(ERL_CLOSURE(ep), 0, j);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- ERL_CLOSURE_ELEMENT(ep,i) = erl_decode_it(ext);
- return ep;
-
- case ERL_NEW_FUN_EXT: /* FIXME: error checking */
- ERL_TYPE(ep) = ERL_FUNCTION;
- i = get32be(*ext); /* size, we don't use it here */
- ERL_FUN_ARITY(ep) = get8(*ext);
- memcpy(ERL_FUN_MD5(ep), *ext, 16);
- *ext += 16;
- ERL_FUN_NEW_INDEX(ep) = get32be(*ext);
- i = get32be(*ext);
- ERL_CLOSURE_SIZE(ep) = i;
- ERL_FUN_MODULE(ep) = erl_decode_it(ext);
- ERL_FUN_INDEX(ep) = erl_decode_it(ext);
- ERL_FUN_UNIQ(ep) = erl_decode_it(ext);
- ERL_FUN_CREATOR(ep) = erl_decode_it(ext);
- j = i * sizeof(ETERM*);
- ERL_CLOSURE(ep) = (ETERM**) erl_malloc(j);
- memset(ERL_CLOSURE(ep), 0, j);
- for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
- ERL_CLOSURE_ELEMENT(ep,i) = erl_decode_it(ext);
- return ep;
-
- } /* switch */
-
- failure:
- erl_free_term(ep);
- return (ETERM *) NULL;
-
-} /* erl_decode_it */
-
-/*
- * DECODE a buffer of BYTES into an ETERM.
- * Returns NULL in case of failure.
- */
-ETERM *erl_decode(unsigned char *t)
-{
- ETERM *ep;
- unsigned char *ext;
-
- ext = t;
-
- /* We ignore the version magic since it might be
- * possible that the buffer has been manipulated
- * with erl_peek_ext.
- */
- if (*ext == ERL_VERSION_MAGIC)
- ext++;
-
- ep = NULL;
- ep = erl_decode_it(&ext);
-#ifdef DEBUG
- if (!ep) erl_err_msg("<ERROR> erl_decode: Error while decoding");
-#endif
- return ep;
-
-} /* erl_decode */
-
-/*
- * This one makes it possible to DECODE two CONSECUTIVE
- * ETERM's in the same buffer.
- */
-ETERM *erl_decode_buf(unsigned char **ext)
-{
- ETERM *ep;
-
- /* We ignore the version magic since it might be
- * possible that the buffer has been manipulated
- * with erl_peek_ext.
- */
- if (**ext == ERL_VERSION_MAGIC)
- (*ext)++;
-
- ep = NULL;
- ep = erl_decode_it(ext);
-#ifdef DEBUG
- if (!ep) erl_err_msg("<ERROR> erl_decode_buf: Error while decoding");
-#endif
- return ep;
-
-} /* erl_decode_buf */
-
-
-/*==============================================================
- * Ok, here comes routines for inspecting/manipulating
- * an encoded buffer of bytes.
- *==============================================================
- */
-
-/*
- * Return 1 if the VERSION MAGIC in the BUFFER is the
- * same as the this library version.
- */
-int erl_verify_magic(unsigned char *ext)
-{
-
- if (*ext == ERL_VERSION_MAGIC)
- return 1;
- else
- return 0;
-
-} /* erl_verify_magic */
-
-/*
- * Return the TYPE of an ENCODED ETERM.
- * At failure, return 0.
- */
-unsigned char erl_ext_type(unsigned char *ext)
-{
- /* FIXME old code could skip multiple magic */
-
- /* Move over magic number if any */
- if (*ext == ERL_VERSION_MAGIC) ext++;
-
- switch (*ext) {
- case ERL_SMALL_INTEGER_EXT:
- case ERL_INTEGER_EXT:
- return ERL_INTEGER;
- case ERL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- case ERL_SMALL_ATOM_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
- return ERL_ATOM;
- case ERL_PID_EXT:
- case ERL_NEW_PID_EXT:
- return ERL_PID;
- case ERL_PORT_EXT:
- case ERL_NEW_PORT_EXT:
- return ERL_PORT;
- case ERL_REFERENCE_EXT:
- case ERL_NEW_REFERENCE_EXT:
- case ERL_NEWER_REFERENCE_EXT:
- return ERL_REF;
- case ERL_NIL_EXT:
- return ERL_EMPTY_LIST;
- case ERL_LIST_EXT:
- return ERL_LIST;
- case ERL_SMALL_TUPLE_EXT:
- case ERL_LARGE_TUPLE_EXT:
- return ERL_TUPLE;
- case ERL_FLOAT_EXT:
- case NEW_FLOAT_EXT:
- return ERL_FLOAT;
- case ERL_BINARY_EXT:
- return ERL_BINARY;
- case ERL_FUN_EXT:
- case ERL_NEW_FUN_EXT:
- return ERL_FUNCTION;
- case ERL_SMALL_BIG_EXT:
- case ERL_LARGE_BIG_EXT:
- return ERL_BIG;
- default:
- return 0;
-
- } /* switch */
-
-} /* erl_ext_type */
-
-/*
- * Returns the number of elements in compund
- * terms. For other kind of terms zero is returned.
- * At failure -1 is returned.
- */
-int erl_ext_size(unsigned char *t)
-{
- int i;
- unsigned char *v;
-
- if (*t == ERL_VERSION_MAGIC)
- return erl_ext_size(t+1);
-
- v = t+1;
- switch(*t) {
- case ERL_SMALL_INTEGER_EXT:
- case ERL_INTEGER_EXT:
- case ERL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- case ERL_SMALL_ATOM_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
- case ERL_PID_EXT:
- case ERL_NEW_PID_EXT:
- case ERL_PORT_EXT:
- case ERL_NEW_PORT_EXT:
- case ERL_REFERENCE_EXT:
- case ERL_NEW_REFERENCE_EXT:
- case ERL_NEWER_REFERENCE_EXT:
- case ERL_NIL_EXT:
- case ERL_BINARY_EXT:
- case ERL_STRING_EXT:
- case ERL_FLOAT_EXT:
- case NEW_FLOAT_EXT:
- case ERL_SMALL_BIG_EXT:
- case ERL_LARGE_BIG_EXT:
- return 0;
- break;
- case ERL_SMALL_TUPLE_EXT:
- i = v[0];
- return i;
- break;
- case ERL_LIST_EXT:
- case ERL_LARGE_TUPLE_EXT:
- i = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
- return i;
- break;
- case ERL_FUN_EXT:
- i = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
- return i+4;
- break;
- case ERL_NEW_FUN_EXT:
- v += 4 + 1 + 16 + 4;
- i = get32be(v);
- return i + 4;
- break;
- default:
- return -1;
- break;
- } /* switch */
-
-} /* ext_size */
-
-
-static int jump_atom(unsigned char** ext)
-{
- unsigned char* e = *ext;
- int len;
-
- switch (*e++) {
- case ERL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- len = (e[0] << 8) | e[1];
- e += (len + 2);
- break;
-
- case ERL_SMALL_ATOM_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
- len = e[0];
- e += (len + 1);
- break;
-
- default:
- return 0;
- }
- *ext = e;
- return 1;
-}
-
-
-/*
- * MOVE the POINTER PAST the ENCODED ETERM we
- * are currently pointing at. Returns 1 at
- * success, otherwise 0.
- */
-static int jump(unsigned char **ext)
-{
- int j,k,i=0;
- int n;
- const int tag = *(*ext)++;
-
- switch (tag) {
- case ERL_VERSION_MAGIC:
- return jump(ext);
- case ERL_INTEGER_EXT:
- *ext += 4;
- break;
- case ERL_SMALL_INTEGER_EXT:
- *ext += 1;
- break;
- case ERL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- case ERL_SMALL_ATOM_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
- jump_atom(ext);
- break;
- case ERL_PID_EXT:
- if (!jump_atom(ext)) return 0;
- *ext += 4 + 4 + 1;
- break;
- case ERL_NEW_PID_EXT:
- if (!jump_atom(ext)) return 0;
- *ext += 4 + 4 + 4;
- break;
- case ERL_REFERENCE_EXT:
- case ERL_PORT_EXT:
- if (!jump_atom(ext)) return 0;
- *ext += 4 + 1;
- break;
- case ERL_NEW_PORT_EXT:
- if (!jump_atom(ext)) return 0;
- *ext += 4 + 4;
- break;
- case ERL_NEW_REFERENCE_EXT:
- case ERL_NEWER_REFERENCE_EXT:
- n = (**ext << 8) | (*ext)[1];
- *ext += 2;
- /* first field is an atom */
- if (!jump_atom(ext)) return 0;
- *ext += 4*n + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4);
- break;
- case ERL_NIL_EXT:
- /* We just passed it... */
- break;
- case ERL_LIST_EXT:
- i = j = 0;
- j = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- for(k=0; k<j; k++)
- if ((i = jump(ext)) == 0)
- return(0);
- if (**ext == ERL_NIL_EXT) {
- *ext += 1;
- break;
- }
- if (jump(ext) == 0) return 0;
- break;
- case ERL_STRING_EXT:
- i = **ext << 8 | (*ext)[1];
- *ext += 2 + i;
- break;
- case ERL_SMALL_TUPLE_EXT:
- i = *(*ext)++;
- goto jump_tuple;
- case ERL_LARGE_TUPLE_EXT:
- i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- jump_tuple:
- for (j = 0; j < i; j++)
- if ((k = jump(ext)) == 0)
- return(0);
- break;
- case ERL_FLOAT_EXT:
- *ext += 31;
- break;
- case NEW_FLOAT_EXT:
- *ext += 8;
- break;
- case ERL_BINARY_EXT:
- i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
- *ext += 4+i;
- break;
- case ERL_FUN_EXT:
- i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
- *ext += 4;
- i += 4;
- for (j = 0; j < i; j++)
- if ((k = jump(ext)) == 0)
- return(0);
- break;
- case ERL_NEW_FUN_EXT:
- i = get32be(*ext);
- *ext += i + 4;
- break;
- case ERL_SMALL_BIG_EXT:
- i = *(*ext);
- *ext += i + 1;
- break;
- case ERL_LARGE_BIG_EXT:
- i = get32be(*ext);
- *ext += i + 4;
- break;
- default:
- return 0;
- } /* switch */
-
- return 1;
-
-} /* jump */
-
-/*
- * The actual PEEK engine.
- */
-static unsigned char *peek_ext(unsigned char **ext, int jumps)
-{
- int i;
-
- switch (*(*ext)++)
- {
- case ERL_VERSION_MAGIC:
- return peek_ext(ext, jumps);
- case ERL_SMALL_TUPLE_EXT:
- i = *(*ext)++;
- goto do_the_peek_stuff;
- case ERL_LARGE_TUPLE_EXT:
- case ERL_LIST_EXT:
- i = (**ext << 24) | ((*ext)[1]) << 16| ((*ext)[2]) << 8| ((*ext)[3]) ;
- *ext += 4;
- do_the_peek_stuff:
- if (i <= jumps) {
-#ifdef DEBUG
- erl_err_msg("<ERROR> peek_ext: Out of range");
-#endif
- return NULL;
- }
- for(i=0; i<jumps; i++)
- if (!jump(ext)) {
-#ifdef DEBUG
- erl_err_msg("<ERROR> peek_ext: Bad data");
-#endif
- return NULL;
- }
- return *ext;
- default:
-#ifdef DEBUG
- erl_err_msg("<ERROR> peek_ext: Can't peek in non list/tuple type");
-#endif
- return NULL;
- } /* switch */
-
-} /* peek_ext */
-
-/*
- * Return a POINTER TO the N:TH ELEMENT in a
- * COMPUND ENCODED ETERM.
- */
-unsigned char *erl_peek_ext(unsigned char *ext, int jumps)
-{
- unsigned char *x=ext;
-
- return peek_ext(&x, jumps);
-
-} /* erl_peek_ext */
-
-/*
- * Lexically compare two strings of bytes,
- * (string s1 length l1 and s2 l2).
- * Return: -1 if s1 < s2
- * 0 if s1 = s2
- * 1 if s1 > s2
- */
-static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2)
-{
- int i;
- i = 0;
- while((i < l1) && (i < l2)) {
- if (s1[i] < s2[i]) return(-1);
- if (s1[i] > s2[i]) return(1);
- i++;
- }
- if (l1 < l2) return(-1);
- if (l1 > l2) return(1);
- return(0);
-
-} /* cmpbytes */
-
-#define tag2enc(T) ((T)==ERL_ATOM_EXT || (T)==ERL_SMALL_ATOM_EXT ? ERLANG_LATIN1 : ERLANG_UTF8)
-
-static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1,
- unsigned char* s2, int l2, unsigned char tag2)
-{
- erlang_char_encoding enc1 = tag2enc(tag1);
- erlang_char_encoding enc2 = tag2enc(tag2);
-
- if (enc1 == enc2) {
- return cmpbytes(s1, l1,s2,l2);
- }
-
- if (enc1 == ERLANG_LATIN1) {
- return cmp_latin1_vs_utf8((char*)s1, l1, (char*)s2, l2);
- }
- else {
- return -cmp_latin1_vs_utf8((char*)s2, l2, (char*)s1, l1);
- }
-}
-
-int cmp_latin1_vs_utf8(const char* strL, int lenL, const char* strU, int lenU)
-{
- unsigned char* sL = (unsigned char*)strL;
- unsigned char* sU = (unsigned char*)strU;
- unsigned char* sL_end = sL + lenL;
- unsigned char* sU_end = sU + lenU;
-
- while(sL < sL_end && sU < sU_end) {
- unsigned char UasL;
- if (*sL >= 0x80) {
- if (*sU < 0xC4 && (sU+1) < sU_end) {
- UasL = ((sU[0] & 0x3) << 6) | (sU[1] & 0x3F);
- }
- else return -1;
- }
- else {
- UasL = *sU;
- }
- if (*sL < UasL) return -1;
- if (*sL > UasL) return 1;
-
- sL++;
- if (*sU < 0x80) sU++;
- else if (*sU < 0xE0) sU += 2;
- else if (*sU < 0xF0) sU += 3;
- else /*if (*sU < 0xF8)*/ sU += 4;
- }
-
- return (sU >= sU_end) - (sL >= sL_end); /* -1, 0 or 1 */
-}
-
-
-#define CMP_EXT_ERROR_CODE 4711
-
-#define CMP_EXT_INT32_BE(AP, BP) \
-do { \
- if ((AP)[0] != (BP)[0]) return (AP)[0] < (BP)[0] ? -1 : 1; \
- if ((AP)[1] != (BP)[1]) return (AP)[1] < (BP)[1] ? -1 : 1; \
- if ((AP)[2] != (BP)[2]) return (AP)[2] < (BP)[2] ? -1 : 1; \
- if ((AP)[3] != (BP)[3]) return (AP)[3] < (BP)[3] ? -1 : 1; \
-} while (0)
-
-#define CMP_EXT_SKIP_ATOM(EP) \
-do { \
- if (!jump_atom(&(EP))) \
- return CMP_EXT_ERROR_CODE; \
-} while (0)
-
-/*
- * We now know that both byte arrays are of the same type.
- */
-static int compare_top_ext(unsigned char**, unsigned char **); /* forward */
-static int cmp_exe2(unsigned char **e1, unsigned char **e2);
-
-static int cmp_refs(unsigned char **e1, unsigned char **e2)
-{
- int tmp, n1, n2;
- unsigned char *node1, *node2, *id1, *id2, cre1, cre2;
-
- if (*((*e1)++) == ERL_REFERENCE_EXT) {
- node1 = *e1;
- CMP_EXT_SKIP_ATOM(*e1);
- n1 = 1;
- id1 = *e1;
- cre1 = (*e1)[4];
- *e1 += 5;
- } else {
- n1 = get16be(*e1);
- node1 = *e1;
- CMP_EXT_SKIP_ATOM(*e1);
- cre1 = **e1;
- id1 = (*e1) + 1 + (n1 - 1)*4;
- *e1 = id1 + 4;
- }
-
- if (*((*e2)++) == ERL_REFERENCE_EXT) {
- node2 = *e2;
- CMP_EXT_SKIP_ATOM(*e2);
- n2 = 1;
- id2 = *e2;
- cre2 = (*e2)[4];
- *e2 += 5;
- } else {
- n2 = get16be(*e2);
- node2 = *e2;
- CMP_EXT_SKIP_ATOM(*e2);
- cre2 = **e2;
- id2 = (*e2) + 1 + (n2 - 1)*4;
- *e2 = id2 + 4;
- }
-
- /* First compare node names... */
- tmp = cmp_exe2(&node1, &node2);
- if (tmp != 0)
- return tmp;
-
- /* ... then creations ... */
- if (cre1 != cre2)
- return cre1 < cre2 ? -1 : 1;
-
- /* ... and then finally ids. */
- if (n1 != n2) {
- unsigned char zero[] = {0, 0, 0, 0};
- if (n1 > n2)
- do {
- CMP_EXT_INT32_BE(id1, zero);
- id1 -= 4;
- n1--;
- } while (n1 > n2);
- else
- do {
- CMP_EXT_INT32_BE(zero, id2);
- id2 -= 4;
- n2--;
- } while (n2 > n1);
- }
-
- for (; n1 > 0; n1--, id1 -= 4, id2 -= 4)
- CMP_EXT_INT32_BE(id1, id2);
-
- return 0;
-}
-
-static int cmp_string_list(unsigned char **e1, unsigned char **e2) {
-
- /* we need to compare a string in **e1 and a list in **e2 */
- /* convert the string to list representation and convert that with e2 */
- /* we need a temporary buffer of: */
- /* 5 (list tag + length) + 2*string length + 1 (end of list tag) */
- /* for short lists we use a stack allocated buffer, otherwise we malloc */
-
- unsigned char *bp;
- unsigned char buf[5+2*255+1]; /* used for short lists */
- int i,e1_len;
- int res;
-
- e1_len = ((*e1)[1] << 8) | ((*e1)[2]);
- if ( e1_len < 256 ) {
- bp = buf;
- } else {
- bp = erl_malloc(5+(2*e1_len)+1);
- }
-
- bp[0] = ERL_LIST_EXT;
- bp[1] = bp[2] = 0;
- bp[3] = (*e1)[1];
- bp[4] = (*e1)[2];
-
- for(i=0;i<e1_len;i++) {
- bp[5+2*i] = ERL_SMALL_INTEGER_EXT;
- bp[5+2*i+1] = (*e1)[3+i];
- }
-
- bp[5+2*e1_len] = ERL_NIL_EXT;
-
- res = cmp_exe2(&bp, e2);
-
- if ( e1_len >= 256 ) free(bp);
-
- return res;
-}
-
-static int cmp_exe2(unsigned char **e1, unsigned char **e2)
-{
- int min, ret,i,j,k;
- double ff1, ff2;
- unsigned char tag1, tag2;
-
- if ( ((*e1)[0] == ERL_STRING_EXT) && ((*e2)[0] == ERL_LIST_EXT) ) {
- return cmp_string_list(e1, e2);
- } else if ( ((*e1)[0] == ERL_LIST_EXT) && ((*e2)[0] == ERL_STRING_EXT) ) {
- return -cmp_string_list(e2, e1);
- }
-
- tag1 = *(*e1)++;
- tag2 = *(*e2)++;
- i = j = 0;
- switch (tag1)
- {
- case ERL_SMALL_INTEGER_EXT:
- if (**e1 < **e2) ret = -1;
- else if (**e1 > **e2) ret = 1;
- else ret = 0;
- *e1 += 1; *e2 += 1;
- return ret;
- case ERL_INTEGER_EXT:
- i = (int) (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
- j = (int) (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
- if ( i < j)
- ret = -1;
- else if ( i > j)
- ret = 1;
- else
- ret = 0;
- *e1 += 4; *e2 += 4;
- return ret;
- case ERL_ATOM_EXT:
- case ERL_ATOM_UTF8_EXT:
- i = (**e1) << 8; (*e1)++;
- j = (**e2) << 8; (*e2)++;
- /*fall through*/
- case ERL_SMALL_ATOM_EXT:
- case ERL_SMALL_ATOM_UTF8_EXT:
- i |= (**e1); (*e1)++;
- j |= (**e2); (*e2)++;
- ret = cmpatoms(*e1, i, tag1, *e2, j, tag2);
- *e1 += i;
- *e2 += j;
- return ret;
- case ERL_PID_EXT:
- case ERL_NEW_PID_EXT: {
- erlang_pid pid1, pid2;
- unsigned char* buf1 = *e1 - 1;
- unsigned char* buf2 = *e2 - 1;
- int ix1 = 0, ix2 = 0;
-
- if (ei_decode_pid((char*)buf1, &ix1, &pid1) ||
- ei_decode_pid((char*)buf2, &ix2, &pid2))
- return CMP_EXT_ERROR_CODE;
-
- *e1 = buf1 + ix1;
- *e2 = buf2 + ix2;
-
- /* First compare serials ... */
- if (pid1.serial < pid2.serial) return -1;
- else if (pid1.serial > pid2.serial) return 1;
-
- /* ... then ids ... */
- if (pid1.num < pid2.num) return -1;
- else if (pid1.num > pid2.num) return 1;
-
- /* ... then node names ... */
- j = strcmp(pid1.node, pid2.node);
- if (j < 0) return -1;
- else if (j > 0) return 1;
-
- /* ... and then finaly creations. */
- if (pid1.creation < pid2.creation) return -1;
- else if (pid1.creation > pid2.creation) return 1;
-
- return 0;
- }
- case ERL_PORT_EXT:
- case ERL_NEW_PORT_EXT: {
- erlang_port port1, port2;
- unsigned char* buf1 = *e1 - 1;
- unsigned char* buf2 = *e2 - 1;
- int ix1 = 0, ix2 = 0;
-
- if (ei_decode_port((char*)buf1, &ix1, &port1) ||
- ei_decode_port((char*)buf2, &ix2, &port2))
- return CMP_EXT_ERROR_CODE;
-
- *e1 = buf1 + ix1;
- *e2 = buf2 + ix2;
-
- /* First compare node names ... */
- j = strcmp(port1.node, port2.node);
- if (j < 0) return -1;
- else if (j > 0) return 1;
-
- /* ... then creations ... */
- if (port1.creation < port2.creation) return -1;
- else if (port1.creation > port2.creation) return 1;
-
- /* ... and then finally ids. */
- if (port1.id < port2.id) return -1;
- else if (port1.id > port2.id) return 1;
-
- return 0;
- }
- case ERL_NIL_EXT: return 0;
- case ERL_LIST_EXT:
- i = (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
- *e1 += 4;
- j = (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
- *e2 += 4;
- if ( i == j && j == 0 ) return 0;
- min = (i < j) ? i : j;
- k = 0;
- while (1) {
- if (k++ == min){
- if (i == j) return compare_top_ext(e1 , e2);
- if (i < j) return -1;
- return 1;
- }
- if ((ret = compare_top_ext(e1 , e2)) == 0)
- continue;
- return ret;
- }
- case ERL_STRING_EXT:
- i = (**e1 << 8) | ((*e1)[1]);
- *e1 += 2;
- j = (**e2 << 8) | ((*e2)[1]);
- *e2 += 2;
- ret = cmpbytes(*e1, i, *e2, j);
- *e1 += i;
- *e2 += j;
- return ret;
- case ERL_SMALL_TUPLE_EXT:
- i = *(*e1)++; j = *(*e2)++;
- if (i < j) return -1;
- if (i > j ) return 1;
- while (i--) {
- if ((j = compare_top_ext(e1, e2))) return j;
- }
- return 0;
- case ERL_LARGE_TUPLE_EXT:
- i = (**e1 << 24) | ((*e1)[1]) << 16| ((*e1)[2]) << 8| ((*e1)[3]) ;
- *e1 += 4;
- j = (**e2 << 24) | ((*e2)[1]) << 16| ((*e2)[2]) << 8| ((*e2)[3]) ;
- *e2 += 4;
- if (i < j) return -1;
- if (i > j ) return 1;
- while (i--) {
- if ((j = compare_top_ext(e1, e2))) return j;
- }
- return 0;
- case ERL_FLOAT_EXT:
- case NEW_FLOAT_EXT:
- i = -1;
- if (ei_decode_double((char *) *e1, &i, &ff1) != 0)
- return -1;
- *e1 += i;
- j = -1;
- if (ei_decode_double((char *) *e2, &j, &ff2) != 0)
- return -1;
- *e2 += j;
- return cmp_floats(ff1,ff2);
-
- case ERL_BINARY_EXT:
- i = (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
- *e1 += 4;
- j = (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
- *e2 += 4;
- ret = cmpbytes(*e1, i , *e2 , j);
- *e1 += i; *e2 += j;
- return ret;
-
- case ERL_FUN_EXT: /* FIXME: */
- case ERL_NEW_FUN_EXT: /* FIXME: */
- return -1;
-
- default:
- return cmpbytes(*e1, 1, *e2, 1);
-
- } /* switch */
-
-} /* cmp_exe2 */
-
-/* Number compare */
-
-static int cmp_floats(double f1, double f2)
-{
-#if defined(VXWORKS) && CPU == PPC860
- return erl_fp_compare((unsigned *) &f1, (unsigned *) &f2);
-#else
- if (f1<f2) return -1;
- else if (f1>f2) return 1;
- else return 0;
-#endif
-}
-
-static INLINE double to_float(long l)
-{
- double f;
-#if defined(VXWORKS) && CPU == PPC860
- erl_long_to_fp(l, (unsigned *) &f);
-#else
- f = l;
-#endif
- return f;
-}
-
-
-static int cmp_small_big(unsigned char**e1, unsigned char **e2)
-{
- int i1,i2;
- int t2;
- int n2;
- long l1;
- int res;
-
- erlang_big *b1,*b2;
-
- i1 = i2 = 0;
- if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) return -1;
-
- ei_get_type((char *)*e2,&i2,&t2,&n2);
-
- /* any small will fit in two digits */
- if ( (b1 = ei_alloc_big(2)) == NULL ) return -1;
- if ( ei_small_to_big(l1,b1) < 0 ) {
- ei_free_big(b1);
- return -1;
- }
-
- if ( (b2 = ei_alloc_big(n2)) == NULL ) {
- ei_free_big(b1);
- return 1;
- }
-
- if ( ei_decode_big((char *)*e2,&i2,b2) < 0 ) {
- ei_free_big(b1);
- ei_free_big(b2);
- return 1;
- }
-
- res = ei_big_comp(b1,b2);
-
- ei_free_big(b1);
- ei_free_big(b2);
-
- *e1 += i1;
- *e2 += i2;
-
- return res;
-}
-
-static int cmp_small_float(unsigned char**e1, unsigned char **e2)
-{
- int i1,i2;
- long l1;
- double f1,f2;
-
- /* small -> float -> float_comp */
-
- i1 = i2 = 0;
- if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) return -1;
- if ( ei_decode_double((char *)*e2,&i2,&f2) < 0 ) return 1;
-
- f1 = to_float(l1);
-
- *e1 += i1;
- *e2 += i2;
-
- return cmp_floats(f1,f2);
-}
-
-static int cmp_float_big(unsigned char**e1, unsigned char **e2)
-{
- int res;
- int i1,i2;
- int t2,n2;
- double f1,f2;
- erlang_big *b2;
-
- /* big -> float if overflow return big sign else float_comp */
-
- i1 = i2 = 0;
- if ( ei_decode_double((char *)*e1,&i1,&f1) < 0 ) return -1;
-
- if (ei_get_type((char *)*e2,&i2,&t2,&n2) < 0) return 1;
- if ((b2 = ei_alloc_big(n2)) == NULL) return 1;
- if (ei_decode_big((char *)*e2,&i2,b2) < 0) return 1;
-
- /* convert the big to float */
- if ( ei_big_to_double(b2,&f2) < 0 ) {
- /* exception look at the sign */
- res = b2->is_neg ? 1 : -1;
- ei_free_big(b2);
- return res;
- }
-
- ei_free_big(b2);
-
- *e1 += i1;
- *e2 += i2;
-
- return cmp_floats(f1,f2);
-}
-
-static int cmp_small_small(unsigned char**e1, unsigned char **e2)
-{
- int i1,i2;
- long l1,l2;
-
- i1 = i2 = 0;
- if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) {
- fprintf(stderr,"Failed to decode 1\r\n");
- return -1;
- }
- if ( ei_decode_long((char *)*e2,&i2,&l2) < 0 ) {
- fprintf(stderr,"Failed to decode 2\r\n");
- return 1;
- }
-
- *e1 += i1;
- *e2 += i2;
-
- if ( l1 < l2 ) return -1;
- else if ( l1 > l2 ) return 1;
- else return 0;
-}
-
-static int cmp_float_float(unsigned char**e1, unsigned char **e2)
-{
- int i1,i2;
- double f1,f2;
-
- i1 = i2 = 0;
- if ( ei_decode_double((char *)*e1,&i1,&f1) < 0 ) return -1;
- if ( ei_decode_double((char *)*e2,&i2,&f2) < 0 ) return 1;
-
- *e1 += i1;
- *e2 += i2;
-
- return cmp_floats(f1,f2);
-}
-
-static int cmp_big_big(unsigned char**e1, unsigned char **e2)
-{
- int res;
- int i1,i2;
- int t1,t2;
- int n1,n2;
- erlang_big *b1,*b2;
-
- i1 = i2 = 0;
- ei_get_type((char *)*e1,&i1,&t1,&n1);
- ei_get_type((char *)*e2,&i2,&t2,&n2);
-
- if ( (b1 = ei_alloc_big(n1)) == NULL) return -1;
- if ( (b2 = ei_alloc_big(n2)) == NULL) {
- ei_free_big(b1);
- return 1;
- }
-
- ei_decode_big((char *)*e1,&i1,b1);
- ei_decode_big((char *)*e2,&i2,b2);
-
- res = ei_big_comp(b1,b2);
-
- ei_free_big(b1);
- ei_free_big(b2);
-
- *e1 += i1;
- *e2 += i2;
-
- return res;
-}
-
-static int cmp_number(unsigned char**e1, unsigned char **e2)
-{
- switch (CMP_NUM_CODE(**e1,**e2)) {
-
- case SMALL_BIG:
- /* fprintf(stderr,"compare small_big\r\n"); */
- return cmp_small_big(e1,e2);
-
- case BIG_SMALL:
- /* fprintf(stderr,"compare sbig_small\r\n"); */
- return -cmp_small_big(e2,e1);
-
- case SMALL_FLOAT:
- /* fprintf(stderr,"compare small_float\r\n"); */
- return cmp_small_float(e1,e2);
-
- case FLOAT_SMALL:
- /* fprintf(stderr,"compare float_small\r\n"); */
- return -cmp_small_float(e2,e1);
-
- case FLOAT_BIG:
- /* fprintf(stderr,"compare float_big\r\n"); */
- return cmp_float_big(e1,e2);
-
- case BIG_FLOAT:
- /* fprintf(stderr,"compare big_float\r\n"); */
- return -cmp_float_big(e2,e1);
-
- case SMALL_SMALL:
- /* fprintf(stderr,"compare small_small\r\n"); */
- return cmp_small_small(e1,e2);
-
- case FLOAT_FLOAT:
- /* fprintf(stderr,"compare float_float\r\n"); */
- return cmp_float_float(e1,e2);
-
- case BIG_BIG:
- /* fprintf(stderr,"compare big_big\r\n"); */
- return cmp_big_big(e1,e2);
-
- default:
- /* should never get here ... */
- /* fprintf(stderr,"compare standard\r\n"); */
- return cmp_exe2(e1,e2);
- }
-
-}
-
-/*
- * If the arrays are of the same type, then we
- * have to do a real compare.
- */
-/*
- * COMPARE TWO encoded BYTE ARRAYS e1 and e2.
- * Return: -1 if e1 < e2
- * 0 if e1 == e2
- * 1 if e2 > e1
- */
-static int compare_top_ext(unsigned char**e1, unsigned char **e2)
-{
- if (**e1 == ERL_VERSION_MAGIC) (*e1)++;
- if (**e2 == ERL_VERSION_MAGIC) (*e2)++;
-
- if (cmp_array[**e1] < cmp_array[**e2]) return -1;
- if (cmp_array[**e1] > cmp_array[**e2]) return 1;
-
- if (IS_ERL_NUM(**e1))
- return cmp_number(e1,e2);
-
- if (cmp_array[**e1] == ERL_REF_CMP)
- return cmp_refs(e1, e2);
-
- return cmp_exe2(e1, e2);
-}
-
-int erl_compare_ext(unsigned char *e1, unsigned char *e2)
-{
- return compare_top_ext(&e1, &e2);
-} /* erl_compare_ext */
-
-#if defined(VXWORKS) && CPU == PPC860
-/* FIXME we have no floating point but don't we have emulation?! */
-int erl_fp_compare(unsigned *a, unsigned *b)
-{
- /* Big endian mode of powerPC, IEEE floating point. */
- unsigned a_split[4] = {a[0] >> 31, /* Sign bit */
- (a[0] >> 20) & 0x7FFU, /* Exponent */
- a[0] & 0xFFFFFU, /* Mantissa MS bits */
- a[1]}; /* Mantissa LS bits */
- unsigned b_split[4] = {b[0] >> 31,
- (b[0] >> 20) & 0x7FFU,
- b[0] & 0xFFFFFU,
- b[1]};
- int a_is_infinite, b_is_infinite;
- int res;
-
-
- /* Make -0 be +0 */
- if (a_split[1] == 0 && a_split[2] == 0 && a_split[3] == 0)
- a_split[0] = 0;
- if (b_split[1] == 0 && b_split[2] == 0 && b_split[3] == 0)
- b_split[0] = 0;
- /* Check for infinity */
- a_is_infinite = (a_split[1] == 0x7FFU && a_split[2] == 0 &&
- a_split[3] == 0);
- b_is_infinite = (b_split[1] == 0x7FFU && b_split[2] == 0 &&
- b_split[3] == 0);
-
- if (a_is_infinite && !b_is_infinite)
- return (a_split[0]) ? -1 : 1;
- if (b_is_infinite && !a_is_infinite)
- return (b_split[0]) ? 1 : -1;
- if (a_is_infinite && b_is_infinite)
- return b[0] - a[0];
- /* Check for indeterminate or nan, infinite is already handled,
- so we only check the exponent. */
- if((a_split[1] == 0x7FFU) || (b_split[1] == 0x7FFU))
- return INT_MAX; /* Well, they are not equal anyway,
- abort() could be an alternative... */
-
- if (a_split[0] && !b_split[0])
- return -1;
- if (b_split[0] && !a_split[0])
- return 1;
- /* Compare */
- res = memcmp(a_split + 1, b_split + 1, 3 * sizeof(unsigned));
- /* Make -1, 0 or 1 */
- res = (!!res) * ((res < 0) ? -1 : 1);
- /* Turn sign if negative values */
- if (a_split[0]) /* Both are negative */
- res = -1 * res;
- return res;
-}
-
-static void join(unsigned d_split[4], unsigned *d)
-{
- d[0] = (d_split[0] << 31) | /* Sign bit */
- ((d_split[1] & 0x7FFU) << 20) | /* Exponent */
- (d_split[2] & 0xFFFFFU); /* Mantissa MS bits */
- d[1] = d_split[3]; /* Mantissa LS bits */
-}
-
-static int blength(unsigned long l)
-{
- int i;
- for(i = 0; l; ++i)
- l >>= 1;
- return i;
-}
-
-static void erl_long_to_fp(long l, unsigned *d)
-{
- unsigned d_split[4];
- unsigned x;
- if (l < 0) {
- d_split[0] = 1;
- x = -l;
- } else {
- d_split[0] = 0;
- x = l;
- }
-
- if (!l) {
- memset(d_split,0,sizeof(d_split));
- } else {
- int len = blength(x);
- x <<= (33 - len);
- d_split[2] = (x >> 12);
- d_split[3] = (x << 20);
- d_split[1] = 1023 + len - 1;
- }
- join(d_split,d);
-}
-
-#endif
-
-
-/*
- * Checks if a term is a "string": a flat list of byte-sized integers.
- *
- * Returns: 0 if the term is not a string, otherwise the length is returned.
- */
-
-static int is_string(ETERM* term)
-{
- int len = 0;
-
- while (ERL_TYPE(term) == ERL_LIST) {
- ETERM* head = HEAD(term);
-
- if (!ERL_IS_INTEGER(head) || ((unsigned)head->uval.ival.i) > 255) {
- return 0;
- }
- len++;
- term = TAIL(term);
- }
-
- if (ERL_IS_EMPTY_LIST(term)) {
- return len;
- }
- return 0;
-}
diff --git a/lib/erl_interface/src/legacy/erl_resolve.c b/lib/erl_interface/src/legacy/erl_resolve.c
deleted file mode 100644
index bb09caec85..0000000000
--- a/lib/erl_interface/src/legacy/erl_resolve.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2003-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/***************************************************************************
- *
- * Compatibility with the old erl_interface library that had some
- * undocumented functions.
- *
- ***************************************************************************/
-
-#include "eidef.h"
-
-#include "erl_interface.h"
-#include "ei_resolve.h"
-#include "ei_connect_int.h"
-#include "ei_epmd.h"
-
-struct hostent *erl_gethostbyname(const char *name)
-{
- return ei_gethostbyname(name);
-}
-
-
-void erl_init_resolve(void)
-{
- ei_init_resolve();
-}
-
-
-struct hostent *erl_gethostbyaddr(const char *addr, int len, int type)
-{
- return ei_gethostbyaddr(addr, len, type);
-}
-
-
-struct hostent *erl_gethostbyname_r(const char *name,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop)
-{
- return ei_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
-}
-
-
-struct hostent *erl_gethostbyaddr_r(const char *addr,
- int length,
- int type,
- struct hostent *hostp,
- char *buffer,
- int buflen,
- int *h_errnop)
-{
- return ei_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
-}
-
-
-int erl_distversion(int fd)
-{
- return ei_distversion(fd);
-}
-
-int erl_epmd_connect(struct in_addr *inaddr)
-{
- return ei_epmd_connect_tmo(inaddr,0);
-}
-
-int erl_epmd_port(struct in_addr *inaddr, const char *alive, int *dist)
-{
- return ei_epmd_port(inaddr, alive, dist);
-}
-
-
-
-/* FIXME !!!!!
-erl_epmd_port ei_epmd_port
-erl_mutex_lock ei_mutex_lock
-erl_malloc erl_free ????
-erl_publish erl_unpublish
-< extern int erl_epmd_connect(struct in_addr *inaddr);
-< extern int erl_epmd_publish(int port, const char *alive);
-< extern int erl_epmd_port(struct in_addr *inaddr, const char *alive, int *dist);
-
-< int erl_unpublish(const char *alive)
----
-> int ei_unpublish_alive(const char *alive)
-
-erl_self
-erl_getfdcookie
-*/
diff --git a/lib/erl_interface/src/legacy/erl_timeout.c b/lib/erl_interface/src/legacy/erl_timeout.c
deleted file mode 100644
index e36ea0e250..0000000000
--- a/lib/erl_interface/src/legacy/erl_timeout.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-/* timeout.c
- *
- * todo: use posix timers (timer_create etc) instead of setitimer.
- *
- */
-#if !defined(__WIN32__) && !defined(VXWORKS)
-
-/* FIXME: well, at least I can compile now... */
-
-#include "eidef.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
-#include <signal.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
-#include "erl_interface.h"
-#include "erl_timeout.h"
-
-typedef struct jmp_s {
- jmp_buf jmpbuf;
- struct itimerval timerinfo;
- void *siginfo;
- struct jmp_s *next;
-} *jmp_t;
-
-static jmp_t push(jmp_t j);
-static jmp_t pop(void);
-static void timeout_handler(int dummy);
-
-jmp_buf *timeout_setup(int ms)
-{
- struct itimerval t;
- jmp_t j;
- void *s;
-
-#ifdef DEBUG
- fprintf(stderr,"timeout setup\n");
-#endif
- s=signal(SIGALRM,timeout_handler);
-
- /* set the timer */
- t.it_interval.tv_sec = 0;
- t.it_interval.tv_usec = 0;
- t.it_value.tv_sec = ms / 1000;
- t.it_value.tv_usec = (ms % 1000) * 1000;
-
- /* get a jump buffer and save it */
- j = erl_malloc(sizeof(*j));
- j->siginfo = s;
- push(j);
-
- setitimer(ITIMER_REAL,&t,&(j->timerinfo));
-
- return &(j->jmpbuf);
-}
-
-
-int timeout_cancel(void)
-{
- jmp_t j;
-
-#ifdef DEBUG
- fprintf(stderr,"timeout cancel\n");
-#endif
- /* retrieve the jump buffer */
- j=pop();
- /* restore the timer and signal disposition */
- setitimer(ITIMER_REAL,&(j->timerinfo),NULL);
- signal(SIGALRM,j->siginfo);
-
- free(j);
-
- return 0;
-}
-
-void timeout_handler(int dummy)
-{
- jmp_t j;
-
-#ifdef DEBUG
- fprintf(stderr,"timeout handler\n");
-#endif
-
- /* retrieve the jump buffer */
- j=pop();
-
- /* restore the timer and signal disposition */
- setitimer(ITIMER_REAL,&(j->timerinfo),NULL);
- signal(SIGALRM,j->siginfo);
-
- free(j);
- longjmp(j->jmpbuf,JMPVAL);
- return; /* not reached */
-}
-
-
-/* a simple stack for saving the jump buffer allows us to pass a
- * variable between functions that don't call each other, in a way
- * that will survive the longjmp().
- */
-
-/* FIXME problem for threaded ? */
-static jmp_t jmp_head=NULL;
-#ifdef DEBUG
-static int depth = 0;
-static int maxdepth = 0;
-#endif
-
-static jmp_t push(jmp_t j)
-{
- j->next = jmp_head;
- jmp_head = j;
-
-#ifdef DEBUG
- depth++;
- if (depth > maxdepth) maxdepth = depth;
-#endif
-
- return j;
-}
-
-static jmp_t pop(void)
-{
- jmp_t j = jmp_head;
- if (j) jmp_head = j->next;
-#ifdef DEBUG
- depth--;
-#endif
- return j;
-}
-
-#endif /* platform */
diff --git a/lib/erl_interface/src/legacy/erl_timeout.h b/lib/erl_interface/src/legacy/erl_timeout.h
deleted file mode 100644
index 6bcfa5ecbb..0000000000
--- a/lib/erl_interface/src/legacy/erl_timeout.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _ERL_TIMEOUT_H
-#define _ERL_TIMEOUT_H
-
-#if !defined (__WIN32__) && !defined (VXWORKS)
-
-#include <setjmp.h>
-
-/*
- use timeout like this (delay in ms):
-
- if (timeout(delay,fun(a,b,c))) {
- printf("timeout occurred\n");
- }
- else {
- ...
- }
-
-If the call to fun() has not returned before 'delay' ms, it will be
-interrupted and and timeout() will return a non-zero value.
-
-If fun() finishes before 'delay' ms, then timeout will return 0.
-
-If you need the return value from fun then assign it like this:
-
- if (timeout(delay,(x = fun(...)))) {
- }
-
-These functions work by setting and catching SIGALRM, and although it
-saves and restores the signal handler, it may not work in situations
-where you are already using SIGALRM (this includes calls to sleep(3)).
-
-Note that although recursive calls to timeout will not fail, they may
-not give the expected results. All invocations of timeout use the same
-timer, which is set on entrance to timeout and restored on exit from
-timeout. So although an inner call to timeout will restart the timer
-for any pending outer call when it exits, any time that has already
-elapsed against the outer timeout is forgotten. In addition, the alarm
-signal will always go to the innermost (last called) timeout, which
-may or may not be the intention in recursive cases.
-
-*/
-
-#define JMPVAL 997 /* magic */
-
-#define timeout(ms,funcall) \
- (setjmp(*timeout_setup(ms)) == JMPVAL ? -1: \
- ((void)(funcall), timeout_cancel()))
-
-
-/* don't call any of these directly - use the macro! see above! */
-jmp_buf *timeout_setup(int ms);
-int timeout_cancel(void);
-
-#endif /* WIN32 && VXWORKS */
-
-#endif /* _ERL_TIMEOUT_H */
diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c
index a188171f40..695c3404f7 100644
--- a/lib/erl_interface/src/misc/ei_format.c
+++ b/lib/erl_interface/src/misc/ei_format.c
@@ -24,10 +24,6 @@
* ei_format to build binary format terms a bit like printf
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
diff --git a/lib/erl_interface/src/misc/ei_locking.c b/lib/erl_interface/src/misc/ei_locking.c
index a5ddbb85f2..20464869ad 100644
--- a/lib/erl_interface/src/misc/ei_locking.c
+++ b/lib/erl_interface/src/misc/ei_locking.c
@@ -26,7 +26,7 @@
/* Note that these locks are NOT recursive on Win32 or Solaris,
* i.e. self-deadlock will occur if a thread tries to obtain a lock it
- * is already holding. The primitives used on VxWorks are recursive however.
+ * is already holding.
*/
#include "eidef.h"
@@ -36,10 +36,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <vxWorks.h>
-#include <semLib.h>
-
#else /* unix */
#include <stdlib.h>
#include <unistd.h>
@@ -63,12 +59,6 @@ ei_mutex_t *ei_mutex_create(void)
#ifdef __WIN32__
l->lock = CreateMutex(NULL,FALSE,NULL);
-
-#elif VXWORKS
- if (!(l->lock = semMCreate(SEM_DELETE_SAFE))) {
- ei_free(l);
- return NULL;
- }
#else /* unix */
l->lock = ei_m_create();
#endif
@@ -97,10 +87,6 @@ int ei_mutex_free(ei_mutex_t *l, int nblock)
/* we are now holding the lock */
#ifdef __WIN32__
CloseHandle(l->lock);
-
-#elif VXWORKS
- if (semDelete(l->lock) == ERROR) return -1;
-
#else /* unix */
ei_m_destroy(l->lock);
#endif
@@ -131,11 +117,6 @@ int ei_mutex_lock(ei_mutex_t *l, int nblock)
/* check valid values for timeout: is 0 ok? */
if (WaitForSingleObject(l->lock,(nblock? 0 : INFINITE)) != WAIT_OBJECT_0)
return -1;
-
-#elif VXWORKS
- if (semTake(l->lock,(nblock? NO_WAIT : WAIT_FOREVER)) == ERROR)
- return -1;
-
#else /* unix */
if (nblock) {
if (ei_m_trylock(l->lock) < 0) return -1;
@@ -151,10 +132,6 @@ int ei_mutex_unlock(ei_mutex_t *l)
{
#ifdef __WIN32__
ReleaseMutex(l->lock);
-
-#elif VXWORKS
- semGive(l->lock);
-
#else /* unix */
ei_m_unlock(l->lock);
#endif
diff --git a/lib/erl_interface/src/misc/ei_locking.h b/lib/erl_interface/src/misc/ei_locking.h
index 1bbee2d499..93aade6b2d 100644
--- a/lib/erl_interface/src/misc/ei_locking.h
+++ b/lib/erl_interface/src/misc/ei_locking.h
@@ -24,11 +24,6 @@
#include "config.h"
-#if defined(VXWORKS)
-#include <taskLib.h>
-#include <taskVarLib.h>
-#endif
-
#ifdef __WIN32__
#include <winsock2.h>
#include <windows.h>
@@ -45,8 +40,6 @@
typedef struct ei_mutex_s {
#ifdef __WIN32__
HANDLE lock;
-#elif VXWORKS
- SEM_ID lock;
#else /* unix */
#if defined(HAVE_MIT_PTHREAD_H) || defined(HAVE_PTHREAD_H)
pthread_mutex_t *lock;
@@ -64,7 +57,7 @@ int ei_mutex_lock(ei_mutex_t *l, int nblock);
int ei_mutex_unlock(ei_mutex_t *l);
-#if defined(_REENTRANT) && !defined(VXWORKS) && !defined(__WIN32__)
+#if defined(_REENTRANT) && !defined(__WIN32__)
void *ei_m_create(void);
int ei_m_destroy(void *l);
@@ -72,6 +65,6 @@ int ei_m_lock(void *l);
int ei_m_trylock(void *l);
int ei_m_unlock(void *l);
-#endif /* _REENTRANT && !VXWORKS && !__WIN32__ */
+#endif /* _REENTRANT && !__WIN32__ */
#endif /* _EI_LOCKING_H */
diff --git a/lib/erl_interface/src/misc/ei_portio.c b/lib/erl_interface/src/misc/ei_portio.c
index bccc86c1b1..afa766a776 100644
--- a/lib/erl_interface/src/misc/ei_portio.c
+++ b/lib/erl_interface/src/misc/ei_portio.c
@@ -42,29 +42,7 @@ static unsigned long param_one = 1;
#define MEANS_SOCKET_ERROR(Ret) ((Ret == SOCKET_ERROR))
#define IS_INVALID_SOCKET(Sock) ((Sock) == INVALID_SOCKET)
-#elif VXWORKS
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-#include <selectLib.h>
-#include <ioLib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <timers.h>
-
-static unsigned long param_zero = 0;
-static unsigned long param_one = 1;
-#define SET_BLOCKING(Sock) ioctl((Sock),FIONBIO,(int)&param_zero)
-#define SET_NONBLOCKING(Sock) ioctl((Sock),FIONBIO,(int)&param_one)
-#define MEANS_SOCKET_ERROR(Ret) ((Ret) == ERROR)
-#define IS_INVALID_SOCKET(Sock) ((Sock) < 0)
-
-#else /* other unix */
+#else /* unix */
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
@@ -622,7 +600,7 @@ int ei_accept_ctx_t__(ei_socket_callbacks *cbs, void **ctx,
} while (error == EINTR);
}
do {
- error = cbs->accept(ctx, addr, len, ms);
+ error = cbs->EI_ACCEPT_NAME(ctx, addr, len, ms);
} while (error == EINTR);
return error;
}
diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h
index 5172d085b4..b47b220d51 100644
--- a/lib/erl_interface/src/misc/ei_portio.h
+++ b/lib/erl_interface/src/misc/ei_portio.h
@@ -23,7 +23,7 @@
#define _EI_PORTIO_H
#undef EI_HAVE_STRUCT_IOVEC__
-#if !defined(__WIN32__) && !defined(VXWORKS) && defined(HAVE_SYS_UIO_H)
+#if !defined(__WIN32__) && defined(HAVE_SYS_UIO_H)
/* Declaration of struct iovec *iov should be visible in this scope. */
# include <sys/uio.h>
# define EI_HAVE_STRUCT_IOVEC__
diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c
index aee7f7eeb0..0dfecb6d4b 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -24,10 +24,6 @@
* ei_print_term to print out a binary coded term
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
diff --git a/lib/erl_interface/src/misc/ei_pthreads.c b/lib/erl_interface/src/misc/ei_pthreads.c
index c6d07a9a0a..df7a7fbb8f 100644
--- a/lib/erl_interface/src/misc/ei_pthreads.c
+++ b/lib/erl_interface/src/misc/ei_pthreads.c
@@ -38,25 +38,6 @@ static LONG volatile tls_init_mutex = 0;
#endif
#endif
-#if defined(VXWORKS)
-
-/*
- Moved to each of the erl_*threads.c files, as they seem to know how
- to get thread-safety.
-*/
-static volatile int __erl_errno;
-volatile int *__erl_errno_place(void)
-{
- /* This check is somewhat insufficient, double task var entries will occur
- if __erl_errno is actually -1, which on the other hand is an invalid
- error code. */
- if (taskVarGet(taskIdSelf(), &__erl_errno) == ERROR) {
- taskVarAdd(taskIdSelf(), &__erl_errno);
- }
- return &__erl_errno;
-}
-#endif /* VXWORKS */
-
#if defined(__WIN32__)
#ifdef USE_DECLSPEC_THREAD
@@ -106,7 +87,7 @@ volatile int *__erl_errno_place(void)
#endif /* __WIN32__ */
-#if defined(_REENTRANT) && !defined(VXWORKS) && !defined(__WIN32__)
+#if defined(_REENTRANT) && !defined(__WIN32__)
#if defined(HAVE_PTHREAD_H) || defined(HAVE_MIT_PTHREAD_H)
@@ -219,9 +200,9 @@ volatile int *__erl_errno_place(void)
#endif /* HAVE_PTHREAD_H || HAVE_MIT_PTHREAD_H */
-#endif /* _REENTRANT && !VXWORKS && !__WIN32__ */
+#endif /* _REENTRANT && !__WIN32__ */
-#if !defined(_REENTRANT) && !defined(VXWORKS) && !defined(__WIN32__)
+#if !defined(_REENTRANT) && !defined(__WIN32__)
volatile int __erl_errno;
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index 8e77679d2a..fe3c20faff 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -23,10 +23,6 @@
* ei_x_encode to encode in a self-expanding buffer
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/lib/erl_interface/src/misc/eidef.h b/lib/erl_interface/src/misc/eidef.h
index f38824d826..f1fdafaa08 100644
--- a/lib/erl_interface/src/misc/eidef.h
+++ b/lib/erl_interface/src/misc/eidef.h
@@ -27,11 +27,6 @@
#include "config.h" /* Central include of config.h */
-/* vxWorks.h needs to be before stddef.h */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stddef.h> /* We want to get definition of NULL */
#include "ei.h" /* Want the API function declarations */
diff --git a/lib/erl_interface/src/not_used/ei_send.c b/lib/erl_interface/src/not_used/ei_send.c
deleted file mode 100644
index 8071876677..0000000000
--- a/lib/erl_interface/src/not_used/ei_send.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- *
-
- */
-#ifdef __WIN32__
-#include <winsock2.h>
-#include <windows.h>
-#include <winbase.h>
-
-#elif VXWORKS
-#include <sys/types.h>
-#include <unistd.h>
-
-#else /* unix */
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/uio.h>
-#endif
-
-#include "eidef.h"
-#include "eiext.h"
-#include "ei_connect.h"
-#include "ei_internal.h"
-#include "putget.h"
-#include "ei_trace.h"
-#include "show_msg.h"
-
-/* FIXME this is not useed !!!!! */
-
-/* length (4), PASS_THROUGH (1), header, message */
-int ei_ei_send_encoded(ei_cnode* ec, int fd, const erlang_pid *to,
- const char *msg, int msglen)
-{
- char *s, header[1200]; /* see size calculation below */
- erlang_trace *token = NULL;
- int index = 5; /* reserve 5 bytes for control message */
-#ifdef HAVE_WRITEV
- struct iovec v[2];
-#endif
-
- /* are we tracing? */
- /* check that he can receive trace tokens first */
- if (ei_distversion(fd) > 0)
- token = ei_trace(0,(erlang_trace *)NULL);
-
- /* header = SEND, cookie, to max sizes: */
- ei_encode_version(header,&index); /* 1 */
- if (token) {
- ei_encode_tuple_header(header,&index,4); /* 2 */
- ei_encode_long(header,&index,ERL_SEND_TT); /* 2 */
- } else {
- ei_encode_tuple_header(header,&index,3);
- ei_encode_long(header,&index,ERL_SEND);
- }
- ei_encode_atom(header,&index, "" /*ei_getfdcookie(ec, fd)*/); /* 258 */
- ei_encode_pid(header,&index,to); /* 268 */
-
- if (token) ei_encode_trace(header,&index,token); /* 534 */
-
- /* control message (precedes header actually) */
- /* length = 1 ('p') + header len + message len */
- s = header;
- put32be(s, index + msglen - 4); /* 4 */
- put8(s, ERL_PASS_THROUGH); /* 1 */
- /*** sum: 1070 */
-
-#ifdef DEBUG_DIST
- if (ei_trace_distribution > 0) ei_show_sendmsg(stderr,header,msg);
-#endif
-
-#ifdef HAVE_WRITEV
-
- v[0].iov_base = (char *)header;
- v[0].iov_len = index;
- v[1].iov_base = (char *)msg;
- v[1].iov_len = msglen;
-
- if (writev(fd,v,2) != index+msglen) return -1;
-
-#else /* !HAVE_WRITEV */
-
- if (writesocket(fd,header,index) != index) return -1;
- if (writesocket(fd,msg,msglen) != msglen) return -1;
-
-#endif /* !HAVE_WRITEV */
-
- return 0;
-}
diff --git a/lib/erl_interface/src/not_used/ei_send_reg.c b/lib/erl_interface/src/not_used/ei_send_reg.c
deleted file mode 100644
index ba9c7348f9..0000000000
--- a/lib/erl_interface/src/not_used/ei_send_reg.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- *
-
- */
-#ifdef __WIN32__
-#include <winsock2.h>
-#include <windows.h>
-#include <winbase.h>
-
-#elif VXWORKS
-#include <sys/types.h>
-#include <unistd.h>
-
-#else /* unix */
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/uio.h>
-#endif
-
-#include "eidef.h"
-#include "eiext.h"
-#include "ei_connect.h"
-#include "ei_internal.h"
-#include "putget.h"
-#include "ei_trace.h"
-#include "show_msg.h"
-
-/* FIXME this is not useed !!!!! */
-/* FIXME merge with ei_send.c */
-
-/* length (4), PASS_THROUGH (1), header, message */
-int ei_ei_send_reg_encoded(ei_cnode* ec, int fd, const erlang_pid *from,
- const char *to, const char *msg, int msglen)
-{
- char *s, header[1400]; /* see size calculation below */
- erlang_trace *token = NULL;
- int index = 5; /* reserve 5 bytes for control message */
-#ifdef HAVE_WRITEV
- struct iovec v[2];
-#endif
-
- /* are we tracing? */
- /* check that he can receive trace tokens first */
- if (ei_distversion(fd) > 0)
- token = ei_trace(0,(erlang_trace *)NULL);
-
- /* header = REG_SEND, from, cookie, toname max sizes: */
- ei_encode_version(header,&index); /* 1 */
- if (token) {
- ei_encode_tuple_header(header,&index,5); /* 2 */
- ei_encode_long(header,&index,ERL_REG_SEND_TT); /* 2 */
- } else {
- ei_encode_tuple_header(header,&index,4);
- ei_encode_long(header,&index,ERL_REG_SEND);
- }
- ei_encode_pid(header,&index,from); /* 268 */
- ei_encode_atom(header,&index,"" /*ei_getfdcookie(ec, fd)*/ ); /* 258 */
- ei_encode_atom(header,&index,to); /* 268 */
-
- if (token) ei_encode_trace(header,&index,token); /* 534 */
-
- /* control message (precedes header actually) */
- /* length = 1 ('p') + header len + message len */
- s = header;
- put32be(s, index + msglen - 4); /* 4 */
- put8(s, ERL_PASS_THROUGH); /* 1 */
- /*** sum: 1336 */
-
-#ifdef DEBUG_DIST
- if (ei_trace_distribution > 0) ei_show_sendmsg(stderr,header,msg);
-#endif
-
-#ifdef HAVE_WRITEV
-
- v[0].iov_base = (char *)header;
- v[0].iov_len = index;
- v[1].iov_base = (char *)msg;
- v[1].iov_len = msglen;
-
- if (writev(fd,v,2) != index+msglen) return -1;
-
-#else
-
- /* no writev() */
- if (writesocket(fd,header,index) != index) return -1;
- if (writesocket(fd,msg,msglen) != msglen) return -1;
-
-#endif
-
- return 0;
-}
diff --git a/lib/erl_interface/src/not_used/send_link.c b/lib/erl_interface/src/not_used/send_link.c
deleted file mode 100644
index 38fae27df4..0000000000
--- a/lib/erl_interface/src/not_used/send_link.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- *
-
- */
-#ifdef __WIN32__
-#include <winsock2.h>
-#include <windows.h>
-#include <winbase.h>
-
-#elif VXWORKS
-#include <unistd.h>
-
-#else /* unix */
-#include <unistd.h>
-
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-#include "eidef.h"
-#include "eiext.h"
-#include "eisend.h"
-#include "ei_internal.h"
-#include "putget.h"
-#include "erl_rport.h"
-
-
-/* this sends either link or unlink ('which' decides) */
-static int link_unlink(int fd, const erlang_pid *from, const erlang_pid *to,
- int which, unsigned ms)
-{
- char msgbuf[EISMALLBUF];
- char *s;
- int index = 0;
- int n;
- unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
-
- index = 5; /* max sizes: */
- ei_encode_version(msgbuf,&index); /* 1 */
- ei_encode_tuple_header(msgbuf,&index,3);
- ei_encode_long(msgbuf,&index,which);
- ei_encode_pid(msgbuf,&index,from); /* 268 */
- ei_encode_pid(msgbuf,&index,to); /* 268 */
-
- /* 5 byte header missing */
- s = msgbuf;
- put32be(s, index - 4); /* 4 */
- put8(s, ERL_PASS_THROUGH); /* 1 */
- /* sum: 542 */
-
-
-#ifdef DEBUG_DIST
- if (ei_trace_distribution > 1) ei_show_sendmsg(stderr,msgbuf,NULL);
-#endif
-
- n = ei_write_fill_t__(fd,msgbuf,index,tmo);
-
- return (n==index ? 0 : -1);
-}
-
-/* FIXME not used? */
-#if 0
-/* use this to send a link */
-int ei_send_unlink(int fd, const erlang_pid *from, const erlang_pid *to)
-{
- return link_unlink(fd, from, to, ERL_UNLINK,0);
-}
-
-/* use this to send an unlink */
-int ei_send_link(int fd, const erlang_pid *from, const erlang_pid *to)
-{
- return link_unlink(fd, from, to, ERL_LINK,0);
-}
-/* use this to send a link */
-int ei_send_unlink_tmo(int fd, const erlang_pid *from, const erlang_pid *to,
- unsigned ms)
-{
- return link_unlink(fd, from, to, ERL_UNLINK,ms);
-}
-
-/* use this to send an unlink */
-int ei_send_link_tmo(int fd, const erlang_pid *from, const erlang_pid *to,
- unsigned ms)
-{
- return link_unlink(fd, from, to, ERL_LINK,ms);
-}
-#endif
diff --git a/lib/erl_interface/src/not_used/whereis.c b/lib/erl_interface/src/not_used/whereis.c
deleted file mode 100644
index 4072fa7b33..0000000000
--- a/lib/erl_interface/src/not_used/whereis.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- *
-
- */
-#ifdef __WIN32__
-#include <winsock2.h>
-#include <windows.h>
-#include <winbase.h>
-
-#elif VXWORKS
-#include <unistd.h>
-
-#else /* unix */
-#include <unistd.h>
-
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-#include "erl_interface.h"
-#include "erl_connect.h"
-#include "erl_format.h"
-#include "erl_eterm.h"
-#include "erl_malloc.h"
-
-/* FIXME rewrite to ei functions */
-/* FIXME not used */
-
-erlang_pid *erl_whereis(int fd, const char *name)
-{
- ETERM *reply;
- ETERM *n;
- /* FIXME problem for threaded ? */
- static erlang_pid pid;
-
- n = erl_format("[~a]",name);
- reply = erl_rpc(fd,"erlang","whereis",n);
- erl_free_term(n);
-
- if (reply && (ERL_IS_PID(reply))) {
- char *node;
- node = ERL_PID_NODE(reply);
- strcpy(pid.node,node);
- pid.num = ERL_PID_NUMBER(reply);
- pid.serial = ERL_PID_SERIAL(reply);
- pid.creation = ERL_PID_CREATION(reply);
- erl_free_term(reply);
- return &pid;
- }
-
- if (reply) erl_free_term(reply);
- return NULL;
-}
-
diff --git a/lib/erl_interface/src/prog/ei_fake_prog.c b/lib/erl_interface/src/prog/ei_fake_prog.c
index 6f58c9833d..c483859c8b 100644
--- a/lib/erl_interface/src/prog/ei_fake_prog.c
+++ b/lib/erl_interface/src/prog/ei_fake_prog.c
@@ -54,11 +54,7 @@
/* #include <netdb.h> now included by ei.h */
#include "ei.h"
-#ifdef VXWORKS
-int ei_fake_prog_main(void)
-#else
int main(void)
-#endif
{
ErlConnect conp;
Erl_IpAddr thisipaddr = (Erl_IpAddr)0;
@@ -91,12 +87,10 @@ int main(void)
unsigned long *ulongp = NULL;
unsigned long ulongx = 0;
void *voidp = NULL;
-#ifndef VXWORKS
EI_LONGLONG *longlongp = (EI_LONGLONG*)NULL;
EI_LONGLONG longlongx = 0;
EI_ULONGLONG *ulonglongp = (EI_ULONGLONG*)NULL;
EI_ULONGLONG ulonglongx = 0;
-#endif
erlang_char_encoding enc;
ei_socket_callbacks cbs;
@@ -260,8 +254,6 @@ int main(void)
}
#endif /* HAVE_GMP_H && HAVE_LIBGMP */
-#ifndef VXWORKS
-
ei_decode_longlong(charp, intp, longlongp);
ei_decode_ulonglong(charp, intp, ulonglongp);
ei_encode_longlong(charp, intp, longlongx);
@@ -269,8 +261,6 @@ int main(void)
ei_x_encode_longlong(&eix, longlongx);
ei_x_encode_ulonglong(&eix, ulonglongx);
-#endif
-
#ifdef USE_EI_UNDOCUMENTED
ei_decode_intlist(charp, intp, longp, intp);
diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c
index ab91157035..f3d025d30f 100644
--- a/lib/erl_interface/src/prog/erl_call.c
+++ b/lib/erl_interface/src/prog/erl_call.c
@@ -33,23 +33,6 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-
-#include <stdio.h>
-#include <string.h>
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <selectLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <time.h>
-
#else /* unix */
#include <sys/types.h>
@@ -104,6 +87,8 @@ struct call_flags {
int debugp;
int verbosep;
int haltp;
+ long port;
+ char *hostname;
char *cookie;
char *node;
char *hidden;
@@ -131,13 +116,7 @@ static char* ei_chk_strdup(char *s);
*
***************************************************************************/
-/* FIXME isn't VxWorks to handle arguments differently? */
-
-#if !defined(VXWORKS)
int main(int argc, char *argv[])
-#else
-int erl_call(int argc, char **argv)
-#endif
{
int i = 1,fd,creation;
struct hostent *hp;
@@ -152,6 +131,8 @@ int erl_call(int argc, char **argv)
struct call_flags flags = {0}; /* Default 0 and NULL in all fields */
char* progname = argv[0];
ei_cnode ec;
+ flags.port = -1;
+ flags.hostname = NULL;
ei_init();
@@ -177,6 +158,29 @@ int erl_call(int argc, char **argv)
flags.node = ei_chk_strdup(argv[i+1]);
i++;
flags.use_long_name = 1;
+ } else if (strcmp(argv[i], "-address") == 0) { /* -address [HOST:]PORT */
+ if (i+1 >= argc) {
+ usage_arg(progname, "-address ");
+ }
+ {
+ char* hostname_port_arg = ei_chk_strdup(argv[i+1]);
+ char* address_string_end = strchr(hostname_port_arg, ':');
+ if (address_string_end == NULL) {
+ flags.port = strtol(hostname_port_arg, NULL, 10);
+ } else {
+ flags.port = strtol(address_string_end + 1, NULL, 10);
+ /* Remove port part from hostname_port_arg*/
+ *address_string_end = '\0';
+ if (strlen(hostname_port_arg) > 0) {
+ flags.hostname = hostname_port_arg;
+ }
+ }
+
+ if (flags.port < 1 || flags.port > 65535) {
+ usage_error(progname, "-address");
+ }
+ i++;
+ }
} else {
if (strlen(argv[i]) != 2) {
usage_error(progname, argv[i]);
@@ -251,11 +255,12 @@ int erl_call(int argc, char **argv)
} /* while */
-
/*
* Can't have them both !
*/
- if (flags.modp && flags.evalp) {
+ if ((flags.modp && flags.evalp) ||
+ (flags.port != -1 && flags.startp) ||
+ (flags.port != -1 && flags.node)) {
usage(progname);
}
@@ -284,7 +289,7 @@ int erl_call(int argc, char **argv)
/*
* What we, at least, requires !
*/
- if (flags.node == NULL) {
+ if (flags.node == NULL && flags.port == -1) {
usage(progname);
}
@@ -292,20 +297,14 @@ int erl_call(int argc, char **argv)
flags.cookie = NULL;
}
- /* FIXME decide how many bits etc or leave to connect_xinit? */
- creation = (time(NULL) % 3) + 1; /* "random" */
+ creation = time(NULL) + 1; /* "random" */
if (flags.hidden == NULL) {
/* As default we are c17@gethostname */
i = flags.randomp ? (time(NULL) % 997) : 17;
flags.hidden = (char *) ei_chk_malloc(10 + 2 ); /* c17 or cXYZ */
-#if defined(VXWORKS)
- sprintf(flags.hidden, "c%d",
- i < 0 ? (int) taskIdSelf() : i);
-#else
sprintf(flags.hidden, "c%d",
i < 0 ? (int) getpid() : i);
-#endif
}
{
/* A name for our hidden node was specified */
@@ -346,10 +345,15 @@ int erl_call(int argc, char **argv)
}
}
- if ((p = strchr((const char *)flags.node, (int) '@')) == 0) {
+ if (flags.port != -1 && flags.hostname != NULL) {
+ host = flags.hostname;
+ strcpy(host_name, flags.hostname);
+ } else if ((flags.port != -1 && flags.hostname == NULL) ||
+ (strchr((const char *)flags.node, (int) '@') == 0)) {
strcpy(host_name, ei_thishostname(&ec));
host = host_name;
} else {
+ p = strchr((const char *)flags.node, (int) '@');
*p = 0;
host = p+1;
}
@@ -368,28 +372,45 @@ int erl_call(int argc, char **argv)
}
strncpy(host_name, hp->h_name, EI_MAXHOSTNAMELEN);
host_name[EI_MAXHOSTNAMELEN] = '\0';
- if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
- fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
- exit(1);
+ if (flags.port == -1) {
+ if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
+ fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
+ exit(1);
+ }
+ sprintf(nodename, "%s@%s", flags.node, host_name);
}
- sprintf(nodename, "%s@%s", flags.node, host_name);
-
/*
* Try to connect. Start an Erlang system if the
* start option is on and no system is running.
*/
if (flags.startp && !flags.haltp) {
fd = do_connect(&ec, nodename, &flags);
- } else if ((fd = ei_connect(&ec, nodename)) < 0) {
- /* We failed to connect ourself */
- /* FIXME do we really know we failed because of node not up? */
- if (flags.haltp) {
- exit(0);
- } else {
- fprintf(stderr,"erl_call: failed to connect to node %s\n",
- nodename);
- exit(1);
- }
+ } else if (flags.port == -1) {
+ if ((fd = ei_connect(&ec, nodename)) < 0) {
+ /* We failed to connect ourself */
+ /* FIXME do we really know we failed because of node not up? */
+ if (flags.haltp) {
+ exit(0);
+ } else {
+ fprintf(stderr,"erl_call: failed to connect to node %s\n",
+ nodename);
+ exit(1);
+ }
+ }
+ } else {
+ /* Connect using address:port */
+ if ((fd = ei_connect_host_port(&ec, host, (int)flags.port)) < 0) {
+ /* We failed to connect ourself */
+ /* FIXME do we really know we failed because of node not up? */
+ if (flags.haltp) {
+ exit(0);
+ } else {
+ fprintf(stderr,"erl_call: failed to connect to node with address \"%s:%ld\"\n",
+ flags.hostname == NULL ? "" : flags.hostname,
+ flags.port);
+ exit(1);
+ }
+ }
}
/* If we are connected and the halt switch is set */
@@ -415,8 +436,14 @@ int erl_call(int argc, char **argv)
}
if (flags.verbosep) {
- fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n",
- nodename);
+ if (flags.port == -1) {
+ fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n",
+ nodename);
+ } else {
+ fprintf(stderr,"erl_call: we are now connected to node with address \"%s:%ld\"\n",
+ flags.hostname == NULL ? "": flags.hostname,
+ flags.port);
+ }
}
/*
@@ -809,7 +836,7 @@ static int get_module(char **mbuf, char **mname)
static void usage_noexit(const char *progname) {
fprintf(stderr,"\nUsage: %s [-[demqrsv]] [-c Cookie] [-h HiddenName] \n", progname);
fprintf(stderr," [-x ErlScript] [-a [Mod [Fun [Args]]]]\n");
- fprintf(stderr," (-n Node | -sname Node | -name Node)\n\n");
+ fprintf(stderr," (-n Node | -sname Node | -name Node | -address [HOSTNAME:]PORT)\n\n");
#ifdef __WIN32__
fprintf(stderr," where: -a apply(Mod,Fun,Args) (e.g -a \"erlang length [[a,b,c]]\"\n");
#else
@@ -817,12 +844,18 @@ static void usage_noexit(const char *progname) {
#endif
fprintf(stderr," -c cookie string; by default read from ~/.erlang.cookie\n");
fprintf(stderr," -d direct Erlang output to ~/.erl_call.out.<Nodename>\n");
- fprintf(stderr," -e evaluate contents of standard input (e.g echo \"X=1,Y=2,{X,Y}.\"|erl_call -e ...)\n");
+ fprintf(stderr," -e evaluate contents of standard input (e.g., echo \"X=1,Y=2,{X,Y}.\"|%s -e ...)\n",
+ progname);
fprintf(stderr," -h specify a name for the erl_call client node\n");
fprintf(stderr," -m read and compile Erlang module from stdin\n");
fprintf(stderr," -n name of Erlang node, same as -name\n");
fprintf(stderr," -name name of Erlang node, expanded to a fully qualified\n");
fprintf(stderr," -sname name of Erlang node, short form will be used\n");
+ fprintf(stderr," -address [HOSTNAME:]PORT of Erlang node\n"
+ " (the default hostname is the hostname of the local manchine)\n"
+ " (e.g., %s -address my_host:36303 ...)\n"
+ " (cannot be combinated with -s, -n, -name and -sname)\n",
+ progname);
fprintf(stderr," -q halt the Erlang node (overrides the -s switch)\n");
fprintf(stderr," -r use a random name for the erl_call client node\n");
fprintf(stderr," -s start a new Erlang node if necessary\n");
diff --git a/lib/erl_interface/src/prog/erl_fake_prog.c b/lib/erl_interface/src/prog/erl_fake_prog.c
deleted file mode 100644
index 093bad8d7c..0000000000
--- a/lib/erl_interface/src/prog/erl_fake_prog.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/***************************************************************************
- *
- * This is a fake program that contains all functions, variables and
- * defined symbols mentioned in the manual. We compile this file to see
- * that the header files and created library is complete.
- *
- * You can't run this program, it is for compiling and linking only.
- *
- ***************************************************************************/
-
-/* Use most of
- * CFLAGS="-I../include -g -O2
- * -ansi -pedantic
- * -Wall
- * -Wshadow
- * -Wstrict-prototypes
- * -Wmissing-prototypes
- * -Wmissing-declarations
- * -Wnested-externs
- * -Winline
- * -Werror"
- */
-
-/* #include <netdb.h> now included by ei.h */
-#include "erl_interface.h"
-
-#ifdef VXWORKS
-int erl_fake_prog_main(void)
-#else
-int main(void)
-#endif
-{
- ei_x_buff eix;
- int index = 0;
- ETERM **etermpp = NULL, *etermp = NULL;
- char *charp = NULL;
- unsigned char uchar, **ucharpp = NULL, *ucharp = NULL;
- void *voidp = NULL;
- Erl_Heap *erl_heapp = NULL;
- int intx = 0;
- int *intp = NULL;
- unsigned int uintx, *uintp;
- unsigned long *ulongp = NULL;
- long longx = 0;
- double doublex = 0.0;
- short shortx = 42;
- FILE *filep = NULL;
- Erl_IpAddr erl_ipaddr = NULL;
- ErlMessage *erlmessagep = NULL;
- ErlConnect *erlconnectp = NULL;
- struct hostent *hostp = NULL;
- struct in_addr *inaddrp = NULL;
-
- /* Converion to erl_interface format is in liberl_interface */
-
- intx = erl_errno;
-
- ei_encode_term(charp, &index, voidp);
- ei_x_encode_term(&eix, voidp);
- ei_decode_term(charp, &index, voidp);
-
- erl_init(voidp, longx);
- erl_connect_init(intx, charp,shortx);
- erl_connect_xinit(charp,charp,charp,erl_ipaddr,charp,shortx);
- erl_connect(charp);
- erl_xconnect(erl_ipaddr,charp);
- erl_close_connection(intx);
- erl_receive(intx, ucharp, intx);
- erl_receive_msg(intx, ucharp, intx, erlmessagep);
- erl_xreceive_msg(intx, ucharpp, intp, erlmessagep);
- erl_send(intx, etermp, etermp);
- erl_reg_send(intx, charp, etermp);
- erl_rpc(intx,charp,charp,etermp);
- erl_rpc_to(intx,charp,charp,etermp);
- erl_rpc_from(intx,intx,erlmessagep);
-
- erl_publish(intx);
- erl_accept(intx,erlconnectp);
-
- erl_thiscookie();
- erl_thisnodename();
- erl_thishostname();
- erl_thisalivename();
- erl_thiscreation();
- erl_unpublish(charp);
- erl_err_msg(charp);
- erl_err_quit(charp);
- erl_err_ret(charp);
- erl_err_sys(charp);
-
- erl_cons(etermp,etermp);
- erl_copy_term(etermp);
- erl_element(intx,etermp);
-
- erl_hd(etermp);
- erl_iolist_to_binary(etermp);
- erl_iolist_to_string(etermp);
- erl_iolist_length(etermp);
- erl_length(etermp);
- erl_mk_atom(charp);
- erl_mk_binary(charp,intx);
- erl_mk_empty_list();
- erl_mk_estring(charp, intx);
- erl_mk_float(doublex);
- erl_mk_int(intx);
- erl_mk_list(etermpp,intx);
- erl_mk_pid(charp,uintx,uintx,uchar);
- erl_mk_port(charp,uintx,uchar);
- erl_mk_ref(charp,uintx,uchar);
- erl_mk_long_ref(charp,uintx,uintx,uintx,uchar);
- erl_mk_string(charp);
- erl_mk_tuple(etermpp,intx);
- erl_mk_uint(uintx);
- erl_mk_var(charp);
- erl_print_term(filep,etermp);
- /* erl_sprint_term(charp,etermp); */
- erl_size(etermp);
- erl_tl(etermp);
- erl_var_content(etermp, charp);
-
- erl_format(charp);
- erl_match(etermp, etermp);
-
- erl_global_names(intx, intp);
- erl_global_register(intx, charp, etermp);
- erl_global_unregister(intx, charp);
- erl_global_whereis(intx, charp, charp);
-
- erl_init_malloc(erl_heapp,longx);
- erl_alloc_eterm(uchar);
- erl_eterm_release();
- erl_eterm_statistics(ulongp,ulongp);
- erl_free_array(etermpp,intx);
- erl_free_term(etermp);
- erl_free_compound(etermp);
- erl_malloc(longx);
- erl_free(voidp);
-
- erl_compare_ext(ucharp, ucharp);
- erl_decode(ucharp);
- erl_decode_buf(ucharpp);
- erl_encode(etermp,ucharp);
- erl_encode_buf(etermp,ucharpp);
- erl_ext_size(ucharp);
- erl_ext_type(ucharp);
- erl_peek_ext(ucharp,intx);
- erl_term_len(etermp);
-
- erl_gethostbyname(charp);
- erl_gethostbyaddr(charp, intx, intx);
- erl_gethostbyname_r(charp, hostp, charp, intx, intp);
- erl_gethostbyaddr_r(charp, intx, intx, hostp, charp, intx, intp);
-
- erl_init_resolve();
- erl_distversion(intx);
-
- erl_epmd_connect(inaddrp);
- erl_epmd_port(inaddrp, charp, intp);
-
- charp = ERL_ATOM_PTR(etermp);
- intx = ERL_ATOM_SIZE(etermp);
- ucharp = ERL_BIN_PTR(etermp);
- intx = ERL_BIN_SIZE(etermp);
- etermp = ERL_CONS_HEAD(etermp);
- etermp = ERL_CONS_TAIL(etermp);
- intx = ERL_COUNT(etermp);
- doublex= ERL_FLOAT_VALUE(etermp);
- uintx = ERL_INT_UVALUE(etermp);
- intx = ERL_INT_VALUE(etermp);
- intx = ERL_IS_ATOM(etermp);
- intx = ERL_IS_BINARY(etermp);
- intx = ERL_IS_CONS(etermp);
- intx = ERL_IS_EMPTY_LIST(etermp);
- intx = ERL_IS_FLOAT(etermp);
- intx = ERL_IS_INTEGER(etermp);
- intx = ERL_IS_LIST(etermp);
- intx = ERL_IS_PID(etermp);
- intx = ERL_IS_PORT(etermp);
- intx = ERL_IS_REF(etermp);
- intx = ERL_IS_TUPLE(etermp);
- intx = ERL_IS_UNSIGNED_INTEGER(etermp);
- uchar = ERL_PID_CREATION(etermp);
- charp = ERL_PID_NODE(etermp);
- uintx = ERL_PID_NUMBER(etermp);
- uintx = ERL_PID_SERIAL(etermp);
- uchar = ERL_PORT_CREATION(etermp);
- charp = ERL_PORT_NODE(etermp);
- uintx = ERL_PORT_NUMBER(etermp);
- uchar = ERL_REF_CREATION(etermp);
- intx = ERL_REF_LEN(etermp);
- charp = ERL_REF_NODE(etermp);
- uintx = ERL_REF_NUMBER(etermp);
- uintp = ERL_REF_NUMBERS(etermp);
- etermp = ERL_TUPLE_ELEMENT(etermp,intx);
- intx = ERL_TUPLE_SIZE(etermp);
-
- return
- BUFSIZ +
- EAGAIN +
- EHOSTUNREACH +
- EINVAL +
- EIO +
- EMSGSIZE +
- ENOMEM +
- ERL_ATOM +
- ERL_BINARY +
- ERL_ERROR +
- ERL_EXIT +
- ERL_FLOAT +
- ERL_INTEGER +
- ERL_LINK +
- ERL_LIST +
- ERL_MSG +
- ERL_NO_TIMEOUT +
- ERL_PID +
- ERL_PORT +
- ERL_REF +
- ERL_REG_SEND +
- ERL_SEND +
- ERL_SMALL_BIG +
- ERL_TICK +
- ERL_TIMEOUT +
- ERL_TUPLE +
- ERL_UNLINK +
- ERL_U_INTEGER +
- ERL_U_SMALL_BIG +
- ERL_VARIABLE +
- ETIMEDOUT +
- MAXNODELEN +
- MAXREGLEN;
-}
diff --git a/lib/erl_interface/src/prog/erl_start.c b/lib/erl_interface/src/prog/erl_start.c
index b7aa451946..a4930443d4 100644
--- a/lib/erl_interface/src/prog/erl_start.c
+++ b/lib/erl_interface/src/prog/erl_start.c
@@ -31,40 +31,7 @@
#include <windows.h>
#include <winbase.h>
-#elif VXWORKS
-#include <stdio.h>
-#include <string.h>
-#include <vxWorks.h>
-#include <hostLib.h>
-#include <selectLib.h>
-#include <ifLib.h>
-#include <sockLib.h>
-#include <taskLib.h>
-#include <inetLib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <symLib.h>
-#include <sysSymTbl.h>
-#include <sysLib.h>
-#include <tickLib.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
-#include <a_out.h>
-
-/* #include "netdb.h" */
-#else /* other unix */
+#else /* unix */
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
@@ -111,7 +78,7 @@ typedef socklen_t SocklenType;
static struct in_addr *get_addr(const char *hostname, struct in_addr *oaddr);
static int wait_for_erlang(int sockd, int magic, struct timeval *timeout);
-#if defined(VXWORKS) || defined(__WIN32__)
+#if defined(__WIN32__)
static int unique_id(void);
static unsigned long spawn_erlang_epmd(ei_cnode *ec,
char *alive,
@@ -148,7 +115,7 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
int port;
int sockd = 0;
int one = 1;
-#if defined(VXWORKS) || defined(__WIN32__)
+#if defined(__WIN32__)
unsigned long pid = 0;
#else
int pid = 0;
@@ -177,7 +144,7 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
listen(sockd,5);
-#if defined(VXWORKS) || defined(__WIN32__)
+#if defined(__WIN32__)
if((pid = spawn_erlang_epmd(ec,alive,adr,flags,erl,args,port,1))
== 0)
return ERL_SYS_ERROR;
@@ -185,15 +152,9 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
timeout.tv_sec = 10; /* ignoring ERL_START_TIME */
if((r = wait_for_erlang(sockd,unique_id(),&timeout))
== ERL_TIMEOUT) {
-#if defined(VXWORKS)
- taskDelete((int) pid);
- if(taskIdVerify((int) pid) != ERROR)
- taskDeleteForce((int) pid);
-#else /* Windows */
/* Well, this is not a nice way to do it, and it does not
always kill the emulator, but the alternatives are few.*/
TerminateProcess((HANDLE) pid,1);
-#endif /* defined(VXWORKS) */
}
#else /* Unix */
switch ((pid = fork())) {
@@ -229,7 +190,7 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
}
}
-#endif /* defined(VXWORKS) || defined(__WIN32__) */
+#endif /* defined(__WIN32__) */
done:
#if defined(__WIN32__)
@@ -240,18 +201,10 @@ done:
return r;
} /* erl_start_sys() */
-#if defined(VXWORKS) || defined(__WIN32__)
-#if defined(VXWORKS)
-#define DEF_ERL_COMMAND ""
-#define DEF_EPMD_COMMAND ""
-#define ERLANG_SYM "start_erl"
-#define EPMD_SYM "start_epmd"
-#define ERL_REPLY_FMT "-s erl_reply reply %s %d %d"
-#else
+#if defined(__WIN32__)
#define DEF_ERL_COMMAND "erl"
#define DEF_EPMD_COMMAND "epmd"
#define ERL_REPLY_FMT "-s erl_reply reply \"%s\" \"%d\" \"%d\""
-#endif
#define ERL_NAME_FMT "-noinput -name %s"
#define ERL_SNAME_FMT "-noinput -sname %s"
@@ -259,11 +212,7 @@ done:
#define FORMATTED_INT_LEN 10
static int unique_id(void){
-#if defined(VXWORKS)
- return taskIdSelf();
-#else
return (int) GetCurrentThreadId();
-#endif
}
static int enquote_args(char **oargs, char ***qargs){
@@ -317,20 +266,7 @@ static void free_args(char **args){
free(args);
}
-#if defined(VXWORKS)
-static FUNCPTR lookup_function(char *symname){
- char *value;
- SYM_TYPE type;
- if(symFindByName(sysSymTbl,
- symname,
- &value,
- &type) == ERROR /*|| type != N_TEXT*/)
- return NULL;
- return (FUNCPTR) value;
-}
-#endif /* defined(VXWORKS) */
-
-/* In NT and VxWorks, we cannot fork(), Erlang and Epmd gets
+/* In NT we cannot fork(), Erlang and Epmd gets
spawned by this function instead. */
static unsigned long spawn_erlang_epmd(ei_cnode *ec,
@@ -342,13 +278,9 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
int port,
int is_erlang)
{
-#if defined(VXWORKS)
- FUNCPTR erlfunc;
-#else /* Windows */
STARTUPINFO sinfo;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pinfo;
-#endif
char *cmdbuf;
int cmdlen;
char *ptr;
@@ -362,14 +294,10 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
if(is_erlang){
get_addr(ei_thishostname(ec), &myaddr);
-#if defined(VXWORKS)
- inet_ntoa_b(myaddr, iaddrbuf);
-#else /* Windows */
if((ptr = inet_ntoa(myaddr)) == NULL)
return 0;
else
strcpy(iaddrbuf,ptr);
-#endif
}
if ((flags & ERL_START_REMOTE) ||
(is_erlang && (hisaddr->s_addr != myaddr.s_addr))) {
@@ -378,11 +306,7 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
num_args = enquote_args(args, &args);
for(cmdlen = i = 0; args[i] != NULL; ++i)
cmdlen += strlen(args[i]) + 1;
-#if !defined(VXWORKS)
- /* On VxWorks, we dont actually run a command,
- we call start_erl() */
if(!erl_or_epmd)
-#endif
erl_or_epmd = (is_erlang) ? DEF_ERL_COMMAND :
DEF_EPMD_COMMAND;
if(is_erlang){
@@ -417,23 +341,6 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
fprintf(stderr,"erl_call: commands are %s\n",cmdbuf);
}
/* OK, one single command line... */
-#if defined(VXWORKS)
- erlfunc = lookup_function((is_erlang) ? ERLANG_SYM :
- EPMD_SYM);
- if(erlfunc == NULL){
- if (flags & ERL_START_VERBOSE) {
- fprintf(stderr,"erl_call: failed to find symbol %s\n",
- (is_erlang) ? ERLANG_SYM : EPMD_SYM);
- }
- ret = 0;
- } else {
- /* Just call it, it spawns itself... */
- ret = (unsigned long)
- (*erlfunc)((int) cmdbuf,0,0,0,0,0,0,0,0,0);
- if(ret == (unsigned long) ERROR)
- ret = 0;
- }
-#else /* Windows */
/* Hmmm, hidden or unhidden window??? */
memset(&sinfo,0,sizeof(sinfo));
sinfo.cb = sizeof(STARTUPINFO);
@@ -460,7 +367,6 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
ret = 0;
else
ret = (unsigned long) pinfo.hProcess;
-#endif
free(cmdbuf);
return ret;
}
@@ -488,7 +394,7 @@ static int exec_erlang(ei_cnode *ec,
char *args[],
int port)
{
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
int fd,len,l,i;
char **s;
char *argv[4];
@@ -587,7 +493,7 @@ static int exec_erlang(ei_cnode *ec,
return ERL_SYS_ERROR;
} /* exec_erlang() */
-#endif /* defined(VXWORKS) || defined(WINDOWS) */
+#endif /* defined(WINDOWS) */
#if defined(__WIN32__)
static void gettimeofday(struct timeval *now,void *dummy){
@@ -601,13 +507,6 @@ static void gettimeofday(struct timeval *now,void *dummy){
now->tv_usec = x % 1000000;
}
-#elif defined(VXWORKS)
-static void gettimeofday(struct timeval *now, void *dummy){
- int rate = sysClkRateGet(); /* Ticks per second */
- unsigned long ctick = tickGet();
- now->tv_sec = ctick / rate; /* secs since reboot */
- now->tv_usec = ((ctick - (now->tv_sec * rate))*1000000)/rate;
-}
#endif
diff --git a/lib/erl_interface/src/registry/reg_get.c b/lib/erl_interface/src/registry/reg_get.c
index 67d99e231e..73975f6a91 100644
--- a/lib/erl_interface/src/registry/reg_get.c
+++ b/lib/erl_interface/src/registry/reg_get.c
@@ -19,10 +19,6 @@
*
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stdarg.h>
#include "reg.h"
diff --git a/lib/erl_interface/src/registry/reg_set.c b/lib/erl_interface/src/registry/reg_set.c
index 95b90adb87..3846df1cb5 100644
--- a/lib/erl_interface/src/registry/reg_set.c
+++ b/lib/erl_interface/src/registry/reg_set.c
@@ -19,10 +19,6 @@
*
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#include <stdarg.h>
#include "reg.h"
diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile
index f8f2ef0156..3e3213d3e1 100644
--- a/lib/erl_interface/test/Makefile
+++ b/lib/erl_interface/test/Makefile
@@ -34,12 +34,7 @@ MODULES= \
ei_print_SUITE \
ei_tmo_SUITE \
erl_call_SUITE \
- erl_connect_SUITE \
- erl_global_SUITE \
- erl_eterm_SUITE \
- erl_ext_SUITE \
- erl_format_SUITE \
- erl_match_SUITE \
+ ei_global_SUITE \
port_call_SUITE \
runner
diff --git a/lib/erl_interface/test/all_SUITE_data/Makefile.src b/lib/erl_interface/test/all_SUITE_data/Makefile.src
index 4f27b097c8..0d242663ea 100644
--- a/lib/erl_interface/test/all_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/all_SUITE_data/Makefile.src
@@ -23,7 +23,7 @@ CC0 = @CC@
CC = .@DS@gccifier@exe@ -CC"$(CC0)"
CFLAGS0 = @CFLAGS@ -I@erl_interface_include@
CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@
-EI_COMMON_OBJS = runner@obj@ ei_runner@obj@
+EI_COMMON_OBJS = runner@obj@ ei_runner@obj@ my_ussi@obj@
ALL_OBJS = gccifier@exe@ $(EI_COMMON_OBJS)
CP=cp
diff --git a/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
index d9ad291f3d..da3d8053a0 100644
--- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl
+++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
@@ -77,11 +77,7 @@ generate_c(Cases, File, TcName) ->
lists:foreach(fun(Case) -> io:format(File, " ~s,~n", [Case]) end, Cases),
io:format(File, "~s",
[["};\n\n",
- "#ifdef VXWORKS\n",
- "int ", TcName, "(int argc, char* argv[])\n",
- "#else\n",
"int main(int argc, char* argv[])\n",
- "#endif\n",
"{\n",
" run_tests(argv[0], test_cases, ",
"sizeof(test_cases)/sizeof(test_cases[0]));\n",
diff --git a/lib/erl_interface/test/all_SUITE_data/my_ussi.c b/lib/erl_interface/test/all_SUITE_data/my_ussi.c
new file mode 100644
index 0000000000..5f8c79b7cf
--- /dev/null
+++ b/lib/erl_interface/test/all_SUITE_data/my_ussi.c
@@ -0,0 +1,198 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * User Supplied Socket Implementation (ussi)
+ * for test purpose.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "ei.h"
+
+struct my_ctx
+{
+ void* ctx;
+};
+
+/*
+ * To minimize effort but still test a different context format
+ * we cheat and wrap the existing TCP default callbacks.
+ */
+extern ei_socket_callbacks ei_default_socket_callbacks;
+
+static int my_socket(void **ctx, void *setup_ctx)
+{
+ struct my_ctx *myctx = malloc(sizeof(struct my_ctx));
+ int ret;
+ ret = ei_default_socket_callbacks.socket(&myctx->ctx, NULL);
+ *ctx = myctx;
+ return ret;
+}
+
+static int my_close(void *ctx)
+{
+ struct my_ctx *myctx = ctx;
+ int ret = ei_default_socket_callbacks.close(myctx->ctx);
+ free(myctx);
+ return ret;
+}
+
+static int my_get_fd(void *ctx, int *fd)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.get_fd(myctx->ctx, fd);
+}
+
+static int my_hs_packet_header_size(void *ctx, int *sz)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.handshake_packet_header_size(myctx->ctx, sz);
+}
+
+static int my_connect_handshake_complete(void *ctx)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.connect_handshake_complete(myctx->ctx);
+}
+
+static int my_accept_handshake_complete(void *ctx)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.accept_handshake_complete(myctx->ctx);
+}
+
+static int my_listen(void *ctx, void *addr, int *len, int backlog)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.listen(myctx->ctx, addr, len, backlog);
+}
+
+static int my_accept(void **ctx, void *addr, int *len, unsigned tmo)
+{
+ struct my_ctx *listen_ctx = *ctx;
+ struct my_ctx *conn_ctx = malloc(sizeof(struct my_ctx));
+ int ret;
+ *conn_ctx = *listen_ctx;
+ ret = ei_default_socket_callbacks.accept(&conn_ctx->ctx, addr, len, tmo);
+ if (ret == 0)
+ *ctx = conn_ctx;
+ else
+ free(conn_ctx);
+ return ret;
+}
+
+static int my_connect(void *ctx, void *addr, int len, unsigned tmo)
+{
+ struct my_ctx *myctx = ctx;
+ return ei_default_socket_callbacks.connect(myctx->ctx, addr, len, tmo);
+}
+
+static void* memdup(const void* mem, int nbytes)
+{
+ void *p = malloc(nbytes);
+ memcpy(p, mem, nbytes);
+ return p;
+}
+
+static void scramble(void* bytes, int nbytes)
+{
+/* Would be nice to really test that only our callbacks are used
+ and the default ones are not.
+ Need corresponding Erlang distribution impl to work.
+
+ unsigned char *p = bytes;
+ int i;
+ for (i=0; i < nbytes; ++i)
+ p[i] = ~p[i];
+*/
+}
+
+/* our own iovec struct to avoid config dependency HAVE_WRITEV */
+struct my_iovec {
+ void *iov_base; /* Starting address */
+ size_t iov_len; /* Number of bytes to transfer */
+};
+
+static int my_writev(void *ctx, const void *viov, int iovcnt, ssize_t *len, unsigned tmo)
+{
+ struct my_ctx *myctx = ctx;
+ struct my_iovec *iov;
+ int i, ret;
+
+ /* create mutable copy of both iovec and data */
+ iov = memdup(viov, sizeof(struct my_iovec) * iovcnt);
+ for (i=0; i < iovcnt; ++i) {
+ iov[i].iov_base = memdup(iov[i].iov_base, iov[i].iov_len);
+ scramble(iov[i].iov_base, iov[i].iov_len);
+ }
+
+ ret = ei_default_socket_callbacks.writev(myctx->ctx, viov, iovcnt, len, tmo);
+
+ for (i=0; i < iovcnt; ++i)
+ free(iov[i].iov_base);
+ free(iov);
+ return ret;
+}
+
+static int my_write(void *ctx, const char* buf, ssize_t *len, unsigned tmo)
+{
+ struct my_ctx *myctx = ctx;
+ unsigned char* copy = memdup(buf, *len);
+ int i, ret;
+
+ scramble(copy, *len);
+ ret = ei_default_socket_callbacks.write(myctx->ctx, copy, len, tmo);
+ free(copy);
+ return ret;
+}
+
+static int my_read(void *ctx, char* buf, ssize_t *len, unsigned tmo)
+{
+ struct my_ctx *myctx = ctx;
+ int ret, i;
+
+ ret = ei_default_socket_callbacks.read(myctx->ctx, buf, len, tmo);
+ if (ret == 0)
+ scramble(buf, *len);
+ return ret;
+}
+
+ei_socket_callbacks my_ussi = {
+ 0, /* flags */
+ my_socket,
+ my_close,
+ my_listen,
+ my_accept,
+ my_connect,
+ my_writev,
+ my_write,
+ my_read,
+ my_hs_packet_header_size,
+ my_connect_handshake_complete,
+ my_accept_handshake_complete,
+ my_get_fd
+};
+
+void my_ussi_init(void)
+{
+ my_ussi.flags = ei_default_socket_callbacks.flags;
+ if (!ei_default_socket_callbacks.writev)
+ my_ussi.writev = NULL;
+}
diff --git a/lib/erl_interface/src/legacy/erl_config.h b/lib/erl_interface/test/all_SUITE_data/my_ussi.h
index fb72169f23..0db07c990e 100644
--- a/lib/erl_interface/src/legacy/erl_config.h
+++ b/lib/erl_interface/test/all_SUITE_data/my_ussi.h
@@ -1,8 +1,8 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2019. All Rights Reserved.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -14,10 +14,15 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* %CopyrightEnd%
*/
-#ifndef _ERL_CONFIG_H
-#define _ERL_CONFIG_H
-#endif /* _ERL_CONFIG_H */
+/*
+ * User Supplied Socket Implementation (ussi)
+ * for test purpose.
+ */
+
+extern ei_socket_callbacks my_ussi;
+
+extern void my_ussi_init(void);
diff --git a/lib/erl_interface/test/all_SUITE_data/runner.c b/lib/erl_interface/test/all_SUITE_data/runner.c
deleted file mode 100644
index 42e8bb03e5..0000000000
--- a/lib/erl_interface/test/all_SUITE_data/runner.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifndef __WIN32__
-#include <unistd.h>
-#endif
-#include <stdarg.h>
-
-#include "runner.h"
-
-#ifndef __WIN32__
-#define _O_BINARY 0
-#define _setmode(fd, mode)
-#endif
-
-#define HEADER_SIZE 4
-
-static char* progname; /* Name of this program (from argv[0]). */
-static int fd_from_erl; /* File descriptor from Erlang. */
-static int fd_to_erl; /* File descriptor to Erlang. */
-
-static int packet_loop();
-static void ensure_buf_big_enough();
-static int readn();
-static void reply(char* buf, unsigned size);
-static void dump();
-
-void
-run_tests(char* argv0, TestCase test_cases[], unsigned number)
-{
- int i;
- int n;
- char* packet;
-
- progname = argv0;
- _setmode(0, _O_BINARY);
- _setmode(1, _O_BINARY);
- fd_from_erl = 0;
- fd_to_erl = 1;
-
- packet = read_packet(&n);
-
- /*
- * Dispatch to the appropriate test function.
- */
-
- i = packet[0] * 256 + packet[1];
- if (i >= number) {
- fprintf(stderr, "%s: bad test case number %d",
- progname, i);
- free(packet);
- exit(1);
- } else {
- (*test_cases[i])();
- free(packet);
- }
-}
-
-
-/***********************************************************************
- *
- * R e a d i n g p a c k e t s
- *
- ************************************************************************/
-
-/*
- * Reads an Erlang term.
- *
- * Returns: A pointer to a term (an ETERM structure) if there was
- * at term available, or a NULL pointer if there was an 'eot' (end-of-test)
- * packet. Aborts if anything else received.
- */
-
-ETERM*
-get_term(void)
-{
- char* encoded;
- ETERM* term;
- int n;
-
- encoded = read_packet(&n);
-
- switch (encoded[0]) {
- case 'e':
- free(encoded);
- return NULL;
- case 't':
- term = erl_decode(encoded+1);
- free(encoded);
- if (term == NULL) {
- fail("Failed to decode term");
- exit(0);
- }
- return term;
- default:
- fprintf(stderr, "Garbage received: ");
- dump(encoded, n, 16);
- putc('\n', stderr);
- fail("C program received garbage");
- free(encoded);
- exit(1);
- }
-}
-
-
-/*
- * Reads a packet from Erlang. The packet must be a standard {packet, 2}
- * packet. This function aborts if any error is detected (including EOF).
- *
- * Returns: The number of bytes in the packet.
- */
-
-char *read_packet(int *len)
-{
-
- unsigned char* io_buf = NULL; /* Buffer for file i/o. */
- int i;
- unsigned char header[HEADER_SIZE];
- unsigned packet_length; /* Length of current packet. */
- int bytes_read;
-
- /*
- * Read the packet header.
- */
-
- bytes_read = readn(fd_from_erl, header, HEADER_SIZE);
-
- if (bytes_read == 0) {
- fprintf(stderr, "%s: Unexpected end of file\n", progname);
- exit(1);
- }
- if (bytes_read != HEADER_SIZE) {
- fprintf(stderr, "%s: Failed to read packet header\n", progname);
- exit(1);
- }
-
- /*
- * Get the length of this packet.
- */
-
- packet_length = 0;
-
- for (i = 0; i < HEADER_SIZE; i++)
- packet_length = (packet_length << 8) | header[i];
-
- if (len) *len=packet_length; /* report length only if caller requested it */
-
- if ((io_buf = (char *) malloc(packet_length)) == NULL) {
- fprintf(stderr, "%s: insufficient memory for i/o buffer of size %d\n",
- progname, packet_length);
- exit(1);
- }
-
- /*
- * Read the packet itself.
- */
-
- bytes_read = readn(fd_from_erl, io_buf, packet_length);
- if (bytes_read != packet_length) {
- fprintf(stderr, "%s: couldn't read packet of length %d\r\n",
- progname, packet_length);
- free(io_buf);
- exit(1);
- }
-
- return io_buf;
-}
-
-
-/***********************************************************************
- * S e n d i n g r e p l i e s
- *
- * The functions below send various types of replies back to Erlang.
- * Each reply start with a letter indicating the type of reply.
- *
- * Reply Translated to on Erlang side
- * ----- ----------------------------
- * [$b|Bytes] {bytes, Bytes}
- * [$e] eot
- * [$f] ct:fail()
- * [$f|Reason] ct:fail(Reason)
- * [$t|EncodedTerm] {term, Term}
- * [$N] 'NULL'
- * [$m|Message] io:format("~s", [Message]) (otherwise ignored)
- *
- ***********************************************************************/
-
-/*
- * This function reports the outcome of a test fail. It is useful if
- * you implement a test case entirely in C code.
- *
- * If the ok argument is zero, a [$f] reply will be sent to the
- * Erlang side (causing ct:fail() to be called); otherwise,
- * the atom 'eot' will be sent to Erlang.
- *
- * If you need to provide more details on a failure, use the fail() function.
- */
-
-void
-do_report(file, line, ok)
- char* file;
- int line;
- int ok; /* Zero if failed; non-zero otherwise. */
-{
- char reason;
- unsigned long ab;
- unsigned long fb;
-
- reason = ok ? 'e' : 'f';
-
- if (!ok) {
- do_fail(file, line, "Generic failure");
- } else {
- /* release all unallocated blocks */
- erl_eterm_release();
- /* check mem usage stats */
- erl_eterm_statistics(&ab, &fb);
- if ((ab == 0) && (fb == 0) ) {
- reply(&reason, 1);
- }
- else {
- char sbuf[128];
-
- sprintf(sbuf, "still %lu terms allocated,"
- " %lu on freelist at end of test", ab, fb);
- do_fail(file, line, sbuf);
- }
- }
-}
-
-
-/*
- * This function causes a call to ct:fail(Reason) on the
- * Erlang side.
- */
-
-void
-do_fail(char* file, int line, char* reason)
-{
- char sbuf[2048];
-
- sbuf[0] = 'f';
- sprintf(sbuf+1, "%s, line %d: %s", file, line, reason);
- reply(sbuf, 1+strlen(sbuf+1));
-}
-
-/*
- * This function sends a message to the Erlang side.
- * The message will be written to the test servers log file,
- * but will otherwise be completly ignored.
- */
-
-void
-message(char* format, ...)
-{
- va_list ap;
- char sbuf[1024];
-
- sbuf[0] = 'm';
- va_start(ap, format);
- vsprintf(sbuf+1, format, ap);
- va_end(ap);
-
- reply(sbuf, 1+strlen(sbuf+1));
-}
-
-/*
- * This function sends the given term to the Erlang side,
- * where it will be received as {term, Term}.
- *
- * If the given pointer is NULL (indicating an invalid term),
- * the result on the Erlang side will be the atom 'NULL'.
- *
- * After sending the term, this function frees the term by
- * calling erl_free_term().
- */
-
-void
-send_term(term)
- ETERM* term; /* Term to be sent to Erlang side. */
-{
- char encoded[64*1024];
- int n;
-
- if (term == NULL) {
- encoded[0] = 'N';
- n = 1;
- } else {
- encoded[0] = 't';
- n = 1 + erl_encode(term, encoded+1);
- erl_free_term(term);
- }
- reply(encoded, n);
-}
-
-#if 0
-
-/* Seriously broken!!! */
-
-void
-send_bin_term(x_ei_buff* x)
-{
- x_ei_buff x2;
- x_ei_new(&x2);
- x2.buff[x2.index++] = 't';
- x_ei_append(&x2, x);
- reply(x2.buff, x2.index);
- free(x2.buff);
-}
-#endif
-
-/*
- * This function sends a raw buffer of data to the
- * Erlang side, where it will be received as {bytes, Bytes}.
- */
-
-void
-send_buffer(buf, size)
- char* buf; /* Buffer with bytes to send to Erlang. */
- int size; /* Size of data to send to Erlang. */
-{
- char* send_buf;
-
- send_buf = (char *) malloc(size+1);
- send_buf[0] = 'b';
- memcpy(send_buf+1, buf, size);
- reply(send_buf, size+1);
- free(send_buf);
-}
-
-/***********************************************************************
- *
- * P r i v a t e h e l p e r s
- *
- ***********************************************************************/
-
-/*
- * Sends a packet back to Erlang.
- */
-
-static void
-reply(reply_buf, size)
- char* reply_buf; /* Buffer with reply. */
- unsigned size; /* Size of reply. */
-{
- int n; /* Temporary to hold size. */
- int i; /* Loop counter. */
- char* buf;
-
-
- buf = (char *) malloc(size+HEADER_SIZE);
- memcpy(buf+HEADER_SIZE, reply_buf, size);
-
- /*
- * Fill the header starting with the least significant byte.
- */
-
- n = size;
- for (i = HEADER_SIZE-1; i >= 0; i--) {
- buf[i] = (char) n; /* Store least significant byte. */
- n = n >> 8;
- }
-
- size += HEADER_SIZE;
-/*
- fprintf(stderr, "\r\nReply size: %u\r\n",
- (unsigned)buf[0] << 8 + (unsigned)buf[1]);
-
- for (i = 0; i < size; i++) {
- fprintf(stderr,"%u %c\r\n",buf[i],buf[i]);
- }
-
- fprintf(stderr, "\r\n");
-*/
- write(fd_to_erl, buf, size);
- free(buf);
-}
-
-
-/*
- * Reads len number of bytes.
- */
-
-static int
-readn(fd, buf, len)
- int fd; /* File descriptor to read from. */
- unsigned char *buf; /* Store in this buffer. */
- int len; /* Number of bytes to read. */
-{
- int n; /* Byte count in last read call. */
- int sofar = 0; /* Bytes read so far. */
-
- do {
- if ((n = read(fd, buf+sofar, len-sofar)) <= 0)
- /* error or EOF in read */
- return(n);
- sofar += n;
- } while (sofar < len);
- return sofar;
-}
-
-void
-dump(buf, sz, max)
- unsigned char* buf;
- int sz;
- int max;
-{
- int i, imax;
- char comma[5] = ",";
-
- if (!sz)
- return;
- if (sz > max)
- imax = max;
- else
- imax = sz;
-
- for (i=0; i<imax; i++) {
- if (i == imax-1) {
- if (sz > max)
- strcpy(comma, ",...");
- else
- comma[0] = 0;
- }
- if (isdigit(buf[i]))
- fprintf(stderr, "%u%s", (int)(buf[i]), comma);
- else {
- if (isalpha(buf[i])) {
- fprintf(stderr, "%c%s", buf[i], comma);
- }
- else
- fprintf(stderr, "%u%s", (int)(buf[i]), comma);
- }
- }
-}
-
diff --git a/lib/erl_interface/test/all_SUITE_data/runner.h b/lib/erl_interface/test/all_SUITE_data/runner.h
deleted file mode 100644
index 493602869f..0000000000
--- a/lib/erl_interface/test/all_SUITE_data/runner.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#include "erl_interface.h"
-
-typedef void (*TestCase)(void);
-
-#define TESTCASE(name) void name(void)
-#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
-
-void run_tests(char* argv0, TestCase cases[], unsigned number);
-
-/*
- * Reading.
- */
-
-ETERM* get_term(void);
-char *read_packet(int *len);
-
-/*
- * Sending replies.
- */
-
-#define fail(reason) do_fail(__FILE__, __LINE__, reason)
-#define report(ok) do_report(__FILE__, __LINE__, ok)
-
-void do_report(char* file, int line, int ok);
-void do_fail(char* file, int line, char* reason);
-void send_term(ETERM* term);
-void send_buffer(char* buf, int size);
-void message(char* format, ...);
-
-void send_bin_term(ei_x_buff* x);
-
diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl
index f40c67375b..c49b8a358a 100644
--- a/lib/erl_interface/test/ei_accept_SUITE.erl
+++ b/lib/erl_interface/test/ei_accept_SUITE.erl
@@ -43,12 +43,15 @@ init_per_testcase(Case, Config) ->
runner:init_per_testcase(?MODULE, Case, Config).
ei_accept(Config) when is_list(Config) ->
- ei_accept_do(Config, 0), % default
- ei_accept_do(Config, 21). % ei_set_compat_rel
+ [ei_accept_do(Config, CR, SI)
+ || CR <- [0,21],
+ SI <- [default, ussi]],
+ ok.
-ei_accept_do(Config, CompatRel) ->
+ei_accept_do(Config, CompatRel, SockImpl) ->
+ io:format("CompatRel=~p, SockImpl=~p\n", [CompatRel, SockImpl]),
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, CompatRel),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, CompatRel, SockImpl),
Myname = hd(tl(string:tokens(atom_to_list(node()), "@"))),
io:format("Myname ~p ~n", [Myname]),
@@ -72,7 +75,8 @@ ei_accept_do(Config, CompatRel) ->
{ok, ListenFd} = ei_publish(P, Port),
{any, EINode} ! TermToSend,
- {ok, Fd, _Node} = ei_accept(P, ListenFd),
+ {ok, Fd, Node} = ei_accept(P, ListenFd),
+ Node = node(),
Got1 = ei_receive(P, Fd),
%% Send again, now without auto-connect
@@ -88,9 +92,13 @@ ei_accept_do(Config, CompatRel) ->
ei_threaded_accept(Config) when is_list(Config) ->
Einode = filename:join(proplists:get_value(data_dir, Config), "eiaccnode"),
+ ei_threaded_accept_do(Einode, default),
+ ei_threaded_accept_do(Einode, ussi),
+ ok.
+
+ei_threaded_accept_do(Einode, SockImpl) ->
N = 3,
- Host = atom_to_list(node()),
- start_einode(Einode, N, Host),
+ start_einode(Einode, N, SockImpl),
io:format("started eiaccnode"),
TestServerPid = self(),
[spawn_link(fun() -> send_rec_einode(I, TestServerPid) end) || I <- lists:seq(0, N-1)],
@@ -101,7 +109,7 @@ ei_threaded_accept(Config) when is_list(Config) ->
%% Test erlang:monitor toward erl_interface "processes"
monitor_ei_process(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, 0, default),
Myname = hd(tl(string:tokens(atom_to_list(node()), "@"))),
io:format("Myname ~p ~n", [Myname]),
@@ -125,10 +133,19 @@ monitor_ei_process(Config) when is_list(Config) ->
runner:finish(P),
- [{'DOWN', MRef1, process, {any, EINode}, noconnection},
- {'DOWN', MRef2, process, {any, EINode}, noconnection}
- ] = lists:sort(flush(2, 1000)),
-
+ ok =receive
+ {'DOWN', MRef1, process, {any, EINode}, noconnection} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ok = receive
+ {'DOWN', MRef2, process, {any, EINode}, noconnection} ->
+ ok
+ after 1000 ->
+ timeout
+ end,
+ [] = flush(0, 1000),
ok.
waitfornode(String,0) ->
@@ -164,9 +181,10 @@ send_rec_einode(N, TestServerPid) ->
ct:fail(EINode)
end.
-start_einode(Einode, N, Host) ->
+start_einode(Einode, N, SockImpl) ->
Einodecmd = Einode ++ " " ++ atom_to_list(erlang:get_cookie())
- ++ " " ++ integer_to_list(N) ++ " " ++ Host,
+ ++ " " ++ integer_to_list(N)
+ ++ " " ++ atom_to_list(SockImpl),
io:format("Einodecmd ~p ~n", [Einodecmd]),
open_port({spawn, Einodecmd}, []),
ok.
@@ -174,8 +192,8 @@ start_einode(Einode, N, Host) ->
%%% Interface functions for ei (erl_interface) functions.
-ei_connect_init(P, Num, Cookie, Creation, Compat) ->
- send_command(P, ei_connect_init, [Num,Cookie,Creation,Compat]),
+ei_connect_init(P, Num, Cookie, Creation, Compat, SockImpl) ->
+ send_command(P, ei_connect_init, [Num,Cookie,Creation,Compat,SockImpl]),
case get_term(P) of
{term,Int} when is_integer(Int) -> Int
end.
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/Makefile.src b/lib/erl_interface/test/ei_accept_SUITE_data/Makefile.src
index 10ef437f8b..7fd0a123b4 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/Makefile.src
@@ -25,6 +25,7 @@ CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
LD = @LD@
LIBEI = @erl_interface_eilib@
LIBFLAGS = ../all_SUITE_data/ei_runner@obj@ \
+ ../all_SUITE_data/my_ussi@obj@ \
$(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
@erl_interface_threadlib@
CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
index 09b0b5440b..64cb2dc447 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
@@ -27,9 +27,6 @@
#include <stdio.h>
#include <string.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
#ifdef __WIN32__
#include <winsock2.h>
@@ -41,6 +38,7 @@
#endif
#include "ei_runner.h"
+#include "my_ussi.h"
static void cmd_ei_connect_init(char* buf, int len);
static void cmd_ei_publish(char* buf, int len);
@@ -58,7 +56,7 @@ static struct {
int num_args; /* Number of arguments. */
void (*func)(char* buf, int len);
} commands[] = {
- "ei_connect_init", 4, cmd_ei_connect_init,
+ "ei_connect_init", 5, cmd_ei_connect_init,
"ei_publish", 1, cmd_ei_publish,
"ei_accept", 1, cmd_ei_accept,
"ei_receive", 1, cmd_ei_receive,
@@ -110,10 +108,11 @@ static void cmd_ei_connect_init(char* buf, int len)
unsigned long compat;
char node_name[100];
char cookie[MAXATOMLEN], * cp = cookie;
+ char socket_impl[10];
ei_x_buff res;
if (ei_decode_long(buf, &index, &num) < 0)
fail("expected int");
- sprintf(node_name, "c%d", num);
+ sprintf(node_name, "c%ld", num);
if (ei_decode_atom(buf, &index, cookie) < 0)
fail("expected atom (cookie)");
if (cookie[0] == '\0')
@@ -124,7 +123,18 @@ static void cmd_ei_connect_init(char* buf, int len)
fail("expected uint");
if (compat)
ei_set_compat_rel(compat);
- r = ei_connect_init(&ec, node_name, cp, creation);
+ if (ei_decode_atom_as(buf, &index, socket_impl, sizeof(socket_impl),
+ ERLANG_ASCII, NULL, NULL) < 0)
+ fail("expected atom (socket_impl)");
+ if (strcmp(socket_impl,"default") == 0)
+ r = ei_connect_init(&ec, node_name, cp, creation);
+ else if (strcmp(socket_impl,"ussi") == 0)
+ r = ei_connect_init_ussi(&ec, node_name, cp, creation,
+ &my_ussi, sizeof(my_ussi), NULL);
+ else
+ fail1("unknown socket_impl atom '%s'", socket_impl);
+
+
ei_x_new_with_version(&res);
ei_x_encode_long(&res, r);
send_bin_term(&res);
@@ -152,9 +162,6 @@ static void cmd_ei_publish(char* buf, int len)
if ((i = ei_publish(&ec, lport)) == -1)
fail("ei_publish");
-#ifdef VXWORKS
- save_fd(i);
-#endif
/* send listen-fd, result and errno */
ei_x_new_with_version(&x);
ei_x_encode_tuple_header(&x, 3);
@@ -179,9 +186,6 @@ static void cmd_ei_accept(char* buf, int len)
fail("expected int (listen fd)");
r = ei_accept(&ec, listen, &conn);
-#ifdef VXWORKS
- save_fd(r);
-#endif
/* send result, errno and nodename */
ei_x_new_with_version(&x);
ei_x_encode_tuple_header(&x, 3);
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
index 90c7a2259f..4ce55cacef 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c
@@ -22,35 +22,28 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#ifdef __WIN32__
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#else
-#ifndef VXWORKS
#include <pthread.h>
-#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include "ei.h"
+#include "my_ussi.h"
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <sockLib.h>
-#include <inetLib.h>
-#define MAIN cnode
-#else
#define MAIN main
-#endif
/*
A small einode.
To be called from the test case ei_accept_SUITE:multi_thread
- usage: eiaccnode <cookie> <n>
+ usage: eiaccnode <cookie> <n> <default|ussi>
- start threads 0..n-1
- in each thread
@@ -61,7 +54,8 @@
- shutdown gracefully
*/
-static const char* cookie, * desthost;
+static const char* cookie;
+static int use_ussi;
#ifndef SD_SEND
#ifdef SHUTWR
@@ -78,7 +72,7 @@ static void*
#endif
einode_thread(void* num)
{
- int n = (int)num;
+ int n = (int)(long)num;
int port;
ei_cnode ec;
char myname[100], destname[100], filename[100];
@@ -88,10 +82,15 @@ static void*
FILE* file;
sprintf(filename, "eiacc%d_trace.txt", n);
- file = fopen(filename, "w");
+ file = fopen(filename, "a");
sprintf(myname, "eiacc%d", n); fflush(file);
- r = ei_connect_init(&ec, myname, cookie, 0);
+ fprintf(file, "---- use_ussi = %d ----\n", use_ussi); fflush(file);
+ if (use_ussi)
+ r = ei_connect_init_ussi(&ec, myname, cookie, 0,
+ &my_ussi, sizeof(my_ussi), NULL);
+ else
+ r = ei_connect_init(&ec, myname, cookie, 0);
port = 0;
listen = ei_listen(&ec, &port, 5);
if (listen <= 0) {
@@ -151,54 +150,52 @@ static void*
return 0;
}
+int
MAIN(int argc, char *argv[])
{
int i, n, no_threads;
-#ifndef VXWORKS
#ifdef __WIN32__
HANDLE threads[100];
#else
pthread_t threads[100];
#endif
-#endif
- if (argc < 3)
+ if (argc < 4)
exit(1);
cookie = argv[1];
n = atoi(argv[2]);
if (n > 100)
exit(2);
- desthost = argv[3];
- if (argc == 3)
+
+ if (strcmp(argv[3], "default") == 0)
+ use_ussi = 0;
+ else if (strcmp(argv[3], "ussi") == 0)
+ use_ussi = 1;
+ else
+ printf("bad argv[3] '%s'", argv[3]);
+
+ if (argc == 4)
no_threads = 0;
else
no_threads = argv[4] != NULL && strcmp(argv[4], "nothreads") == 0;
-#ifdef VXWORKS
- no_threads = 1;
-#endif
ei_init();
for (i = 0; i < n; ++i) {
if (!no_threads) {
-#ifndef VXWORKS
#ifdef __WIN32__
unsigned tid;
threads[i] = (HANDLE)_beginthreadex(NULL, 0, einode_thread,
- (void*)i, 0, &tid);
-#else
- pthread_create(&threads[i], NULL, einode_thread, (void*)i);
-#endif
+ (void*)(size_t)i, 0, &tid);
#else
- ;
+ pthread_create(&threads[i], NULL, einode_thread, (void*)(size_t)i);
#endif
} else
- einode_thread((void*)i);
+ einode_thread((void*)(size_t)i);
}
if (!no_threads)
-#ifndef VXWORKS
for (i = 0; i < n; ++i) {
#ifdef __WIN32__
if (WaitForSingleObject(threads[i], INFINITE) != WAIT_OBJECT_0)
@@ -207,9 +204,6 @@ MAIN(int argc, char *argv[])
#endif
printf("bad wait thread %d\n", i);
}
-#else
- ;
-#endif
printf("ok\n");
return 0;
}
diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl
index 6184ce801b..2ec1237e8e 100644
--- a/lib/erl_interface/test/ei_connect_SUITE.erl
+++ b/lib/erl_interface/test/ei_connect_SUITE.erl
@@ -24,7 +24,7 @@
-include_lib("common_test/include/ct.hrl").
-include("ei_connect_SUITE_data/ei_connect_test_cases.hrl").
--export([all/0, suite/0,
+-export([all/0, suite/0, groups/0,
init_per_testcase/2,
ei_send/1,
ei_reg_send/1,
@@ -33,7 +33,8 @@
rpc_test/1,
ei_send_funs/1,
ei_threaded_send/1,
- ei_set_get_tracelevel/1]).
+ ei_set_get_tracelevel/1,
+ ei_connect_host_port_test/1]).
-import(runner, [get_term/1,send_term/2]).
@@ -42,15 +43,30 @@ suite() ->
{timetrap, {seconds, 30}}].
all() ->
- [ei_send, ei_reg_send, ei_rpc, ei_format_pid, ei_send_funs,
- ei_threaded_send, ei_set_get_tracelevel].
+ [ei_threaded_send,
+ ei_connect_host_port_test,
+ {group, default},
+ {group, ussi}].
+
+groups() ->
+ Members = [ei_send,
+ ei_format_pid,
+ ei_send_funs,
+ ei_set_get_tracelevel,
+ ei_reg_send,
+ ei_rpc],
+ [{default, [], Members},
+ {ussi, [], Members}].
+
+get_group(Config) ->
+ proplists:get_value(name, proplists:get_value(tc_group_properties,Config)).
init_per_testcase(Case, Config) ->
runner:init_per_testcase(?MODULE, Case, Config).
ei_send(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
ok = ei_send(P, Fd, self(), AMsg={a,message}),
@@ -63,7 +79,7 @@ ei_send(Config) when is_list(Config) ->
ei_format_pid(Config) when is_list(Config) ->
S = self(),
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
ok = ei_format_pid(P, Fd, S),
@@ -75,7 +91,7 @@ ei_format_pid(Config) when is_list(Config) ->
ei_send_funs(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
Fun1 = fun ei_send/1,
@@ -94,7 +110,7 @@ ei_send_funs(Config) when is_list(Config) ->
ei_reg_send(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
ARegName = a_strange_registred_name,
@@ -143,7 +159,7 @@ start_einode(Einode, N, Host) ->
ei_rpc(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
S= "Hej du glade!", SRev = lists:reverse(S),
@@ -157,7 +173,7 @@ ei_rpc(Config) when is_list(Config) ->
ei_set_get_tracelevel(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
5 = ei_set_get_tracelevel(P, 5),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, get_group(Config)),
{ok,Fd} = ei_connect(P, node()),
S= "Hej du glade!", SRev = lists:reverse(S),
@@ -171,10 +187,31 @@ ei_set_get_tracelevel(Config) when is_list(Config) ->
ok.
+ei_connect_host_port_test(Config) when is_list(Config) ->
+ P = runner:start(Config, ?interpret),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, default),
+ [NodeName, Hostname] = string:lexemes(atom_to_list(node()), "@"),
+ {ok, NamePortList} = net_adm:names(),
+ {value, {_, Port}}
+ = lists:search(fun({N, _}) ->
+ string:equal(N, NodeName)
+ end,
+ NamePortList),
+ {ok,Fd} = ei_connect_host_port(P,
+ erlang:list_to_atom(Hostname),
+ Port),
+ ok = ei_send(P, Fd, self(), AMsg={a,message}),
+ receive AMsg -> ok end,
+
+ runner:send_eot(P),
+ runner:recv_eot(P),
+ ok.
+
+
%%% Interface functions for ei (erl_interface) functions.
-ei_connect_init(P, Num, Cookie, Creation) ->
- send_command(P, ei_connect_init, [Num,Cookie,Creation]),
+ei_connect_init(P, Num, Cookie, Creation, SockImpl) ->
+ send_command(P, ei_connect_init, [Num,Cookie,Creation,SockImpl]),
case get_term(P) of
{term,Int} when is_integer(Int) -> Int
end.
@@ -186,6 +223,13 @@ ei_connect(P, Node) ->
{term,{-1,Errno}} -> {error,Errno}
end.
+ei_connect_host_port(P, Hostname, Port) ->
+ send_command(P, ei_connect_host_port, [Hostname, Port]),
+ case get_term(P) of
+ {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
+ {term,{-1,Errno}} -> {error,Errno}
+ end.
+
ei_set_get_tracelevel(P, Tracelevel) ->
send_command(P, ei_set_get_tracelevel, [Tracelevel]),
case get_term(P) of
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/Makefile.src b/lib/erl_interface/test/ei_connect_SUITE_data/Makefile.src
index c2d8261dd8..bbea076ebd 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/Makefile.src
@@ -23,10 +23,9 @@ include @erl_interface_mk_include@
CC0 = @CC@
CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
LD = @LD@
-LIBERL = @erl_interface_lib@
LIBEI = @erl_interface_eilib@
LIBFLAGS = ../all_SUITE_data/ei_runner@obj@ \
- $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
+ $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
@erl_interface_threadlib@
CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
EI_CONNECT_OBJS = ei_connect_test@obj@ ei_connect_test_decl@obj@
@@ -39,7 +38,8 @@ clean:
$(RM) ei_connect_test@exe@ einode@exe@
ei_connect_test@exe@: $(EI_CONNECT_OBJS) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(EI_CONNECT_OBJS) $(LIBFLAGS)
+ $(LD) @CROSSLDFLAGS@ -o $@ $(EI_CONNECT_OBJS) \
+ ../all_SUITE_data/my_ussi@obj@ $(LIBFLAGS)
einode@exe@: $(EINODE_OBJS) $(LIBEI)
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
index 385bcdd422..54e78253a0 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
@@ -27,14 +27,13 @@
#include <stdio.h>
#include <string.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
#include "ei_runner.h"
+#include "my_ussi.h"
static void cmd_ei_connect_init(char* buf, int len);
static void cmd_ei_connect(char* buf, int len);
+static void cmd_ei_connect_host_port(char* buf, int len);
static void cmd_ei_send(char* buf, int len);
static void cmd_ei_format_pid(char* buf, int len);
static void cmd_ei_send_funs(char* buf, int len);
@@ -52,8 +51,9 @@ static struct {
int num_args; /* Number of arguments. */
void (*func)(char* buf, int len);
} commands[] = {
- "ei_connect_init", 3, cmd_ei_connect_init,
+ "ei_connect_init", 4, cmd_ei_connect_init,
"ei_connect", 1, cmd_ei_connect,
+ "ei_connect_host_port", 2, cmd_ei_connect_host_port,
"ei_send", 3, cmd_ei_send,
"ei_send_funs", 3, cmd_ei_send_funs,
"ei_reg_send", 3, cmd_ei_reg_send,
@@ -107,9 +107,11 @@ TESTCASE(interpret)
static void cmd_ei_connect_init(char* buf, int len)
{
int index = 0, r = 0;
- long l;
+ long l, creation;
char b[100];
char cookie[MAXATOMLEN], * cp = cookie;
+ char socket_impl[10];
+ int use_ussi;
ei_x_buff res;
if (ei_decode_long(buf, &index, &l) < 0)
fail("expected int");
@@ -118,7 +120,23 @@ static void cmd_ei_connect_init(char* buf, int len)
fail("expected atom (cookie)");
if (cookie[0] == '\0')
cp = NULL;
- r = ei_connect_init(&ec, b, cp, 0);
+ if (ei_decode_long(buf, &index, &creation) < 0)
+ fail("expected int (creation)");
+ if (ei_decode_atom_as(buf, &index, socket_impl,
+ sizeof(socket_impl), ERLANG_ASCII, NULL, NULL) < 0)
+ fail("expected atom (socket_impl)");
+ if (strcmp(socket_impl, "default") == 0)
+ use_ussi = 0;
+ else if (strcmp(socket_impl, "ussi") == 0)
+ use_ussi = 1;
+ else
+ fail1("expected atom 'default' or 'ussi', got '%s'", socket_impl);
+
+ if (use_ussi)
+ r = ei_connect_init_ussi(&ec, b, cp, (short)creation,
+ &my_ussi, sizeof(my_ussi), NULL);
+ else
+ r = ei_connect_init(&ec, b, cp, (short)creation);
ei_x_new_with_version(&res);
ei_x_encode_long(&res, r);
send_bin_term(&res);
@@ -133,11 +151,20 @@ static void cmd_ei_connect(char* buf, int len)
if (ei_decode_atom(buf, &index, node) < 0)
fail("expected atom");
i=ei_connect(&ec, node);
-#ifdef VXWORKS
- if(i >= 0) {
- save_fd(i);
- }
-#endif
+ send_errno_result(i);
+}
+
+static void cmd_ei_connect_host_port(char* buf, int len)
+{
+ int index = 0;
+ char hostname[256];
+ int i;
+ long port;
+ if (ei_decode_atom(buf, &index, hostname) < 0)
+ fail("expected atom");
+ if (ei_decode_long(buf, &index, &port) < 0)
+ fail("expected int");
+ i = ei_connect_host_port(&ec, hostname, (int)port);
send_errno_result(i);
}
@@ -225,7 +252,7 @@ static void cmd_ei_send_funs(char* buf, int len)
fail("expected Fun1");
if (ei_decode_fun(buf, &index, &fun2) < 0)
fail("expected Fun2");
- if (ei_decode_bitstring(buf, &index, &bitstring, &bitoffs, &bits) < 0)
+ if (ei_decode_bitstring(buf, &index, (const char**)&bitstring, &bitoffs, &bits) < 0)
fail("expected bitstring");
if (ei_x_new_with_version(&x) < 0)
fail("ei_x_new_with_version");
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/einode.c b/lib/erl_interface/test/ei_connect_SUITE_data/einode.c
index bb71575740..083ca1d372 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/einode.c
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/einode.c
@@ -28,20 +28,13 @@
#include <windows.h>
#include <process.h>
#else
-#ifndef VXWORKS
#include <pthread.h>
-#endif
#include <sys/socket.h>
#endif
#include "ei.h"
-#include "erl_interface.h"
-#ifdef VXWORKS
-#define MAIN cnode
-#else
#define MAIN main
-#endif
/*
A small einode.
@@ -105,32 +98,25 @@ static void*
MAIN(int argc, char *argv[])
{
int i, n, no_threads;
-#ifndef VXWORKS
#ifdef __WIN32__
HANDLE threads[100];
#else
pthread_t threads[100];
#endif
-#endif
if (argc < 3)
exit(1);
- erl_init(NULL, 0);
+ ei_init();
cookie = argv[1];
n = atoi(argv[2]);
if (n > 100)
exit(2);
desthost = argv[3];
-#ifndef VXWORKS
no_threads = argv[4] != NULL && strcmp(argv[4], "nothreads") == 0;
-#else
- no_threads = 1;
-#endif
for (i = 0; i < n; ++i) {
if (!no_threads) {
-#ifndef VXWORKS
#ifdef __WIN32__
unsigned tid;
threads[i] = (HANDLE)_beginthreadex(NULL, 0, einode_thread,
@@ -138,14 +124,10 @@ MAIN(int argc, char *argv[])
#else
pthread_create(&threads[i], NULL, einode_thread, (void*)i);
#endif
-#else
- ;
-#endif
} else
einode_thread((void*)i);
}
if (!no_threads)
-#ifndef VXWORKS
for (i = 0; i < n; ++i) {
#ifdef __WIN32__
if (WaitForSingleObject(threads[i], INFINITE) != WAIT_OBJECT_0)
@@ -154,9 +136,6 @@ MAIN(int argc, char *argv[])
#endif
printf("bad wait thread %d\n", i);
}
-#else
- ;
-#endif
printf("ok\n");
return 0;
}
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index e005ec89c7..35feeea42c 100644
--- a/lib/erl_interface/test/ei_decode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_SUITE.erl
@@ -77,29 +77,19 @@ test_ei_decode_ulong(Config) when is_list(Config) ->
%% ######################################################################## %%
test_ei_decode_longlong(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Skipped on VxWorks"};
- _ ->
- P = runner:start(Config, ?test_ei_decode_longlong),
- send_integers2(P),
- runner:recv_eot(P),
- ok
- end.
+ P = runner:start(Config, ?test_ei_decode_longlong),
+ send_integers2(P),
+ runner:recv_eot(P),
+ ok.
%% ######################################################################## %%
test_ei_decode_ulonglong(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Skipped on VxWorks"};
- _ ->
- P = runner:start(Config, ?test_ei_decode_ulonglong),
- send_integers2(P),
- runner:recv_eot(P),
- ok
- end.
+ P = runner:start(Config, ?test_ei_decode_ulonglong),
+ send_integers2(P),
+ runner:recv_eot(P),
+ ok.
%% ######################################################################## %%
@@ -128,13 +118,8 @@ test_ei_decode_nonoptimal(Config) when is_list(Config) ->
send_non_optimal_pos(P), % decode_char
send_non_optimal(P), % decode_long
send_non_optimal_pos(P), % decode_ulong
- case os:type() of
- vxworks ->
- ok;
- _ ->
- send_non_optimal(P), % decode_longlong
- send_non_optimal_pos(P) % decode_ulonglong
- end,
+ send_non_optimal(P), % decode_longlong
+ send_non_optimal_pos(P), % decode_ulonglong
runner:recv_eot(P),
ok.
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index 46d6b8f2af..b27c3c589c 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -20,10 +20,6 @@
#include <string.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
-
#include "ei_runner.h"
/*
@@ -31,15 +27,9 @@
* Author: Kent
*/
-#ifdef VXWORKS
-#define MESSAGE_BACK(SIZE) \
- message("err = %d, size2 = %d, expected size = %d", \
- err, size1, SIZE);
-#else
#define MESSAGE_BACK(SIZE) \
message("err = %d, size2 = %d, expected size = %d, long long val = %lld", \
err, size1, SIZE, (EI_LONGLONG)p);
-#endif
#define ERLANG_ANY (ERLANG_ASCII|ERLANG_LATIN1|ERLANG_UTF8)
@@ -483,7 +473,6 @@ TESTCASE(test_ei_decode_longlong)
{
ei_init();
-#ifndef VXWORKS
EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 0);
EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 255);
EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 256);
@@ -509,7 +498,6 @@ TESTCASE(test_ei_decode_longlong)
EI_DECODE_2_FAIL(decode_longlong, 11, EI_LONGLONG, ll(0xffffffffffffffff));
EI_DECODE_2_FAIL(decode_longlong, 1, EI_LONGLONG, 0); /* Illegal type */
-#endif
report(1);
}
@@ -519,7 +507,6 @@ TESTCASE(test_ei_decode_ulonglong)
{
ei_init();
-#ifndef VXWORKS
EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 0);
EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 255);
EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 256);
@@ -545,7 +532,6 @@ TESTCASE(test_ei_decode_ulonglong)
EI_DECODE_2 (decode_ulonglong,11, EI_ULONGLONG, ll(0xffffffffffffffff));
EI_DECODE_2_FAIL(decode_ulonglong, 1, EI_ULONGLONG, 0); /* Illegal type */
-#endif
report(1);
}
@@ -637,8 +623,6 @@ TESTCASE(test_ei_decode_nonoptimal)
/* ---------------------------------------------------------------- */
-#ifndef VXWORKS
-
EI_DECODE_2(decode_longlong, 2, EI_LONGLONG, 42);
EI_DECODE_2(decode_longlong, 5, EI_LONGLONG, 42);
EI_DECODE_2(decode_longlong, 4, EI_LONGLONG, 42);
@@ -681,8 +665,6 @@ TESTCASE(test_ei_decode_nonoptimal)
/* EI_DECODE_2(decode_ulonglong, EI_ULONGLONG, -42); */
/* EI_DECODE_2(decode_ulonglong, EI_ULONGLONG, -42); */
-#endif /* !VXWORKS */
-
/* ---------------------------------------------------------------- */
report(1);
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 512f9ed0c7..5594bec757 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
@@ -18,10 +18,6 @@
* %CopyrightEnd%
*/
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
-
#include "ei_runner.h"
/*
diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl
index 0267a5126f..10fcd6b871 100644
--- a/lib/erl_interface/test/ei_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_encode_SUITE.erl
@@ -101,59 +101,49 @@ test_ei_encode_ulong(Config) when is_list(Config) ->
%% ######################################################################## %%
test_ei_encode_longlong(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Skipped on VxWorks"};
- _ ->
- P = runner:start(Config, ?test_ei_encode_longlong),
-
- {<<97,0>> ,0} = get_buf_and_term(P),
- {<<97,255>> ,255} = get_buf_and_term(P),
- {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P),
- {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P),
-
- {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P),
- {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P),
- {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P),
- {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P),
-
- {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P),
- {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P),
- {<<110,6,0, 255,255,255,255,255,127>> , 16#7fffffffffff} = get_buf_and_term(P),
- {<<110,6,1, 0,0,0,0,0,128>> ,-16#800000000000} = get_buf_and_term(P),
- {<<110,8,0, 255,255,255,255,255,255,255,127>>,16#7fffffffffffffff} = get_buf_and_term(P),
- {<<110,8,1, 0,0,0,0,0,0,0,128>> ,-16#8000000000000000} = get_buf_and_term(P),
-
- runner:recv_eot(P),
- ok
- end.
+ P = runner:start(Config, ?test_ei_encode_longlong),
+
+ {<<97,0>> ,0} = get_buf_and_term(P),
+ {<<97,255>> ,255} = get_buf_and_term(P),
+ {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P),
+ {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P),
+
+ {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P),
+ {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P),
+ {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P),
+ {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P),
+
+ {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P),
+ {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P),
+ {<<110,6,0, 255,255,255,255,255,127>> , 16#7fffffffffff} = get_buf_and_term(P),
+ {<<110,6,1, 0,0,0,0,0,128>> ,-16#800000000000} = get_buf_and_term(P),
+ {<<110,8,0, 255,255,255,255,255,255,255,127>>,16#7fffffffffffffff} = get_buf_and_term(P),
+ {<<110,8,1, 0,0,0,0,0,0,0,128>> ,-16#8000000000000000} = get_buf_and_term(P),
+
+ runner:recv_eot(P),
+ ok.
%% ######################################################################## %%
test_ei_encode_ulonglong(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Skipped on VxWorks"};
- _ ->
- P = runner:start(Config, ?test_ei_encode_ulonglong),
-
- {<<97,0>> ,0} = get_buf_and_term(P),
- {<<97,255>> ,255} = get_buf_and_term(P),
- {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P),
-
- {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P),
- {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P),
-
- {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P),
- {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P),
- {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P),
- {<<110,6,0, 255,255,255,255,255,255>>,16#ffffffffffff} = get_buf_and_term(P),
- {<<110,8,0, 255,255,255,255,255,255,255,255>>,16#ffffffffffffffff} = get_buf_and_term(P),
+ P = runner:start(Config, ?test_ei_encode_ulonglong),
- runner:recv_eot(P),
- ok
- end.
+ {<<97,0>> ,0} = get_buf_and_term(P),
+ {<<97,255>> ,255} = get_buf_and_term(P),
+ {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P),
+
+ {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P),
+ {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P),
+
+ {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P),
+ {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P),
+ {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P),
+ {<<110,6,0, 255,255,255,255,255,255>>,16#ffffffffffff} = get_buf_and_term(P),
+ {<<110,8,0, 255,255,255,255,255,255,255,255>>,16#ffffffffffffffff} = get_buf_and_term(P),
+
+ runner:recv_eot(P),
+ ok.
%% ######################################################################## %%
diff --git a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
index 6f63cc5d7e..6f1276e016 100644
--- a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
+++ b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
@@ -18,10 +18,6 @@
* %CopyrightEnd%
*/
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
-
#include "ei_runner.h"
/*
@@ -460,8 +456,6 @@ TESTCASE(test_ei_encode_longlong)
{
ei_init();
-#ifndef VXWORKS
-
EI_ENCODE_1(encode_longlong, 0);
EI_ENCODE_1(encode_longlong, 255);
@@ -490,8 +484,6 @@ TESTCASE(test_ei_encode_longlong)
EI_ENCODE_1(encode_longlong, -ll(0x8000000000000000));
-#endif /* !VXWORKS */
-
report(1);
}
@@ -501,8 +493,6 @@ TESTCASE(test_ei_encode_ulonglong)
{
ei_init();
-#ifndef VXWORKS
-
EI_ENCODE_1(encode_ulonglong, 0);
EI_ENCODE_1(encode_ulonglong, 255);
@@ -523,8 +513,6 @@ TESTCASE(test_ei_encode_ulonglong)
EI_ENCODE_1(encode_ulonglong, ll(0xffffffffffffffff));
-#endif /* !VXWORKS */
-
report(1);
}
diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
index 1c0443c0f4..19d2cc1510 100644
--- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
+++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
@@ -18,10 +18,6 @@
* %CopyrightEnd%
*/
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
-
#include "ei_runner.h"
#include <string.h>
diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/ei_global_SUITE.erl
index 6d3a75c8d7..da80ab24cd 100644
--- a/lib/erl_interface/test/erl_global_SUITE.erl
+++ b/lib/erl_interface/test/ei_global_SUITE.erl
@@ -19,22 +19,27 @@
%%
%%
--module(erl_global_SUITE).
+-module(ei_global_SUITE).
-include_lib("common_test/include/ct.hrl").
--include("erl_global_SUITE_data/erl_global_test_cases.hrl").
+-include("ei_global_SUITE_data/ei_global_test_cases.hrl").
-export([all/0,suite/0,
init_per_testcase/2,
- erl_global_registration/1,
- erl_global_whereis/1, erl_global_names/1]).
+ ei_global_registration/1,
+ ei_global_whereis/1,
+ ei_global_names/1
+ ]).
-import(runner, [get_term/1,send_term/2]).
-define(GLOBAL_NAME, global_register_node_test).
all() ->
- [erl_global_registration, erl_global_whereis, erl_global_names].
+ [ei_global_registration, ei_global_whereis, ei_global_names].
+
+get_group(Config) ->
+ proplists:get_value(name, proplists:get_value(tc_group_properties,Config)).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -43,82 +48,76 @@ suite() ->
init_per_testcase(Case, Config) ->
runner:init_per_testcase(?MODULE, Case, Config).
-erl_global_registration(Config) when is_list(Config) ->
+ei_global_registration(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, ussi),
+ {ok,Fd} = ei_connect(P, node()),
- ok = erl_global_register(P, Fd, ?GLOBAL_NAME),
- ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME),
+ ok = ei_global_register(P, Fd, ?GLOBAL_NAME),
+ ok = ei_global_unregister(P, Fd, ?GLOBAL_NAME),
- 0 = erl_close_connection(P,Fd),
runner:send_eot(P),
runner:recv_eot(P),
ok.
-erl_global_whereis(Config) when is_list(Config) ->
+ei_global_whereis(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, ussi),
+ {ok,Fd} = ei_connect(P, node()),
Self = self(),
yes = global:register_name(?GLOBAL_NAME, Self),
- Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME),
+ Self = ei_global_whereis(P, Fd, ?GLOBAL_NAME),
global:unregister_name(?GLOBAL_NAME),
- 0 = erl_close_connection(P, Fd),
runner:send_eot(P),
runner:recv_eot(P),
ok.
-erl_global_names(Config) when is_list(Config) ->
+ei_global_names(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, ussi),
+ {ok,Fd} = ei_connect(P, node()),
Self = self(),
global:register_name(?GLOBAL_NAME, Self),
- {Names1, _N1} = erl_global_names(P, Fd),
+ {Names1, _N1} = ei_global_names(P, Fd),
true = lists:member(atom_to_list(?GLOBAL_NAME), Names1),
global:unregister_name(?GLOBAL_NAME),
- {Names2, _N2} = erl_global_names(P, Fd),
+ {Names2, _N2} = ei_global_names(P, Fd),
false = lists:member(atom_to_list(?GLOBAL_NAME), Names2),
- 0 = erl_close_connection(P, Fd),
runner:send_eot(P),
runner:recv_eot(P),
ok.
-%%% Interface functions for erl_interface functions.
-
-erl_connect(P, Node, Num, Cookie, Creation) ->
- send_command(P, erl_connect, [Num, Node, Cookie, Creation]),
- case get_term(P) of
- {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
- {term,{-1,Errno}} -> {error,Errno}
- end.
+%% %%% Interface functions for erl_interface functions.
-erl_close_connection(P, FD) ->
- send_command(P, erl_close_connection, [FD]),
- case get_term(P) of
- {term,Int} when is_integer(Int) -> Int
- end.
+%% erl_connect(P, Node, Num, Cookie, Creation) ->
+%% send_command(P, erl_connect, [Num, Node, Cookie, Creation]),
+%% case get_term(P) of
+%% {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
+%% {term,{-1,Errno}} -> {error,Errno}
+%% end.
-erl_global_register(P, Fd, Name) ->
- send_command(P, erl_global_register, [Fd,Name]),
+ei_global_register(P, Fd, Name) ->
+ send_command(P, ei_global_register, [Fd,Name]),
get_send_result(P).
-erl_global_whereis(P, Fd, Name) ->
- send_command(P, erl_global_whereis, [Fd,Name]),
+ei_global_whereis(P, Fd, Name) ->
+ send_command(P, ei_global_whereis, [Fd,Name]),
case get_term(P) of
{term, What} ->
What
end.
-erl_global_names(P, Fd) ->
- send_command(P, erl_global_names, [Fd]),
+ei_global_names(P, Fd) ->
+ send_command(P, ei_global_names, [Fd]),
case get_term(P) of
{term, What} ->
What
end.
-erl_global_unregister(P, Fd, Name) ->
- send_command(P, erl_global_unregister, [Fd,Name]),
+ei_global_unregister(P, Fd, Name) ->
+ send_command(P, ei_global_unregister, [Fd,Name]),
get_send_result(P).
get_send_result(P) ->
@@ -132,3 +131,17 @@ get_send_result(P) ->
send_command(P, Name, Args) ->
runner:send_term(P, {Name,list_to_tuple(Args)}).
+
+
+ei_connect_init(P, Num, Cookie, Creation, SockImpl) ->
+ send_command(P, ei_connect_init, [Num,Cookie,Creation,SockImpl]),
+ case get_term(P) of
+ {term,Int} when is_integer(Int) -> Int
+ end.
+
+ei_connect(P, Node) ->
+ send_command(P, ei_connect, [Node]),
+ case get_term(P) of
+ {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
+ {term,{-1,Errno}} -> {error,Errno}
+ end.
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first b/lib/erl_interface/test/ei_global_SUITE_data/Makefile.first
index b2c62be1f2..5ec0c06af8 100644
--- a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first
+++ b/lib/erl_interface/test/ei_global_SUITE_data/Makefile.first
@@ -18,5 +18,5 @@
# %CopyrightEnd%
#
-erl_global_test_decl.c: erl_global_test.c
- erl -noinput -pa ../all_SUITE_data -s init_tc run erl_global_test -s erlang halt
+erl_global_test_decl.c: ei_global_test.c
+ erl -noinput -pa ../all_SUITE_data -s init_tc run ei_global_test -s erlang halt
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src b/lib/erl_interface/test/ei_global_SUITE_data/Makefile.src
index 1c1530d1b6..43c9095ab4 100644
--- a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/ei_global_SUITE_data/Makefile.src
@@ -25,17 +25,18 @@ CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
LD = @LD@
LIBERL = @erl_interface_lib@
LIBEI = @erl_interface_eilib@
-LIBFLAGS = ../all_SUITE_data/runner@obj@ \
- $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
+LIBFLAGS = ../all_SUITE_data/ei_runner@obj@ \
+ $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
@erl_interface_threadlib@
CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
-OBJS = erl_global_test@obj@ erl_global_test_decl@obj@
+EI_GLOBAL_OBJS = ei_global_test@obj@ ei_global_test_decl@obj@
-all: erl_global_test@exe@
-
-erl_global_test@exe@: $(OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS)
+all: ei_global_test@exe@
clean:
- $(RM) $(OBJS)
- $(RM) erl_global_test@exe@
+ $(RM) $(EI_GLOBAL_OBJS)
+ $(RM) ei_global_test@exe@
+
+ei_global_test@exe@: $(EI_GLOBAL_OBJS) $(LIBEI)
+ $(LD) @CROSSLDFLAGS@ -o $@ $(EI_GLOBAL_OBJS) \
+ ../all_SUITE_data/my_ussi@obj@ $(LIBFLAGS)
diff --git a/lib/erl_interface/test/ei_global_SUITE_data/ei_global_test.c b/lib/erl_interface/test/ei_global_SUITE_data/ei_global_test.c
new file mode 100644
index 0000000000..4c018667fe
--- /dev/null
+++ b/lib/erl_interface/test/ei_global_SUITE_data/ei_global_test.c
@@ -0,0 +1,239 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: Tests the functions in erl_global.c.
+ *
+ * See the ei_global_SUITE.erl file for a "table of contents".
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ei_runner.h"
+#include "my_ussi.h"
+#include "ei_connect.h"
+
+static void cmd_ei_connect_init(char* buf, int len);
+static void cmd_ei_connect(char* buf, int len);
+static void cmd_ei_global_register(char* buf, int len);
+static void cmd_ei_global_whereis(char* buf, int len);
+static void cmd_ei_global_names(char* buf, int len);
+static void cmd_ei_global_unregister(char* buf, int len);
+static void cmd_ei_close_connection(char* buf, int len);
+
+static void send_errno_result(int value);
+
+ei_cnode ec;
+
+static struct {
+ char* name;
+ int num_args; /* Number of arguments. */
+ void (*func)(char* buf, int len);
+} commands[] = {
+ "ei_connect_init", 4, cmd_ei_connect_init,
+ "ei_connect", 1, cmd_ei_connect,
+ "ei_global_register", 2, cmd_ei_global_register,
+ "ei_global_whereis", 2, cmd_ei_global_whereis,
+ "ei_global_names", 1, cmd_ei_global_names,
+ "ei_global_unregister", 2, cmd_ei_global_unregister
+};
+
+
+/*
+ * Sends a list contaning all data types to the Erlang side.
+ */
+
+TESTCASE(interpret)
+{
+ ei_x_buff x;
+ int i;
+ ei_term term;
+
+ ei_init();
+
+ ei_x_new(&x);
+ while (get_bin_term(&x, &term) == 0) {
+ char* buf = x.buff, func[MAXATOMLEN];
+ int index = x.index, arity;
+ if (term.ei_type != ERL_SMALL_TUPLE_EXT || term.arity != 2)
+ fail("term should be a tuple of size 2");
+ if (ei_decode_atom(buf, &index, func) < 0)
+ fail("function name should be an atom");
+ if (ei_decode_tuple_header(buf, &index, &arity) != 0)
+ fail("function arguments should be a tuple");
+ for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
+ if (strcmp(func, commands[i].name) == 0) {
+ if (arity != commands[i].num_args)
+ fail("wrong number of arguments");
+ commands[i].func(buf + index, x.buffsz - index);
+ break;
+ }
+ }
+ if (i >= sizeof(commands)/sizeof(commands[0])) {
+ message("\"%d\" \n", func);
+ fail("bad command");
+ }
+ }
+ report(1);
+ ei_x_free(&x);
+ return;
+}
+
+static void cmd_ei_connect_init(char* buf, int len)
+{
+ int index = 0, r = 0;
+ long l, creation;
+ char b[100];
+ char cookie[MAXATOMLEN], * cp = cookie;
+ char socket_impl[10];
+ int use_ussi;
+ ei_x_buff res;
+ if (ei_decode_long(buf, &index, &l) < 0)
+ fail("expected int");
+ sprintf(b, "c%ld", l);
+ if (ei_decode_atom(buf, &index, cookie) < 0)
+ fail("expected atom (cookie)");
+ if (cookie[0] == '\0')
+ cp = NULL;
+ if (ei_decode_long(buf, &index, &creation) < 0)
+ fail("expected int (creation)");
+ if (ei_decode_atom_as(buf, &index, socket_impl,
+ sizeof(socket_impl), ERLANG_ASCII, NULL, NULL) < 0)
+ fail("expected atom (socket_impl)");
+ if (strcmp(socket_impl, "default") == 0)
+ use_ussi = 0;
+ else if (strcmp(socket_impl, "ussi") == 0)
+ use_ussi = 1;
+ else
+ fail1("expected atom 'default' or 'ussi', got '%s'", socket_impl);
+
+ if (use_ussi)
+ r = ei_connect_init_ussi(&ec, b, cp, (short)creation,
+ &my_ussi, sizeof(my_ussi), NULL);
+ else
+ r = ei_connect_init(&ec, b, cp, (short)creation);
+ ei_x_new_with_version(&res);
+ ei_x_encode_long(&res, r);
+ send_bin_term(&res);
+ ei_x_free(&res);
+}
+
+static void cmd_ei_connect(char* buf, int len)
+{
+ int index = 0;
+ char node[256];
+ int i;
+ if (ei_decode_atom(buf, &index, node) < 0)
+ fail("expected atom");
+ i=ei_connect(&ec, node);
+ send_errno_result(i);
+}
+
+static void
+cmd_ei_global_register(char* buf, int len)
+{
+ int index = 0;
+ long fd;
+ char name[256];
+ if (ei_decode_long(buf, &index, &fd) < 0)
+ fail("expected long");
+ if (ei_decode_atom(buf, &index, name) < 0)
+ fail("expected atom");
+ send_errno_result(ei_global_register((int)fd, name, ei_self(&ec)));
+}
+
+static void
+cmd_ei_global_whereis(char* buf, int len)
+{
+ int index = 0;
+ long fd;
+ char name[512];
+ char node_name[512];
+ erlang_pid pid;
+ if (ei_decode_long(buf, &index, &fd) < 0)
+ fail("expected long");
+ if (ei_decode_atom(buf, &index, name) < 0)
+ fail("expected atom");
+
+ if (ei_global_whereis(&ec, fd, name, &pid, node_name) < 0)
+ fail("ei_global_whereis error code");
+
+ {
+ ei_x_buff x;
+ ei_x_new_with_version(&x);
+ ei_x_encode_pid(&x, &pid);
+ send_bin_term(&x);
+ ei_x_free(&x);
+ }
+}
+
+static void
+cmd_ei_global_names(char* buf, int len)
+{
+ int index = 0;
+ long fd;
+ char** names = NULL;
+ int count = 0, i;
+ if (ei_decode_long(buf, &index, &fd) < 0)
+ fail("expected long");
+
+ names = ei_global_names(&ec, (int)fd, &count);
+
+ {
+ ei_x_buff x;
+ ei_x_new_with_version(&x);
+ ei_x_encode_tuple_header(&x, 2);
+ ei_x_encode_list_header(&x, count);
+ for(i=0; i<count; i++) {
+ ei_x_encode_string(&x, names[i]);
+ }
+ ei_x_encode_empty_list(&x);
+ ei_x_encode_long(&x, count);
+ send_bin_term(&x);
+ ei_x_free(&x);
+ }
+}
+
+static void
+cmd_ei_global_unregister(char* buf, int len)
+{
+ int index = 0;
+ long fd;
+ char name[256];
+ if (ei_decode_long(buf, &index, &fd) < 0)
+ fail("expected long");
+ if (ei_decode_atom(buf, &index, name) < 0)
+ fail("expected atom");
+
+ send_errno_result(ei_global_unregister(&ec, (int)fd, name));
+}
+
+static void send_errno_result(int value)
+{
+ ei_x_buff x;
+ ei_x_new_with_version(&x);
+ ei_x_encode_tuple_header(&x, 2);
+ ei_x_encode_long(&x, value);
+ ei_x_encode_long(&x, erl_errno);
+ send_bin_term(&x);
+ ei_x_free(&x);
+}
diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
index 27d4153250..2d332c8b0c 100644
--- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
+++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
@@ -37,13 +37,9 @@ send_printed_buf(ei_x_buff* x)
FILE* f;
int n, index = 0, ver;
-#ifdef VXWORKS
- tmp = ".";
-#else
if (tmp == NULL) {
tmp = "/tmp";
}
-#endif
strcpy(fn, tmp);
strcat(fn, "/ei_print_test.txt");
f = fopen(fn, "w+");
diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl
index 5b9de80128..8d8776949c 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE.erl
+++ b/lib/erl_interface/test/ei_tmo_SUITE.erl
@@ -25,10 +25,12 @@
-include_lib("kernel/include/inet.hrl").
-include("ei_tmo_SUITE_data/ei_tmo_test_cases.hrl").
--export([all/0, suite/0,
+-export([all/0, groups/0, suite/0,
init_per_testcase/2, end_per_testcase/2,
- framework_check/1, ei_accept_tmo/1, ei_connect_tmo/1, ei_send_tmo/1,
- ei_connect_tmo/0,
+ framework_check/1, ei_accept_tmo/1, ei_connect_tmo/1,
+ ei_send_tmo/1,
+ ei_send_failure_tmo/1,
+ ei_connect_unreachable_tmo/0, ei_connect_unreachable_tmo/1,
ei_recv_tmo/1]).
suite() ->
@@ -36,19 +38,25 @@ suite() ->
{timetrap, {minutes, 1}}].
all() ->
- [framework_check, ei_accept_tmo, ei_connect_tmo,
- ei_send_tmo, ei_recv_tmo].
+ [framework_check,
+ ei_connect_unreachable_tmo,
+ ei_send_failure_tmo,
+ {group, default},
+ {group, ussi}].
+
+groups() ->
+ Members = [ei_recv_tmo,
+ ei_accept_tmo,
+ ei_connect_tmo,
+ ei_send_tmo],
+ [{default, [], Members},
+ {ussi, [], Members}].
+
+get_group(Config) ->
+ proplists:get_value(name, proplists:get_value(tc_group_properties,Config)).
init_per_testcase(Case, Config) ->
- Config1 = runner:init_per_testcase(?MODULE, Case, Config),
-
- % test if platform is vxworks_simso
- {_,Host} = split(node()),
- Bool = case atom_to_list(Host) of
- [$v,$x,$s,$i,$m | _] -> true;
- _ -> false
- end,
- [{vxsim,Bool} | Config1].
+ runner:init_per_testcase(?MODULE, Case, Config).
end_per_testcase(_Case, _Config) ->
ok.
@@ -76,7 +84,8 @@ do_one_recv(Config,CNode) ->
P1 = runner:start(Config, ?recv_tmo),
runner:send_term(P1,{CNode,
erlang:get_cookie(),
- node()}),
+ node(),
+ get_group(Config)}),
{term, X} = runner:get_term(P1, 10000),
true = is_integer(X),
CNode1 = join(CNode,Host),
@@ -89,24 +98,22 @@ do_one_recv_failure(Config,CNode) ->
P1 = runner:start(Config, ?recv_tmo),
runner:send_term(P1,{CNode,
erlang:get_cookie(),
- node()}),
+ node(),
+ get_group(Config)}),
{term, X} = runner:get_term(P1, 10000),
true = is_integer(X),
{term, {Ret,ETimedout,ETimedout}} = runner:get_term(P1, 10000),
true = (Ret < 0),
runner:recv_eot(P1).
+-define(EI_DIST_LOW, 5).
+-define(EI_DIST_HIGH, 6).
%% Check send with timeouts.
ei_send_tmo(Config) when is_list(Config) ->
- %dbg:tracer(),
- %dbg:p(self()),
- VxSim = proplists:get_value(vxsim, Config),
register(ei_send_tmo_1,self()),
do_one_send(Config,self(),c_node_send_tmo_1),
do_one_send(Config,ei_send_tmo_1,c_node_send_tmo_2),
- do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3,VxSim),
- do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim),
ok.
@@ -115,7 +122,8 @@ do_one_send(Config,From,CNode) ->
P1 = runner:start(Config, ?send_tmo),
runner:send_term(P1,{CNode,
erlang:get_cookie(),
- node()}),
+ node(),
+ get_group(Config)}),
{term, X} = runner:get_term(P1, 10000),
true = is_integer(X),
CNode1 = join(CNode,Host),
@@ -130,7 +138,17 @@ do_one_send(Config,From,CNode) ->
{term, 0} = runner:get_term(P1, 10000),
runner:recv_eot(P1).
-do_one_send_failure(Config,From,FakeName,CName,VxSim) ->
+ei_send_failure_tmo(Config) when is_list(Config) ->
+ register(ei_send_tmo_1,self()),
+ [begin
+ io:format("Test dist version ~p\n", [Ver]),
+ do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3, Ver),
+ do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4, Ver)
+ end
+ || Ver <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH)],
+ ok.
+
+do_one_send_failure(Config,From,FakeName,CName, OurVer) ->
{_,Host} = split(node()),
OurName = join(FakeName,Host),
Node = join(CName,Host),
@@ -140,22 +158,23 @@ do_one_send_failure(Config,From,FakeName,CName,VxSim) ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = epmd_register(OurName, LSocket, OurVer),
P3 = runner:start(Config, ?send_tmo),
Cookie = kaksmula_som_ingen_bryr_sig_om,
runner:send_term(P3,{CName,
Cookie,
- OurName}),
+ OurName,
+ default}),
SocketB = case gen_tcp:accept(LSocket) of
{ok, Socket1} ->
Socket1;
Else2 ->
exit(Else2)
end,
- {hidden,Node,5} = recv_name(SocketB), % See 1)
+ {hidden,Node} = recv_name(SocketB, OurVer), % See 1)
send_status(SocketB, ok),
MyChallengeB = gen_challenge(),
- send_challenge(SocketB, OurName, MyChallengeB, 5),
+ send_challenge(SocketB, OurName, MyChallengeB, OurVer),
HisChallengeB = recv_challenge_reply(SocketB,
MyChallengeB,
Cookie),
@@ -178,53 +197,43 @@ do_one_send_failure(Config,From,FakeName,CName,VxSim) ->
%% must be large enough so there's time for the select() to time out and
%% the test program to return the error tuple (below).
- Res0 = if VxSim == false ->
- {term,{Res,ETO,Iters,ETO}} = runner:get_term(P3, 20000),
- Res;
- true -> % relax the test for vxsim
- case runner:get_term(P3, 20000) of
- {term,{Res,ETO,Iters,ETO}} ->
- Res;
- {term,{Res,_,Iters,_ETO}} -> % EIO?
- Res
- end
- end,
+ {term,{Res,ETO,Iters,ETO}} = runner:get_term(P3, 20000),
runner:recv_eot(P3),
- true = ((Res0 < 0) and (Iters > 0)),
+ true = ((Res < 0) and (Iters > 0)),
gen_tcp:close(SocketB),
gen_tcp:close(EpmdSocket),
ok.
%% Check accept with timeouts.
-ei_connect_tmo() -> [{require, test_host_not_reachable}].
+ei_connect_unreachable_tmo() -> [{require, test_host_not_reachable}].
-ei_connect_tmo(Config) when is_list(Config) ->
- %dbg:tracer(),
- %dbg:p(self()),
- VxSim = proplists:get_value(vxsim, Config),
+ei_connect_unreachable_tmo(Config) when is_list(Config) ->
DummyNode = make_and_check_dummy(),
P = runner:start(Config, ?connect_tmo),
runner:send_term(P,{c_nod_connect_tmo_1,
kaksmula_som_ingen_bryr_sig_om,
- DummyNode}),
- ETimedout =
- if VxSim == false ->
- {term,{-3,ETO,ETO}} = runner:get_term(P, 10000),
- ETO;
- true -> % relax the test for vxsim
- case runner:get_term(P, 10000) of
- {term,{-3,ETO,ETO}} ->
- ETO;
- {term,{-1,_,ETO}} -> % EHOSTUNREACH = ok
- ETO
- end
- end,
+ DummyNode,
+ default}),
+ {term,{-3,ETimedout,ETimedout}} = runner:get_term(P, 10000),
runner:recv_eot(P),
+ ok.
+
+ei_connect_tmo(Config) when is_list(Config) ->
+ [begin
+ io:format("Test dist version ~p published as ~p\n", [OurVer,OurEpmdVer]),
+ do_ei_connect_tmo(Config, OurVer, OurEpmdVer)
+ end
+ || OurVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurEpmdVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurVer >= OurEpmdVer].
+
+do_ei_connect_tmo(Config, OurVer, OurEpmdVer) ->
P2 = runner:start(Config, ?connect_tmo),
runner:send_term(P2,{c_nod_connect_tmo_2,
erlang:get_cookie(),
- node()}),
+ node(),
+ get_group(Config)}),
{term, X} = runner:get_term(P2, 10000),
runner:recv_eot(P2),
true = is_integer(X),
@@ -238,22 +247,24 @@ ei_connect_tmo(Config) when is_list(Config) ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = epmd_register(OurName, LSocket, OurEpmdVer),
P3 = runner:start(Config, ?connect_tmo),
Cookie = kaksmula_som_ingen_bryr_sig_om,
runner:send_term(P3,{c_nod_connect_tmo_3,
Cookie,
- OurName}),
+ OurName,
+ get_group(Config)}),
SocketB = case gen_tcp:accept(LSocket) of
{ok, Socket1} ->
Socket1;
Else2 ->
exit(Else2)
end,
- {hidden,Node,5} = recv_name(SocketB), % See 1)
+ {hidden,Node} = recv_name(SocketB, OurEpmdVer), % See 1)
send_status(SocketB, ok),
MyChallengeB = gen_challenge(),
- send_challenge(SocketB, OurName, MyChallengeB, 5),
+ send_challenge(SocketB, OurName, MyChallengeB, OurVer),
+ recv_complement(SocketB, OurVer, OurEpmdVer),
_HisChallengeB = recv_challenge_reply(SocketB,
MyChallengeB,
Cookie),
@@ -266,16 +277,27 @@ ei_connect_tmo(Config) when is_list(Config) ->
%% Check accept with timeouts.
ei_accept_tmo(Config) when is_list(Config) ->
- %%dbg:tracer(),
- %%dbg:p(self()),
+ [begin
+ io:format("Test our dist ver=~p and assumed ver=~p\n",
+ [OurVer, AssumedVer]),
+ do_ei_accept_tmo(Config, OurVer, AssumedVer)
+ end
+ || OurVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ AssumedVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurVer >= AssumedVer],
+ ok.
+
+do_ei_accept_tmo(Config, OurVer, AssumedVer) ->
P = runner:start(Config, ?accept_tmo),
runner:send_term(P,{c_nod_som_ingen_kontaktar_1,
- kaksmula_som_ingen_bryr_sig_om}),
+ kaksmula_som_ingen_bryr_sig_om,
+ get_group(Config)}),
{term,{-1,ETimedout,ETimedout}} = runner:get_term(P, 10000),
runner:recv_eot(P),
P2 = runner:start(Config, ?accept_tmo),
runner:send_term(P2,{c_nod_som_vi_kontaktar_1,
- erlang:get_cookie()}),
+ erlang:get_cookie(),
+ get_group(Config)}),
receive after 1000 -> ok end,
CNode1 = make_node(c_nod_som_vi_kontaktar_1),
{ignored,CNode1} ! tjenare,
@@ -284,19 +306,20 @@ ei_accept_tmo(Config) when is_list(Config) ->
true = is_integer(X),
P3 = runner:start(Config, ?accept_tmo),
runner:send_term(P3,{c_nod_som_vi_kontaktar_2,
- erlang:get_cookie()}),
+ erlang:get_cookie(),
+ get_group(Config)}),
receive after 1000 -> ok end,
CNode2 = make_node(c_nod_som_vi_kontaktar_2),
{NA,NB} = split(CNode2),
{_,Host} = split(node()),
OurName = join(ccc,Host),
- {port,PortNo,_} = erl_epmd:port_please(NA,NB),
+ {port,PortNo,?EI_DIST_HIGH} = erl_epmd:port_please(NA,NB),
{ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
- send_name(SocketA,OurName,5),
+ send_name(SocketA,OurName,OurVer,AssumedVer),
ok = recv_status(SocketA),
- {hidden,_Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1)
+ {hidden,_Node,HisChallengeA} = recv_challenge(SocketA,OurVer), % See 1)
_OurChallengeA = gen_challenge(),
_OurDigestA = gen_digest(HisChallengeA, erlang:get_cookie()),
%% Dont do the last two steps of the connection setup...
@@ -342,6 +365,7 @@ make_and_check_dummy() ->
-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
-define(DFLAG_NEW_FLOATS,16#800).
-define(DFLAG_DIST_MONITOR,8).
+-define(DFLAG_HANDSHAKE_23,16#1000000).
%% From R9 and forward extended references is compulsory
%% From 14 and forward new float is compulsory
@@ -405,31 +429,61 @@ recv_status(Socket) ->
exit(Bad)
end.
-send_challenge(Socket, Node, Challenge, Version) ->
- send_challenge(Socket, Node, Challenge, Version, ?COMPULSORY_DFLAGS).
-send_challenge(Socket, Node, Challenge, Version, Flags) ->
- {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket),
- ?to_port(Socket, [$n,?int16(Version),?int32(Flags),
- ?int32(Challenge), atom_to_list(Node)]).
-
-recv_challenge(Socket) ->
+send_challenge(Socket, Node, Challenge, OurVer) ->
+ send_challenge(Socket, Node, Challenge, OurVer, ?COMPULSORY_DFLAGS).
+
+send_challenge(Socket, Node, Challenge, OurVer, Flags) ->
+ if OurVer =:= 5 ->
+ ?to_port(Socket, [$n, ?int16(OurVer), ?int32(Flags),
+ ?int32(Challenge), atom_to_list(Node)]);
+ OurVer >= 6 ->
+ NodeName = atom_to_binary(Node, latin1),
+ NameLen = byte_size(NodeName),
+ Creation = erts_internal:get_creation(),
+ ?to_port(Socket, [$N,
+ <<(Flags bor ?DFLAG_HANDSHAKE_23):64,
+ Challenge:32,
+ Creation:32,
+ NameLen:16>>,
+ NodeName])
+ end.
+
+recv_challenge(Socket, OurVer) ->
case gen_tcp:recv(Socket, 0) of
{ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
+ 5 = OurVer,
Flags = ?u32(Fl1,Fl2,Fl3,Fl4),
- Type = case Flags band ?DFLAG_PUBLISHED of
- 0 ->
- hidden;
- _ ->
- normal
- end,
+ Type = flags_to_type(Flags),
Node =list_to_atom(Ns),
- Version = ?u16(V1,V0),
+ OurVer = ?u16(V1,V0), % echoed back
+ Challenge = ?u32(CA3,CA2,CA1,CA0),
+ {Type,Node,Challenge};
+
+ {ok,[$N, F7,F6,F5,F4,F3,F2,F1,F0, CA3,CA2,CA1,CA0,
+ _Cr3,_Cr2,_Cr1,_Cr0, NL1,NL0 | Rest]} ->
+ true = (OurVer >= 6),
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ Type = flags_to_type(Flags),
+ NameLen = ?u16(NL1,NL0),
+ {NodeName,_} = lists:split(NameLen, Rest),
+ Node = list_to_atom(NodeName),
Challenge = ?u32(CA3,CA2,CA1,CA0),
- {Type,Node,Version,Challenge};
+ %%Creation = ?u32(Cr3,Cr2,Cr1,Cr0),
+ %%true = (Creation =/= 0),
+ {Type,Node,Challenge};
+
_ ->
?shutdown(no_node)
end.
+flags_to_type(Flags) ->
+ case Flags band ?DFLAG_PUBLISHED of
+ 0 ->
+ hidden;
+ _ ->
+ normal
+ end.
+
%send_challenge_reply(Socket, Challenge, Digest) ->
% ?to_port(Socket, [$r,?int32(Challenge),Digest]).
@@ -443,8 +497,8 @@ recv_challenge_reply(Socket, ChallengeA, Cookie) ->
true ->
?shutdown(bad_challenge_reply)
end;
- _ ->
- ?shutdown(no_node)
+ Other ->
+ ?shutdown({recv_challenge_reply,Other})
end.
send_challenge_ack(Socket, Digest) ->
@@ -463,37 +517,53 @@ send_challenge_ack(Socket, Digest) ->
% ?shutdown(bad_challenge_ack)
% end.
-send_name(Socket, MyNode0, Version) ->
- send_name(Socket, MyNode0, Version, ?COMPULSORY_DFLAGS).
-send_name(Socket, MyNode0, Version, Flags) ->
- MyNode = atom_to_list(MyNode0),
- ?to_port(Socket, [$n,?int16(Version),?int32(Flags)] ++
- MyNode).
+send_name(Socket, MyNode, OurVer, AssumedVer) ->
+ Flags = ?COMPULSORY_DFLAGS bor (case OurVer of
+ 5 -> 0;
+ 6 -> ?DFLAG_HANDSHAKE_23
+ end),
+ send_name(Socket, MyNode, OurVer, AssumedVer, Flags).
+
+send_name(Socket, MyNode, OurVer, AssumedVer, Flags) ->
+ NodeName = atom_to_binary(MyNode, latin1),
+ if AssumedVer =:= 5 ->
+ ?to_port(Socket, [$n,?int16(OurVer),?int32(Flags),NodeName]);
+ AssumedVer >= 6 ->
+ Creation = erts_internal:get_creation(),
+ ?to_port(Socket, [$N,
+ <<Flags:64,
+ Creation:32,
+ (byte_size(NodeName)):16>>,
+ NodeName])
+ end.
-%%
-%% recv_name is common for both old and new handshake.
-%%
-recv_name(Socket) ->
+recv_name(Socket, OurEpmdVer) ->
case gen_tcp:recv(Socket, 0) of
- {ok,Data} ->
- get_name(Data);
+ {ok,[$n, V1,V0, F3,F2,F1,F0 | OtherNode]} ->
+ 5 = OurEpmdVer,
+ 5 = ?u16(V1,V0),
+ Type = flags_to_type(?u32(F3,F2,F1,F0)),
+ {Type, list_to_atom(OtherNode)};
+ {ok,[$N, F7,F6,F5,F4,F3,F2,F1,F0, _Cr3,_Cr2,_Cr1,_Cr0, NL1, NL0 | Rest]} ->
+ true = (OurEpmdVer >= 6),
+ {OtherNode, _Residue} = lists:split(?u16(NL1,NL0), Rest),
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ Type = flags_to_type(Flags),
+ {Type, list_to_atom(OtherNode)};
Res ->
?shutdown({no_node,Res})
end.
-get_name([$m,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {normal, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$h,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {hidden, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) ->
- Type = case ?u32(Flag1, Flag2, Flag3, Flag4) band ?DFLAG_PUBLISHED of
- 0 -> hidden;
- _ -> normal
- end,
- {Type, list_to_atom(OtherNode),
- ?u16(VersionA,VersionB)};
-get_name(Data) ->
- ?shutdown(Data).
+recv_complement(Socket, OurVer, 5) when OurVer > 5 ->
+ case gen_tcp:recv(Socket, 0) of
+ {ok,[$c, _F7,_F6,_F5,_F4, _Cr3,_Cr2,_Cr1,_Cr0]} ->
+ ok;
+ Res ->
+ ?shutdown({no_node,Res})
+ end;
+recv_complement(_, _OurVer, _OurEpmdVer) ->
+ ok.
+
%%
%% tell_name is for old handshake
@@ -538,13 +608,10 @@ wait_for_reg_reply(Socket, SoFar) ->
receive
{tcp, Socket, Data0} ->
case SoFar ++ Data0 of
- [$y, Result, A, B] ->
- case Result of
- 0 ->
- {alive, Socket, ?u16(A, B)};
- _ ->
- {error, duplicate_name}
- end;
+ [$y, 0, Cr1,Cr0] ->
+ {alive, Socket, ?u16(Cr1,Cr0)};
+ [$v, 0, Cr3,Cr2,Cr1,Cr0] ->
+ {alive, Socket, ?u32(Cr3,Cr2,Cr1,Cr0)};
Data when length(Data) < 4 ->
wait_for_reg_reply(Socket, Data);
Garbage ->
@@ -558,9 +625,9 @@ wait_for_reg_reply(Socket, SoFar) ->
end.
-register(NodeName, ListenSocket, VLow, VHigh) ->
+epmd_register(NodeName, ListenSocket, OurVer) ->
{ok,{_,TcpPort}} = inet:sockname(ListenSocket),
- case do_register_node(NodeName, TcpPort, VLow, VHigh) of
+ case do_register_node(NodeName, TcpPort, ?EI_DIST_LOW, OurVer) of
{alive, Socket, _Creation} ->
Socket;
Other ->
diff --git a/lib/erl_interface/test/ei_tmo_SUITE_data/Makefile.src b/lib/erl_interface/test/ei_tmo_SUITE_data/Makefile.src
index b4ee361939..378e276524 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/ei_tmo_SUITE_data/Makefile.src
@@ -25,7 +25,8 @@ CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
LD = @LD@
LIBEI = @erl_interface_eilib@
LIBFLAGS = ../all_SUITE_data/ei_runner@obj@ \
- $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
+ ../all_SUITE_data/my_ussi@obj@ \
+ $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
@erl_interface_threadlib@
CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
EI_TMO_OBJS = ei_tmo_test@obj@ ei_tmo_test_decl@obj@
diff --git a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
index 693e405f75..3c20e94c02 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
+++ b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c
@@ -21,9 +21,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#endif
#ifdef __WIN32__
#include <winsock2.h>
@@ -35,6 +32,7 @@
#endif
#include "ei_runner.h"
+#include "my_ussi.h"
#ifndef __WIN32__
#define closesocket(X) close(X)
@@ -67,7 +65,7 @@ static void debugf_open(int number)
{
char filename[1024];
sprintf(filename,"ei_tmo_test%d.debug",number);
-#if !defined(VXWORKS) && !defined(__WIN32__)
+#if !defined(__WIN32__)
close(2);
#endif
debugfile = fopen(filename,"a");
@@ -86,6 +84,7 @@ static void debugf_close(void)
#define DEBUGF(X) /* noop */
#endif
+
TESTCASE(framework_check)
{
char *ptr = NULL;
@@ -128,19 +127,26 @@ TESTCASE(framework_check)
report(1);
}
-int decode_request(char **nodename_p, char **cookie_p, char **peername_p)
+int decode_request(char **nodename_p, char **cookie_p, char **peername_p,
+ int *use_ussi_p)
{
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
+ char socket_impl[10];
char *ptr = NULL;
ei_x_buff x;
int len;
int version;
int type;
int size;
- int expected_size = (peername_p == NULL) ? 2 : 3;
+ int expected_size = 2;
int ret = -1;
+
+ if (peername_p)
+ ++expected_size;
+ if (use_ussi_p)
+ ++expected_size;
ptr = read_packet(&len);
ei_x_new(&x);
@@ -180,7 +186,6 @@ int decode_request(char **nodename_p, char **cookie_p, char **peername_p)
}
nodename = malloc(size+1);
ei_decode_atom(x.buff,&len,nodename);
- nodename[size] = '\0'; /* needed????? */
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
@@ -191,8 +196,7 @@ int decode_request(char **nodename_p, char **cookie_p, char **peername_p)
}
cookie = malloc(size + 1);
ei_decode_atom(x.buff,&len,cookie);
- cookie[size] = '\0'; /* needed????? */
- if (expected_size > 2) {
+ if (peername_p) {
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
@@ -212,6 +216,22 @@ int decode_request(char **nodename_p, char **cookie_p, char **peername_p)
DEBUGF(("nodename = %s, cookie = %s\n",
nodename, cookie));
}
+
+ if (use_ussi_p) {
+ if (ei_decode_atom_as(x.buff,&len,socket_impl,sizeof(socket_impl),
+ ERLANG_ASCII, NULL, NULL)) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ if (strcmp(socket_impl,"default") == 0)
+ *use_ussi_p = 0;
+ else if (strcmp(socket_impl,"ussi") == 0)
+ *use_ussi_p = 1;
+ else {
+ DEBUGF(("Unkown socket_impl '%s' at %d\n",socket_impl,__LINE__));
+ goto cleanup;
+ }
+ }
*nodename_p = nodename;
nodename = NULL;
*cookie_p = cookie;
@@ -339,6 +359,7 @@ TESTCASE(recv_tmo)
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
+ int use_ussi;
int com_sock = -1;
ei_cnode nodeinfo;
@@ -346,12 +367,22 @@ TESTCASE(recv_tmo)
OPEN_DEBUGFILE(5);
- if (decode_request(&nodename,&cookie,&peername) != 0) {
+ if (decode_request(&nodename,&cookie,&peername,&use_ussi) != 0) {
goto cleanup;
}
- if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
+ if (use_ussi) {
+ my_ussi_init();
+ if (ei_connect_init_ussi(&nodeinfo, nodename, cookie, 0,
+ &my_ussi, sizeof(my_ussi), NULL) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ }
+ else {
+ if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
@@ -451,18 +482,29 @@ TESTCASE(send_tmo)
char *cookie = NULL;
char *peername = NULL;
int com_sock = -1;
+ int use_ussi;
ei_cnode nodeinfo;
ei_init();
OPEN_DEBUGFILE(4);
- if (decode_request(&nodename,&cookie,&peername) != 0) {
+ if (decode_request(&nodename,&cookie,&peername,&use_ussi) != 0) {
goto cleanup;
}
- if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
+ if (use_ussi) {
+ my_ussi_init();
+ if (ei_connect_init_ussi(&nodeinfo, nodename, cookie, 0,
+ &my_ussi, sizeof(my_ussi), NULL) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ }
+ else {
+ if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
@@ -593,18 +635,29 @@ TESTCASE(connect_tmo)
char *cookie = NULL;
char *peername = NULL;
int com_sock = -1;
+ int use_ussi;
ei_cnode nodeinfo;
ei_init();
OPEN_DEBUGFILE(3);
- if (decode_request(&nodename,&cookie,&peername) != 0) {
+ if (decode_request(&nodename,&cookie,&peername,&use_ussi) != 0) {
goto cleanup;
}
- if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
+ if (use_ussi) {
+ my_ussi_init();
+ if (ei_connect_init_ussi(&nodeinfo, nodename, cookie, 0,
+ &my_ussi, sizeof(my_ussi), NULL) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ }
+ else {
+ if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
@@ -679,10 +732,12 @@ TESTCASE(accept_tmo)
int listen_sock = -1;
int epmd_sock = -1;
int com_sock = -1;
+ int use_ussi;
struct sockaddr_in sin;
int sin_siz = sizeof(sin);
ErlConnect peer;
ei_cnode nodeinfo;
+ int port_no;
ei_init();
@@ -690,38 +745,55 @@ TESTCASE(accept_tmo)
putenv("EI_TRACELEVEL=10");
- if (decode_request(&nodename,&cookie,NULL) != 0) {
- goto cleanup;
- }
- if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
+ if (decode_request(&nodename,&cookie,NULL,&use_ussi) != 0) {
goto cleanup;
}
+ if (use_ussi) {
+ my_ussi_init();
+ if (ei_connect_init_ussi(&nodeinfo, nodename, cookie, 0,
+ &my_ussi, sizeof(my_ussi), NULL) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ port_no = 0;
+ listen_sock = ei_listen(&nodeinfo, &port_no, 5);
+ if (listen_sock == ERL_ERROR) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ }
+ else {
+ if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
- if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
- }
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
-
- if (bind(listen_sock,(struct sockaddr *) &sin, sizeof(sin)) != 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
- }
- if (getsockname(listen_sock,
- (struct sockaddr *) &sin, &sin_siz) != 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
- }
- if (listen(listen_sock, 5) != 0) {
- DEBUGF(("Failure at line %d\n",__LINE__));
- goto cleanup;
- }
-
- if ((epmd_sock = ei_publish(&nodeinfo, ntohs(sin.sin_port))) < 0) {
- DEBUGF(("Failure at line %d[%d,%d]\n",__LINE__,sin.sin_port,erl_errno));
+ if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(listen_sock,(struct sockaddr *) &sin, sizeof(sin)) != 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ if (getsockname(listen_sock,
+ (struct sockaddr *) &sin, &sin_siz) != 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ if (listen(listen_sock, 5) != 0) {
+ DEBUGF(("Failure at line %d\n",__LINE__));
+ goto cleanup;
+ }
+ port_no = ntohs(sin.sin_port);
+ }
+
+ if ((epmd_sock = ei_publish(&nodeinfo, port_no)) < 0) {
+ DEBUGF(("Failure at line %d[%d,%d]\n",__LINE__,port_no,erl_errno));
goto cleanup;
}
diff --git a/lib/erl_interface/test/erl_call_SUITE.erl b/lib/erl_interface/test/erl_call_SUITE.erl
index 9e2b2e4251..0d95a1361b 100644
--- a/lib/erl_interface/test/erl_call_SUITE.erl
+++ b/lib/erl_interface/test/erl_call_SUITE.erl
@@ -23,42 +23,88 @@
-include_lib("common_test/include/ct.hrl").
--export([all/0, smoke/1]).
+-export([all/0, smoke/1, test_connect_to_host_port/1]).
-all() ->
- [smoke].
+all() ->
+ [smoke,
+ test_connect_to_host_port].
smoke(Config) when is_list(Config) ->
- ErlCall = find_erl_call(),
- NameSwitch = case net_kernel:longnames() of
- true ->
- "-name";
- false ->
- "-sname"
- end,
Name = atom_to_list(?MODULE)
++ "-"
++ integer_to_list(erlang:system_time(microsecond)),
- ArgsList = ["-s", "-a", "erlang node", NameSwitch, Name],
- io:format("erl_call: \"~ts\"\n~nargs list: ~p~n", [ErlCall, ArgsList]),
- CmdRes = get_smoke_port_res(open_port({spawn_executable, ErlCall},
- [{args, ArgsList}, eof]), []),
- io:format("CmdRes: ~p~n", [CmdRes]),
+ RetNodeName = start_node_and_get_node_name(Name),
+
+ halt_node(Name),
[_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
NodeName = list_to_atom(Name ++ "@" ++ Hostname),
- io:format("NodeName: ~p~n~n", [NodeName]),
+ NodeName = list_to_atom(RetNodeName),
+ ok.
- pong = net_adm:ping(NodeName),
- rpc:cast(NodeName, erlang, halt, []),
- NodeName = list_to_atom(string:trim(CmdRes, both, "'")),
+
+test_connect_to_host_port(Config) when is_list(Config) ->
+ Name = atom_to_list(?MODULE)
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(microsecond)),
+ Port = start_node_and_get_port(Name),
+ AddressCaller =
+ fun(Address) ->
+ get_erl_call_result(["-address",
+ Address,
+ "-a",
+ "erlang length [[1,2,3,4,5,6,7,8,9]]"])
+ end,
+ "9" = AddressCaller(erlang:integer_to_list(Port)),
+ "9" = AddressCaller(":" ++ erlang:integer_to_list(Port)),
+ [_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
+ "9" = AddressCaller(Hostname ++ ":" ++ erlang:integer_to_list(Port)),
+ FailedRes = AddressCaller("80"),
+ case string:find(FailedRes, "80") of
+ nomatch -> ct:fail("Incorrect error message");
+ _ -> ok
+ end,
+ halt_node(Name),
ok.
%
% Utility functions...
%
+
+halt_node(Name) ->
+ [_, Hostname] = string:lexemes(atom_to_list(node()), "@"),
+ NodeName = list_to_atom(Name ++ "@" ++ Hostname),
+ io:format("NodeName: ~p~n~n", [NodeName]),
+
+ pong = net_adm:ping(NodeName),
+ rpc:cast(NodeName, erlang, halt, []).
+
+start_node_and_get_node_name(Name) ->
+ NameSwitch = case net_kernel:longnames() of
+ true ->
+ "-name";
+ false ->
+ "-sname"
+ end,
+ string:trim(get_erl_call_result(["-s",
+ NameSwitch,
+ Name, "-a",
+ "erlang node"]),
+ both,
+ "'").
+
+start_node_and_get_port(Name) ->
+ start_node_and_get_node_name(Name),
+ {ok, NamePortList} = net_adm:names(),
+ {value, {_, Port}}
+ = lists:search(fun({N, _}) ->
+ string:equal(N, Name)
+ end,
+ NamePortList),
+ Port.
+
find_erl_call() ->
ErlCallName = case os:type() of
{win32, _} -> "erl_call.exe";
@@ -86,10 +132,19 @@ find_erl_call() ->
ErlCall
end.
-get_smoke_port_res(Port, Acc) when is_port(Port) ->
+
+get_erl_call_result(ArgsList) ->
+ ErlCall = find_erl_call(),
+ io:format("erl_call: \"~ts\"\n~nargs list: ~p~n", [ErlCall, ArgsList]),
+ CmdRes = get_port_res(open_port({spawn_executable, ErlCall},
+ [{args, ArgsList}, eof, stderr_to_stdout]), []),
+ io:format("CmdRes: ~p~n", [CmdRes]),
+ CmdRes.
+
+get_port_res(Port, Acc) when is_port(Port) ->
receive
{Port, {data, Data}} ->
- get_smoke_port_res(Port, [Acc|Data]);
+ get_port_res(Port, [Acc|Data]);
{Port, eof} ->
lists:flatten(Acc)
end.
diff --git a/lib/erl_interface/test/erl_connect_SUITE.erl b/lib/erl_interface/test/erl_connect_SUITE.erl
deleted file mode 100644
index 782691b8fb..0000000000
--- a/lib/erl_interface/test/erl_connect_SUITE.erl
+++ /dev/null
@@ -1,131 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(erl_connect_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("erl_connect_SUITE_data/erl_connect_test_cases.hrl").
-
--export([all/0, suite/0,
- init_per_testcase/2,
- erl_send/1, erl_reg_send/1,
- erl_send_cookie_file/1]).
-
--import(runner, [get_term/1,send_term/2]).
-
-suite() ->
- [{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
-
-all() ->
- [erl_send, erl_reg_send, erl_send_cookie_file].
-
-
-init_per_testcase(Case, Config) ->
- runner:init_per_testcase(?MODULE, Case, Config).
-
-erl_send(Config) when is_list(Config) ->
- P = runner:start(Config, ?interpret),
- 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0),
- {ok,Fd} = erl_connect(P, node()),
-
- ok = erl_send(P, Fd, self(), AMsg={a,message}),
- receive AMsg -> ok end,
-
- 0 = erl_close_connection(P,Fd),
- runner:send_eot(P),
- runner:recv_eot(P),
- ok.
-
-erl_send_cookie_file(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Skipped on VxWorks"};
- _ ->
- P = runner:start(Config, ?interpret),
- 1 = erl_connect_init(P, 42, '', 0),
- {ok,Fd} = erl_connect(P, node()),
-
- ok = erl_send(P, Fd, self(), AMsg={a,message}),
- receive AMsg -> ok end,
-
- 0 = erl_close_connection(P,Fd),
- runner:send_eot(P),
- runner:recv_eot(P),
- ok
- end.
-
-erl_reg_send(Config) when is_list(Config) ->
- P = runner:start(Config, ?interpret),
- 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0),
- {ok,Fd} = erl_connect(P, node()),
-
- ARegName = a_strange_registred_name,
- register(ARegName, self()),
- ok = erl_reg_send(P, Fd, ARegName, AMsg={another,[strange],message}),
- receive AMsg -> ok end,
-
- 0 = erl_close_connection(P,Fd),
- runner:send_eot(P),
- runner:recv_eot(P),
- ok.
-
-
-%%% Interface functions for erl_interface functions.
-
-erl_connect_init(P, Num, Cookie, Creation) ->
- send_command(P, erl_connect_init, [Num,Cookie,Creation]),
- case get_term(P) of
- {term,Int} when is_integer(Int) -> Int
- end.
-
-erl_connect(P, Node) ->
- send_command(P, erl_connect, [Node]),
- case get_term(P) of
- {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
- {term,{-1,Errno}} -> {error,Errno}
- end.
-
-erl_close_connection(P, FD) ->
- send_command(P, erl_close_connection, [FD]),
- case get_term(P) of
- {term,Int} when is_integer(Int) -> Int
- end.
-
-erl_send(P, Fd, To, Msg) ->
- send_command(P, erl_send, [Fd,To,Msg]),
- get_send_result(P).
-
-erl_reg_send(P, Fd, To, Msg) ->
- send_command(P, erl_reg_send, [Fd,To,Msg]),
- get_send_result(P).
-
-get_send_result(P) ->
- case get_term(P) of
- {term,{1,_}} -> ok;
- {term,{-1,Errno}} -> {error,Errno};
- {term,{Res,Errno}}->
- io:format("Return value: ~p\nerl_errno: ~p", [Res,Errno]),
- ct:fail(bad_return_value)
- end.
-
-send_command(P, Name, Args) ->
- runner:send_term(P, {Name,list_to_tuple(Args)}).
diff --git a/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.first
deleted file mode 100644
index 21a7aac0b0..0000000000
--- a/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.first
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-erl_connect_test_decl.c: erl_connect_test.c
- erl -noinput -pa ../all_SUITE_data -s init_tc run erl_connect_test -s erlang halt
diff --git a/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.src
deleted file mode 100644
index ff4c382c97..0000000000
--- a/lib/erl_interface/test/erl_connect_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-include @erl_interface_mk_include@
-
-CC0 = @CC@
-CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
-LD = @LD@
-LIBERL = @erl_interface_lib@
-LIBEI = @erl_interface_eilib@
-LIBFLAGS = ../all_SUITE_data/runner@obj@ \
- $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
- @erl_interface_threadlib@
-CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
-OBJS = erl_connect_test@obj@ erl_connect_test_decl@obj@
-
-all: erl_connect_test@exe@
-
-erl_connect_test@exe@: $(OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS)
-
-clean:
- $(RM) $(OBJS)
- $(RM) erl_connect_test@exe@
diff --git a/lib/erl_interface/test/erl_connect_SUITE_data/erl_connect_test.c b/lib/erl_interface/test/erl_connect_SUITE_data/erl_connect_test.c
deleted file mode 100644
index 0adaa79a33..0000000000
--- a/lib/erl_interface/test/erl_connect_SUITE_data/erl_connect_test.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Purpose: Tests the functions in erl_connect.c.
- * Author: Bjorn Gustavsson
- *
- * See the erl_connect_SUITE.erl file for a "table of contents".
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "runner.h"
-
-static void cmd_erl_connect_init(ETERM* args);
-static void cmd_erl_connect(ETERM* args);
-static void cmd_erl_send(ETERM* args);
-static void cmd_erl_reg_send(ETERM* args);
-static void cmd_erl_close_connection(ETERM *args);
-
-static void send_errno_result(int value);
-
-static struct {
- char* name;
- int num_args; /* Number of arguments. */
- void (*func)(ETERM* args);
-} commands[] = {
- "erl_connect_init", 3, cmd_erl_connect_init,
- "erl_connect", 1, cmd_erl_connect,
- "erl_close_connection", 1, cmd_erl_close_connection,
- "erl_send", 3, cmd_erl_send,
- "erl_reg_send", 3, cmd_erl_reg_send,
-};
-
-
-/*
- * Sends a list contaning all data types to the Erlang side.
- */
-
-TESTCASE(interpret)
-{
- ETERM* term;
-
- erl_init(NULL, 0);
-
- outer_loop:
-
- term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
- ETERM* Func;
- ETERM* Args;
- int i;
-
- if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) {
- fail("term should be a tuple of size 2");
- }
-
- Func = erl_element(1, term);
- if (!ERL_IS_ATOM(Func)) {
- fail("function name should be an atom");
- }
- Args = erl_element(2, term);
- if (!ERL_IS_TUPLE(Args)) {
- fail("function arguments should be a tuple");
- }
- erl_free_term(term);
- for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
- int n = strlen(commands[i].name);
- if (ERL_ATOM_SIZE(Func) != n) {
- continue;
- }
- if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) {
- erl_free_term(Func);
- if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) {
- fail("wrong number of arguments");
- }
- commands[i].func(Args);
- erl_free_term(Args);
- goto outer_loop;
- }
- }
- fail("bad command");
- }
-}
-
-#define VERIFY_TYPE(Test, Term) \
-if (!Test(Term)) { \
- fail("wrong type for " #Term); \
-} else { \
-}
-
-static void
-cmd_erl_connect_init(ETERM* args)
-{
- ETERM* number;
- ETERM* res;
- ETERM* cookie;
- char cookie_buffer[256];
-
- number = ERL_TUPLE_ELEMENT(args, 0);
- VERIFY_TYPE(ERL_IS_INTEGER, number);
- cookie = ERL_TUPLE_ELEMENT(args, 1);
- VERIFY_TYPE(ERL_IS_ATOM, cookie);
- if (ERL_ATOM_SIZE(cookie) == 0) {
- res = erl_mk_int(erl_connect_init(ERL_INT_VALUE(number), 0, 0));
- } else {
- memcpy(cookie_buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie));
- cookie_buffer[ERL_ATOM_SIZE(cookie)] = '\0';
- res = erl_mk_int(erl_connect_init(ERL_INT_VALUE(number),
- cookie_buffer, 0));
- }
- send_term(res);
- erl_free_term(res);
-}
-
-static void
-cmd_erl_connect(ETERM* args)
-{
- ETERM* node;
- char node_buffer[256];
-
- node = ERL_TUPLE_ELEMENT(args, 0);
- VERIFY_TYPE(ERL_IS_ATOM, node);
- memcpy(node_buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node));
- node_buffer[ERL_ATOM_SIZE(node)] = '\0';
- send_errno_result(erl_connect(node_buffer));
-}
-
-static void
-cmd_erl_close_connection(ETERM* args)
-{
- ETERM* number;
- ETERM* res;
-
- number = ERL_TUPLE_ELEMENT(args, 0);
- VERIFY_TYPE(ERL_IS_INTEGER, number);
- res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number)));
- send_term(res);
- erl_free_term(res);
-}
-
-static void
-cmd_erl_send(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
- ETERM* to = ERL_TUPLE_ELEMENT(args, 1);
- ETERM* msg = ERL_TUPLE_ELEMENT(args, 2);
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
- send_errno_result(erl_send(ERL_INT_VALUE(fd_term), to, msg));
-}
-
-static void
-cmd_erl_reg_send(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
- ETERM* to = ERL_TUPLE_ELEMENT(args, 1);
- ETERM* msg = ERL_TUPLE_ELEMENT(args, 2);
- char reg_name[256];
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
- VERIFY_TYPE(ERL_IS_ATOM, to);
- memcpy(reg_name, ERL_ATOM_PTR(to), ERL_ATOM_SIZE(to));
- reg_name[ERL_ATOM_SIZE(to)] = '\0';
- send_errno_result(erl_reg_send(ERL_INT_VALUE(fd_term), reg_name, msg));
-}
-
-static void
-send_errno_result(int value)
-{
- ETERM* res_array[2];
- ETERM* res_tuple;
-
- res_array[0] = erl_mk_int(value);
- res_array[1] = erl_mk_int(erl_errno);
- res_tuple = erl_mk_tuple(res_array, 2);
- send_term(res_tuple);
- erl_free_term(res_array[0]);
- erl_free_term(res_array[1]);
- erl_free_term(res_tuple);
-}
diff --git a/lib/erl_interface/test/erl_eterm_SUITE.erl b/lib/erl_interface/test/erl_eterm_SUITE.erl
deleted file mode 100644
index 77910a9fc7..0000000000
--- a/lib/erl_interface/test/erl_eterm_SUITE.erl
+++ /dev/null
@@ -1,1084 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(erl_eterm_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("erl_eterm_SUITE_data/eterm_test_cases.hrl").
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% The tests are organised as follows:
-%%%
-%%% 1. Basic tests (encoding, decoding, memory allocation).
-%%% 2. Constructing terms (the erl_mk_xxx() functions and erl_copy_term()).
-%%% 3. Extracting & info functions (erl_hd(), erl_length() etc).
-%%% 4. I/O list functions.
-%%% 5. Miscellaneous functions.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
--export([all/0, suite/0,
- init_per_testcase/2,
- build_terms/1, round_trip_conversion/1,
- decode_terms/1, decode_float/1,
- t_erl_mk_int/1, t_erl_mk_list/1,
- basic_copy/1,
- t_erl_cons/1,
- t_erl_mk_atom/1,
- t_erl_mk_binary/1,
- t_erl_mk_empty_list/1,
- t_erl_mk_float/1,
- t_erl_mk_pid/1,
- t_erl_mk_xpid/1,
- t_erl_mk_port/1,
- t_erl_mk_xport/1,
- t_erl_mk_ref/1,
- t_erl_mk_long_ref/1,
- t_erl_mk_string/1,
- t_erl_mk_estring/1,
- t_erl_mk_tuple/1,
- t_erl_mk_uint/1,
- t_erl_mk_var/1,
- t_erl_size/1,
- t_erl_var_content/1,
- t_erl_element/1,
- t_erl_length/1, t_erl_hd/1, t_erl_tl/1,
- type_checks/1, extractor_macros/1,
- t_erl_iolist_length/1, t_erl_iolist_to_binary/1,
- t_erl_iolist_to_string/1,
- erl_print_term/1, print_string/1,
- t_erl_free_compound/1,
- high_chaparal/1,
- broken_data/1,
- cnode_1/1]).
-
--export([start_cnode/1]).
-
--import(runner, [get_term/1]).
-
-%% This test suite controls the running of the C language functions
-%% in eterm_test.c and print_term.c.
-
-suite() ->
- [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [build_terms, round_trip_conversion, decode_terms,
- decode_float, t_erl_mk_int, t_erl_mk_list, basic_copy,
- t_erl_mk_atom, t_erl_mk_binary, t_erl_mk_empty_list,
- t_erl_mk_float, t_erl_mk_pid, t_erl_mk_xpid,
- t_erl_mk_port, t_erl_mk_xport, t_erl_mk_ref,
- t_erl_mk_long_ref, t_erl_mk_string, t_erl_mk_estring,
- t_erl_mk_tuple, t_erl_mk_uint, t_erl_mk_var, t_erl_size,
- t_erl_var_content, t_erl_element, t_erl_cons,
- t_erl_length, t_erl_hd, t_erl_tl, type_checks,
- extractor_macros, t_erl_iolist_length,
- t_erl_iolist_to_binary, t_erl_iolist_to_string,
- erl_print_term, print_string, t_erl_free_compound,
- high_chaparal, broken_data, cnode_1].
-
-
-init_per_testcase(Case, Config) ->
- runner:init_per_testcase(?MODULE, Case, Config).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%
-%%% 1. B a s i c t e s t s
-%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% This test asks the C function to construct all data types in
-%% a list and verifies that the result is as expected.
-
-build_terms(Config) when is_list(Config) ->
- P = runner:start(Config, ?build_terms),
- {term, Term} = get_term(P),
- io:format("Received: ~p", [Term]),
- [ARefLN, ARef, APortLN, APort, APidLN, APid,
- {element1, 42, 767}, "A string",
- 1, -1, 0, 3.0, ABin, 'I am an atom'] = Term,
- "A binary" = binary_to_list(ABin),
- case ARef of
- R when is_reference(R), node(R) == kalle@localhost -> ok
- end,
- case ARefLN of
- R1 when is_reference(R1), node(R1) == abcdefghijabcdefghij@localhost -> ok
- end,
- case APort of
- Port when is_port(Port), node(Port) == kalle@localhost -> ok
- end,
- case APortLN of
- Port1 when is_port(Port1), node(Port1) == abcdefghijabcdefghij@localhost -> ok
- end,
- case APid of
- Pid when is_pid(Pid), node(Pid) == kalle@localhost -> ok
- end,
- case APidLN of
- Pid1 when is_pid(Pid1), node(Pid1) == abcdefghijabcdefghij@localhost -> ok
- end,
-
- runner:recv_eot(P),
- ok.
-
-%% This test is run entirely in C code.
-
-round_trip_conversion(Config) when is_list(Config) ->
- runner:test(Config, ?round_trip_conversion),
- ok.
-
-%% This test sends a list of all data types to the C code function,
-%% which decodes it and verifies it.
-
-decode_terms(Config) when is_list(Config) ->
- Dummy1 = list_to_atom(filename:join(proplists:get_value(priv_dir, Config),
- dummy_file1)),
- Dummy2 = list_to_atom(filename:join(proplists:get_value(priv_dir, Config),
- dummy_file2)),
- Port1 = open_port(Dummy1, [out]),
- Port2 = open_port(Dummy2, [out]),
- ABinary = list_to_binary("A binary"),
- Terms = [make_ref(), make_ref(),
- Port1, Port2,
- self(), self(),
- {element1, 42, 767}, "A string",
- 1, -1, 0, 3.0, ABinary, 'I am an atom'],
-
- P = runner:start(Config, ?decode_terms),
- runner:send_term(P, Terms),
- runner:recv_eot(P),
-
- ok.
-
-%% Decodes the floating point number 3.1415.
-
-decode_float(Config) when is_list(Config) ->
- P = runner:start(Config, ?decode_float),
- runner:send_term(P, 3.1415),
- runner:recv_eot(P),
- ok.
-
-%% Tests the erl_free_compound() function.
-
-t_erl_free_compound(Config) when is_list(Config) ->
- runner:test(Config, ?t_erl_free_compound),
- ok.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%
-%%% 2. C o n s t r u c t i n g t e r m s
-%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% This tests the erl_mk_list() function.
-
-t_erl_mk_list(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_list),
-
- {term, []} = get_term(P),
- {term, [abc]} = get_term(P),
- {term, [abcdef, 42]} = get_term(P),
- {term, [0.0, 23, [], 3.1415]} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_int() function.
-
-t_erl_mk_int(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_int),
-
- {term, 0} = get_term(P),
- {term, 127} = get_term(P),
- {term, 128} = get_term(P),
- {term, 255} = get_term(P),
- {term, 256} = get_term(P),
-
- {term, 16#FFFF} = get_term(P),
- {term, 16#10000} = get_term(P),
-
- {term, 16#07FFFFFF} = get_term(P),
- {term, 16#0FFFFFFF} = get_term(P),
- {term, 16#1FFFFFFF} = get_term(P),
- {term, 16#3FFFFFFF} = get_term(P),
- {term, 16#7FFFFFFF} = get_term(P),
-
- {term, 16#08000000} = get_term(P),
- {term, 16#10000000} = get_term(P),
- {term, 16#20000000} = get_term(P),
- {term, 16#40000000} = get_term(P),
-
-
- {term, -16#07FFFFFF} = get_term(P),
- {term, -16#0FFFFFFF} = get_term(P),
- {term, -16#1FFFFFFF} = get_term(P),
- {term, -16#3FFFFFFF} = get_term(P),
- {term, -16#7FFFFFFF} = get_term(P),
-
- {term, -16#08000000} = get_term(P),
- {term, -16#10000000} = get_term(P),
- {term, -16#20000000} = get_term(P),
- {term, -16#40000000} = get_term(P),
-
- {term, -16#08000001} = get_term(P),
- {term, -16#10000001} = get_term(P),
- {term, -16#20000001} = get_term(P),
- {term, -16#40000001} = get_term(P),
-
- {term, -16#08000002} = get_term(P),
- {term, -16#10000002} = get_term(P),
- {term, -16#20000002} = get_term(P),
- {term, -16#40000002} = get_term(P),
-
- {term, -1999999999} = get_term(P),
- {term, -2000000000} = get_term(P),
- {term, -2000000001} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-%% Basic test of erl_copy_term().
-
-basic_copy(Config) when is_list(Config) ->
- runner:test(Config, ?basic_copy),
- ok.
-
-
-%% This tests the erl_mk_tuple() function.
-
-t_erl_mk_tuple(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_tuple),
-
- {term, {madonna, 21, 'mad donna', 12}} = get_term(P),
- {term, {'Madonna',21,{children,{"Isabella",2}},
- {'home page',"http://www.madonna.com/"}}} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_atom() function.
-
-t_erl_mk_atom(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_atom),
-
- {term, madonna} = (get_term(P)),
- {term, 'Madonna'} = (get_term(P)),
- {term, 'mad donna'} = (get_term(P)),
- {term, '_madonna_'} = (get_term(P)),
- {term, '/home/madonna/tour_plan'} = (get_term(P)),
- {term, 'http://www.madonna.com/tour_plan'} = (get_term(P)),
- {term, '\'madonna\''} = (get_term(P)),
- {term, '\"madonna\"'} = (get_term(P)),
- {term, '\\madonna\\'} = (get_term(P)),
- {term, '{madonna,21,\'mad donna\',12}'} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_binary() function.
-
-t_erl_mk_binary(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_binary),
-
- {term, Bin} = (get_term(P)),
- "{madonna,21,'mad donna',1234.567.890, !#$%&/()=?+-@, \" \\}" = binary_to_list(Bin),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_empty_list() function.
-
-t_erl_mk_empty_list(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_empty_list),
-
- {term, []} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_float() function.
-
-t_erl_mk_float(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "Floating point numbers never compare equal on PPC"};
- _ ->
- P = runner:start(Config, ?t_erl_mk_float),
- {term, {3.1415, 1.999999, 2.000000, 2.000001,
- 2.000002, 12345.67890}} = get_term(P),
- runner:recv_eot(P),
- ok
- end.
-
-
-%% This tests the erl_mk_pid() function.
-
-t_erl_mk_pid(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_pid),
-
- {term, A_pid} = (get_term(P)),
- {pid, kalle@localhost, 3, 2} = nc2vinfo(A_pid),
-
- runner:recv_eot(P),
- ok.
-
-t_erl_mk_xpid(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_xpid),
-
- {term, A_pid} = (get_term(P)),
- {pid, kalle@localhost, 32767, 8191} = nc2vinfo(A_pid),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_port() function.
-
-t_erl_mk_port(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_port),
-
- {term, A_port} = (get_term(P)),
- {port, kalle@localhost, 4} = nc2vinfo(A_port),
-
- runner:recv_eot(P),
- ok.
-
-t_erl_mk_xport(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_xport),
-
- {term, A_port} = (get_term(P)),
- {port, kalle@localhost, 268435455} = nc2vinfo(A_port),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_ref() function.
-
-t_erl_mk_ref(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_ref),
-
- {term, A_ref} = (get_term(P)),
- {ref, kalle@localhost, _Length, [6]} = nc2vinfo(A_ref),
-
- runner:recv_eot(P),
- ok.
-
-t_erl_mk_long_ref(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_long_ref),
-
- {term, A_ref} = (get_term(P)),
- {ref, kalle@localhost, _Length, [4294967295,4294967295,262143]}
- = nc2vinfo(A_ref),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_string() function.
-
-t_erl_mk_string(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_string),
-
- {term, "madonna"} = (get_term(P)),
- {term, "Madonna"} = (get_term(P)),
- {term, "mad donna"} = (get_term(P)),
- {term, "_madonna_"} = (get_term(P)),
- {term, "/home/madonna/tour_plan"} = (get_term(P)),
- {term, "http://www.madonna.com/tour_plan"} = (get_term(P)),
- {term, "\'madonna\'"} = (get_term(P)),
- {term, "\"madonna\""} = (get_term(P)),
- {term, "\\madonna\\"} = (get_term(P)),
- {term, "{madonna,21,'mad donna',12}"} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_estring() function.
-
-t_erl_mk_estring(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_estring),
-
- {term, "madonna"} = (get_term(P)),
- {term, "Madonna"} = (get_term(P)),
- {term, "mad donna"} = (get_term(P)),
- {term, "_madonna_"} = (get_term(P)),
- {term, "/home/madonna/tour_plan"} = (get_term(P)),
- {term, "http://www.madonna.com/tour_plan"} = (get_term(P)),
- {term, "\'madonna\'"} = (get_term(P)),
- {term, "\"madonna\""} = (get_term(P)),
- {term, "\\madonna\\"} = (get_term(P)),
- {term, "{madonna,21,'mad donna',12}"} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_uint() function.
-
-t_erl_mk_uint(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_uint),
-
- {term, 54321} = (get_term(P)),
- {term, 2147483647} = (get_term(P)),
- {term, 2147483648} = (get_term(P)),
- {term, 2147483649} = (get_term(P)),
- {term, 2147483650} = (get_term(P)),
- {term, 4294967295} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_mk_var() function.
-
-t_erl_mk_var(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_mk_var),
-
- {term, 1} = (get_term(P)),
- {term, 0} = (get_term(P)),
- {term, 1} = (get_term(P)),
- {term, 0} = (get_term(P)),
- {term, 1} = (get_term(P)),
- {term, 0} = (get_term(P)),
- {term, 1} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_cons() function.
-
-t_erl_cons(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_cons),
-
- {term, [madonna, 21]} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%
-%%% 3. E x t r a c t i n g & i n f o f u n c t i o n s
-%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Tests the erl_length() function.
-
-t_erl_length(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_length),
-
- 0 = erl_length(P, []),
- 1 = erl_length(P, [a]),
- 2 = erl_length(P, [a, b]),
- 3 = erl_length(P, [a, b, c]),
-
- 4 = erl_length(P, [a, [x, y], c, []]),
-
- -1 = erl_length(P, [a|b]),
- -1 = erl_length(P, a),
-
- runner:finish(P),
- ok.
-
-%% Invokes the erl_length() function.
-
-erl_length(Port, List) ->
- call_erl_function(Port, List).
-
-%% Tests the erl_hd() function.
-
-t_erl_hd(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_hd),
-
- 'NULL' = erl_hd(P, 42),
- 'NULL' = erl_hd(P, abc),
- 'NULL' = erl_hd(P, []),
-
- [] = erl_hd(P, [[], a]),
- a = erl_hd(P, [a]),
- a = erl_hd(P, [a, b]),
- a = erl_hd(P, [a, b, c]),
- a = erl_hd(P, [a|b]),
-
- runner:send_eot(P),
- runner:recv_eot(P),
- ok.
-
-%% Invokes the erl_hd() function.
-
-erl_hd(Port, List) ->
- call_erl_function(Port, List).
-
-%% Tests the erl_tail() function.
-
-t_erl_tl(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_tl),
-
- 'NULL' = erl_tl(P, 42),
- 'NULL' = erl_tl(P, abc),
- 'NULL' = erl_tl(P, []),
-
- [] = erl_tl(P, [a]),
- [b] = erl_tl(P, [a, b]),
- [b, c] = erl_tl(P, [a, b, c]),
-
- b = erl_tl(P, [a|b]),
-
- runner:send_eot(P),
- runner:recv_eot(P),
- ok.
-
-%% Invokes the erl_tail() function in erl_interface.
-
-erl_tl(Port, List) ->
- call_erl_function(Port, List).
-
-%% Tests the type checking macros (done in the C program).
-
-type_checks(Config) when is_list(Config) ->
- runner:test(Config, ?type_checks),
- ok.
-
-%% Tests the extractor macros (done in the C program).
-
-extractor_macros(Config) when is_list(Config) ->
- runner:test(Config, ?extractor_macros),
- ok.
-
-
-%% This tests the erl_size() function.
-
-t_erl_size(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_size),
-
- {term, 0} = (get_term(P)),
- {term, 4} = (get_term(P)),
-
- {term, 0} = (get_term(P)),
- {term, 27} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_var_content() function.
-
-t_erl_var_content(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_var_content),
-
- {term, 17} = (get_term(P)),
- {term, "http://www.madonna.com"} = (get_term(P)),
- {term, 2} = (get_term(P)),
- {term, "http://www.madonna.com"} = (get_term(P)),
- {term, 2} = (get_term(P)),
-
- runner:recv_eot(P),
- ok.
-
-
-%% This tests the erl_element() function.
-
-t_erl_element(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_element),
-
- {term, madonna} = get_term(P),
- {term, 21} = get_term(P),
- {term, 'mad donna'} = get_term(P),
- {term, 12} = get_term(P),
-
- {term, 'Madonna'} = get_term(P),
- {term, 21} = get_term(P),
- {term, {children,{"Isabella",2}}} = get_term(P),
- {term, {'home page',"http://www.madonna.com/"}} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%
-%%% 4. I / O l i s t f u n c t i o n s
-%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Tests the erl_iolist_length() function.
-
-t_erl_iolist_length(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_iolist_length),
-
- %% Flat lists.
-
- 0 = erl_iolist_length(P, []),
- 1 = erl_iolist_length(P, [10]),
- 2 = erl_iolist_length(P, [10, 20]),
- 3 = erl_iolist_length(P, [10, 20, 30]),
- 256 = erl_iolist_length(P, lists:seq(0, 255)),
-
- %% Deep lists.
-
- 0 = erl_iolist_length(P, [[]]),
- 1 = erl_iolist_length(P, [[], 42]),
- 1 = erl_iolist_length(P, [42, []]),
- 2 = erl_iolist_length(P, [42, [], 45]),
-
- 3 = erl_iolist_length(P, [42, [90], 45]),
- 3 = erl_iolist_length(P, [[42, [90]], 45]),
- 3 = erl_iolist_length(P, [[42, [90]], 45]),
-
- %% List with binaries.
-
- 0 = erl_iolist_length(P, [list_to_binary([])]),
- 0 = erl_iolist_length(P, [[], list_to_binary([])]),
- 1 = erl_iolist_length(P, [[1], list_to_binary([])]),
- 1 = erl_iolist_length(P, [[], list_to_binary([2])]),
- 2 = erl_iolist_length(P, [[42], list_to_binary([2])]),
- 4 = erl_iolist_length(P, [[42], list_to_binary([2, 3, 4])]),
-
- %% Binaries as tail.
-
- 0 = erl_iolist_length(P, [[]| list_to_binary([])]),
- 1 = erl_iolist_length(P, [[1]| list_to_binary([])]),
- 1 = erl_iolist_length(P, [[]| list_to_binary([2])]),
- 2 = erl_iolist_length(P, [[42]| list_to_binary([2])]),
-
- %% Binaries only.
-
- 0 = erl_iolist_length(P, list_to_binary("")),
- 1 = erl_iolist_length(P, list_to_binary([1])),
- 2 = erl_iolist_length(P, list_to_binary([1, 2])),
-
- %% Illegal cases.
-
- -1 = erl_iolist_length(P, [42|43]),
- -1 = erl_iolist_length(P, a),
-
- -1 = erl_iolist_length(P, [a]),
- -1 = erl_iolist_length(P, [256]),
- -1 = erl_iolist_length(P, [257]),
- -1 = erl_iolist_length(P, [-1]),
- -1 = erl_iolist_length(P, [-2]),
- -1 = erl_iolist_length(P, [-127]),
- -1 = erl_iolist_length(P, [-128]),
-
- runner:finish(P),
- ok.
-
-%% Invokes the erl_iolist_length() function.
-
-erl_iolist_length(Port, List) ->
- call_erl_function(Port, List).
-
-%% Tests the erl_iolist_to_binary() function.
-
-t_erl_iolist_to_binary(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_iolist_to_binary),
-
- %% Flat lists.
-
- [] = iolist_to_list(P, []),
- [10] = iolist_to_list(P, [10]),
- [10, 20] = iolist_to_list(P, [10, 20]),
- [10, 20, 30] = iolist_to_list(P, [10, 20, 30]),
- AllBytes = lists:seq(0, 255),
- AllBytes = iolist_to_list(P, AllBytes),
-
- %% Deep lists.
-
- [] = iolist_to_list(P, [[]]),
- [42] = iolist_to_list(P, [[], 42]),
- [42] = iolist_to_list(P, [42, []]),
- [42, 45] = iolist_to_list(P, [42, [], 45]),
-
- [42, 90, 45] = iolist_to_list(P, [42, [90], 45]),
- [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]),
- [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]),
-
- %% List with binaries.
-
- [] = iolist_to_list(P, [list_to_binary([])]),
- [] = iolist_to_list(P, [[], list_to_binary([])]),
- [1] = iolist_to_list(P, [[1], list_to_binary([])]),
- [2] = iolist_to_list(P, [[], list_to_binary([2])]),
- [42, 2] = iolist_to_list(P, [[42], list_to_binary([2])]),
- [42, 2, 3, 4] = iolist_to_list(P, [[42], list_to_binary([2, 3, 4])]),
-
- %% Binaries as tail.
-
- [] = iolist_to_list(P, [[]| list_to_binary([])]),
- [1] = iolist_to_list(P, [[1]| list_to_binary([])]),
- [2] = iolist_to_list(P, [[]| list_to_binary([2])]),
- [42, 2] = iolist_to_list(P, [[42]| list_to_binary([2])]),
-
- %% Binaries only.
-
- [] = iolist_to_list(P, list_to_binary("")),
- [1] = iolist_to_list(P, list_to_binary([1])),
- [1, 2] = iolist_to_list(P, list_to_binary([1, 2])),
-
- %% Illegal cases.
-
- 'NULL' = iolist_to_list(P, [42|43]),
- 'NULL' = iolist_to_list(P, a),
-
- 'NULL' = iolist_to_list(P, [a]),
- 'NULL' = iolist_to_list(P, [256]),
- 'NULL' = iolist_to_list(P, [257]),
- 'NULL' = iolist_to_list(P, [-1]),
- 'NULL' = iolist_to_list(P, [-2]),
- 'NULL' = iolist_to_list(P, [-127]),
- 'NULL' = iolist_to_list(P, [-128]),
-
- runner:finish(P),
- ok.
-
-iolist_to_list(Port, Term) ->
- case call_erl_function(Port, Term) of
- 'NULL' ->
- 'NULL';
- Bin when is_binary(Bin) ->
- binary_to_list(Bin)
- end.
-
-%% Tests the erl_iolist_to_string() function.
-
-t_erl_iolist_to_string(Config) when is_list(Config) ->
- P = runner:start(Config, ?t_erl_iolist_to_string),
-
- %% Flat lists.
-
- [0] = iolist_to_string(P, []),
- [10, 0] = iolist_to_string(P, [10]),
- [10, 20, 0] = iolist_to_string(P, [10, 20]),
- [10, 20, 30, 0] = iolist_to_string(P, [10, 20, 30]),
- AllBytes = lists:seq(1, 255)++[0],
- AllBytes = iolist_to_string(P, lists:seq(1, 255)),
-
- %% Deep lists.
-
- [0] = iolist_to_string(P, [[]]),
- [42, 0] = iolist_to_string(P, [[], 42]),
- [42, 0] = iolist_to_string(P, [42, []]),
- [42, 45, 0] = iolist_to_string(P, [42, [], 45]),
-
- [42, 90, 45, 0] = iolist_to_string(P, [42, [90], 45]),
- [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]),
- [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]),
-
- %% List with binaries.
-
- [0] = iolist_to_string(P, [list_to_binary([])]),
- [0] = iolist_to_string(P, [[], list_to_binary([])]),
- [1, 0] = iolist_to_string(P, [[1], list_to_binary([])]),
- [2, 0] = iolist_to_string(P, [[], list_to_binary([2])]),
- [42, 2, 0] = iolist_to_string(P, [[42], list_to_binary([2])]),
- [42, 2, 3, 4, 0] = iolist_to_string(P, [[42],
- list_to_binary([2, 3, 4])]),
-
- %% Binaries as tail.
-
- [0] = iolist_to_string(P, [[]| list_to_binary([])]),
- [1, 0] = iolist_to_string(P, [[1]| list_to_binary([])]),
- [2, 0] = iolist_to_string(P, [[]| list_to_binary([2])]),
- [42, 2, 0] = iolist_to_string(P, [[42]| list_to_binary([2])]),
-
- %% Binaries only.
-
- [0] = iolist_to_string(P, list_to_binary("")),
- [1, 0] = iolist_to_string(P, list_to_binary([1])),
- [1, 2, 0] = iolist_to_string(P, list_to_binary([1, 2])),
-
- %% Illegal cases.
-
- 'NULL' = iolist_to_string(P, [0]),
- 'NULL' = iolist_to_string(P, [65, 0, 66]),
- 'NULL' = iolist_to_string(P, [65, 66, 67, 0]),
-
- 'NULL' = iolist_to_string(P, [42|43]),
- 'NULL' = iolist_to_string(P, a),
-
- 'NULL' = iolist_to_string(P, [a]),
- 'NULL' = iolist_to_string(P, [256]),
- 'NULL' = iolist_to_string(P, [257]),
- 'NULL' = iolist_to_string(P, [-1]),
- 'NULL' = iolist_to_string(P, [-2]),
- 'NULL' = iolist_to_string(P, [-127]),
- 'NULL' = iolist_to_string(P, [-128]),
-
- runner:finish(P),
- ok.
-
-%% Invokes the erl_iolist_to_string() function.
-
-iolist_to_string(Port, Term) ->
- runner:send_term(Port, Term),
- case get_term(Port) of
- {bytes, Result} -> Result;
- 'NULL' -> 'NULL'
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%
-%%% 5. M i s c e l l a n o u s T e s t s
-%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Tests the erl_print_term() function
-erl_print_term(Config) when is_list(Config) ->
- PrintTerm = print_term(Config),
- P = open_port({spawn, PrintTerm}, [stream]),
-
- %% Lists.
-
- print(P, "[]", []),
- print(P, "[a]", [a]),
- print(P, "[[a]]", [[a]]),
- print(P, "[[]]", [[]]),
- print(P, "[a,b,c]", [a,b,c]),
- print(P, "[a,b|c]", [a,b|c]),
- print(P, "[a,[],c]", [a,[],c]),
- print(P, "[a,[1000,1],c]", [a,[1000,1],c]),
-
- %% Tuples.
-
- print(P, "{}", {}),
- print(P, "{ok}", {ok}),
- print(P, "{1,2,3}", {1, 2, 3}),
-
- %% Pids.
-
- {_X, Y, Z} = split_pid(self()),
- PidString = lists:flatten(io_lib:format("<~s.~w.~w>",
- [node(), Y, Z])),
- print(P, PidString, self()),
-
- unlink(P),
- exit(P, die),
- ok.
-
-split_pid(Pid) when is_pid(Pid) ->
- split_pid(pid_to_list(Pid), 0, []).
-
-split_pid([$<|Rest], Cur, Result) ->
- split_pid(Rest, Cur, Result);
-split_pid([Digit|Rest], Cur, Result) when $0 =< Digit, Digit =< $9 ->
- split_pid(Rest, 10*Cur+Digit-$0, Result);
-split_pid([$.|Rest], Cur, Result) ->
- split_pid(Rest, 0, Result++[Cur]);
-split_pid([$>], Cur, Result) ->
- list_to_tuple(Result++[Cur]).
-
-%% Test printing a string with erl_print_term()
-print_string(Config) when is_list(Config) ->
- PrintTerm = print_term(Config),
- P = open_port({spawn, PrintTerm}, [stream]),
-
- %% Strings.
-
- print(P, "\"ABC\"", "ABC"),
- {11, "\"\\tABC\\r\\n\""} = print(P, "\tABC\r\n"),
-
- %% Not strings.
-
- print(P, "[65,66,67,0]", "ABC\000"),
-
- unlink(P),
- exit(P, die),
- ok.
-
-print(Port, TermString, Term) ->
- Length = length(TermString),
- {Length, TermString} = print(Port, Term).
-
-%% This function uses the erl_print_term() function in erl_interface
-%% to print a term.
-%% Returns: {NumChars, Chars}
-
-print(Port, Term) ->
- Bin = term_to_binary(Term),
- Size = size(Bin),
- Port ! {self(), {command, [Size div 256, Size rem 256, Bin]}},
- collect_line(Port, []).
-
-collect_line(Port, Result) ->
- receive
- {Port, {data, Data}} ->
- case lists:reverse(Data) of
- [$\n|Rest] ->
- collect_line1(Rest++Result, []);
- Chars ->
- collect_line(Port, Chars++Result)
- end
- after 5000 ->
- ct:fail("No response from C program")
- end.
-
-collect_line1([$\r|Rest], Result) ->
- {list_to_integer(Result), lists:reverse(Rest)};
-collect_line1([C|Rest], Result) ->
- collect_line1(Rest, [C|Result]).
-
-%% Test case submitted by Per Lundgren, ERV.
-
-high_chaparal(Config) when is_list(Config) ->
- P = runner:start(Config, ?high_chaparal),
- {term, [hello, world]} = get_term(P),
- runner:recv_eot(P),
- ok.
-
-%% OTP-7448
-broken_data(Config) when is_list(Config) ->
- P = runner:start(Config, ?broken_data),
- runner:recv_eot(P),
- ok.
-
-%% This calls a C function with one parameter and returns the result.
-
-call_erl_function(Port, Term) ->
- runner:send_term(Port, Term),
- case get_term(Port) of
- {term, Result} -> Result;
- 'NULL' -> 'NULL'
- end.
-
-print_term(Config) when is_list(Config) ->
- filename:join(proplists:get_value(data_dir, Config), "print_term").
-
-
-
-%%% We receive a ref from the cnode, and expect it to be a long ref.
-%%% We also send a ref we created ourselves, and expect to get it
-%%% back, without having been mutated into short form. We must take
-%%% care then to check the actual returned ref, and not the original
-%%% one, which is equal to it.
-
-%% Tests involving cnode: sends a long ref from a cnode to us
-cnode_1(Config) when is_list(Config) ->
- Cnode = filename:join(proplists:get_value(data_dir, Config), "cnode"),
- register(mip, self()),
- spawn_link(?MODULE, start_cnode, [Cnode]),
- Ref1 = get_ref(),
- io:format("Ref1 ~p~n", [Ref1]),
- check_ref(Ref1),
- Ref2 = make_ref(),
- Pid = receive
- Msg -> Msg %% pid
- end,
- Fun1 = fun(X) -> {Pid, X} end, % sneak in a fun test here
- %Fun1 = {wait_with_funs, new_dist_format},
- Term = {Ref2, Fun1, {1,2,3,4,5,6,7,8,9,10}},
- %% A term which will overflow the original buffer used in 'cnode'.
- Pid ! Term,
- receive
- Term2 ->
- io:format("received ~p~n", [Term2]),
- case Term2 of
- Term ->
- {Ref22,_,_} = Term2,
- check_ref(Ref22);
- X ->
- ct:fail({receive1,X})
- end
- after 5000 ->
- ct:fail(receive1)
- end,
- receive
- Pid ->
- ok;
- Y ->
- ct:fail({receive1,Y})
- after 5000 ->
- ct:fail(receive2)
- end,
- io:format("ref = ~p~n", [Ref1]),
- check_ref(Ref1),
- ok.
-
-check_ref(Ref) ->
- case bin_ext_type(Ref) of
- 101 ->
- ct:fail(oldref);
- 114 ->
- ok;
- Type ->
- ct:fail({type, Type})
- end.
-
-bin_ext_type(T) ->
- [131, Type | _] = binary_to_list(term_to_binary(T)),
- Type.
-
-get_ref() ->
- receive
- X when is_reference(X) ->
- X
- after 5000 ->
- ct:fail({cnode, timeout})
- end.
-
-start_cnode(Cnode) ->
- open_port({spawn, Cnode ++ " " ++ atom_to_list(erlang:get_cookie())}, []),
- rec_cnode().
-
-rec_cnode() ->
- receive
- X ->
- io:format("from cnode: ~p~n", [X]),
- rec_cnode()
- end.
-
-nc2vinfo(Pid) when is_pid(Pid) ->
- [_NodeStr, NumberStr, SerialStr]
- = string:tokens(pid_to_list(Pid), "<.>"),
- Number = list_to_integer(NumberStr),
- Serial = list_to_integer(SerialStr),
- {pid, node(Pid), Number, Serial};
-nc2vinfo(Port) when is_port(Port) ->
- ["#Port", _NodeStr, NumberStr]
- = string:tokens(erlang:port_to_list(Port), "<.>"),
- Number = list_to_integer(NumberStr),
- {port, node(Port), Number};
-nc2vinfo(Ref) when is_reference(Ref) ->
- ["#Ref", _NodeStr | NumStrList]
- = string:tokens(erlang:ref_to_list(Ref), "<.>"),
- {Len, RevNumList} = lists:foldl(fun ("0", {N, []}) ->
- {N+1, []};
- (IStr, {N, Is}) ->
- {N+1,
- [list_to_integer(IStr)|Is]}
- end,
- {0, []},
- NumStrList),
- {ref, node(Ref), Len, lists:reverse(RevNumList)};
-nc2vinfo(Other) ->
- {badarg, Other}.
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.src
deleted file mode 100644
index 4b1ddf77b6..0000000000
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-include @erl_interface_mk_include@
-
-CC0 = @CC@
-CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
-LD = @LD@
-LIBERL = @erl_interface_lib@
-LIBEI = @erl_interface_eilib@
-LIBFLAGS = ../all_SUITE_data/runner@obj@ \
- $(LIBERL) $(LIBEI) @erl_interface_sock_libs@ @LIBS@ \
- @erl_interface_threadlib@
-CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
-ETERM_OBJS = eterm_test@obj@ eterm_test_decl@obj@
-CNODE_OBJS = cnode@obj@
-PRINT_OBJS = print_term@obj@
-EXE_FILES = eterm_test@exe@ print_term@exe@ cnode@exe@
-
-all: $(EXE_FILES)
-
-eterm_test@exe@: $(ETERM_OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(ETERM_OBJS) $(LIBFLAGS)
-
-cnode@exe@: $(CNODE_OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(CNODE_OBJS) $(LIBFLAGS)
-
-print_term@exe@: print_term@obj@ $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(PRINT_OBJS) $(LIBFLAGS)
-
-clean:
- $(RM) $(ETERM_OBJS) $(CNODE_OBJS) $(PRINT_OBJS)
- $(RM) $(EXE_FILES)
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c b/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c
deleted file mode 100644
index b87feb9dfc..0000000000
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "ei.h"
-#include "erl_interface.h"
-
-#define MSGSIZE 13
-
-#define SELF(fd) erl_mk_pid(erl_thisnodename(),fd,0,erl_thiscreation())
-
-#ifdef VXWORKS
-#define MAIN cnode
-#else
-#define MAIN main
-#endif
-
-/* FIXME uses mix och ei and erl_interface */
-
-/*
- A small cnode.
- To be called from the test case erl_eterm_SUITE:cnode_1.
-
- 1) Set up connection to node 'test_server' on the same host.
- All sends are done to a registered process named 'mip'.
- 2) Create a long ref and send it.
- 3) Create a pid for ourselves and send it.
- 4) Receive a message.
- 5) Send back the message part of the message.
- 6) Send back the 'to' part of the message.
- 7) Exit.
-*/
-
-MAIN(int argc, char **argv)
-
-{
- unsigned char *msgbufp;
- int msgsize;
- ErlMessage msg;
- char msgbuf[MSGSIZE];
- char buf[100];
- char buf1[100];
- char buf2[100];
- int ix;
- int s;
- int fd;
- char node[80];
- char server[80];
- char host[80];
- int number;
- ETERM *ref, *ref1, *ref2;
- FILE *dfile = fopen("cnode_debug_printout", "w");
-
- erl_init(NULL, 0);
-
- number = 1;
- if (argc >= 2) {
- s = erl_connect_init(number, argv[1], 0);
- } else {
- s = erl_connect_init(number, (char *) 0, 0);
- }
- gethostname(host, sizeof(host));
- sprintf(node, "c%d@%s", number, host);
-
- fprintf(dfile, "s = %d\n", s); fflush(dfile);
-
- sprintf(server, "test_server@%s", host);
- fd = erl_connect(server);
- fprintf(dfile, "fd = %d\n", fd);
-
-/* fprintf(dfile, "dist = %d\n", erl_distversion(fd)); */
-
-#if 1
- ref = erl_mk_long_ref(node, 4711, 113, 98, 0);
-#else
- ref = erl_mk_ref(node, 4711, 0);
-#endif
- fprintf(dfile, "ref = %p\n", ref); fflush(dfile);
-
- s = erl_reg_send(fd, "mip", ref);
- fprintf(dfile, "s = %d\n", s); fflush(dfile);
-
- {
- ETERM* emsg;
- emsg = SELF(fd);
- fprintf(dfile, "pid = %p\n", emsg); fflush(dfile);
- s = erl_reg_send(fd,"mip",emsg);
- fprintf(dfile, "s2 = %d\n", s); fflush(dfile);
- erl_free_term(emsg);
- }
-
- msgsize = 4;
- msgbufp = (unsigned char *) malloc(msgsize);
-
- do {
-#if 0
- s = erl_receive_msg(fd, msgbuf, MSGSIZE, &msg);
-#else
- s = erl_xreceive_msg(fd, &msgbufp, &msgsize, &msg);
-#endif
- switch (s) {
- case ERL_TICK:
- fprintf(dfile, "tick\n");
- break;
- case ERL_ERROR:
- fprintf(dfile, "error: %s (%d)\n", strerror(erl_errno), erl_errno);
- break;
- case ERL_MSG:
- fprintf(dfile, "msg %d\n", msgsize);
- break;
- default:
- fprintf(dfile, "unknown result %d\n", s);
- break;
- }
- fflush(dfile);
- } while (s == ERL_TICK);
-
- s = erl_reg_send(fd, "mip", msg.msg);
- fprintf(dfile, "s = %d\n", s); fflush(dfile);
- s = erl_reg_send(fd, "mip", msg.to);
- fprintf(dfile, "s = %d\n", s); fflush(dfile);
-#if 0
- /* from = NULL! */
- s = erl_reg_send(fd, "mip", msg.from);
- fprintf(dfile, "s = %d\n", s); fflush(dfile);
-#endif
-
-#if 0
- /* Unused code which tests refs in some ways. */
- ix = 0;
- s = ei_encode_term(buf, &ix, ref);
- printf ("ei encode = %d, ix = %d\n", s, ix);
-
- /* Compare old and new ref equal */
- ref1 = erl_mk_long_ref(node, 4711, 113, 98, 0);
- ref2 = erl_mk_ref(node, 4711, 0);
- s = erl_encode(ref1, buf1);
- fprintf(dfile, "enc1 s = %d\n", s); fflush(dfile);
- s = erl_encode(ref2, buf2);
- fprintf(dfile, "enc2 s = %d\n", s); fflush(dfile);
- s = erl_compare_ext(buf1, buf2);
- fprintf(dfile, "comp s = %d\n", s); fflush(dfile);
-
- /* Compare, in another way */
- s = erl_match(ref1, ref2);
- fprintf(dfile, "match s = %d\n", s); fflush(dfile);
-#endif
-
- fclose(dfile);
-
- erl_close_connection(fd);
-
- return 0;
-}
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
deleted file mode 100644
index d97f218a26..0000000000
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
+++ /dev/null
@@ -1,1604 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Purpose: Tests the functions in erl_eterm.c and erl_malloc.c.
- * Author: Bjorn Gustavsson
- *
- * See the erl_eterm_SUITE.erl file for a "table of contents".
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "runner.h"
-
-/*
- * Find out which version of erl_interface we are using.
- */
-
-#ifdef ERL_IS_STRING
-#undef NEW_ERL_INTERFACE
-#else
-#define NEW_ERL_INTERFACE
-#endif
-
-void dump_term (FILE *fp, ETERM *t);
-
-static ETERM* all_types();
-
-/***********************************************************************
- *
- * 1. B a s i c t e s t s
- *
- ***********************************************************************/
-
-/*
- * Sends a list contaning all data types to the Erlang side.
- */
-
-TESTCASE(build_terms)
-{
- ETERM* t;
-
- erl_init(NULL, 0);
- t = all_types();
- send_term(t);
- report(1);
-}
-
-static int abs_and_sign(ETERM* v, unsigned long long* av, int* sign)
-{
- long long sv;
- switch (ERL_TYPE(v)) {
- case ERL_INTEGER: sv = ERL_INT_VALUE(v); break;
- case ERL_U_INTEGER: *av = ERL_INT_UVALUE(v); *sign = 0; return 1;
- case ERL_LONGLONG: sv = ERL_LL_VALUE(v); break;
- case ERL_U_LONGLONG: *av = ERL_LL_UVALUE(v); *sign = 0; return 1;
- default: return 0;
- }
- if (sv < 0) {
- *av = -sv;
- *sign = 1;
- }
- else {
- *av = sv;
- *sign = 0;
- }
- return 1;
-}
-
-/* Shouldn't erl_match() cope with this?
-*/
-static int eq_ints(ETERM* a, ETERM* b)
-{
- unsigned long long a_abs, b_abs;
- int a_sign, b_sign;
- return abs_and_sign(a, &a_abs, &a_sign) && abs_and_sign(b, &b_abs, &b_sign)
- && (a_abs == b_abs) && (a_sign == b_sign);
-}
-
-static void encode_decode(ETERM* original, const char* text)
-{
- static unsigned char encoded[16*1024];
- ETERM* new_terms;
- ETERM* head;
- int bytes;
- int len;
-
- /* If a list, check the elements one by one first */
- head = erl_hd(original);
- if (head != NULL) {
- encode_decode(head, "CAR");
- encode_decode(erl_tl(original), "CDR");
- }
-
- bytes = erl_encode(original, encoded);
- if (bytes == 0) {
- fail("failed to encode terms");
- }
- else if (bytes > sizeof(encoded)) {
- fail("encoded terms buffer overflow");
- }
- else if (bytes != (len=erl_term_len(original))) {
- fprintf(stderr, "bytes(%d) != len(%d) for term ", bytes, len);
- erl_print_term(stderr, original);
- fprintf(stderr, " [%s]\r\n", text);
- fail("erl_encode and erl_term_len do not agree");
- }
- else if ((new_terms = erl_decode(encoded)) == NULL) {
- fail("failed to decode terms");
- }
- else if (!erl_match(original, new_terms) && !eq_ints(original, new_terms)) {
- erl_print_term(stderr, original);
- fprintf(stderr, "(%i) != (%i)", ERL_TYPE(original), ERL_TYPE(new_terms));
- erl_print_term(stderr, new_terms);
- fprintf(stderr, " [%s]\r\n", text);
- fail("decoded terms didn't match original");
- }
- erl_free_term(original);
- erl_free_term(new_terms);
-}
-/*
- * Converts an Erlang term to the external term format and back again.
- */
-
-TESTCASE(round_trip_conversion)
-{
- int n, i;
-
- erl_init(NULL, 0);
- encode_decode(all_types(), "ALL");
-
- {
- int v;
- for (v = 8, n = 0; n < (sizeof(v)*8-4-1); v <<= 1, n++) {
- for (i=-4; i<4; i++) {
- encode_decode(erl_mk_int(v+i), "INT");
- encode_decode(erl_mk_int(-(v+i)), "NEG INT");
- }
- }
- }
- {
- unsigned int v;
- for (v = 8; v; v <<= 1) {
- for (i=-4; i<4; i++) {
- encode_decode(erl_mk_uint(v+i), "UINT");
- }
- }
- }
- {
- long long v;
- for (v = 8, n = 0; n < (sizeof(v)*8-4-1); v <<= 1, n++) {
- for (i=-4; i<4; i++) {
- encode_decode(erl_mk_longlong(v+i), "LONGLONG");
- encode_decode(erl_mk_longlong(-(v+i)), "NEG LONGLONG");
- }
- }
- }
- {
- unsigned long long v;
- for (v = 8; v; v <<= 1) {
- for (i=-4; i<4; i++) {
- encode_decode(erl_mk_ulonglong(v+i), "ULONGLONG");
- }
- }
- }
-
- report(1);
-}
-
-/*
- * Decodes data from the Erlang side and verifies.
- */
-
-TESTCASE(decode_terms)
-{
- ETERM* terms;
- char* message;
-
- erl_init(NULL, 0);
- terms = get_term();
- if (terms == NULL) {
- fail("unexpected end of file");
- } else {
- ETERM* all;
- ETERM* p;
- ETERM* t;
- int i;
-
- all = p = all_types();
- t = terms;
-
- /*
- * XXX For now, skip the reference, pid, and port, because
- * the match will fail. Must write code here to do some other
- * validating.
- */
-
- for (i=0; i<6; i++) {
-
- p = erl_tl(p);
- t = erl_tl(t);
- erl_free_term(p);
- erl_free_term(t);
-
- }
-
- /*
- * Match the tail of the lists.
- */
-
- if (!erl_match(p, t))
- {
- fail("Received terms didn't match expected");
- }
- erl_free_term(all);
- erl_free_term(terms);
- report(1);
- }
-}
-
-/*
- * Decodes a float from the Erlang side and verifies.
- */
-
-TESTCASE(decode_float)
-{
- ETERM* afnum;
- ETERM* efnum;
- int result;
-
- erl_init(NULL, 0);
- afnum = get_term();
- efnum = erl_mk_float(3.1415);
- result = erl_match(efnum, afnum);
- erl_free_term(afnum);
- erl_free_term(efnum);
- report(result);
-}
-
-/*
- * Tests the erl_free_compound() function.
- */
-
-TESTCASE(t_erl_free_compound)
-{
- ETERM* t;
-
- erl_init(NULL, 0);
-
- t = all_types();
- erl_free_compound(t);
- report(1);
-}
-
-
-/***********************************************************************
- *
- * 2. C o n s t r u c t i n g t e r m s
- *
- ***********************************************************************/
-
-/*
- * Makes various integers, and sends them to Erlang for verification.
- */
-
-TESTCASE(t_erl_mk_int)
-{
-#define SEND_INT(i) \
- do { \
- ETERM* t = erl_mk_int(i); \
- send_term(t); \
- } while (0);
-
- erl_init(NULL, 0);
-
- SEND_INT(0);
- SEND_INT(127);
- SEND_INT(128);
- SEND_INT(255);
- SEND_INT(256);
-
- SEND_INT(0xFFFF);
- SEND_INT(0x10000);
-
- SEND_INT(0x07FFFFFF);
- SEND_INT(0x0FFFFFFF);
- SEND_INT(0x1FFFFFFF);
- SEND_INT(0x3FFFFFFF);
- SEND_INT(0x7FFFFFFF);
-
- SEND_INT(0x08000000);
- SEND_INT(0x10000000);
- SEND_INT(0x20000000);
- SEND_INT(0x40000000);
-
- SEND_INT(-0x07FFFFFF);
- SEND_INT(-0x0FFFFFFF);
- SEND_INT(-0x1FFFFFFF);
- SEND_INT(-0x3FFFFFFF);
- SEND_INT(-0x7FFFFFFF);
-
- SEND_INT(-0x08000000);
- SEND_INT(-0x10000000);
- SEND_INT(-0x20000000);
- SEND_INT(-0x40000000);
-
- SEND_INT(-0x08000001);
- SEND_INT(-0x10000001);
- SEND_INT(-0x20000001);
- SEND_INT(-0x40000001);
-
- SEND_INT(-0x08000002);
- SEND_INT(-0x10000002);
- SEND_INT(-0x20000002);
- SEND_INT(-0x40000002);
-
- SEND_INT(-1999999999);
- SEND_INT(-2000000000);
- SEND_INT(-2000000001);
-
- report(1);
-}
-
-
-/*
- * Makes lists of various sizes, and sends them to Erlang for verification.
- */
-
-TESTCASE(t_erl_mk_list)
-{
- ETERM* a[4];
-
- erl_init(NULL, 0);
-
- /*
- * Empty list.
- */
-
- send_term(erl_mk_list(a, 0));
-
- /*
- * One element: [abc]
- */
-
- a[0] = erl_mk_atom("abc");
- send_term(erl_mk_list(a, 1));
- erl_free_term(a[0]);
-
- /*
- * Two elements: [abcdef, 42].
- */
-
- a[0] = erl_mk_atom("abcdef");
- a[1] = erl_mk_int(42);
- send_term(erl_mk_list(a, 2));
- erl_free_term(a[0]);
- erl_free_term(a[1]);
-
- /*
- * Four elements.
- */
-
- a[0] = erl_mk_float(0.0);
- a[1] = erl_mk_int(23);
- a[2] = erl_mk_empty_list();
- a[3] = erl_mk_float(3.1415);
- send_term(erl_mk_list(a, 4));
- erl_free_term(a[0]);
- erl_free_term(a[1]);
- erl_free_term(a[2]);
- erl_free_term(a[3]);
-
- report(1);
-}
-
-/*
- * A basic test of erl_copy_term().
- */
-
-TESTCASE(basic_copy)
-{
- ETERM* original;
- ETERM* copy;
- int result;
-
- erl_init(NULL, 0);
- original = all_types();
- copy = erl_copy_term(original);
- if (copy == NULL) {
- fail("erl_copy_term() failed");
- } else if (!erl_match(original, copy))
- {
- fail("copy doesn't match original");
- }
-
- erl_free_term(original);
- erl_free_term(copy);
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_atom().
- */
-
-TESTCASE(t_erl_mk_atom)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_atom("madonna"));
- send_term(erl_mk_atom("Madonna"));
- send_term(erl_mk_atom("mad donna"));
- send_term(erl_mk_atom("_madonna_"));
- send_term(erl_mk_atom("/home/madonna/tour_plan"));
- send_term(erl_mk_atom("http://www.madonna.com/tour_plan"));
- send_term(erl_mk_atom("\'madonna\'"));
- send_term(erl_mk_atom("\"madonna\""));
- send_term(erl_mk_atom("\\madonna\\"));
- send_term(erl_mk_atom("{madonna,21,'mad donna',12}"));
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_binary().
- */
-
-TESTCASE(t_erl_mk_binary)
-{
-
- char* string;
- erl_init(NULL, 0);
-
- string = "{madonna,21,'mad donna',1234.567.890, !#$%&/()=?+-@, \" \\}";
- send_term(erl_mk_binary(string,strlen(string)));
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_empty_list().
- */
-
-TESTCASE(t_erl_mk_empty_list)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_empty_list());
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_float().
- */
-
-TESTCASE(t_erl_mk_float)
-{
- ETERM* arr[6];
- ETERM* emsg;
-
- erl_init(NULL, 0);
-
- arr[0] = erl_mk_float(3.1415);
- arr[1] = erl_mk_float(1.999999);
- arr[2] = erl_mk_float(2.000000);
- arr[3] = erl_mk_float(2.000001);
- arr[4] = erl_mk_float(2.000002);
- arr[5] = erl_mk_float(12345.67890);
- emsg = (erl_mk_tuple(arr,6));
-
- send_term(emsg);
-
- erl_free_array(arr,6);
- /* emsg already freed by send_term() */
- /* erl_free_term(emsg); */
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_pid().
- */
-
-TESTCASE(t_erl_mk_pid)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_pid("kalle@localhost", 3, 2, 1));
- report(1);
-}
-
-/*
- * A basic test of erl_mk_pid().
- */
-
-TESTCASE(t_erl_mk_xpid)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_pid("kalle@localhost", 32767, 8191, 1));
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_port().
- */
-
-TESTCASE(t_erl_mk_port)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_port("kalle@localhost", 4, 1));
- report(1);
-}
-
-/*
- * A basic test of erl_mk_port().
- */
-
-TESTCASE(t_erl_mk_xport)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_port("kalle@localhost", 268435455, 1));
- report(1);
-}
-
-/*
- * A basic test of erl_mk_ref().
- */
-
-TESTCASE(t_erl_mk_ref)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_ref("kalle@localhost", 6, 1));
- report(1);
-}
-
-/*
- * A basic test of erl_mk_long_ref().
- */
-
-
-TESTCASE(t_erl_mk_long_ref)
-{
- erl_init(NULL, 0);
-
- send_term(erl_mk_long_ref("kalle@localhost",
- 4294967295, 4294967295, 262143,
- 1));
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_string().
- */
-
-TESTCASE(t_erl_mk_string)
-{
-
- erl_init(NULL, 0);
-
- send_term(erl_mk_string("madonna"));
- send_term(erl_mk_string("Madonna"));
- send_term(erl_mk_string("mad donna"));
- send_term(erl_mk_string("_madonna_"));
- send_term(erl_mk_string("/home/madonna/tour_plan"));
- send_term(erl_mk_string("http://www.madonna.com/tour_plan"));
- send_term(erl_mk_string("\'madonna\'"));
- send_term(erl_mk_string("\"madonna\""));
- send_term(erl_mk_string("\\madonna\\"));
- send_term(erl_mk_string("{madonna,21,'mad donna',12}"));
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_estring().
- */
-
-TESTCASE(t_erl_mk_estring)
-{
- char* string;
- erl_init(NULL, 0);
-
- string = "madonna";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "Madonna";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "mad donna";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "_madonna_";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "/home/madonna/tour_plan";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "http://www.madonna.com/tour_plan";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "\'madonna\'";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "\"madonna\"";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "\\madonna\\";
- send_term(erl_mk_estring(string,strlen(string)));
- string = "{madonna,21,'mad donna',12}";
- send_term(erl_mk_estring(string,strlen(string)));
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_tuple().
- */
-
-TESTCASE(t_erl_mk_tuple)
-{
- ETERM* arr[4];
- ETERM* arr2[2];
- ETERM* arr3[2];
- ETERM* arr4[2];
-
- erl_init(NULL, 0);
-
- /* {madonna,21,'mad donna',12} */
- arr[0] = erl_mk_atom("madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_atom("mad donna");
- arr[3] = erl_mk_int(12);
-
- send_term(erl_mk_tuple(arr,4));
-
- erl_free_array(arr,4);
-
-
- /* {'Madonna',21,{children,{"Isabella",2}},{'home page',"http://www.madonna.com/"} */
- arr4[0] = erl_mk_atom("home page");
- arr4[1] = erl_mk_string("http://www.madonna.com/");
-
- arr3[0] = erl_mk_string("Isabella");
- arr3[1] = erl_mk_int(2);
-
- arr2[0] = erl_mk_atom("children");
- arr2[1] = erl_mk_tuple(arr3,2);
-
- arr[0] = erl_mk_atom("Madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_tuple(arr2,2);
- arr[3] = erl_mk_tuple(arr4,2);
-
- send_term(erl_mk_tuple(arr,4));
-
- erl_free_array(arr,4);
- erl_free_array(arr2,2);
- erl_free_array(arr3,2);
- erl_free_array(arr4,2);
-
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_uint().
- */
-
-TESTCASE(t_erl_mk_uint)
-{
- unsigned i;
-
- erl_init(NULL, 0);
-
- send_term(erl_mk_uint(54321));
- i = 2147483647;
- send_term(erl_mk_uint(i));
- send_term(erl_mk_uint(i+1));
- send_term(erl_mk_uint(i+2));
- send_term(erl_mk_uint(i+3));
- send_term(erl_mk_uint(i+i+1));
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_mk_var().
- */
-
-TESTCASE(t_erl_mk_var)
-{
- ETERM* mk_var;
- ETERM* term;
- ETERM* term2;
- ETERM* arr[4];
- ETERM* arr_term[2];
- ETERM* mk_var_tuple;
- ETERM* term_tuple;
-
- erl_init(NULL, 0);
-
-
- /* match unbound/bound variable against an integer */
- term = erl_mk_int(17);
- term2 = erl_mk_int(2);
- mk_var = erl_mk_var("New_var");
- send_term(erl_mk_int(erl_match(mk_var, term))); /* should be ok */
- send_term(erl_mk_int(erl_match(mk_var, term2))); /* should fail */
- send_term(erl_mk_int(erl_match(mk_var, term))); /* should be ok */
- send_term(erl_mk_int(erl_match(mk_var, term2))); /* should fail */
- erl_free_term(mk_var);
- erl_free_term(term);
- erl_free_term(term2);
-
- /* match unbound variable against a tuple */
- arr[0] = erl_mk_atom("madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_atom("mad donna");
- arr[3] = erl_mk_int(12);
- mk_var = erl_mk_var("New_var");
- term = erl_mk_tuple(arr,4);
- send_term(erl_mk_int(erl_match(mk_var, term))); /* should be ok */
- erl_free_term(mk_var);
- erl_free_term(term);
- erl_free_array(arr,4);
-
-
- /* match (twice) unbound variable against an incorrect tuple */
- arr[0] = erl_mk_var("New_var");
- arr[1] = erl_mk_var("New_var");
- arr_term[0] = erl_mk_int(17);
- arr_term[1] = erl_mk_int(27);
- mk_var_tuple = erl_mk_tuple(arr,2);
- term_tuple = erl_mk_tuple(arr_term,2);
- send_term(erl_mk_int(erl_match(mk_var_tuple, term_tuple))); /* should fail */
- erl_free_array(arr,2);
- erl_free_array(arr_term,2);
- erl_free_term(mk_var_tuple);
- erl_free_term(term_tuple);
-
-
- /* match (twice) unbound variable against a correct tuple */
- arr[0] = erl_mk_var("New_var");
- arr[1] = erl_mk_var("New_var");
- arr_term[0] = erl_mk_int(17);
- arr_term[1] = erl_mk_int(17);
- mk_var_tuple = erl_mk_tuple(arr,2);
- term_tuple = erl_mk_tuple(arr_term,2);
- send_term(erl_mk_int(erl_match(mk_var_tuple, term_tuple))); /* should be ok */
- erl_free_array(arr,2);
- erl_free_array(arr_term,2);
- erl_free_term(mk_var_tuple);
- erl_free_term(term_tuple);
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_size().
- */
-
-TESTCASE(t_erl_size)
-{
- ETERM* arr[4];
- ETERM* tuple;
- ETERM* bin;
- char* string;
-
- erl_init(NULL, 0);
-
- /* size of a tuple */
- tuple = erl_format("{}");
- send_term(erl_mk_int(erl_size(tuple)));
- erl_free_term(tuple);
-
- arr[0] = erl_mk_atom("madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_atom("mad donna");
- arr[3] = erl_mk_int(12);
- tuple = erl_mk_tuple(arr,4);
-
- send_term(erl_mk_int(erl_size(tuple)));
-
- erl_free_array(arr,4);
- erl_free_term(tuple);
-
- /* size of a binary */
- string = "";
- bin = erl_mk_binary(string,strlen(string));
- send_term(erl_mk_int(erl_size(bin)));
- erl_free_term(bin);
-
- string = "{madonna,21,'mad donna',12}";
- bin = erl_mk_binary(string,strlen(string));
- send_term(erl_mk_int(erl_size(bin)));
- erl_free_term(bin);
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_var_content().
- */
-
-TESTCASE(t_erl_var_content)
-{
- ETERM* mk_var;
- ETERM* term;
- ETERM* tuple;
- ETERM* list;
- ETERM* a;
- ETERM* b;
- ETERM* arr[4];
- ETERM* arr2[2];
- ETERM* arr3[2];
- ETERM* arr4[2];
-
- erl_init(NULL, 0);
-
- term = erl_mk_int(17);
- mk_var = erl_mk_var("Var");
-
- /* unbound, should return NULL */
- if (erl_var_content(mk_var,"Var") != NULL)
- fail("t_erl_var_content() failed");
-
- erl_match(mk_var, term);
- send_term(erl_var_content(mk_var,"Var")); /* should return 17 */
-
- /* integer, should return NULL */
- if (erl_var_content(term,"Var") != NULL)
- fail("t_erl_var_content() failed");
-
- /* unknown variable, should return NULL */
- if (erl_var_content(mk_var,"Unknown_Var") != NULL)
- fail("t_erl_var_content() failed");
-
- erl_free_term(mk_var);
- erl_free_term(term);
-
- /* {'Madonna',21,{children,{"Name","Age"}},{"Home_page","Tel_no"}} */
- arr4[0] = erl_mk_var("Home_page");
- arr4[1] = erl_mk_var("Tel_no");
- a = erl_mk_string("http://www.madonna.com");
- erl_match(arr4[0], a);
-
- arr3[0] = erl_mk_var("Name");
- arr3[1] = erl_mk_var("Age");
- b = erl_mk_int(2);
- erl_match(arr3[1], b);
-
- arr2[0] = erl_mk_atom("children");
- arr2[1] = erl_mk_tuple(arr3,2);
-
- arr[0] = erl_mk_atom("Madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_tuple(arr2,2);
- arr[3] = erl_mk_tuple(arr4,2);
-
- tuple = erl_mk_tuple(arr,4);
-
- /* should return "http://www.madonna.com" */
- send_term(erl_var_content(tuple,"Home_page"));
-
- /* unbound, should return NULL */
- if (erl_var_content(tuple,"Tel_no") != NULL)
- fail("t_erl_var_content() failed");
-
- /* unbound, should return NULL */
- if (erl_var_content(tuple,"Name") != NULL)
- fail("t_erl_var_content() failed");
-
- /* should return 2 */
- send_term(erl_var_content(tuple,"Age"));
-
- erl_free_array(arr,4);
- erl_free_array(arr2,2);
- erl_free_array(arr3,2);
- erl_free_array(arr4,2);
- erl_free_term(tuple);
- erl_free_term(a);
- erl_free_term(b);
-
-
- /* [] */
- list = erl_mk_empty_list();
- if (erl_var_content(list,"Tel_no") != NULL)
- fail("t_erl_var_content() failed");
- erl_free_term(list);
-
-
- /* ['Madonna',[],{children,{"Name","Age"}},{"Home_page","Tel_no"}] */
- arr4[0] = erl_mk_var("Home_page");
- arr4[1] = erl_mk_var("Tel_no");
- a = erl_mk_string("http://www.madonna.com");
- erl_match(arr4[0], a);
-
- arr3[0] = erl_mk_var("Name");
- arr3[1] = erl_mk_var("Age");
- b = erl_mk_int(2);
- erl_match(arr3[1], b);
-
- arr2[0] = erl_mk_atom("children");
- arr2[1] = erl_mk_tuple(arr3,2);
-
- arr[0] = erl_mk_atom("Madonna");
- arr[1] = erl_mk_empty_list();
- arr[2] = erl_mk_tuple(arr2,2);
- arr[3] = erl_mk_tuple(arr4,2);
-
- list = erl_mk_list(arr,4);
-
- /* should return "http://www.madonna.com" */
- send_term(erl_var_content(list,"Home_page"));
-
- /* unbound, should return NULL */
- if (erl_var_content(list,"Tel_no") != NULL)
- fail("t_erl_var_content() failed");
-
- /* unbound, should return NULL */
- if (erl_var_content(list,"Name") != NULL)
- fail("t_erl_var_content() failed");
-
- /* should return 2 */
- send_term(erl_var_content(list,"Age"));
-
- erl_free_array(arr,4);
- erl_free_array(arr2,2);
- erl_free_array(arr3,2);
- erl_free_array(arr4,2);
- erl_free_term(list);
- erl_free_term(a);
- erl_free_term(b);
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_element().
- */
-
-TESTCASE(t_erl_element)
-{
- ETERM* arr[4];
- ETERM* arr2[2];
- ETERM* arr3[2];
- ETERM* arr4[2];
- ETERM* tuple;
-
- erl_init(NULL, 0);
-
- arr[0] = erl_mk_atom("madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_atom("mad donna");
- arr[3] = erl_mk_int(12);
- tuple = erl_mk_tuple(arr,4);
-
- send_term(erl_element(1,tuple));
- send_term(erl_element(2,tuple));
- send_term(erl_element(3,tuple));
- send_term(erl_element(4,tuple));
-
- erl_free_array(arr,4);
- erl_free_term(tuple);
-
- /* {'Madonna',21,{children,{"Isabella",2}},{'home page',"http://www.madonna.com/"} */
- arr4[0] = erl_mk_atom("home page");
- arr4[1] = erl_mk_string("http://www.madonna.com/");
-
- arr3[0] = erl_mk_string("Isabella");
- arr3[1] = erl_mk_int(2);
-
- arr2[0] = erl_mk_atom("children");
- arr2[1] = erl_mk_tuple(arr3,2);
-
- arr[0] = erl_mk_atom("Madonna");
- arr[1] = erl_mk_int(21);
- arr[2] = erl_mk_tuple(arr2,2);
- arr[3] = erl_mk_tuple(arr4,2);
-
- tuple = erl_mk_tuple(arr,4);
- send_term(erl_element(1,tuple));
- send_term(erl_element(2,tuple));
- send_term(erl_element(3,tuple));
- send_term(erl_element(4,tuple));
-
- erl_free_term(tuple);
- erl_free_array(arr,4);
- erl_free_array(arr2,2);
- erl_free_array(arr3,2);
- erl_free_array(arr4,2);
-
- report(1);
-}
-
-
-/*
- * A basic test of erl_cons().
- */
-
-TESTCASE(t_erl_cons)
-{
- ETERM* list;
- ETERM* anAtom;
- ETERM* anInt;
-
- erl_init(NULL, 0);
-
- anAtom = erl_mk_atom("madonna");
- anInt = erl_mk_int(21);
- list = erl_mk_empty_list();
- list = erl_cons(anInt, list);
- send_term(erl_cons(anAtom, list));
-
- erl_free_term(anAtom);
- erl_free_term(anInt);
- erl_free_compound(list);
-
- report(1);
-}
-
-
-
-
-/***********************************************************************
- *
- * 3. E x t r a c t i n g & i n f o f u n c t i o n s
- *
- ***********************************************************************/
-
-/*
- * Calculates the length of each list sent to it and sends back the result.
- */
-
-TESTCASE(t_erl_length)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
- ETERM* len_term;
-
- len_term = erl_mk_int(erl_length(term));
- erl_free_term(term);
- send_term(len_term);
- }
- }
-}
-
-/*
- * Gets the head of each term and sends the result back.
- */
-
-TESTCASE(t_erl_hd)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
- ETERM* head;
-
- head = erl_hd(term);
- send_term(head);
- erl_free_term(term);
- }
- }
-}
-
-/*
- * Gets the tail of each term and sends the result back.
- */
-
-TESTCASE(t_erl_tl)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
- ETERM* tail;
-
- tail = erl_tl(term);
- send_term(tail);
- erl_free_term(term);
- }
- }
-}
-
-/*
- * Checks the type checking macros.
- */
-
-TESTCASE(type_checks)
-{
- ETERM* t;
- ETERM* atom;
-
- erl_init(NULL, 0);
- atom = erl_mk_atom("an_atom");
-
-#define TYPE_CHECK(macro, term) \
- { ETERM* t = term; \
- if (macro(t)) { \
- erl_free_term(t); \
- } else { \
- fail("Macro " #macro " failed on " #term); \
- } \
- }
-
- TYPE_CHECK(ERL_IS_INTEGER, erl_mk_int(0x7FFFFFFF));
-#ifdef NEW_ERL_INTERFACE
- TYPE_CHECK(ERL_IS_UNSIGNED_INTEGER, erl_mk_uint(0x7FFFFFFF));
-#endif
- TYPE_CHECK(ERL_IS_FLOAT, erl_mk_float(5.5));
- TYPE_CHECK(ERL_IS_ATOM, erl_mk_atom("another_atom"));
-
- TYPE_CHECK(ERL_IS_EMPTY_LIST, erl_mk_empty_list());
- TYPE_CHECK(!ERL_IS_EMPTY_LIST, erl_cons(atom, atom));
-
-#ifdef NEW_ERL_INTERFACE
- TYPE_CHECK(!ERL_IS_CONS, erl_mk_empty_list());
- TYPE_CHECK(ERL_IS_CONS, erl_cons(atom, atom));
-#endif
-
- TYPE_CHECK(ERL_IS_LIST, erl_mk_empty_list());
- TYPE_CHECK(ERL_IS_LIST, erl_cons(atom, atom));
-
- TYPE_CHECK(ERL_IS_PID, erl_mk_pid("a@a", 42, 1, 1));
- TYPE_CHECK(ERL_IS_PORT, erl_mk_port("a@a", 42, 1));
- TYPE_CHECK(ERL_IS_REF, erl_mk_ref("a@a", 42, 1));
-
- TYPE_CHECK(ERL_IS_BINARY, erl_mk_binary("a", 1));
- TYPE_CHECK(ERL_IS_TUPLE, erl_mk_tuple(&atom, 1));
-#undef TYPE_CHECK
-
- erl_free_term(atom);
-
- report(1);
-}
-
-/*
- * Checks the extractor macros.
- */
-
-TESTCASE(extractor_macros)
-{
- ETERM* t;
-
- erl_init(NULL, 0);
-
-#ifdef NEW_ERL_INTERFACE
-#define MATCH(a, b) ((a) == (b) ? 1 : fail("bad match: " #a))
-#define STR_MATCH(a, b) (strcmp((a), (b)) ? fail("bad match: " #a) : 0)
-
- { /* Integer */
- int anInt = 0x7FFFFFFF;
- t = erl_mk_int(anInt);
- MATCH(ERL_INT_VALUE(t), anInt);
- MATCH(ERL_INT_UVALUE(t), anInt);
- erl_free_term(t);
- }
-
- { /* Float */
- double aFloat = 3.1415;
- t = erl_mk_float(aFloat);
- MATCH(ERL_FLOAT_VALUE(t), aFloat);
- erl_free_term(t);
- }
-
- { /* Atom. */
- char* aString = "nisse";
- t = erl_mk_atom(aString);
- if (memcmp(ERL_ATOM_PTR(t), aString, strlen(aString)) != 0)
- fail("bad match");
- MATCH(ERL_ATOM_SIZE(t), strlen(aString));
- erl_free_term(t);
- }
-
- { /* Pid. */
- char* node = "arne@strider";
- int number = 42;
- int serial = 5;
- int creation = 1;
-
- t = erl_mk_pid(node, number, serial, creation);
- STR_MATCH(ERL_PID_NODE(t), node);
- MATCH(ERL_PID_NUMBER(t), number);
- MATCH(ERL_PID_SERIAL(t), serial);
- MATCH(ERL_PID_CREATION(t), creation);
- erl_free_term(t);
- }
-
- { /* Port. */
- char* node = "kalle@strider";
- int number = 45;
- int creation = 1;
-
- t = erl_mk_port(node, number, creation);
- STR_MATCH(ERL_PORT_NODE(t), node);
- MATCH(ERL_PORT_NUMBER(t), number);
- MATCH(ERL_PORT_CREATION(t), creation);
- erl_free_term(t);
- }
-
- { /* Reference. */
- char* node = "kalle@strider";
- int number = 48;
- int creation = 1;
-
- t = erl_mk_ref(node, number, creation);
- STR_MATCH(ERL_REF_NODE(t), node);
- MATCH(ERL_REF_NUMBER(t), number);
- MATCH(ERL_REF_CREATION(t), creation);
- erl_free_term(t);
- }
-
- { /* Tuple. */
- ETERM* arr[2];
-
- arr[0] = erl_mk_int(51);
- arr[1] = erl_mk_int(52);
- t = erl_mk_tuple(arr, ASIZE(arr));
- MATCH(ERL_TUPLE_SIZE(t), ASIZE(arr));
- MATCH(ERL_TUPLE_ELEMENT(t, 0), arr[0]);
- MATCH(ERL_TUPLE_ELEMENT(t, 1), arr[1]);
- erl_free_array(arr, ASIZE(arr));
- erl_free_term(t);
- }
-
- { /* Binary. */
- static char bin[] = {1, 2, 3, 0, 4, 5};
-
- t = erl_mk_binary(bin, ASIZE(bin));
- MATCH(ERL_BIN_SIZE(t), ASIZE(bin));
- if (memcmp(ERL_BIN_PTR(t), bin, ASIZE(bin)) != 0)
- fail("bad match");
- erl_free_term(t);
- }
-
- {
- ETERM* head = erl_mk_atom("head");
- ETERM* tail = erl_mk_atom("tail");
-
- t = erl_cons(head, tail);
- MATCH(ERL_CONS_HEAD(t), head);
- MATCH(ERL_CONS_TAIL(t), tail);
- erl_free_term(head);
- erl_free_term(tail);
- erl_free_term(t);
- }
-#undef MATCH
-#undef STR_MATCH
-#endif
-
- report(1);
-}
-
-
-
-/***********************************************************************
- *
- * 4. I / O l i s t f u n c t i o n s
- *
- ***********************************************************************/
-
-/*
- * Invokes erl_iolist_length() on each term and send backs the result.
- */
-
-TESTCASE(t_erl_iolist_length)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
-#ifndef NEW_ERL_INTERFACE
- fail("Function not present in this version of erl_interface");
-#else
- ETERM* len_term;
-
- len_term = erl_mk_int(erl_iolist_length(term));
- erl_free_term(term);
- send_term(len_term);
-#endif
- }
- }
-}
-
-/*
- * Invokes erl_iolist_to_binary() on each term and send backs the result.
- */
-
-TESTCASE(t_erl_iolist_to_binary)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
-#ifndef NEW_ERL_INTERFACE
- fail("Function not present in this version of erl_interface");
-#else
- ETERM* new_term;
-
- new_term = erl_iolist_to_binary(term);
-
- erl_free_term(term);
- send_term(new_term);
-#endif
- }
- }
-}
-
-/*
- * Invokes erl_iolist_to_string() on each term and send backs the result.
- */
-
-TESTCASE(t_erl_iolist_to_string)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
-#ifndef NEW_ERL_INTERFACE
- fail("Function not present in this version of erl_interface");
-#else
- char* result;
-
- result = erl_iolist_to_string(term);
- erl_free_term(term);
- if (result != NULL) {
- send_buffer(result, strlen(result)+1);
- erl_free(result);
- } else {
- send_term(NULL);
- }
-#endif
- }
- }
-}
-
-
-/***********************************************************************
- *
- * 5. M i s c e l l a n o u s T e s t s
- *
- ***********************************************************************/
-
-/*
- * Test some combinations of operations to verify that the reference pointers
- * are handled correctly.
- *
- * "Det verkar vara lite High Chaparal med minneshanteringen i erl_interface"
- * Per Lundgren, ERV.
- */
-
-TESTCASE(high_chaparal)
-{
- ETERM *L1, *A1, *L2, *A2, *L3;
-
- erl_init(NULL, 0);
-
- L1 = erl_mk_empty_list();
- A1 = erl_mk_atom("world");
- L2 = erl_cons(A1, L1);
- A2 = erl_mk_atom("hello");
- L3 = erl_cons(A2, L2);
-
- erl_free_term(L1);
- erl_free_term(A1);
- erl_free_term(L2);
- erl_free_term(A2);
-
- send_term(L3);
-
- /* already freed by send_term() */
- /* erl_free_term(L3);*/
-
- report(1);
-}
-
-/*
- * Test erl_decode to recover from broken list data (OTP-7448)
- */
-TESTCASE(broken_data)
-{
- ETERM* original;
- ETERM* new_terms;
- char encoded[16*1024];
- int n;
-
- erl_init(NULL, 0);
- original = all_types();
- if ((n=erl_encode(original, encoded)) == 0)
- {
- fail("failed to encode terms");
- } else
- {
- int offs = n/2;
- memset(encoded+offs,0,n-offs); /* destroy */
-
- if ((new_terms = erl_decode(encoded)) != NULL)
- {
- fail("decode accepted broken data");
- erl_free_term(new_terms);
- }
- }
- erl_free_term(original);
- report(1);
-}
-
-/*
- * Returns a list containing instances of all types.
- *
- * Be careful changing the contents of the list returned, because both
- * the build_terms() and decode_terms() test cases depend on it.
- */
-
-static ETERM*
-all_types(void)
-{
- ETERM* t;
- ETERM* terms[3];
- int i;
- static char a_binary[] = "A binary";
-
-#define CONS_AND_FREE(expr, tail) \
- do { \
- ETERM* term = expr; \
- ETERM* nl = erl_cons(term, tail); \
- erl_free_term(term); \
- erl_free_term(tail); \
- tail = nl; \
- } while (0)
-
- t = erl_mk_empty_list();
-
- CONS_AND_FREE(erl_mk_atom("I am an atom"), t);
- CONS_AND_FREE(erl_mk_binary("A binary", sizeof(a_binary)-1), t);
- CONS_AND_FREE(erl_mk_float(3.0), t);
- CONS_AND_FREE(erl_mk_int(0), t);
- CONS_AND_FREE(erl_mk_int(-1), t);
- CONS_AND_FREE(erl_mk_int(1), t);
-
- CONS_AND_FREE(erl_mk_string("A string"), t);
-
- terms[0] = erl_mk_atom("element1");
- terms[1] = erl_mk_int(42);
- terms[2] = erl_mk_int(767);
- CONS_AND_FREE(erl_mk_tuple(terms, ASIZE(terms)), t);
- for (i = 0; i < ASIZE(terms); i++) {
- erl_free_term(terms[i]);
- }
-
- CONS_AND_FREE(erl_mk_pid("kalle@localhost", 3, 2, 1), t);
- CONS_AND_FREE(erl_mk_pid("abcdefghijabcdefghij@localhost", 3, 2, 1), t);
- CONS_AND_FREE(erl_mk_port("kalle@localhost", 4, 1), t);
- CONS_AND_FREE(erl_mk_port("abcdefghijabcdefghij@localhost", 4, 1), t);
- CONS_AND_FREE(erl_mk_ref("kalle@localhost", 6, 1), t);
- CONS_AND_FREE(erl_mk_ref("abcdefghijabcdefghij@localhost", 6, 1), t);
- return t;
-
-#undef CONS_AND_FREE
-}
-
-/*
- * Dump (print for debugging) a term. Useful if/when things go wrong.
- */
-void
-dump_term (FILE *fp, ETERM *t)
-{
- if (fp == NULL) return;
-
- fprintf(fp, "#<%p ", t);
-
- if(t != NULL)
- {
- fprintf(fp, "count:%d, type:%d", ERL_COUNT(t), ERL_TYPE(t));
-
- switch(ERL_TYPE(t))
- {
- case ERL_UNDEF:
- fprintf(fp, "==undef");
- break;
- case ERL_INTEGER:
- fprintf(fp, "==int, val:%d", ERL_INT_VALUE(t));
- break;
- case ERL_U_INTEGER:
- fprintf(fp, "==uint, val:%u", ERL_INT_UVALUE(t));
- break;
- case ERL_FLOAT:
- fprintf(fp, "==float, val:%g", ERL_FLOAT_VALUE(t));
- break;
- case ERL_ATOM:
- fprintf(fp, "==atom, name:%p \"%s\"",
- ERL_ATOM_PTR(t), ERL_ATOM_PTR(t));
- break;
- case ERL_BINARY:
- fprintf(fp, "==binary, data:%p,%u",
- ERL_BIN_PTR(t), ERL_BIN_SIZE(t));
- break;
- case ERL_PID:
- fprintf(fp, "==pid, node:%p \"%s\"",
- ERL_PID_NODE(t), ERL_PID_NODE(t));
- break;
- case ERL_PORT:
- fprintf(fp, "==port, node:%p \"%s\"",
- ERL_PORT_NODE(t), ERL_PORT_NODE(t));
- break;
- case ERL_REF:
- fprintf(fp, "==ref, node:%p \"%s\"",
- ERL_REF_NODE(t), ERL_REF_NODE(t));
- break;
- case ERL_CONS:
- fprintf(fp, "==cons");
- fprintf(fp, ", car:");
- dump_term(fp, ERL_CONS_HEAD(t));
- fprintf(fp, ", cdr:");
- dump_term(fp, ERL_CONS_TAIL(t));
- break;
- case ERL_NIL:
- fprintf(fp, "==nil");
- break;
- case ERL_TUPLE:
- fprintf(fp, "==tuple, elems:%p,%u",
- ERL_TUPLE_ELEMS(t), ERL_TUPLE_SIZE(t));
- {
- size_t i;
- for(i = 0; i < ERL_TUPLE_SIZE(t); i++)
- {
- fprintf(fp, "elem[%u]:", i);
- dump_term(fp, ERL_TUPLE_ELEMENT(t, i));
- }
- }
- break;
- case ERL_VARIABLE:
- fprintf(fp, "==variable, name:%p \"%s\"",
- ERL_VAR_NAME(t), ERL_VAR_NAME(t));
- fprintf(fp, ", value:");
- dump_term(fp, ERL_VAR_VALUE(t));
- break;
-
- default:
- break;
- }
- }
- fprintf(fp, ">");
-}
-
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c b/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c
deleted file mode 100644
index 5b7cb1aec8..0000000000
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Purpose: Test the erl_print_term() function.
- * Author: Bjorn Gustavsson
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifndef __WIN32__
-#include <unistd.h>
-#endif
-
-#include "erl_interface.h"
-
-#ifndef __WIN32__
-#define _O_BINARY 0
-#define _setmode(fd, mode)
-#endif
-
-#define HEADER_SIZE 2
-
-static int readn(int, unsigned char*, int);
-
-/*
- * This program doesn't use the runner, because it needs a packet
- * on input, but the result will be as a stream of bytes (since
- * erl_print_term() prints directly on a file).
- *
- * Input is a package of with a packet header size of two bytes.
- *
- * +------------------------------------------------------------+
- * | length | Encoded term... |
- * | (2 bytes) | (as given by "length") |
- * +------------------------------------------------------------+
- *
- * <------------------- length --------------------->
- *
- * This program decodes the encoded terms and passes it to
- * erl_print_term(). Then this program prints
- *
- * CR <result> LF
- *
- * and waits for a new package. <result> is the return value from
- * erl_print_term(), formatted as an ASCII string.
- */
-
-#ifdef VXWORKS
-int print_term()
-#else
-int main()
-#endif
-{
- _setmode(0, _O_BINARY);
- _setmode(1, _O_BINARY);
-
- erl_init(NULL, 0);
-
- for (;;) {
- char buf[4*1024];
- ETERM* term;
- char* message;
- int n;
-
- if (readn(0, buf, 2) <= 0) {
- /* fprintf(stderr, "error reading message header\n"); */
- /* actually this is where we leave the infinite loop */
- exit(1);
- }
- n = buf[0] * 256 + buf[1];
- if (readn(0, buf, n) < 0) {
- fprintf(stderr, "error reading message contents\n");
- exit(1);
- }
-
- term = erl_decode(buf);
- if (term == NULL) {
- fprintf(stderr, "erl_decode() failed\n");
- exit(1);
- }
- n = erl_print_term(stdout, term);
- erl_free_compound(term);
- fprintf(stdout,"\r%d\n", n);
- fflush(stdout);
- }
-}
-
-/*
- * Reads len number of bytes.
- */
-
-static int
-readn(fd, buf, len)
- int fd; /* File descriptor to read from. */
- unsigned char *buf; /* Store in this buffer. */
- int len; /* Number of bytes to read. */
-{
- int n; /* Byte count in last read call. */
- int sofar = 0; /* Bytes read so far. */
-
- do {
- if ((n = read(fd, buf+sofar, len-sofar)) <= 0)
- /* error or EOF in read */
- return(n);
- sofar += n;
- } while (sofar < len);
- return sofar;
-}
-
diff --git a/lib/erl_interface/test/erl_format_SUITE.erl b/lib/erl_interface/test/erl_format_SUITE.erl
deleted file mode 100644
index 69dfdcc4c8..0000000000
--- a/lib/erl_interface/test/erl_format_SUITE.erl
+++ /dev/null
@@ -1,135 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(erl_format_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("erl_format_SUITE_data/format_test_cases.hrl").
-
--export([all/0, suite/0,
- init_per_testcase/2,
- atoms/1, tuples/1, lists/1]).
-
--import(runner, [get_term/1]).
-
-%% This test suite test the erl_format() function.
-%% It uses the port program "format_test".
-
-suite() ->
- [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [atoms, tuples, lists].
-
-init_per_testcase(Case, Config) ->
- runner:init_per_testcase(?MODULE, Case, Config).
-
-%% Tests formatting various atoms.
-
-atoms(Config) when is_list(Config) ->
- P = runner:start(Config, ?atoms),
-
- {term, ''} = get_term(P),
- {term, 'a'} = get_term(P),
- {term, 'A'} = get_term(P),
- {term, 'abc'} = get_term(P),
- {term, 'Abc'} = get_term(P),
- {term, 'ab@c'} = get_term(P),
- {term, 'The rain in Spain stays mainly in the plains'} = get_term(P),
-
- {term, a} = get_term(P),
- {term, ab} = get_term(P),
- {term, abc} = get_term(P),
- {term, ab@c} = get_term(P),
- {term, abcdefghijklmnopq} = get_term(P),
-
- {term, ''} = get_term(P),
- {term, 'a'} = get_term(P),
- {term, 'A'} = get_term(P),
- {term, 'abc'} = get_term(P),
- {term, 'Abc'} = get_term(P),
- {term, 'ab@c'} = get_term(P),
- {term, 'The rain in Spain stays mainly in the plains'} = get_term(P),
-
- {term, a} = get_term(P),
- {term, ab} = get_term(P),
- {term, abc} = get_term(P),
- {term, ab@c} = get_term(P),
- {term, ' abcdefghijklmnopq '} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-
-%% Tests formatting various tuples
-
-tuples(Config) when is_list(Config) ->
- P = runner:start(Config, ?tuples),
-
- {term, {}} = get_term(P),
- {term, {a}} = get_term(P),
- {term, {a, b}} = get_term(P),
- {term, {a, b, c}} = get_term(P),
- {term, {1}} = get_term(P),
- {term, {[]}} = get_term(P),
- {term, {[], []}} = get_term(P),
- {term, {[], a, b, c}} = get_term(P),
- {term, {[], a, [], b, c}} = get_term(P),
- {term, {[], a, '', b, c}} = get_term(P),
-
- runner:recv_eot(P),
- ok.
-
-
-
-%% Tests formatting various lists
-
-lists(Config) when is_list(Config) ->
- P = runner:start(Config, ?lists),
-
- {term, []} = get_term(P),
- {term, [a]} = get_term(P),
- {term, [a, b]} = get_term(P),
- {term, [a, b, c]} = get_term(P),
- {term, [1]} = get_term(P),
- {term, [[]]} = get_term(P),
- {term, [[], []]} = get_term(P),
- {term, [[], a, b, c]} = get_term(P),
- {term, [[], a, [], b, c]} = get_term(P),
- {term, [[], a, '', b, c]} = get_term(P),
-
- {term, [{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]} = get_term(P),
- case os:type() of
- vxworks ->
- {term, [{pi, _}, {'cos(70)', _}]} = get_term(P),
- {term, [[pi, _], ['cos(70)', _]]} = get_term(P),
- {term, [[pi, _], [], ["cos(70)", _]]} = get_term(P);
- _ ->
- {term, [{pi, 3.1415}, {'cos(70)', 0.34202}]} = get_term(P),
- {term, [[pi, 3.1415], ['cos(70)', 0.34202]]} = get_term(P),
- {term, [[pi, 3.1415], [], ["cos(70)", 0.34202]]} = get_term(P)
- end,
-
- {term, [-1]} = get_term(P),
-
- runner:recv_eot(P),
- ok.
diff --git a/lib/erl_interface/test/erl_format_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_format_SUITE_data/Makefile.src
deleted file mode 100644
index 2ba59ab651..0000000000
--- a/lib/erl_interface/test/erl_format_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-include @erl_interface_mk_include@
-
-CC0 = @CC@
-CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
-LD = @LD@
-LIBERL = @erl_interface_lib@
-LIBEI = @erl_interface_eilib@
-LIBFLAGS = ../all_SUITE_data/runner@obj@ \
- $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
- @erl_interface_threadlib@
-CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
-FORMAT_OBJS = format_test@obj@ format_test_decl@obj@
-
-all: format_test@exe@
-
-clean:
- $(RM) $(FORMAT_OBJS)
- $(RM) format_test@exe@
-
-format_test@exe@: $(FORMAT_OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(FORMAT_OBJS) $(LIBFLAGS)
-
-
diff --git a/lib/erl_interface/test/erl_format_SUITE_data/format_test.c b/lib/erl_interface/test/erl_format_SUITE_data/format_test.c
deleted file mode 100644
index 258ae92e0f..0000000000
--- a/lib/erl_interface/test/erl_format_SUITE_data/format_test.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#include "runner.h"
-
-/*
- * Purpose: Tests the erl_format() function.
- * Author: Bjorn Gustavsson
- */
-
-static void
-send_format(char* format)
-{
- send_term(erl_format(format));
-}
-
-TESTCASE(atoms)
-{
- erl_init(NULL, 0);
-
- send_format("''");
- send_format("'a'");
- send_format("'A'");
- send_format("'abc'");
- send_format("'Abc'");
- send_format("'ab@c'");
- send_format("'The rain in Spain stays mainly in the plains'");
-
- send_format("a");
- send_format("ab");
- send_format("abc");
- send_format("ab@c");
- send_format(" abcdefghijklmnopq ");
-
- send_term(erl_format("~a", ""));
- send_term(erl_format("~a", "a"));
- send_term(erl_format("~a", "A"));
- send_term(erl_format("~a", "abc"));
- send_term(erl_format("~a", "Abc"));
- send_term(erl_format("~a", "ab@c"));
- send_term(erl_format("~a", "The rain in Spain stays mainly in the plains"));
-
- send_term(erl_format("~a", "a"));
- send_term(erl_format("~a", "ab"));
- send_term(erl_format("~a", "abc"));
- send_term(erl_format("~a","ab@c"));
- send_term(erl_format("~a", " abcdefghijklmnopq "));
-
-
- report(1);
-}
-
-TESTCASE(tuples)
-{
- erl_init(NULL, 0);
-
- send_format("{}");
- send_format("{a}");
- send_format("{a, b}");
- send_format("{a, b, c}");
- send_format("{1}");
- send_format("{[]}");
- send_format("{[], []}");
- send_format("{[], a, b, c}");
- send_format("{[], a, [], b, c}");
- send_format("{[], a, '', b, c}");
-
- report(1);
-}
-
-
-
-TESTCASE(lists)
-{
- ETERM* a;
- ETERM* b;
- ETERM* c;
-
- erl_init(NULL, 0);
-
- send_format("[]");
- send_format("[a]");
- send_format("[a, b]");
- send_format("[a, b, c]");
- send_format("[1]");
- send_format("[[]]");
- send_format("[[], []]");
- send_format("[[], a, b, c]");
- send_format("[[], a, [], b, c]");
- send_format("[[], a, '', b, c]");
-
- b = erl_format("[{addr, ~s, ~i}]", "E-street", 42);
- a = erl_format("[{name, ~a}, {age, ~i}, {data, ~w}]", "Madonna", 21, b);
- send_term(a);
- erl_free_term(b);
-
- send_term(erl_format("[{pi, ~f}, {'cos(70)', ~f}]", 3.1415, 0.34202));
-
- a = erl_mk_float(3.1415);
- b = erl_mk_float(0.34202);
- send_term(erl_format("[[pi, ~w], ['cos(70)', ~w]]", a, b));
- erl_free_term(a);
- erl_free_term(b);
-
- a = erl_mk_float(3.1415);
- b = erl_mk_float(0.34202);
- c = erl_mk_empty_list();
- send_term(erl_format("[[~a, ~w], ~w, [~s, ~w]]", "pi", a, c, "cos(70)", b));
- erl_free_term(a);
- erl_free_term(b);
- erl_free_term(c);
-
- send_term(erl_format("[~i]", -1));
-
- report(1);
-}
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
deleted file mode 100644
index 0f08727225..0000000000
--- a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Purpose: Tests the functions in erl_global.c.
- *
- * See the erl_global_SUITE.erl file for a "table of contents".
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "runner.h"
-
-static void cmd_erl_connect(ETERM* args);
-static void cmd_erl_global_register(ETERM *args);
-static void cmd_erl_global_whereis(ETERM *args);
-static void cmd_erl_global_names(ETERM *args);
-static void cmd_erl_global_unregister(ETERM *args);
-static void cmd_erl_close_connection(ETERM *args);
-
-static void send_errno_result(int value);
-
-static struct {
- char* name;
- int num_args; /* Number of arguments. */
- void (*func)(ETERM* args);
-} commands[] = {
- "erl_connect", 4, cmd_erl_connect,
- "erl_close_connection", 1, cmd_erl_close_connection,
- "erl_global_register", 2, cmd_erl_global_register,
- "erl_global_whereis", 2, cmd_erl_global_whereis,
- "erl_global_names", 1, cmd_erl_global_names,
- "erl_global_unregister", 2, cmd_erl_global_unregister,
-};
-
-
-/*
- * Sends a list contaning all data types to the Erlang side.
- */
-
-TESTCASE(interpret)
-{
- ETERM* term;
-
- erl_init(NULL, 0);
-
- outer_loop:
-
- term = get_term();
-
- if (term == NULL) {
- report(1);
- return;
- } else {
- ETERM* Func;
- ETERM* Args;
- int i;
-
- if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) {
- fail("term should be a tuple of size 2");
- }
-
- Func = erl_element(1, term);
- if (!ERL_IS_ATOM(Func)) {
- fail("function name should be an atom");
- }
- Args = erl_element(2, term);
- if (!ERL_IS_TUPLE(Args)) {
- fail("function arguments should be a tuple");
- }
- erl_free_term(term);
- for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
- int n = strlen(commands[i].name);
- if (ERL_ATOM_SIZE(Func) != n) {
- continue;
- }
- if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) {
- erl_free_term(Func);
- if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) {
- fail("wrong number of arguments");
- }
- commands[i].func(Args);
- erl_free_term(Args);
- goto outer_loop;
- }
- }
- fail("bad command");
- }
-}
-
-#define VERIFY_TYPE(Test, Term) \
-if (!Test(Term)) { \
- fail("wrong type for " #Term); \
-} else { \
-}
-
-static void
-cmd_erl_connect(ETERM* args)
-{
- ETERM* number;
- ETERM* node;
- ETERM* cookie;
-
- int res;
- char buffer[256];
-
- number = ERL_TUPLE_ELEMENT(args, 0);
- VERIFY_TYPE(ERL_IS_INTEGER, number);
- node = ERL_TUPLE_ELEMENT(args, 1);
- VERIFY_TYPE(ERL_IS_ATOM, node);
- cookie = ERL_TUPLE_ELEMENT(args, 2);
- VERIFY_TYPE(ERL_IS_ATOM, cookie);
-
- if (ERL_ATOM_SIZE(cookie) == 0) {
- res = erl_connect_init(ERL_INT_VALUE(number), 0, 0);
- } else {
- memcpy(buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie));
- buffer[ERL_ATOM_SIZE(cookie)] = '\0';
- res = erl_connect_init(ERL_INT_VALUE(number), buffer, 0);
- }
-
- if(!res) {
- send_errno_result(res);
- return;
- }
-
- memcpy(buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node));
- buffer[ERL_ATOM_SIZE(node)] = '\0';
- send_errno_result(erl_connect(buffer));
-}
-
-static void
-cmd_erl_close_connection(ETERM* args)
-{
- ETERM* number;
- ETERM* res;
-
- number = ERL_TUPLE_ELEMENT(args, 0);
- VERIFY_TYPE(ERL_IS_INTEGER, number);
- res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number)));
- send_term(res);
- erl_free_term(res);
-}
-
-static void
-cmd_erl_global_register(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
- ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
- ETERM* pid = erl_mk_pid(erl_thisnodename(), 14, 0, 0);
-
- char buffer[256];
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
- VERIFY_TYPE(ERL_IS_ATOM, name);
-
- memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
- buffer[ERL_ATOM_SIZE(name)] = '\0';
-
- send_errno_result(erl_global_register(ERL_INT_VALUE(fd_term), buffer, pid));
- erl_free_term(pid);
-}
-
-static void
-cmd_erl_global_whereis(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
- ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
- ETERM* pid = NULL;
-
- char buffer[256];
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
- VERIFY_TYPE(ERL_IS_ATOM, name);
-
- memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
- buffer[ERL_ATOM_SIZE(name)] = '\0';
-
- pid = erl_global_whereis(ERL_INT_VALUE(fd_term), buffer, NULL);
- send_term(pid);
- erl_free_term(pid);
-}
-
-static void
-cmd_erl_global_names(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
-
- ETERM* res_array[2], *res_tuple, *name;
- char** names = NULL;
- int count = 0, i;
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
-
- names = erl_global_names(ERL_INT_VALUE(fd_term), &count);
-
- res_array[0] = erl_mk_empty_list();
- for(i=0; i<count; i++) {
- name = erl_mk_string(names[i]);
- res_array[0] = erl_cons(name, res_array[0]);
- }
-
- free(names);
-
- res_array[1] = erl_mk_int(count);
- res_tuple = erl_mk_tuple(res_array, 2);
-
- send_term(res_tuple);
-
- erl_free_compound(res_array[0]);
- erl_free_term(res_array[1]);
- erl_free_term(res_tuple);
-}
-
-static void
-cmd_erl_global_unregister(ETERM* args)
-{
- ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
- ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
-
- char buffer[256];
-
- VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
- VERIFY_TYPE(ERL_IS_ATOM, name);
-
- memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
- buffer[ERL_ATOM_SIZE(name)] = '\0';
-
- send_errno_result(erl_global_unregister(ERL_INT_VALUE(fd_term), buffer));
-}
-
-static void
-send_errno_result(int value)
-{
- ETERM* res_array[2];
- ETERM* res_tuple;
-
- res_array[0] = erl_mk_int(value);
- res_array[1] = erl_mk_int(erl_errno);
- res_tuple = erl_mk_tuple(res_array, 2);
- send_term(res_tuple);
- erl_free_term(res_array[0]);
- erl_free_term(res_array[1]);
- erl_free_term(res_tuple);
-}
diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl
deleted file mode 100644
index bb62d6288d..0000000000
--- a/lib/erl_interface/test/erl_match_SUITE.erl
+++ /dev/null
@@ -1,280 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(erl_match_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("erl_match_SUITE_data/match_test_cases.hrl").
-
--export([all/0, suite/0,
- init_per_testcase/2,
- atoms/1, lists/1, tuples/1, references/1, pids/1, ports/1,
- bind/1, integers/1, floats/1, binaries/1, strings/1]).
-
-%% For interactive running of matcher.
--export([start_matcher/1, erl_match/3]).
-
-%% This test suite tests the erl_match() function.
-
-suite() ->
- [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [atoms, lists, tuples, references, pids, ports, bind,
- integers, floats, binaries, strings].
-
-init_per_testcase(Case, Config) ->
- runner:init_per_testcase(?MODULE, Case, Config).
-
-atoms(Config) when is_list(Config) ->
- P = start_matcher(Config),
-
- eq(P, '', ''),
- eq(P, a, a),
- ne(P, a, b),
- ne(P, a, aa),
- eq(P, kalle, kalle),
- ne(P, kalle, arne),
-
- ne(P, kalle, 42),
- ne(P, 42, kalle),
-
- runner:finish(P),
- ok.
-
-lists(Config) when is_list(Config) ->
- P = start_matcher(Config),
- eq(P, [], []),
-
- ne(P, [], [a]),
- ne(P, [a], []),
-
- eq(P, [a], [a]),
- ne(P, [a], [b]),
-
- eq(P, [a|b], [a|b]),
- ne(P, [a|b], [a|x]),
-
- eq(P, [a, b], [a, b]),
- ne(P, [a, b], [a, x]),
-
- eq(P, [a, b, c], [a, b, c]),
- ne(P, [a, b|c], [a, b|x]),
- ne(P, [a, b, c], [a, b, x]),
- ne(P, [a, b|c], [a, b|x]),
- ne(P, [a, x|c], [a, b|c]),
- ne(P, [a, b, c], [a, x, c]),
-
- runner:finish(P),
- ok.
-
-tuples(Config) when is_list(Config) ->
- P = start_matcher(Config),
-
- ne(P, {}, {a, b}),
- ne(P, {a, b}, {}),
- ne(P, {a}, {a, b}),
- ne(P, {a, b}, {a}),
-
- eq(P, {}, {}),
-
- eq(P, {a}, {a}),
- ne(P, {a}, {b}),
-
- eq(P, {1}, {1}),
- ne(P, {1}, {2}),
-
- eq(P, {a, b}, {a, b}),
- ne(P, {x, b}, {a, b}),
-
- ne(P, {error, x}, {error, y}),
- ne(P, {error, {undefined, {subscriber, last}}},
- {error, {undefined, {subscriber, name}}}),
-
- runner:finish(P),
- ok.
-
-
-references(Config) when is_list(Config) ->
- P = start_matcher(Config),
- Ref1 = make_ref(),
- Ref2 = make_ref(),
-
- eq(P, Ref1, Ref1),
- eq(P, Ref2, Ref2),
- ne(P, Ref1, Ref2),
- ne(P, Ref2, Ref1),
-
- runner:finish(P),
- ok.
-
-
-pids(Config) when is_list(Config) ->
- P = start_matcher(Config),
- Pid1 = c:pid(0,1,2),
- Pid2 = c:pid(0,1,3),
-
- eq(P, self(), self()),
- eq(P, Pid1, Pid1),
- ne(P, Pid1, self()),
- ne(P, Pid2, Pid1),
-
- runner:finish(P),
- ok.
-
-
-ports(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"not on vxworks, pucko"};
- _ ->
- P = start_matcher(Config),
- P2 = start_matcher(Config),
-
- eq(P, P, P),
- ne(P, P, P2),
-
- runner:finish(P),
- runner:finish(P2),
- ok
- end.
-
-integers(Config) when is_list(Config) ->
- P = start_matcher(Config),
- I1 = 123,
- I2 = 12345,
- I3 = -123,
- I4 = 2234,
-
- eq(P, I1, I1),
- eq(P, I2, I2),
- ne(P, I1, I2),
- ne(P, I1, I3),
- eq(P, I4, I4),
-
- runner:finish(P),
- ok.
-
-
-
-floats(Config) when is_list(Config) ->
- P = start_matcher(Config),
- F1 = 3.1414,
- F2 = 3.1415,
- F3 = 3.1416,
-
- S1 = "string",
- S2 = "string2",
-
- eq(P, F1, F1),
- eq(P, F2, F2),
- ne(P, F1, F2),
- ne(P, F3, F2),
-
- eq(P, S2, S2),
- ne(P, S1, S2),
-
- runner:finish(P),
- ok.
-
-
-
-binaries(Config) when is_list(Config) ->
- P = start_matcher(Config),
- Bin1 = term_to_binary({kalle, 146015, {kungsgatan, 23}}),
- Bin2 = term_to_binary(sune),
- Bin3 = list_to_binary("sune"),
-
- eq(P, Bin1, Bin1),
- eq(P, Bin2, Bin2),
- eq(P, Bin3, Bin3),
- ne(P, Bin1, Bin2),
- ne(P, Bin1, Bin3),
- ne(P, Bin2, Bin3),
-
- runner:finish(P),
- ok.
-
-
-strings(Config) when is_list(Config) ->
- P = start_matcher(Config),
-
- S1 = "string",
- S2 = "streng",
- S3 = "String",
-
- eq(P, S1, S1),
- ne(P, S1, S2),
- ne(P, S1, S3),
-
- runner:finish(P),
- ok.
-
-
-bind(Config) when is_list(Config) ->
- P = start_bind(Config),
- S = "[X,Y,Z]",
- L1 = [301,302,302],
- L2 = [65,66,67],
-
- bind_ok(P, S, L1),
- bind_ok(P, S, L2),
-
- runner:finish(P),
- ok.
-
-start_bind(Config) ->
- runner:start(Config, ?erl_match_bind).
-
-bind_ok(Port, Bind, Term) ->
- true = erl_bind(Port, Bind, Term).
-
-%bind_nok(Port, Bind, Term) ->
-% false = erl_bind(Port, Bind, Term).
-
-erl_bind(Port, Pattern, Term) ->
- Port ! {self(), {command, [$b, Pattern, 0]}},
- runner:send_term(Port, Term),
- case runner:get_term(Port) of
- {term, 0} -> false;
- {term, 1} -> true
- end.
-
-
-
-start_matcher(Config) ->
- runner:start(Config, ?erl_match_server).
-
-eq(Port, Pattern, Term) ->
- true = erl_match(Port, Pattern, Term).
-
-ne(Port, Pattern, Term) ->
- false = erl_match(Port, Pattern, Term).
-
-
-
-erl_match(Port, Pattern, Term) ->
- runner:send_term(Port, Pattern),
- runner:send_term(Port, Term),
- case runner:get_term(Port) of
- {term, 0} -> false;
- {term, 1} -> true
- end.
diff --git a/lib/erl_interface/test/erl_match_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_match_SUITE_data/Makefile.first
deleted file mode 100644
index 459b5c14c2..0000000000
--- a/lib/erl_interface/test/erl_match_SUITE_data/Makefile.first
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-match_test_decl.c: match_test.c
- erl -noinput -pa ../all_SUITE_data -s init_tc run match_test -s erlang halt
diff --git a/lib/erl_interface/test/erl_match_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_match_SUITE_data/Makefile.src
deleted file mode 100644
index 156214a269..0000000000
--- a/lib/erl_interface/test/erl_match_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-#
-
-include @erl_interface_mk_include@
-
-CC0 = @CC@
-CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
-LD = @LD@
-LIBERL = @erl_interface_lib@
-LIBEI = @erl_interface_eilib@
-LIBFLAGS = ../all_SUITE_data/runner@obj@ \
- $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
- @erl_interface_threadlib@
-CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
-MATCH_OBJS = match_test@obj@ match_test_decl@obj@
-
-all: match_test@exe@
-
-clean:
- $(RM) $(MATCH_OBJS)
- $(RM) match_test@exe@
-
-match_test@exe@: $(MATCH_OBJS) $(LIBERL) $(LIBEI)
- $(LD) @CROSSLDFLAGS@ -o $@ $(MATCH_OBJS) $(LIBFLAGS)
-
diff --git a/lib/erl_interface/test/erl_match_SUITE_data/match_test.c b/lib/erl_interface/test/erl_match_SUITE_data/match_test.c
deleted file mode 100644
index d577417f5b..0000000000
--- a/lib/erl_interface/test/erl_match_SUITE_data/match_test.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Purpose: Tests the erl_match() function.
- * Author: Bjorn Gustavsson
- */
-
-#include "runner.h"
-
-TESTCASE(erl_match_server)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- ETERM* pattern;
- ETERM* term;
-
- pattern = get_term();
- if (pattern == NULL) {
- report(1);
- return;
- } else {
- term = get_term();
- if (term == NULL) {
- fail("Unexpected EOF term");
- } else {
- send_term(erl_mk_int(erl_match(pattern, term)));
- erl_free_term(pattern);
- erl_free_term(term);
- }
- }
- }
-
-}
-
-TESTCASE(erl_match_bind)
-{
- erl_init(NULL, 0);
-
- for (;;) {
- char* pattern;
- ETERM* term;
-
- pattern=read_packet(NULL);
-
- switch (pattern[0]) {
- case 'e':
- free(pattern);
- report(1);
- return;
-
- case 'b':
- {
- ETERM* patt_term;
-
- /*
- * Get the pattern string and convert it using erl_format().
- *
- * Note that the call to get_term() below destroys the buffer
- * that the pattern variable points to. Therefore, it is
- * essential to call erl_format() here, before
- * calling get_term().
- */
-
- message("Pattern: %s", pattern+1);
- patt_term = erl_format(pattern+1);
- free(pattern);
-
- if (patt_term == NULL) {
- fail("erl_format() failed");
- }
-
- /*
- * Get the term and send back the result of the erl_match()
- * call.
- */
-
- term = get_term();
- if (term == NULL) {
- fail("Unexpected eof term");
- }
- else {
- send_term(erl_mk_int(erl_match(patt_term, term)));
- }
- erl_free_term(patt_term);
- erl_free_term(term);
- }
- break;
-
- default:
- free(pattern);
- fail("Illegal character received");
- }
-
- }
-}
diff --git a/lib/erl_interface/test/port_call_SUITE_data/Makefile.src b/lib/erl_interface/test/port_call_SUITE_data/Makefile.src
index 0f97ce9f70..0088c11a14 100644
--- a/lib/erl_interface/test/port_call_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/port_call_SUITE_data/Makefile.src
@@ -23,10 +23,9 @@ include @erl_interface_mk_include@
CC0 = @CC@
CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
LD = @LD@
-LIBERL = @erl_interface_lib_drv@
LIBEI = @erl_interface_eilib_drv@
-SHLIB_EXTRA_LDLIBS = $(LIBERL) $(LIBEI) @erl_interface_threadlib@
+SHLIB_EXTRA_LDLIBS = $(LIBEI) @erl_interface_threadlib@
SHLIB_EXTRA_CFLAGS = -I@erl_interface_include@ -I../all_SUITE_data
diff --git a/lib/erl_interface/test/port_call_SUITE_data/port_call_drv.c b/lib/erl_interface/test/port_call_SUITE_data/port_call_drv.c
index 4617cb0316..8f7303d645 100644
--- a/lib/erl_interface/test/port_call_SUITE_data/port_call_drv.c
+++ b/lib/erl_interface/test/port_call_SUITE_data/port_call_drv.c
@@ -21,7 +21,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-#include "erl_interface.h"
+#include "ei.h"
#include "erl_driver.h"
static ErlDrvPort my_erlang_port;
diff --git a/lib/et/Makefile b/lib/et/Makefile
index f0bb7be211..1b89cff83e 100644
--- a/lib/et/Makefile
+++ b/lib/et/Makefile
@@ -35,3 +35,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=runtime_tools wx
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/et/doc/src/Makefile b/lib/et/doc/src/Makefile
index 93e2f8eeee..fb13e07d9e 100644
--- a/lib/et/doc/src/Makefile
+++ b/lib/et/doc/src/Makefile
@@ -29,87 +29,11 @@ include ../../vsn.mk
VSN=$(ET_VSN)
APPLICATION=et
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-
include files.mk
-# ----------------------------------------------------
-
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
XML_GEN_FILES = $(GEN_XML:%=$(XMLDIR)/%)
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(GEN_XML:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-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)/%: %
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: images $(HTML_REF_MAN_FILE)
-
-clean clean_docs:
- for file in $(XML_FILES); do \
- if [ -f $$file\src ]; then \
- rm -f $$file; \
- fi \
- done
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/et/doc/src/et_collector.xml b/lib/et/doc/src/et_collector.xml
index f908612797..d258be2d40 100644
--- a/lib/et/doc/src/et_collector.xml
+++ b/lib/et/doc/src/et_collector.xml
@@ -139,19 +139,6 @@
</desc>
</func>
<func>
- <name since="">load_event_file(CollectorPid, FileName) -> {ok, BadBytes} | exit(Reason)</name>
- <fsummary>Load the event table from a file</fsummary>
- <type>
- <v>CollectorPid = pid()</v>
- <v>FileName = string()</v>
- <v>BadBytes = integer(X) where X >= 0</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Load the event table from a file.</p>
- </desc>
- </func>
- <func>
<name since="">report(Handle, TraceOrEvent) -> {ok, Continuation} | exit(Reason)</name>
<name since="">report_event(Handle, DetailLevel, FromTo, Label, Contents) -> {ok, Continuation} | exit(Reason)</name>
<name since="">report_event(Handle, DetailLevel, From, To, Label, Contents) -> {ok, Continuation} | exit(Reason)</name>
@@ -193,17 +180,6 @@
</desc>
</func>
<func>
- <name since="">get_table_handle(CollectorPid) -> Handle</name>
- <fsummary>Return a table handle</fsummary>
- <type>
- <v>CollectorPid = pid()</v>
- <v>Handle = record(table_handle)</v>
- </type>
- <desc>
- <p>Return a table handle.</p>
- </desc>
- </func>
- <func>
<name since="">get_global_pid() -> CollectorPid | exit(Reason)</name>
<fsummary>Return a the identity of the globally registered collector if there is any</fsummary>
<type>
diff --git a/lib/et/doc/src/files.mk b/lib/et/doc/src/files.mk
index c9041caa81..24815d0674 100644
--- a/lib/et/doc/src/files.mk
+++ b/lib/et/doc/src/files.mk
@@ -38,7 +38,6 @@ GEN_XML = \
et_desc.xml \
et_examples.xml
-
BOOK_FILES = book.xml
IMAGE_FILES = \
@@ -53,4 +52,3 @@ IMAGE_FILES = \
sim_trans_mgr_actors.png \
sim_trans_move_actor.png \
sim_trans_write_lock.png
-
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index 3609238509..7fa5dbda38 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -40,12 +40,10 @@
start_trace_client/3,
start_trace_port/1,
- %% load_event_file/2,
save_event_file/3,
clear_table/1,
get_global_pid/0,
- %% get_table_handle/1,
get_table_size/1,
change_pattern/2,
make_key/2,
diff --git a/lib/eunit/Makefile b/lib/eunit/Makefile
index 15dae19896..d0dd447a6c 100644
--- a/lib/eunit/Makefile
+++ b/lib/eunit/Makefile
@@ -48,13 +48,7 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
-
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
@@ -94,3 +88,5 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index dc9f858812..8ab89fca73 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -277,6 +277,9 @@ while testing, you can write to the `user' output stream, as in
`io:format(user, "~w", [Term])'. The recommended way of doing this is to
use the EUnit {@section Debugging macros}, which make it much simpler.
+For checking the output produced by the unit under test, see
+{@section Macros for checking output}.
+
=== Writing test generating functions ===
A drawback of simple test functions is that you must write a separate
@@ -415,6 +418,7 @@ your code is compiled with testing enabled or disabled.
<li>{@section Compilation control macros}</li>
<li>{@section Utility macros}</li>
<li>{@section Assert macros}</li>
+<li>{@section Macros for checking output}</li>
<li>{@section Macros for running external commands}</li>
<li>{@section Debugging macros}</li>
</ul>
@@ -608,6 +612,22 @@ Examples:
</dd>
</dl>
+=== Macros for checking output ===
+
+The following macro can be used within a test case to retreive the
+output written to standard output.
+
+<dl>
+<dt>`capturedOutput'</dt>
+<dd>The output captured by EUnit in the current test case, as a string.
+
+Examples:
+
+```io:format("Hello~n"),
+ ?assertEqual("Hello\n", ?capturedOutput)'''
+</dd>
+</dl>
+
=== Macros for running external commands ===
Keep in mind that external commands are highly dependent on the
diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile
index 117542cb37..22f2460fe9 100644
--- a/lib/eunit/doc/src/Makefile
+++ b/lib/eunit/doc/src/Makefile
@@ -29,43 +29,22 @@ VSN=$(EUNIT_VSN)
APPLICATION=eunit
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Help application directory specification
-# ----------------------------------------------------
-
-EDOC_DIR = $(ERL_TOP)/lib/edoc
-SYNTAX_TOOLS_DIR = $(ERL_TOP)/lib/syntax_tools
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-EUNIT_DIR = $(ERL_TOP)/lib/eunit/src
-EUNIT_INC_DIR = $(ERL_TOP)/lib/eunit/include
-
-EUNIT_MODULES = \
- eunit eunit_surefire
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = $(EUNIT_MODULES:=.xml)
-
-XML_PART_FILES = \
- part.xml
+EDOC_REF3_FILES = \
+ eunit.xml eunit_surefire.xml
-XML_CHAPTER_FILES = \
+EDOC_CHAPTER_FILE = \
chapter.xml
XML_NOTES_FILES = \
notes.xml
-HTML_EXAMPLE_FILES =
-
-HTML_STYLESHEET_FILES = \
- ../stylesheet.css
+XML_PART_FILES = \
+ part.xml
BOOK_FILES = book.xml
@@ -73,100 +52,6 @@ XML_FILES = \
$(BOOK_FILES) $(XML_NOTES_FILES) \
$(XML_PART_FILES) $(XML_APPLICATION_FILES)
-XML_GEN_FILES = $(XML_REF3_FILES:%=$(XMLDIR)/%) $(XML_CHAPTER_FILES:%=$(XMLDIR)/%)
-
-# ----------------------------------------------------
-INFO_FILE = ../../info
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-
-EXTRA_FILES = \
- $(DEFAULT_HTML_FILES) \
- $(DEFAULT_GIF_FILES) \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)\
- $(XML_NOTES_FILES:%.xml=$(HTMLDIR)/%.html)
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_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)/%)
-
-$(XML_REF3_FILES:%=$(XMLDIR)/%):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EUNIT_VSN) -i $(EUNIT_INC_DIR) -dir $(XMLDIR) $(EUNIT_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
-
-$(XML_CHAPTER_FILES:%=$(XMLDIR)/%):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EUNIT_VSN) -chapter -dir $(XMLDIR) ../overview.edoc
-
-info:
- @echo "XML_PART_FILES: $(XML_PART_FILES)"
- @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)"
- @echo "EUNIT_XML_FILES: $(EUNIT_XML_FILES)"
- @echo "EUNIT_MODULES: $(EUNIT_MODULES)"
- @echo "HTML_FILES: $(HTML_FILES)"
- @echo "HTMLDIR: $(HTMLDIR)"
- @echo "DEFAULT_GIF_FILES: $(DEFAULT_GIF_FILES)"
- @echo "DEFAULT_HTML_FILES: $(DEFAULT_HTML_FILES)"
- @echo "EXTRA_FILES: $(EXTRA_FILES)"
-
-xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 3cc36a27bc..6842147fda 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -147,6 +147,16 @@
-define(_assertNotException(Class, Term, Expr),
?_test(?assertNotException(Class, Term, Expr))).
+%% Macros for retrieving the output of a test case
+
+-ifndef(capturedOutput).
+-define(capturedOutput,
+ case ?UNDER_EUNIT of
+ true -> eunit_proc:get_output();
+ false -> ""
+ end).
+-endif.
+
%% Macros for running operating system commands. (Note that these
%% require EUnit to be present at runtime, or at least eunit_lib.)
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
index 6e702543d0..fb6619a6d4 100644
--- a/lib/eunit/src/eunit_proc.erl
+++ b/lib/eunit/src/eunit_proc.erl
@@ -29,7 +29,7 @@
-include("eunit.hrl").
-include("eunit_internal.hrl").
--export([start/4]).
+-export([start/4, get_output/0]).
%% This must be exported; see new_group_leader/1 for details.
-export([group_leader_process/1]).
@@ -52,6 +52,18 @@ start(Tests, Order, Super, Reference)
order = Order},
spawn_group(local, #group{tests = Tests}, St).
+%% Fetches the output captured by the eunit group leader. This is
+%% provided to allow test cases to check the captured output.
+
+-spec get_output() -> string().
+get_output() ->
+ group_leader() ! {get_output, self()},
+ receive
+ {output, Output} -> Output
+ after 100 ->
+ %% The group leader is not an eunit_proc
+ abort_task(get_output)
+ end.
%% Status messages sent to the supervisor process. (A supervisor does
%% not have to act on these messages - it can e.g. just log them, or
@@ -594,6 +606,9 @@ group_leader_loop(Runner, Wait, Buf) ->
receive after 2 -> ok end,
process_flag(priority, low),
group_leader_loop(Runner, 0, Buf);
+ {get_output, From} ->
+ From ! {output, lists:flatten(lists:reverse(Buf))},
+ group_leader_loop(Runner, Wait, Buf);
_ ->
%% discard any other messages
group_leader_loop(Runner, Wait, Buf)
@@ -669,4 +684,9 @@ io_error_test_() ->
[?_assertMatch({error, enotsup}, io:getopts()),
?_assertMatch({error, enotsup}, io:columns()),
?_assertMatch({error, enotsup}, io:rows())].
+
+get_output_test() ->
+ io:format("Hello"),
+ Output = get_output(),
+ ?assertEqual("Hello", Output).
-endif.
diff --git a/lib/eunit/src/eunit_tests.erl b/lib/eunit/src/eunit_tests.erl
index 07a415eeb1..f43f4cca09 100644
--- a/lib/eunit/src/eunit_tests.erl
+++ b/lib/eunit/src/eunit_tests.erl
@@ -45,3 +45,10 @@ if_test_() ->
matches_test_() ->
[?_assert(?MATCHES("hel"++_, "hello")),
?_assertNot(?MATCHES("hal"++_, "hello"))].
+
+get_output_test() ->
+ io:format(<<"Hello ~p!~n">>, [eunit]),
+ ?assertEqual("Hello eunit!\n", ?capturedOutput),
+ io:format("System working?~n~s~n", ["Seems to be."]),
+ ?assertEqual("Hello eunit!\nSystem working?\nSeems to be.\n",
+ ?capturedOutput).
diff --git a/lib/ftp/Makefile b/lib/ftp/Makefile
index e0c9de42e4..a26d1de0a0 100644
--- a/lib/ftp/Makefile
+++ b/lib/ftp/Makefile
@@ -32,47 +32,11 @@ VSN = $(FTP_VSN)
SPECIAL_TARGETS =
-DIA_PLT = ./priv/plt/$(APPLICATION).plt
-DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
-
-
# ----------------------------------------------------
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info gclean dialyzer dialyzer_plt dclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "FTP_VSN: $(FTP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
+DIA_PLT_APPS = runtime_tools ssl
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ftp/doc/src/Makefile b/lib/ftp/doc/src/Makefile
index 20fbbc73a9..fc95e83ae8 100644
--- a/lib/ftp/doc/src/Makefile
+++ b/lib/ftp/doc/src/Makefile
@@ -26,12 +26,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
include ../../vsn.mk
VSN=$(FTP_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
+APPLICATION=ftp
# ----------------------------------------------------
# Target Specs
@@ -59,97 +54,6 @@ XML_FILES = \
$(XML_REF3_FILES) \
$(XML_APPLICATION_FILES)
-# GIF_FILES = ftp.gif
-
-
-# ----------------------------------------------------
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-EXTRA_FILES = \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_REF6_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 +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-ldocs: local_docs
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs: clean_html clean_man clean_pdf
- rm -rf $(XMLDIR)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean_pdf:
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
-
-clean_html:
- rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
-
-clean_man:
- rm -f $(MAN3_FILES)
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+# IMAGE_FILES = ftp.gif
-info:
- @echo "GIF_FILES:\n$(GIF_FILES)"
- @echo ""
- @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
- @echo ""
- @echo "HTML_FILES:\n$(HTML_FILES)"
- @echo ""
- @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
- @echo ""
- @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
- @echo ""
- @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
- @echo ""
- @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
- @echo ""
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml
index 9645b03364..5a1f289b57 100644
--- a/lib/ftp/doc/src/ftp.xml
+++ b/lib/ftp/doc/src/ftp.xml
@@ -58,7 +58,7 @@
binaries (see <c>recv_bin/2</c>) and for sending binaries to be
stored as remote files (see <c>send_bin/3</c>).</p>
- <p>A set of functions is provvided for sending and receiving
+ <p>A set of functions is provided for sending and receiving
contiguous parts of a file to be stored in a remote file. For send,
see <c>send_chunk_start/2</c>, <c>send_chunk/2</c>, and
<c>send_chunk_end/1</c>. For receive, see
diff --git a/lib/ftp/test/Makefile b/lib/ftp/test/Makefile
index 147f8e5dd6..41f23ac7ba 100644
--- a/lib/ftp/test/Makefile
+++ b/lib/ftp/test/Makefile
@@ -115,7 +115,7 @@ SOURCE = $(ERL_FILES) $(HRL_FILES)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-FTP_SPECS = ftp.spec ftp_bench.spec
+FTP_SPECS = ftp.spec
COVER_FILE = ftp.cover
FTP_FILES = ftp.config $(FTP_SPECS)
diff --git a/lib/ftp/test/ftp_bench.spec b/lib/ftp/test/ftp_bench.spec
deleted file mode 100644
index 4d1ecf8891..0000000000
--- a/lib/ftp/test/ftp_bench.spec
+++ /dev/null
@@ -1 +0,0 @@
-{suites,"../ftp_test",[]}.
diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile
index 0676484fca..4998522943 100644
--- a/lib/hipe/Makefile
+++ b/lib/hipe/Makefile
@@ -75,3 +75,6 @@ distclean:
realclean:
$(V_at)$(MAKE) MAKETARGET="realclean" all-subdirs all-subdirs-x
+DIA_PLT_APPS=compiler syntax_tools
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl
index a2bd7fe0f0..583c5d624a 100644
--- a/lib/hipe/cerl/cerl_closurean.erl
+++ b/lib/hipe/cerl/cerl_closurean.erl
@@ -797,7 +797,8 @@ take_work({Queue0, Set0}) ->
-spec is_escape_op(atom(), arity()) -> boolean().
-is_escape_op(match_fail, 1) -> false;
+is_escape_op(match_fail, 1) -> false;
+is_escape_op(recv_wait_timeout, 1) -> false;
is_escape_op(F, A) when is_atom(F), is_integer(A) -> true.
-spec is_escape_op(atom(), atom(), arity()) -> boolean().
@@ -814,6 +815,7 @@ is_escape_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> true.
-spec is_literal_op(atom(), arity()) -> boolean().
+is_literal_op(recv_wait_timeout, 1) -> true;
is_literal_op(match_fail, 1) -> true;
is_literal_op(F, A) when is_atom(F), is_integer(A) -> false.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 0b6db544ca..bac489c07c 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -115,6 +115,7 @@
t_map_is_key/3,
t_map_entries/2,
t_map_put/3,
+ t_map_remove/3,
t_map_update/3,
t_map_pairwise_merge/4
]).
@@ -1700,6 +1701,11 @@ type(maps, put, 3, Xs, Opaques) ->
fun ([Key, Value, Map]) ->
t_map_put({Key, Value}, Map, Opaques)
end, Opaques);
+type(maps, remove, 2, Xs, Opaques) ->
+ strict(maps, remove, 2, Xs,
+ fun ([Key, Map]) ->
+ t_map_remove(Key, Map, Opaques)
+ end, Opaques);
type(maps, size, 1, Xs, Opaques) ->
strict(maps, size, 1, Xs,
fun ([Map]) ->
@@ -2648,6 +2654,8 @@ arg_types(maps, merge, 2) ->
[t_map(), t_map()];
arg_types(maps, put, 3) ->
[t_any(), t_any(), t_map()];
+arg_types(maps, remove, 2) ->
+ [t_any(), t_map()];
arg_types(maps, size, 1) ->
[t_map()];
arg_types(maps, update, 3) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index badf58936f..5f5612fcd3 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -154,6 +154,7 @@
t_map_update/2, t_map_update/3,
t_map_pairwise_merge/4,
t_map_put/2, t_map_put/3,
+ t_map_remove/3,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -1925,6 +1926,27 @@ map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) ->
end
end.
+-spec t_map_remove(erl_type(), erl_type(), opaques()) -> erl_type().
+
+t_map_remove(Key, Map, Opaques) ->
+ do_opaque(Map, Opaques, fun(UM) -> map_remove(Key, UM) end).
+
+map_remove(_, ?none) -> ?none;
+map_remove(_, ?unit) -> ?none;
+map_remove(Key, Map) ->
+ %% ?map(lists:keydelete(Key, 1, Pairs), DefK, DefV).
+ case is_singleton_type(Key) of
+ false -> Map;
+ true ->
+ ?map(Pairs,DefK,DefV) = Map,
+ case lists:keyfind(Key, 1, Pairs) of
+ false -> Map;
+ {Key, _, _} ->
+ Pairs1 = lists:keydelete(Key, 1, Pairs),
+ t_map(Pairs1, DefK, DefV)
+ end
+ end.
+
-spec t_map_update({erl_type(), erl_type()}, erl_type()) -> erl_type().
t_map_update(KV, Map) ->
diff --git a/lib/hipe/doc/src/Makefile b/lib/hipe/doc/src/Makefile
index 104c15f2bb..e517e90fd0 100644
--- a/lib/hipe/doc/src/Makefile
+++ b/lib/hipe/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(HIPE_VSN)
APPLICATION=hipe
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -47,73 +42,4 @@ 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 -rf $(XMLDIR)
- 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:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index efdaeecca3..97d50eb472 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -689,8 +689,8 @@ trans_fun([{call_fun,N}|Instructions], Env) ->
Dst = [mk_var({r,0})],
[hipe_icode:mk_comment('call_fun'),
hipe_icode:mk_primop(Dst,call_fun,Args) | trans_fun(Instructions,Env)];
-%%--- patched_make_fun --- make_fun/make_fun2 after fixes
-trans_fun([{patched_make_fun,MFA,Magic,FreeVarNum,Index}|Instructions], Env) ->
+%%--- make_fun2 ---
+trans_fun([{make_fun2,MFA,Index,Magic,FreeVarNum}|Instructions], Env) ->
Args = extract_fun_args(FreeVarNum),
Dst = [mk_var({r,0})],
Fun = hipe_icode:mk_primop(Dst,
@@ -1204,6 +1204,17 @@ trans_fun([{bs_get_position=Name,_,_,_}|_Instructions], _Env) ->
trans_fun([{bs_set_position=Name,_,_}|_Instructions], _Env) ->
nyi(Name);
%%--------------------------------------------------------------------
+%% New instructions added in OTP 23.
+%%--------------------------------------------------------------------
+%%--- swap ---
+trans_fun([{swap,Reg1,Reg2}|Instructions], Env) ->
+ Var1 = mk_var(Reg1),
+ Var2 = mk_var(Reg2),
+ Temp = mk_var(new),
+ [hipe_icode:mk_move(Temp, Var1),
+ hipe_icode:mk_move(Var1, Var2),
+ hipe_icode:mk_move(Var2, Temp) | trans_fun(Instructions, Env)];
+%%--------------------------------------------------------------------
%%--- ERROR HANDLING ---
%%--------------------------------------------------------------------
trans_fun([X|_], _) ->
@@ -1946,7 +1957,7 @@ mod_find_closure_info([FunCode|Fs], CI) ->
mod_find_closure_info([], CI) ->
CI.
-find_closure_info([{patched_make_fun,MFA={_M,_F,A},_Magic,FreeVarNum,_Index}|BeamCode],
+find_closure_info([{make_fun2,{_M,_F,A}=MFA,_Index,_Magic,FreeVarNum}|BeamCode],
ClosureInfo) ->
NewClosure = %% A-FreeVarNum+1 (The real arity + 1 for the closure)
#closure_info{mfa=MFA, arity=A-FreeVarNum+1, fv_arity=FreeVarNum},
@@ -2024,41 +2035,8 @@ split_params(N, [ArgN|OrgArgs], Args) ->
%%-----------------------------------------------------------------------
preprocess_code(ModuleCode) ->
- PatchedCode = patch_R7_funs(ModuleCode),
- ClosureInfo = find_closure_info(PatchedCode),
- {PatchedCode, ClosureInfo}.
-
-%%-----------------------------------------------------------------------
-%% Patches the "make_fun" BEAM instructions of R7 so that they also
-%% contain the index that the BEAM loader generates for funs.
-%%
-%% The index starts from 0 and is incremented by 1 for each make_fun
-%% instruction encountered.
-%%
-%% Retained only for compatibility with BEAM code prior to R8.
-%%
-%% Temporarily, it also rewrites R8-PRE-RELEASE "make_fun2"
-%% instructions, since their embedded indices don't work.
-%%-----------------------------------------------------------------------
-
-patch_R7_funs(ModuleCode) ->
- patch_make_funs(ModuleCode, 0).
-
-patch_make_funs([FunCode0|Fs], FunIndex0) ->
- {PatchedFunCode,FunIndex} = patch_make_funs(FunCode0, FunIndex0, []),
- [PatchedFunCode|patch_make_funs(Fs, FunIndex)];
-patch_make_funs([], _) -> [].
-
-patch_make_funs([{make_fun,MFA,Magic,FreeVarNum}|Is], FunIndex, Acc) ->
- Patched = {patched_make_fun,MFA,Magic,FreeVarNum,FunIndex},
- patch_make_funs(Is, FunIndex+1, [Patched|Acc]);
-patch_make_funs([{make_fun2,MFA,_BogusIndex,Magic,FreeVarNum}|Is], FunIndex, Acc) ->
- Patched = {patched_make_fun,MFA,Magic,FreeVarNum,FunIndex},
- patch_make_funs(Is, FunIndex+1, [Patched|Acc]);
-patch_make_funs([I|Is], FunIndex, Acc) ->
- patch_make_funs(Is, FunIndex, [I|Acc]);
-patch_make_funs([], FunIndex, Acc) ->
- {lists:reverse(Acc),FunIndex}.
+ ClosureInfo = find_closure_info(ModuleCode),
+ {ModuleCode, ClosureInfo}.
%%-----------------------------------------------------------------------
@@ -2358,9 +2336,8 @@ catch_handler('catch', [TagVar,ValueVar,TraceVar], OldCatchLbl) ->
ValueVar]),
hipe_icode:mk_goto(Cont),
ErrorLbl,
- %% We use the trace variable to hold the symbolic trace. Its previous
- %% value is just that in p->ftrace, so get_stacktrace() works fine.
- hipe_icode:mk_call([TraceVar],erlang,get_stacktrace,[],remote),
+ %% We use the trace variable to hold the symbolic trace.
+ hipe_icode:mk_primop([TraceVar],build_stacktrace,[TraceVar]),
hipe_icode:mk_primop([ValueVar],mktuple, [ValueVar, TraceVar]),
hipe_icode:mk_goto(hipe_icode:label_name(ExitLbl)),
OldCatchLbl, % normal execution paths must go through end_try
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index 63b34f23a4..2941cf15fc 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -436,14 +436,8 @@ type(Primop, Args) ->
#element{} ->
erl_bif_types:type(erlang, element, 2, Args);
#unsafe_element{index = N} ->
- [Type] = Args,
- case erl_types:t_is_tuple(Type) of
- false ->
- erl_types:t_none();
- true ->
- Index = erl_types:t_from_term(N),
- erl_bif_types:type(erlang, element, 2, [Index|Args])
- end;
+ Index = erl_types:t_from_term(N),
+ erl_bif_types:type(erlang, element, 2, [Index | Args]);
#unsafe_update_element{index = N} ->
%% Same, same
erl_bif_types:type(erlang, setelement, 3, [erl_types:t_integer(N)|Args]);
diff --git a/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl b/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
index 430e097b91..c6bec39632 100644
--- a/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
+++ b/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
@@ -8,6 +8,9 @@
-export([test/0]).
+% Ensure type optimization is turned off for id/1
+-export([id/1]).
+
test() ->
ok = test_ets_bifs(),
ok = test_szar_bug(),
@@ -19,6 +22,7 @@ test() ->
ok = test_switch_neg_int(),
ok = test_icode_range_anal(),
ok = test_icode_range_call(),
+ ok = test_icode_type_miscompile(),
ok.
%%-----------------------------------------------------------------------
@@ -503,3 +507,25 @@ range_client(Server, N) ->
receive proceed -> ok end,
range_client(Server, N - 1), % non-tailrecursive call with ignored result
ok.
+
+test_icode_type_miscompile() ->
+ List0 = id([{1,1},{1,1}]),
+
+ %% The expressions below produce a list that the SSA type pass knows is
+ %% a list of two-tuples, but hipe_icode_type does not.
+ %%
+ %% Changing the `F(X)` call to just `X` helps the icode type pass figure
+ %% things out, making the bug disappear.
+ F = fun({_, _}=X) -> X end,
+ List = [F(X) || {_,_}=X <- List0],
+
+ type_miscompile(List, List, []).
+
+type_miscompile([{Same, Same} | As], [{Same, Same} | Bs], Acc) ->
+ type_miscompile(As, Bs, [gaffel | Acc]);
+type_miscompile([], [], Acc) ->
+ %% Acc is non-empty when everything works as expected.
+ true = Acc =/= [],
+ ok.
+
+id(I) -> I.
diff --git a/lib/inets/Makefile b/lib/inets/Makefile
index 872df9d055..a7723dc0d8 100644
--- a/lib/inets/Makefile
+++ b/lib/inets/Makefile
@@ -32,47 +32,11 @@ VSN = $(INETS_VSN)
SPECIAL_TARGETS =
-DIA_PLT = ./priv/plt/$(APPLICATION).plt
-DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
-
-
# ----------------------------------------------------
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info gclean dialyzer dialyzer_plt dclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "INETS_VSN: $(INETS_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
+DIA_PLT_APPS=runtime_tools ftp mnesia ssl tftp
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index cbc0e384d8..a4405c5728 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -26,12 +26,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
include ../../vsn.mk
VSN=$(INETS_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
+APPLICATION=inets
# ----------------------------------------------------
# Target Specs
@@ -71,97 +66,8 @@ XML_FILES = \
$(XML_REF3_FILES) \
$(XML_APPLICATION_FILES)
-# GIF_FILES = inets.gif
-
-
-# ----------------------------------------------------
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-EXTRA_FILES = \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_REF6_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 +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-ldocs: local_docs
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs: clean_html clean_man clean_pdf
- rm -rf $(XMLDIR)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+NO_CHUNKS = httpd_custom_api.xml
-debug opt:
-
-clean_pdf:
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
-
-clean_html:
- rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
-
-clean_man:
- rm -f $(MAN3_FILES)
-
-
-# ----------------------------------------------------
-# Release Target
# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-info:
- @echo "GIF_FILES:\n$(GIF_FILES)"
- @echo ""
- @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
- @echo ""
- @echo "HTML_FILES:\n$(HTML_FILES)"
- @echo ""
- @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
- @echo ""
- @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
- @echo ""
- @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
- @echo ""
- @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
- @echo ""
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index 65b3dcde95..865abc6f5f 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -49,9 +49,7 @@
</list>
<p>The configuration of the server is provided as an Erlang
- property list. For backwards compatibility, a configuration
- file using apache-style configuration directives is
- supported.</p>
+ property list.</p>
<p>As of <c>Inets</c> 5.0 the HTTP server is an easy to
start/stop and customize web server providing the most basic
@@ -77,16 +75,13 @@
<p>The server is configured using an Erlang property list.
For the available properties, see
<seealso marker="httpd">httpd(3)</seealso>.
- For backwards compatibility, apache-like configuration files
- are also supported.
</p>
<p>The available configuration properties are as follows:</p>
<code type="none">
httpd_service() -> {httpd, httpd()}
httpd() -> [httpd_config()]
- httpd_config() -> {file, file()} |
- {proplist_file, file()}
+ httpd_config() -> {proplist_file, file()}
{debug, debug()} |
{accept_timeout, integer()}
debug() -> disable | [debug_options()]
@@ -95,13 +90,11 @@
{disable, modules()}
modules() -> [atom()]</code>
<p>Here:</p>
- <taglist>
- <tag><c>{file, file()}</c></tag>
- <item><p>If you use an old apace-like configuration file.</p></item>
+ <taglist>
<tag><c>{proplist_file, file()}</c></tag>
<item><p>File containing an Erlang property
- list, followed by a full stop, describing the HTTP server
- configuration.</p></item>
+ list, followed by a full stop, describing the HTTP server
+ configuration.</p></item>
<tag><c>{debug, debug()}</c></tag>
<item><p>Can enable trace on all functions or only exported functions
on chosen modules.</p></item>
@@ -171,162 +164,9 @@
</section>
<section>
- <title>Htaccess - User Configurable Authentication</title>
- <marker id="htaccess"></marker>
- <p>Web server users without server administrative privileges
- that need to manage authentication of web pages that are local
- to their user can use the per-directory runtime configurable
- user-authentication scheme <c>htaccess</c>.
- It works as follows:</p>
- <list type="bulleted">
- <item>Each directory in the path to the requested asset is
- searched for an access file (default is <c>.htaccess</c>), which
- restricts the web servers rights to respond to a request.
- If an access file is found, the rules in that file is applied to the
- request.</item>
- <item>The rules in an access file apply to files in the same
- directory and in subdirectories. If there exists more than one
- access file in the path to an asset, the rules in the
- access file nearest the requested asset is applied.</item>
- <item>To change the rules that restrict the use of
- an asset, the user only needs write access
- to the directory where the asset is.</item>
- <item>All access files in the path to a requested asset are read
- once per request. This means that the load on the server
- increases when <c>htaccess</c> is used.</item>
- <item>If a directory is limited both by authentication directives
- in the HTTP server configuration file and by the <c>htaccess</c>
- files, the user must be allowed to get access to the file by both
- methods for the request to succeed.</item>
- </list>
-
- <section>
- <title>Access Files Directives</title>
- <p>In every directory under <c>DocumentRoot</c> or under an
- <c>Alias</c> a user can place an access file. An access file
- is a plain text file that specifies the restrictions to
- consider before the web server answers to a
- request. If there are more than one access file in the path
- to the requested asset, the directives in the access file in
- the directory nearest the asset is used.</p>
- <taglist>
- <tag><em>"allow"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>Allow</c> from subnet <c>subnet | from all</c></p>
- <p><em>Default:</em> <c>from all</c></p>
- <p>Same as directive <c>allow</c> for the server configuration file.</p>
- </item>
- <tag><em>"AllowOverRide"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>AllowOverRide</c> <c>all | none | Directives</c></p>
- <p><em>Default:</em> <c>none</c></p>
- <p><c>AllowOverRide</c> specifies the parameters that
- access files in subdirectories are not allowed to alter the value
- for. If the parameter is set to <c>none</c>, no further
- access files is parsed.
- </p>
- <p>If only one access file exists, setting this parameter to
- <c>none</c> can ease the burden on the server as the server
- then stops looking for access files.</p>
- </item>
- <tag><em>"AuthGroupfile"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>AuthGroupFile</c> Filename</p>
- <p><em>Default:</em> <c>none</c></p>
- <p><c>AuthGroupFile</c> indicates which file that contains the list
- of groups. The filename must contain the absolute path to the
- file. The format of the file is one group per row and
- every row contains the name of the group and the members
- of the group, separated by a space, for example:</p>
- <pre>
-GroupName: Member1 Member2 .... MemberN</pre>
- </item>
- <tag><em>"AuthName"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>AuthName</c> auth-domain</p>
- <p><em>Default:</em> <c>none</c></p>
- <p>Same as directive <c>AuthName</c> for the server
- configuration file.</p>
- </item>
- <tag><em>"AuthType"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>AuthType</c> <c>Basic</c></p>
- <p><em>Default:</em> <c>Basic</c></p>
- <p><c>AuthType</c> specifies which authentication scheme to
- be used. Only Basic Authenticating using UUEncoding of
- the password and user ID is implemented.</p>
- </item>
- <tag><em>"AuthUserFile"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>AuthUserFile</c> Filename</p>
- <p><em>Default:</em><c>none</c></p>
- <p><c>AuthUserFile</c> indicates which file that contains the list
- of users. The filename must contain the absolute path to the
- file. The username and password are not encrypted so do not
- place the file with users in a directory that is accessible
- through the web server. The format of the file is one user per row.
- Every row contains <c>UserName</c> and <c>Password</c> separated
- by a colon, for example:</p>
- <pre>
-UserName:Password
-UserName:Password</pre>
- </item>
- <tag><em>"deny"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>deny</c> from subnet <c>subnet | from all</c></p>
- <p><em>Context:</em> Limit</p>
- <p>Same as directive <c>deny</c> for the server configuration file.</p>
- </item>
- <tag><em>"Limit"</em></tag>
- <item>
- <p><em>Syntax:</em> <c><![CDATA[<Limit]]></c> RequestMethods<c>></c></p>
- <p><em>Default:</em> <c>none</c></p>
- <p><c><![CDATA[<Limit>]]></c> and <c>&lt;/Limit&gt;</c> are used to enclose
- a group of directives applying only to requests using
- the specified methods. If no request method is specified,
- all request methods are verified against the restrictions.</p>
- <p>Example:</p>
- <pre>
-&lt;Limit POST GET HEAD&gt;
- order allow deny
- require group group1
- allow from 123.145.244.5
-&lt;/Limit&gt;</pre>
- </item>
- <tag><em>"order"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>order</c> <c>allow deny | deny allow</c></p>
- <p><em>Default:</em> <c>allow deny</c></p>
- <p><c>order</c> defines if the deny or allow control is to
- be performed first.</p>
- <p>If the order is set to <c>allow deny</c>, the users
- network address is first controlled to be in the allow subset.
- If the user network address is not in the allowed subset, the user
- is denied to get the asset. If the network address is in the
- allowed subset, a second control is performed. That is,
- the user network address is not in the subset of network
- addresses to be denied as specified by parameter <c>deny</c>.</p>
- <p>If the order is set to <c>deny allow</c>, only users from networks
- specified to be in the allowed subset succeeds to request
- assets in the limited area.</p>
- </item>
- <tag><em>"require"</em></tag>
- <item>
- <p><em>Syntax:</em> <c>require</c>
- <c>group group1 group2... | user user1 user2...</c></p>
- <p><em>Default:</em> <c>none</c></p>
- <p><em>Context:</em> Limit</p>
- <p>For more information, see directive <c>require</c> in
- <seealso marker="mod_auth">mod_auth(3)</seealso>.</p>
- </item>
- </taglist>
- </section>
- </section>
-
- <section>
- <title>Dynamic Web Pages</title>
- <marker id="dynamic_we_pages"></marker>
- <p><c>Inets</c> HTTP server provides two ways of creating dynamic web
+ <title>Dynamic Web Pages</title>
+ <marker id="dynamic_we_pages"></marker>
+ <p><c>Inets</c> HTTP server provides two ways of creating dynamic web
pages, each with its own advantages and disadvantages:</p>
<taglist>
<tag><em>CGI scripts</em></tag>
@@ -393,36 +233,6 @@ http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo)</code>
see <seealso marker="mod_esi">mod_esi(3)</seealso>.</p>
</section>
- <section>
- <title>EVAL Scheme</title>
- <p>The eval scheme is straight-forward and does not mimic the
- behavior of plain CGI. An URL that calls an Erlang <c>eval</c>
- function has the following syntax:</p>
- <code type="none">
-http://your.server.org/***/Mod:Func(Arg1,...,ArgN)</code>
- <p>*** depends on how the ErlScriptAlias config
- directive has been used.</p>
- <p>The module <c>Mod</c> referred to must be found in the code
- path and data returned by the function <c>Func</c> is passed
- back to the client. Data returned from the
- function must take the form as specified in
- the CGI specification. For implementation details of the ESI
- callback function,
- see <seealso marker="mod_esi">mod_esi(3)</seealso>.</p>
- <note>
- <p>The eval scheme can seriously threaten the
- integrity of the Erlang node housing a web server, for
- example:</p>
- <code type="none">
-http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[])))</code>
- <p>This effectively closes down the Erlang node.
- Therefore, use the erl scheme instead, until this
- security breach is fixed.</p>
- <p>Today there are no good ways of solving this problem
- and therefore the eval scheme can be removed in future
- release of <c>Inets</c>.</p>
- </note>
- </section>
</section>
</section>
@@ -718,23 +528,7 @@ start() ->
<item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
</section>
-
- <section>
- <title>mod_htaccess - User Configurable Access</title>
- <p>This module provides per-directory user configurable access
- control.</p>
- <p>Uses the following Erlang Web Server API interaction data:
- </p>
- <list type="bulleted">
- <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
- </list>
- <p>Exports the following Erlang Web Server API interaction data:
- </p>
- <taglist>
- <tag><c>{remote_user_name, User}</c></tag>
- <item>The username used for authentication.</item>
- </taglist>
- </section>
+
<section>
<title>mod_log - Logging Using Text Files.</title>
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 6d3547f4fe..7fc524ff7f 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -31,12 +31,13 @@
</header>
<module since="OTP R15B01">http_uri</module>
- <modulesummary>URI utility module</modulesummary>
+ <modulesummary>Old URI utility module, use uri_string instead</modulesummary>
<description>
- <p>This module provides utility functions for working with URIs,
- according to
- <url href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.</p>
+ <p>This module is deprecated since OTP 23.
+ Use the module <seealso marker="stdlib:uri_string">uri_string</seealso> to properly handle URIs,
+ this is the recommend module since OTP 21.
+ </p>
</description>
<section>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 987f0c3cf4..19dd0c0abf 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -68,15 +68,13 @@
<marker id="props_file"></marker>
<p><em>File Properties</em></p>
- <p>When the web server is started
- at application start time, the properties are to be fetched from a
- configuration file that can consist of a regular Erlang property
- list, that is, <c>[{Option, Value}]</c>, where <c> Option = property()
- </c> and <c>Value = term()</c>, followed by a full stop, or for
- backwards compatibility, an Apache-like configuration file. If the
- web server is started dynamically at runtime,
- a file can still be specified but also the complete property
- list.</p>
+ <p>When the web server is started at application start time, the
+ properties are to be fetched from a configuration file that can
+ consist of a regular Erlang property list, that is, <c>[{Option,
+ Value}]</c>, where <c> Option = property() </c> and <c>Value =
+ term()</c>, followed by a full stop. If the web server is started
+ dynamically at runtime, a file can still be specified but also the
+ complete property list.</p>
<taglist>
<tag><marker id="prop_proplist_file"></marker>{proplist_file, path()}</tag>
@@ -87,34 +85,9 @@
properties.</p>
</item>
- <tag><marker id="prop_file"></marker>{file, path()}</tag>
- <item>
- <p>If this property is defined, <c>Inets</c> expects to find all
- other properties defined in this file, which uses Apache-like
- syntax. The file must include all properties listed
- under mandatory properties. The Apache-like syntax is the property,
- written as one word where each new word begins with a capital,
- followed by a white-space, followed by the value, followed by a
- new line.</p>
- <p>Example:</p>
- <code>
-{server_root, "/urs/local/www"} -> ServerRoot /usr/local/www</code>
-
- <p>A few exceptions are documented
- for each property that behaves differently,
- and the special cases <c>{directory, {path(), PropertyList}}</c>
- and <c>{security_directory, {Dir, PropertyList}}</c>, are represented
- as:</p>
- <pre>
- <![CDATA[
-<Directory Dir>
- <Properties handled as described above>
-</Directory>
- ]]></pre>
- </item>
</taglist>
<note>
- <p>The properties <c>proplist_file</c> and <c>file</c> are mutually exclusive. Also newer properties may not be supported as Apache-like options, this is a legacy feature.</p>
+ <p>Note support for legacy configuration file with Apache syntax is dropped in OTP-23.</p>
</note>
<marker id="props_mand"></marker>
@@ -152,8 +125,7 @@
<taglist>
<tag><marker id="prop_bind_address"></marker>{bind_address, ip_address() | hostname() | any}</tag>
<item>
- <p>Default is <c>any</c>. <c>any</c> is denoted <em>*</em>
- in the Apache-like configuration file.</p>
+ <p>Default is <c>any</c></p>
</item>
<tag><marker id="profile"></marker>{profile, atom()}</tag>
@@ -303,7 +275,7 @@
1590. File suffixes are mapped to MIME types before file delivery.
The mapping between file suffixes and MIME types can be specified
as an Apache-like file or directly in the property list. Such
- a file can look like the follwoing:</p>
+ a file can look like the follwoing:</p>
<pre>
# MIME type Extension
text/html html htm
@@ -519,19 +491,8 @@ Transport: TLS
<code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}}</code>
<p>Access to http://your.server.org/~bob/foo.gif would refer to
- the file /home/bob/public/foo.gif.
-
- In an Apache-like configuration file, <c>Re</c> is separated
- from <c>Replacement</c> with one single space, and as expected
- backslashes do not need to be backslash escaped, the
- same example would become:</p>
+ the file /home/bob/public/foo.gif. </p>
- <code>ReWrite ^/[~]([^/]+)(.*)$ /home/\1/public\2</code>
-
- <p>Beware of trailing space in <c>Replacement</c> to be used.
- If you must have a space in <c>Re</c>, use, for example, the character
- encoding <c>\040</c>, see
- <seealso marker="stdlib:re">re(3)</seealso>.</p>
</item>
<tag><marker id="prop_dir_idx"></marker>{directory_index, [string()]}</tag>
@@ -663,16 +624,8 @@ Transport: TLS
<c>mod_esi:deliver/2</c>. Default is <c>15</c>. This is only relevant
for scripts that use the erl scheme.</p>
</item>
-
- <tag><marker id="prop_esi_timeout"></marker>{eval_script_alias, {URLPath, [AllowedModule]}}</tag>
- <item>
- <p><c>URLPath = string()</c> and <c>AllowedModule = atom()</c>.
- Same as <c>erl_script_alias</c> but for scripts
- using the eval scheme. This is only supported
- for backwards compatibility. The eval scheme is deprecated.</p>
- </item>
</taglist>
-
+
<marker id="props_log"></marker>
<p><em>Log Properties - Requires mod_log</em></p>
<taglist>
@@ -799,9 +752,7 @@ Transport: TLS
<p>Sets the type of authentication database that is used for the
directory. The key difference between the different methods is
that dynamic data can be saved when Mnesia and Dets
- are used.
- This property is called <c>AuthDbType</c> in the Apache-like
- configuration files.</p>
+ are used.</p>
</item>
<tag><marker id="prop_auth_user_file"></marker>{auth_user_file, path()}</tag>
@@ -881,20 +832,6 @@ Transport: TLS
</taglist>
- <marker id="props_htaccess"></marker>
- <p><em>Htaccess Authentication Properties - Requires mod_htaccess</em></p>
- <taglist>
- <tag><marker id="prop_access_files"></marker>{access_files, [path()]}</tag>
- <item>
- <p>Specifies the filenames that are used for
- access files. When a request comes, every directory in the path
- to the requested asset are searched after files with the
- names specified by this parameter. If such a file is found, the
- file is parsed and the restrictions specified in it are
- applied to the request.</p>
- </item>
- </taglist>
-
<marker id="props_sec"></marker>
<p><em>Security Properties - Requires mod_security</em></p>
@@ -1179,30 +1116,7 @@ Transport: TLS
closing the connection.</p>
</desc>
</func>
-
- <func>
- <name since="">Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason}</name>
- <fsummary>Converts a line in an Apache-like config
- file to an <c>{Option, Value}</c> tuple.</fsummary>
- <type>
- <v>Line = string()</v>
- <v>AccIn = [{Option, Value}]</v>
- <v>AccOut = [{Option, Value}]</v>
- <v>Option = property()</v>
- <v>Value = term() </v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Converts a line in an Apache-like
- configuration file to an <c>{Option, Value}</c> tuple. Some
- more complex configuration options, such as <c>directory</c>
- and <c>security_directory</c>, create an
- accumulator. This function only needs clauses for the
- options implemented by this particular callback module.
- </p>
- </desc>
- </func>
-
+
<func>
<name since="">Module:remove(ConfigDB) -> ok | {error, Reason} </name>
<fsummary>Callback function that is called when the web server is closed.</fsummary>
diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml
index e0f947f860..cd22b89314 100644
--- a/lib/inets/doc/src/httpd_util.xml
+++ b/lib/inets/doc/src/httpd_util.xml
@@ -171,9 +171,7 @@
<desc>
<p><c>lookup_mime</c> returns the MIME type associated with a
specific file suffix as specified in the file <c>mime.types</c>
- (located in the
- <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">
- config directory</path>).</p>
+ (located in the config directory).</p>
</desc>
</func>
@@ -191,9 +189,7 @@
<desc>
<p><c>lookup_mime_default</c> returns the MIME type associated
with a specific file suffix as specified in the
- <c>mime.types</c> file (located in the
- <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">
- config directory</path>).
+ <c>mime.types</c> file (located in the config directory).
If no appropriate association is found, the value of <c>DefaultType</c> is
returned.</p>
</desc>
diff --git a/lib/inets/doc/src/introduction.xml b/lib/inets/doc/src/introduction.xml
index faf911f188..a5037b02dd 100644
--- a/lib/inets/doc/src/introduction.xml
+++ b/lib/inets/doc/src/introduction.xml
@@ -37,7 +37,7 @@
<p><c>Inets</c> is a container for Internet clients and servers
including the following:</p>
<list type="bulleted">
- <item>An <term id="HTTP"></term> client and server</item>
+ <item>An HTTP client and server</item>
</list>
<p>The HTTP client and server are HTTP 1.1 compliant as
defined in
diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml
index b9c8ed674c..eca680cdb6 100644
--- a/lib/inets/doc/src/part.xml
+++ b/lib/inets/doc/src/part.xml
@@ -33,7 +33,7 @@
<p>The <c>Inets</c> application provides a set of
Internet-related services as follows:</p>
<list type="bulleted">
- <item>An <term id="HTTP"></term> client and server</item>
+ <item>An HTTP client and server</item>
</list>
<p>The HTTP client and server are HTTP 1.1 compliant as
defined in
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index b1c5cf13bb..3f91ae062c 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1637,7 +1637,7 @@ host_header(#http_request_h{host = Host}, _) ->
%% Handles headers_as_is
host_header(_, URI) ->
- {ok, {_, _, Host, _, _, _}} = http_uri:parse(URI),
+ #{host := Host} = uri_string:parse(URI),
Host.
tls_upgrade(#state{status =
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 0dc0483fa9..ba561100a1 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -472,10 +472,10 @@ handle_call(which_cookies, _, #state{cookie_db = CookieDb} = State) ->
handle_call({which_cookies, Url, Options}, _,
#state{cookie_db = CookieDb} = State) ->
?hcrv("which cookies", [{url, Url}, {options, Options}]),
- case uri_parse(Url, Options) of
- {ok, {Scheme, _, Host, Port, Path, _}} ->
+ case uri_parse(Url) of
+ {ok, {Scheme, Host, Port, Path}} ->
CookieHeaders =
- httpc_cookie:header(CookieDb, Scheme, {Host, Port}, Path),
+ httpc_cookie:header(CookieDb, erlang:list_to_existing_atom(Scheme), {Host, Port}, Path),
{reply, CookieHeaders, State};
{error, _} = ERROR ->
{reply, ERROR, State}
@@ -948,14 +948,31 @@ make_db_name(ProfileName, Post) ->
%%--------------------------------------------------------------------------
%% These functions is just simple wrappers to parse specifically HTTP URIs
%%--------------------------------------------------------------------------
+uri_parse(URI) ->
+ case uri_string:parse(uri_string:normalize(URI)) of
+ #{scheme := Scheme,
+ host := Host,
+ port := Port,
+ path := Path} ->
+ {ok, {Scheme, Host, Port, Path}};
+ #{scheme := Scheme,
+ host := Host,
+ path := Path} ->
+ {ok, {Scheme, Host, scheme_default_port(Scheme), Path}};
+ Other ->
+ {error, maybe_error(Other)}
+ end.
-scheme_defaults() ->
- [{http, 80}, {https, 443}].
-
-uri_parse(URI, Opts) ->
- http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]).
-
+maybe_error({error, Atom, Term}) ->
+ {Atom, Term};
+maybe_error(Other) ->
+ {unexpected, Other}.
+scheme_default_port("http") ->
+ 80;
+scheme_default_port("https") ->
+ 443.
+
%%--------------------------------------------------------------------------
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 6805b0293d..8b3eaf1930 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -61,6 +61,14 @@
scheme_defaults/0,
encode/1, decode/1]).
+
+-deprecated({parse, 1, "use uri_string functions instead"}).
+-deprecated({parse, 2, "use uri_string functions instead"}).
+-deprecated({encode, 1, "use uri_string functions instead"}).
+-deprecated({decode, 1, "use uri_string functions instead"}).
+-deprecated({scheme_defaults, 0, "use uri_string functions instead"}).
+
+
-export_type([uri/0,
user_info/0,
scheme/0, default_scheme_port_number/0,
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 9848fd4b35..da9549406f 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -74,14 +74,12 @@ MODULES = \
mod_auth_dets \
mod_auth_mnesia \
mod_auth_server \
- mod_browser \
mod_cgi \
mod_dir \
mod_disk_log \
mod_esi \
mod_get \
mod_head \
- mod_htaccess \
mod_log \
mod_range \
mod_responsecontrol \
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index f4b53ce129..ae35f109ed 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -41,16 +41,19 @@
reload_config/2,
info/1,
info/2,
- info/3
+ info/3,
+ info/4
]).
+-deprecated({parse_query, 1,
+ "use uri_string:dissect_query/1 instead"}).
+
%%%========================================================================
%%% API
%%%========================================================================
parse_query(String) ->
- SplitString = re:split(String,"[&;]", [{return, list}]),
- foreach(SplitString).
+ uri_string:dissect_query(String).
reload_config(Config = [Value| _], Mode) when is_tuple(Value) ->
do_reload_config(Config, Mode);
@@ -58,15 +61,7 @@ reload_config(ConfigFile, Mode) ->
try file:consult(ConfigFile) of
{ok, [PropList]} ->
%% Erlang terms format
- do_reload_config(PropList, Mode);
- {error, _ } ->
- %% Apache format
- case httpd_conf:load(ConfigFile) of
- {ok, ConfigList} ->
- do_reload_config(ConfigList, Mode);
- Error ->
- Error
- end
+ do_reload_config(PropList, Mode)
catch
exit:_ ->
throw({error, {could_not_consult_proplist_file, ConfigFile}})
@@ -260,18 +255,6 @@ unblock(Addr, Port, Profile) when is_integer(Port) ->
{error,not_started}
end.
-foreach([]) ->
- [];
-foreach([KeyValue|Rest]) ->
- Plus2Space = re:replace(KeyValue,"[\+]"," ", [{return,list}, global]),
- case re:split(Plus2Space,"=", [{return, list}]) of
- [Key|Value] ->
- [{http_uri:decode(Key),
- http_uri:decode(lists:flatten(Value))}|foreach(Rest)];
- _ ->
- foreach(Rest)
- end.
-
make_name(Addr, Port, Profile) ->
httpd_util:make_name("httpd", Addr, Port, Profile).
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index d42fc7c607..e85455178a 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -21,321 +21,35 @@
-module(httpd_conf).
%% Application internal API
--export([load/1, load/2, load_mime_types/1, store/1, store/2,
+-export([load_mime_types/1, store/1, store/2,
remove/1, remove_all/1, get_config/3, get_config/4,
lookup_socket_type/1,
lookup/2, lookup/3, lookup/4,
validate_properties/1, white_space_clean/1]).
-%% Deprecated
--export([is_directory/1, is_file/1, make_integer/1, clean/1,
- custom_clean/3, check_enum/2]).
-
--deprecated({is_directory, 1, next_major_release}).
--deprecated({is_file, 1, next_major_release}).
--deprecated({make_integer, 1, next_major_release}).
--deprecated({clean, 1, next_major_release}).
--deprecated({custom_clean, 3, next_major_release}).
--deprecated({check_enum, 2, next_major_release}).
-
-define(VMODULE,"CONF").
-include("httpd_internal.hrl").
-include("httpd.hrl").
-include_lib("inets/src/http_lib/http_internal.hrl").
+%% Removed functions
+
+-removed([{check_enum,2,"use lists:member/2 instead"},
+ {clean,1,"use sting:strip/1 instead or possibly the re module"},
+ {custom_clean,3,"use sting:strip/1 instead or possibly the re module"},
+ {is_directory,1,"use filelib:is_dir/1 instead"},
+ {is_file,1,"use filelib:is_file/1 instead"},
+ {make_integer,1,"use erlang:list_to_integer/1 instead"}]).
+
%%%=========================================================================
%%% Application internal API
%%%=========================================================================
-%% The configuration data is handled in three (3) phases:
-%% 1. Parse the config file and put all directives into a key-vale
-%% tuple list (load/1).
-%% 2. Traverse the key-value tuple list store it into an ETS table.
+%% The configuration data is handled in three (2) phases:
+%% 1. Traverse the key-value tuple list store it into an ETS table.
%% Directives depending on other directives are taken care of here
%% (store/1).
%% 3. Traverse the ETS table and do a complete clean-up (remove/1).
-%% Phase 1: Load
-load(ConfigFile) ->
- case read_config_file(ConfigFile) of
- {ok, Config} ->
- case bootstrap(Config) of
- {error, Reason} ->
- {error, Reason};
- {ok, Modules} ->
- load_config(Config, lists:append(Modules, [?MODULE]))
- end;
- {error, Reason} ->
- {error, ?NICE("Error while reading config file: "++Reason)}
- end.
-
-load(eof, []) ->
- eof;
-
-load("MaxHeaderSize " ++ MaxHeaderSize, []) ->
- case make_integer(MaxHeaderSize) of
- {ok, Integer} ->
- {ok, [], {max_header_size,Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(MaxHeaderSize)++
- " is an invalid number of MaxHeaderSize")}
- end;
-
-load("MaxURISize " ++ MaxHeaderSize, []) ->
- case make_integer(MaxHeaderSize) of
- {ok, Integer} ->
- {ok, [], {max_uri_size, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(MaxHeaderSize)++
- " is an invalid number of MaxHeaderSize")}
- end;
-
-load("MaxContentLength " ++ Max, []) ->
- case make_integer(Max) of
- {ok, Integer} ->
- {ok, [], {max_content_length, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(Max) ++
- " is an invalid number of MaxContentLength")}
- end;
-
-load("ServerName " ++ ServerName, []) ->
- {ok,[], {server_name, string:strip(ServerName)}};
-
-load("ServerTokens " ++ ServerTokens, []) ->
- %% These are the valid *plain* server tokens:
- %% none, prod, major, minor, minimum, os, full
- %% It can also be a "private" server token: private:<any string>
- case string:tokens(ServerTokens, [$:]) of
- ["private", Private] ->
- {ok,[], {server_tokens, string:strip(Private)}};
- [TokStr] ->
- Tok = list_to_atom(string:strip(TokStr)),
- case lists:member(Tok, [none, prod, major, minor, minimum, os, full]) of
- true ->
- {ok,[], {server_tokens, Tok}};
- false ->
- {error, ?NICE(string:strip(ServerTokens) ++
- " is an invalid ServerTokens")}
- end;
- _ ->
- {error, ?NICE(string:strip(ServerTokens) ++ " is an invalid ServerTokens")}
- end;
-
-load("SocketType " ++ SocketType, []) ->
- %% ssl is the same as HTTP_DEFAULT_SSL_KIND
- %% essl is the pure Erlang-based ssl (the "new" ssl)
- case check_enum(string:strip(SocketType), ["ssl", "essl", "ip_comm"]) of
- {ok, ValidSocketType} ->
- {ok, [], {socket_type, ValidSocketType}};
- {error,_} ->
- {error, ?NICE(string:strip(SocketType) ++ " is an invalid SocketType")}
- end;
-
-load("Port " ++ Port, []) ->
- case make_integer(Port) of
- {ok, Integer} ->
- {ok, [], {port, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(Port)++" is an invalid Port")}
- end;
-
-load("BindAddress " ++ Address0, []) ->
- %% If an ipv6 address is provided in URL-syntax strip the
- %% url specific part e.i. "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]"
- %% -> "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"
-
- try
- begin
- {Address, IpFamily} =
- case string:tokens(Address0, [$|]) of
- [Address1] ->
- {clean_address(Address1), inet};
- [Address1, IpFamilyStr] ->
- {clean_address(Address1), make_ipfamily(IpFamilyStr)};
- _Bad ->
- throw({error, {bad_bind_address, Address0}})
- end,
-
- case Address of
- "*" ->
- {ok, [], [{bind_address, any}, {ipfamily, IpFamily}]};
- _ ->
- case httpd_util:ip_address(Address, IpFamily) of
- {ok, IPAddr} ->
- Entries = [{bind_address, IPAddr},
- {ipfamily, IpFamily}],
- {ok, [], Entries};
- {error, _} ->
- {error, ?NICE(Address ++ " is an invalid address")}
- end
- end
- end
- catch
- throw:{error, {bad_bind_address, _}} ->
- {error, ?NICE(Address0 ++ " is an invalid address")};
- throw:{error, {bad_ipfamily, _}} ->
- {error, ?NICE(Address0 ++ " has an invalid ipfamily")}
- end;
-
-load("KeepAlive " ++ OnorOff, []) ->
- case list_to_atom(string:strip(OnorOff)) of
- off ->
- {ok, [], {keep_alive, false}};
- _ ->
- {ok, [], {keep_alive, true}}
- end;
-
-load("MaxKeepAliveRequests " ++ MaxRequests, []) ->
- case make_integer(MaxRequests) of
- {ok, Integer} ->
- {ok, [], {max_keep_alive_request, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(MaxRequests) ++
- " is an invalid MaxKeepAliveRequests")}
- end;
-
-%% This clause is kept for backwards compatibility
-load("MaxKeepAliveRequest " ++ MaxRequests, []) ->
- case make_integer(MaxRequests) of
- {ok, Integer} ->
- {ok, [], {max_keep_alive_request, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(MaxRequests) ++
- " is an invalid MaxKeepAliveRequest")}
- end;
-
-load("KeepAliveTimeout " ++ Timeout, []) ->
- case make_integer(Timeout) of
- {ok, Integer} ->
- {ok, [], {keep_alive_timeout, Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(Timeout)++" is an invalid KeepAliveTimeout")}
- end;
-
-load("Modules " ++ Modules, []) ->
- ModuleList = re:split(Modules," ", [{return, list}]),
- {ok, [], {modules,[list_to_atom(X) || X <- ModuleList]}};
-
-load("ServerAdmin " ++ ServerAdmin, []) ->
- {ok, [], {server_admin,string:strip(ServerAdmin)}};
-
-load("ServerRoot " ++ ServerRoot, []) ->
- case is_directory(string:strip(ServerRoot)) of
- {ok, Directory} ->
- {ok, [], [{server_root,string:strip(Directory,right,$/)}]};
- {error, _} ->
- {error, ?NICE(string:strip(ServerRoot)++" is an invalid ServerRoot")}
- end;
-
-load("MimeTypes " ++ MimeTypes, []) ->
- case load_mime_types(white_space_clean(MimeTypes)) of
- {ok, MimeTypesList} ->
- {ok, [], [{mime_types, MimeTypesList}]};
- {error, Reason} ->
- {error, Reason}
- end;
-
-load("MaxClients " ++ MaxClients, []) ->
- case make_integer(MaxClients) of
- {ok, Integer} ->
- {ok, [], {max_clients,Integer}};
- {error, _} ->
- {error, ?NICE(string:strip(MaxClients) ++
- " is an invalid number of MaxClients")}
- end;
-load("DocumentRoot " ++ DocumentRoot,[]) ->
- case is_directory(string:strip(DocumentRoot)) of
- {ok, Directory} ->
- {ok, [], {document_root,string:strip(Directory,right,$/)}};
- {error, _} ->
- {error, ?NICE(string:strip(DocumentRoot)++" is an invalid DocumentRoot")}
- end;
-load("DefaultType " ++ DefaultType, []) ->
- {ok, [], {default_type,string:strip(DefaultType)}};
-load("SSLCertificateFile " ++ SSLCertificateFile, []) ->
- case is_file(string:strip(SSLCertificateFile)) of
- {ok, File} ->
- {ok, [], {ssl_certificate_file,File}};
- {error, _} ->
- {error, ?NICE(string:strip(SSLCertificateFile)++
- " is an invalid SSLCertificateFile")}
- end;
-load("SSLLogLevel " ++ SSLLogAlert, []) ->
- case SSLLogAlert of
- "none" ->
- {ok, [], {ssl_log_alert, false}};
- _ ->
- {ok, [], {ssl_log_alert, true}}
- end;
-load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) ->
- case is_file(string:strip(SSLCertificateKeyFile)) of
- {ok, File} ->
- {ok, [], {ssl_certificate_key_file,File}};
- {error, _} ->
- {error, ?NICE(string:strip(SSLCertificateKeyFile)++
- " is an invalid SSLCertificateKeyFile")}
- end;
-load("SSLVerifyClient " ++ SSLVerifyClient, []) ->
- case make_integer(string:strip(SSLVerifyClient)) of
- {ok, Integer} when (Integer >=0) andalso (Integer =< 2) ->
- {ok, [], {ssl_verify_client,Integer}};
- {ok, _Integer} ->
- {error,?NICE(string:strip(SSLVerifyClient) ++
- " is an invalid SSLVerifyClient")};
- {error, nomatch} ->
- {error,?NICE(string:strip(SSLVerifyClient) ++
- " is an invalid SSLVerifyClient")}
- end;
-load("SSLVerifyDepth " ++ SSLVerifyDepth, []) ->
- case make_integer(string:strip(SSLVerifyDepth)) of
- {ok, Integer} when Integer > 0 ->
- {ok, [], {ssl_verify_client_depth,Integer}};
- {ok, _Integer} ->
- {error,?NICE(string:strip(SSLVerifyDepth) ++
- " is an invalid SSLVerifyDepth")};
- {error, nomatch} ->
- {error,?NICE(string:strip(SSLVerifyDepth) ++
- " is an invalid SSLVerifyDepth")}
- end;
-load("SSLCiphers " ++ SSLCiphers, []) ->
- {ok, [], {ssl_ciphers, string:strip(SSLCiphers)}};
-load("SSLCACertificateFile " ++ SSLCACertificateFile, []) ->
- case is_file(string:strip(SSLCACertificateFile)) of
- {ok, File} ->
- {ok, [], {ssl_ca_certificate_file,File}};
- {error, _} ->
- {error, ?NICE(string:strip(SSLCACertificateFile)++
- " is an invalid SSLCACertificateFile")}
- end;
-load("SSLPasswordCallbackModule " ++ SSLPasswordCallbackModule, []) ->
- {ok, [], {ssl_password_callback_module,
- list_to_atom(string:strip(SSLPasswordCallbackModule))}};
-load("SSLPasswordCallbackFunction " ++ SSLPasswordCallbackFunction, []) ->
- {ok, [], {ssl_password_callback_function,
- list_to_atom(string:strip(SSLPasswordCallbackFunction))}};
-load("SSLPasswordCallbackArguments " ++ SSLPasswordCallbackArguments, []) ->
- {ok, [], {ssl_password_callback_arguments,
- SSLPasswordCallbackArguments}};
-load("DisableChunkedTransferEncodingSend " ++ TrueOrFalse, []) ->
- case list_to_atom(string:strip(TrueOrFalse)) of
- true ->
- {ok, [], {disable_chunked_transfer_encoding_send, true}};
- _ ->
- {ok, [], {disable_chunked_transfer_encoding_send, false}}
- end;
-load("LogFormat " ++ LogFormat, []) ->
- {ok,[],{log_format, list_to_atom(string:strip(LogFormat))}};
-load("ErrorLogFormat " ++ LogFormat, []) ->
- {ok,[],{error_log_format, list_to_atom(string:strip(LogFormat))}}.
-
-
-clean_address(Addr) ->
- string:strip(string:strip(string:strip(Addr), left, $[), right, $]).
-
-
-make_ipfamily(IpFamilyStr) ->
- validate_ipfamily(list_to_atom(IpFamilyStr)).
-
validate_ipfamily(inet) ->
inet;
validate_ipfamily(inet6) ->
@@ -861,139 +575,9 @@ ssl_config(ConfigDB) ->
ssl_ca_certificate_file(ConfigDB) ++
ssl_log_level(ConfigDB).
-
-
%%%========================================================================
%%% Internal functions
%%%========================================================================
-%%% Phase 1 Load:
-bootstrap([]) ->
- {ok, ?DEFAULT_MODS};
-bootstrap([Line|Config]) ->
- case Line of
- "Modules " ++ Modules ->
- ModuleList = re:split(Modules," ", [{return, list}]),
- TheMods = [list_to_atom(X) || X <- ModuleList],
- case verify_modules(TheMods) of
- ok ->
- {ok, TheMods};
- {error, Reason} ->
- {error, Reason}
- end;
- _ ->
- bootstrap(Config)
- end.
-
-load_config(Config, Modules) ->
- %% Create default contexts for all modules
- Contexts = lists:duplicate(length(Modules), []),
- load_config(Config, Modules, Contexts, []).
-
-load_config([], _Modules, _Contexts, ConfigList) ->
- {ok, ConfigList};
-
-load_config([Line|Config], Modules, Contexts, ConfigList) ->
- case load_traverse(Line, Contexts, Modules, [], ConfigList, no) of
- {ok, NewContexts, NewConfigList} ->
- load_config(Config, Modules, NewContexts, NewConfigList);
- {error, Reason} ->
- {error, Reason}
- end.
-
-
-%% This loads the config file into each module specified by Modules
-%% Each module has its own context that is passed to and (optionally)
-%% returned by the modules load function. The module can also return
-%% a ConfigEntry, which will be added to the global configuration
-%% list.
-%% All configuration directives are guaranteed to be passed to all
-%% modules. Each module only implements the function clauses of
-%% the load function for the configuration directives it supports,
-%% it's ok if an apply returns {'EXIT', {function_clause, ..}}.
-load_traverse(Line, [], [], _NewContexts, _ConfigList, no) ->
- {error, ?NICE("Configuration directive not recognized: "++Line)};
-load_traverse(_Line, [], [], NewContexts, ConfigList, yes) ->
- {ok, lists:reverse(NewContexts), ConfigList};
-load_traverse(Line, [Context|Contexts], [Module|Modules], NewContexts,
- ConfigList, State) ->
- case catch apply(Module, load, [Line, Context]) of
- {'EXIT', {function_clause, _FC}} ->
- load_traverse(Line, Contexts, Modules,
- [Context|NewContexts], ConfigList, State);
-
- {'EXIT', {undef, _}} ->
- load_traverse(Line, Contexts, Modules,
- [Context|NewContexts], ConfigList, yes);
-
- {'EXIT', Reason} ->
- error_logger:error_report({'EXIT', Reason}),
- load_traverse(Line, Contexts, Modules,
- [Context|NewContexts], ConfigList, State);
-
- ok ->
- load_traverse(Line, Contexts, Modules,
- [Context|NewContexts], ConfigList, yes);
-
- {ok, NewContext} ->
- load_traverse(Line, Contexts, Modules,
- [NewContext|NewContexts], ConfigList, yes);
-
- {ok, NewContext, ConfigEntry} when is_tuple(ConfigEntry) ->
- load_traverse(Line, Contexts,
- Modules, [NewContext|NewContexts],
- [ConfigEntry|ConfigList], yes);
-
- {ok, NewContext, ConfigEntry} when is_list(ConfigEntry) ->
- load_traverse(Line, Contexts, Modules, [NewContext|NewContexts],
- lists:append(ConfigEntry, ConfigList), yes);
-
- {error, Reason} ->
- {error, Reason}
- end.
-
-%% Verifies that all specified modules are available.
-verify_modules([]) ->
- ok;
-verify_modules([Mod|Rest]) ->
- case code:which(Mod) of
- non_existing ->
- {error, ?NICE(string:strip(atom_to_list(Mod), right, $\n) ++" does not exist")};
- _Path ->
- verify_modules(Rest)
- end.
-
-%% Reads the entire configuration file and returns list of strings or
-%% and error.
-read_config_file(FileName) ->
- case file:open(FileName, [read]) of
- {ok, Stream} ->
- read_config_file(Stream, []);
- {error, _Reason} ->
- {error, ?NICE("Cannot open "++FileName)}
- end.
-read_config_file(Stream, SoFar) ->
- case io:get_line(Stream, []) of
- eof ->
- file:close(Stream),
- {ok, lists:reverse(SoFar)};
- {error, Reason} ->
- file:close(Stream),
- {error, Reason};
- [$#|_Rest] ->
- %% Ignore commented lines for efficiency later ..
- read_config_file(Stream, SoFar);
- Line ->
- NewLine = re:replace(white_space_clean(Line),
- "[\t\r\f ]"," ", [{return,list}, global]),
- case NewLine of
- [] ->
- %% Also ignore empty lines ..
- read_config_file(Stream, SoFar);
- _Other ->
- read_config_file(Stream, [NewLine|SoFar])
- end
- end.
-
parse_mime_types(Stream,MimeTypesList) ->
Line=
case io:get_line(Stream,'') of
@@ -1191,63 +775,7 @@ validate_logger([{error, Domain}]) when is_atom(Domain) ->
validate_logger(List) ->
throw({logger, List}).
-%%%=========================================================================
-%%% Deprecated remove in 19
-%%%=========================================================================
-is_directory(Directory) ->
- case file:read_file_info(Directory) of
- {ok,FileInfo} ->
- #file_info{type = Type, access = Access} = FileInfo,
- is_directory(Type,Access,FileInfo,Directory);
- {error,Reason} ->
- {error,Reason}
- end.
-is_directory(directory,read,_FileInfo,Directory) ->
- {ok,Directory};
-is_directory(directory,read_write,_FileInfo,Directory) ->
- {ok,Directory};
-is_directory(_Type,_Access,FileInfo,_Directory) ->
- {error,FileInfo}.
-
-is_file(File) ->
- case file:read_file_info(File) of
- {ok,FileInfo} ->
- #file_info{type = Type, access = Access} = FileInfo,
- is_file(Type,Access,FileInfo,File);
- {error,Reason} ->
- {error,Reason}
- end.
-is_file(regular,read,_FileInfo,File) ->
- {ok,File};
-is_file(regular,read_write,_FileInfo,File) ->
- {ok,File};
-is_file(_Type,_Access,FileInfo,_File) ->
- {error,FileInfo}.
-
-make_integer(String) ->
- case re:run(string:strip(String),"[0-9]+", [{capture, none}]) of
- match ->
- {ok, list_to_integer(string:strip(String))};
- nomatch ->
- {error, nomatch}
- end.
-
-clean(String) ->
- re:replace(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$","",
- [{return,list}, global]).
-
-custom_clean(String,MoreBefore,MoreAfter) ->
- re:replace(String,
- "^[ \t\n\r\f"++MoreBefore++
- "]*|[ \t\n\r\f"++MoreAfter++"]*\$","",
- [{return,list}, global]).
-check_enum(_Enum,[]) ->
- {error, not_valid};
-check_enum(Enum,[Enum|_Rest]) ->
- {ok, list_to_atom(Enum)};
-check_enum(Enum, [_NotValid|Rest]) ->
- check_enum(Enum, Rest).
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 3c25ca336f..78b781aa96 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -19,13 +19,27 @@
%%
%%
-module(httpd_example).
--export([print/1]).
--export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2,new_status_and_location/2]).
--export([newformat/3, post_chunked/3, post_204/3, ignore_invalid_header/3]).
-%% These are used by the inets test-suite
--export([delay/1, chunk_timeout/3, get_chunks/3]).
+-export([print/3,
+ get/3,
+ put/3,
+ post/3,
+ yahoo/3,
+ test1/3,
+ get_bin/3,
+ peer/3,
+ new_status_and_location/3,
+ newformat/3,
+ post_chunked/3,
+ post_204/3,
+ ignore_invalid_header/3,
+ delay/3,
+ chunk_timeout/3,
+ get_chunks/3]).
+%% ------------------------------------------------------
+print(SessionID, _Env, Input) ->
+ mod_esi:deliver(SessionID, print(Input)).
print(String) ->
[header(),
@@ -33,7 +47,11 @@ print(String) ->
String++"\n",
footer()].
-test1(Env, []) ->
+%% ------------------------------------------------------
+test1(SessionID, Env, _Input) ->
+ mod_esi:deliver(SessionID, test1(Env)).
+
+test1(Env) ->
io:format("Env:~p~n",[Env]),
["<html>",
"<head>",
@@ -44,9 +62,10 @@ test1(Env, []) ->
"<h2>Stuff</h2>",
"</body>",
"</html>"].
-
-
-get(_Env,[]) ->
+%% ------------------------------------------------------
+get(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, do_get(Env, Input)).
+do_get(_Env,[]) ->
[header(),
top("GET Example"),
"<FORM ACTION=\"/cgi-bin/erl/httpd_example:get\" METHOD=GET>
@@ -55,24 +74,35 @@ get(_Env,[]) ->
<INPUT TYPE=\"submit\"><BR>
</FORM>" ++ "\n",
footer()];
-
-get(Env,Input) ->
+do_get(Env,Input) ->
default(Env,Input).
+%% ------------------------------------------------------
+put(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, do_put(Env, Input)).
-put(Env,{Input,_Body}) ->
+do_put(Env,{Input,_Body}) ->
default(Env,Input);
-put(Env,Input) ->
+do_put(Env,Input) ->
default(Env,Input).
+%% ------------------------------------------------------
+get_bin(SessionID, Env, Input) ->
+ Header = header(),
+ IoData = get_bin(Env, Input),
+ Size = erlang:iolist_size(IoData),
+ mod_esi:deliver(SessionID, ["Content-Length:" ++ erlang:integer_to_list(Size) ++ "\r\n",
+ Header, IoData]).
get_bin(_Env,_Input) ->
- [list_to_binary(header()),
- list_to_binary(top("GET Example")),
+ [list_to_binary(top("GET Example")),
list_to_binary("<FORM ACTION=\"/cgi-bin/erl/httpd_example:get\" METHOD=GET>
<B>Input:</B> <INPUT TYPE=\"text\" NAME=\"input1\">
<INPUT TYPE=\"text\" NAME=\"input2\">
<INPUT TYPE=\"submit\"><BR>
</FORM>" ++ "\n"),
list_to_binary(footer())].
+%% ------------------------------------------------------
+post(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, post(Env, Input)).
post(_Env,[]) ->
[header(),
@@ -86,21 +116,22 @@ post(_Env,[]) ->
post(Env,Input) ->
default(Env,Input).
+%% ------------------------------------------------------
+yahoo(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, yahoo(Env, Input)).
yahoo(_Env,_Input) ->
"Location: http://www.yahoo.com\r\n\r\n".
+%% ------------------------------------------------------
+new_status_and_location(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, new_status_and_location(Env, Input)).
new_status_and_location(_Env,_Input) ->
"status:201 Created\r\n Location: http://www.yahoo.com\r\n\r\n".
+%% ------------------------------------------------------
-default(Env,Input) ->
- [header(),
- top("Default Example"),
- "<B>Environment:</B> ",io_lib:format("~p",[Env]),"<BR>\n",
- "<B>Input:</B> ",Input,"<BR>\n",
- "<B>Parsed Input:</B> ",
- io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
- footer()].
+peer(SessionID, Env, Input) ->
+ mod_esi:deliver(SessionID, peer(Env, Input)).
peer(Env, _Input) ->
Header =
@@ -116,23 +147,7 @@ peer(Env, _Input) ->
io_lib:format("~p",[proplists:get_value(peer_cert, Env)]),"\n",
footer()].
-header() ->
- header("text/html").
-header(MimeType) ->
- "Content-type: " ++ MimeType ++ "\r\n\r\n".
-header(MimeType, Other) ->
- "Content-type: " ++ MimeType ++ "\r\n" ++ Other ++ "\r\n\r\n".
-
-top(Title) ->
- "<HTML>
-<HEAD>
-<TITLE>" ++ Title ++ "</TITLE>
-</HEAD>
-<BODY>\n".
-
-footer() ->
- "</BODY>
-</HTML>\n".
+%% ------------------------------------------------------
post_chunked(_SessionID, _Env, {first, _Body} = _Bodychunk) ->
{continue, {state, 1}};
@@ -150,11 +165,13 @@ post_chunked(SessionID, _Env, {last, _Body, undefined} = _Bodychunk) ->
mod_esi:deliver(SessionID, footer());
post_chunked(_, _, _Body) ->
exit(body_not_chunked).
+%% ------------------------------------------------------
post_204(SessionID, _Env, _Input) ->
mod_esi:deliver(SessionID,
["Status: 204 No Content" ++ "\r\n\r\n"]),
mod_esi:deliver(SessionID, []).
+%% ------------------------------------------------------
ignore_invalid_header(SessionID, Env, _Input) ->
case proplists:get_value(content_length, Env, undefined) of
@@ -165,7 +182,8 @@ ignore_invalid_header(SessionID, Env, _Input) ->
mod_esi:deliver(SessionID,
["Status: 500 Internal Server Error" ++ "\r\n\r\n"])
end.
-
+%% ------------------------------------------------------
+
newformat(SessionID,_,_) ->
mod_esi:deliver(SessionID, "Content-Type:text/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("new esi format test")),
@@ -176,28 +194,16 @@ newformat(SessionID,_,_) ->
%% ------------------------------------------------------
-delay(Time) when is_integer(Time) ->
- i("httpd_example:delay(~p) -> do the delay",[Time]),
- sleep(Time),
- i("httpd_example:delay(~p) -> done, now reply",[Time]),
- delay_reply("delay ok");
-delay(Time) when is_list(Time) ->
- delay(list_to_integer(Time));
-delay({error,_Reason}) ->
- i("delay -> called with invalid time"),
- delay_reply("delay failed: invalid delay time").
+delay(SessionID,_, _) ->
+ sleep(10000),
+ Reply = delay_reply("delay ok"),
+ mod_esi:deliver(SessionID, Reply).
delay_reply(Reply) ->
[header(),
top("delay"),
Reply,
footer()].
-
-i(F) -> i(F,[]).
-i(F,A) -> io:format(F ++ "~n",A).
-
-sleep(T) -> receive after T -> ok end.
-
%% ------------------------------------------------------
chunk_timeout(SessionID, _, _StrInt) ->
@@ -224,3 +230,34 @@ get_chunks(Sid, _Env, In) ->
mod_esi:deliver(Sid, io_lib:format("Chunk ~p ms\r\n", [ChunkDelay])),
timer:sleep(ChunkDelay + BadChunkDelay),
mod_esi:deliver(Sid, "BAD Chunk\r\n").
+
+%% ------------------------------------------------------
+default(Env,Input) ->
+ [header(),
+ top("Default Example"),
+ "<B>Environment:</B> ",io_lib:format("~p",[Env]),"<BR>\n",
+ "<B>Input:</B> ",Input,"<BR>\n",
+ "<B>Parsed Input:</B> ",
+ io_lib:format("~p",[uri_string:dissect_query(Input)]),"\n",
+ footer()].
+
+header() ->
+ header("text/html").
+header(MimeType) ->
+ "Content-type: " ++ MimeType ++ "\r\n\r\n".
+header(MimeType, Other) ->
+ "Content-type: " ++ MimeType ++ "\r\n" ++ Other ++ "\r\n\r\n".
+
+top(Title) ->
+ "<HTML>
+<HEAD>
+<TITLE>" ++ Title ++ "</TITLE>
+</HEAD>
+<BODY>\n".
+
+footer() ->
+ "</BODY>
+</HTML>\n".
+
+sleep(T) -> receive after T -> ok end.
+
diff --git a/lib/inets/src/http_server/httpd_instance_sup.erl b/lib/inets/src/http_server/httpd_instance_sup.erl
index b77aa174ca..d5750f21ad 100644
--- a/lib/inets/src/http_server/httpd_instance_sup.erl
+++ b/lib/inets/src/http_server/httpd_instance_sup.erl
@@ -53,23 +53,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, Debug) ->
{error, Reason} ->
error_logger:error_report(Reason),
{stop, Reason}
- end;
-
-start_link(ConfigFile, AcceptTimeout, Debug) ->
- case file_2_config(ConfigFile) of
- {ok, ConfigList, Address, Port} ->
- Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
- Name = make_name(Address, Port, Profile),
- SupName = {local, Name},
- supervisor:start_link(SupName, ?MODULE,
- [ConfigFile, ConfigList, AcceptTimeout,
- Debug, Address, Port]);
- {error, Reason} ->
- error_logger:error_report(Reason),
- {stop, Reason}
end.
-
start_link([{_, _}| _] = Config, AcceptTimeout, ListenInfo, Debug) ->
case (catch httpd_conf:validate_properties(Config)) of
{ok, Config2} ->
@@ -84,23 +69,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, ListenInfo, Debug) ->
{error, Reason} ->
error_logger:error_report(Reason),
{stop, Reason}
- end;
-
-start_link(ConfigFile, AcceptTimeout, ListenInfo, Debug) ->
- case file_2_config(ConfigFile) of
- {ok, ConfigList, Address, Port} ->
- Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
- Name = make_name(Address, Port, Profile),
- SupName = {local, Name},
- supervisor:start_link(SupName, ?MODULE,
- [ConfigFile, ConfigList, AcceptTimeout,
- Debug, Address, Port, ListenInfo]);
- {error, Reason} ->
- error_logger:error_report(Reason),
- {stop, Reason}
end.
-
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
@@ -183,18 +153,3 @@ worker_spec(WorkerModule, Address, Port, Profile, ListenInfo, ConfigFile,
make_name(Address, Port, Profile) ->
httpd_util:make_name("httpd_instance_sup", Address, Port, Profile).
-
-file_2_config(ConfigFile) ->
- case httpd_conf:load(ConfigFile) of
- {ok, ConfigList} ->
- case (catch httpd_conf:validate_properties(ConfigList)) of
- {ok, Config} ->
- Address = proplists:get_value(bind_address, ConfigList),
- Port = proplists:get_value(port, ConfigList),
- {ok, Config, Address, Port};
- Error ->
- Error
- end;
- Error ->
- Error
- end.
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index 7cb39937e8..617e03a68f 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -364,25 +364,16 @@ handle_block(non_disturbing, Timeout,
handle_reload(undefined, #state{config_file = undefined} = State) ->
{continue, {error, undefined_config_file}, State};
-handle_reload(undefined, #state{config_file = ConfigFile} = State) ->
- case load_config(ConfigFile) of
- {ok, Config} ->
- do_reload(Config, State);
- {error, Reason} ->
- error_logger:error_msg("Bad config file: ~p~n", [Reason]),
- {continue, {error, Reason}, State}
+handle_reload(undefined, #state{config_file = ConfigFile, admin_state = AdminState} = State) ->
+ try httpd:reload_config(ConfigFile, AdminState) of
+ Result ->
+ Result
+ catch throw:Err ->
+ {config_file, Err, State}
end;
handle_reload(Config, State) ->
do_reload(Config, State).
-load_config(ConfigFile) ->
- case httpd_conf:load(ConfigFile) of
- {ok, Config} ->
- httpd_conf:validate_properties(Config);
- Error ->
- Error
- end.
-
do_reload(Config, #state{config_db = Db} = State) ->
case (catch check_constant_values(Db, Config)) of
ok ->
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 3df55c0f7a..958b122255 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -340,31 +340,13 @@ whole_body(Body, Length) ->
%% Prevent people from trying to access directories/files
%% relative to the ServerRoot.
validate_uri(RequestURI) ->
- UriNoQueryNoHex =
- case string:str(RequestURI, "?") of
- 0 ->
- (catch http_uri:decode(RequestURI));
- Ndx ->
- (catch http_uri:decode(string:left(RequestURI, Ndx)))
- end,
- case UriNoQueryNoHex of
- {'EXIT', _Reason} ->
- {error, {bad_request, {malformed_syntax, RequestURI}}};
- _ ->
- Path = format_request_uri(UriNoQueryNoHex),
- Path2 = [X||X<-string:tokens(Path, "/"),X=/="."], %% OTP-5938
- validate_path(Path2, 0, RequestURI)
+ case uri_string:normalize(RequestURI) of
+ {error, _, _} ->
+ {error, {bad_request, {malformed_syntax, RequestURI}}};
+ URI ->
+ {ok, URI}
end.
-
-validate_path([], _, _) ->
- ok;
-validate_path([".." | _], 0, RequestURI) ->
- {error, {bad_request, {forbidden, RequestURI}}};
-validate_path([".." | Rest], N, RequestURI) ->
- validate_path(Rest, N - 1, RequestURI);
-validate_path([_ | Rest], N, RequestURI) ->
- validate_path(Rest, N + 1, RequestURI).
-
+
validate_version("HTTP/1.1") ->
true;
validate_version("HTTP/1.0") ->
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index e48555f4d7..e82b1c46e9 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -400,9 +400,9 @@ handle_http_msg({_, _, Version, {_, _}, _},
handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
#state{status = accept, mod = ModData} = State) ->
case httpd_request:validate(Method, Uri, Version) of
- ok ->
+ {ok, NormalizedURI} ->
{ok, NewModData} =
- httpd_request:update_mod_data(ModData, Method, Uri,
+ httpd_request:update_mod_data(ModData, Method, NormalizedURI,
Version, Headers),
case is_host_specified_if_required(NewModData#mod.absolute_uri,
@@ -421,10 +421,6 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
httpd_response:send_status(ModData#mod{http_version = Version},
501, {Method, Uri, Version}, {not_sup, What}),
{stop, normal, State#state{response_sent = true}};
- {error, {bad_request, {forbidden, URI}}} ->
- httpd_response:send_status(ModData#mod{http_version = Version},
- 403, URI),
- {stop, normal, State#state{response_sent = true}};
{error, {bad_request, {malformed_syntax, URI}}} ->
httpd_response:send_status(ModData#mod{http_version = Version},
400, URI, {malformed_syntax, URI}),
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index d1216e01b0..76fa15f991 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -135,23 +135,18 @@ child_spec(HttpdService) ->
httpd_child_spec(Config, AcceptTimeout, Debug).
httpd_config([Value| _] = Config) when is_tuple(Value) ->
- case proplists:get_value(file, Config) of
- undefined ->
- case proplists:get_value(proplist_file, Config) of
- undefined ->
- httpd_conf:validate_properties(Config);
- File ->
- try file:consult(File) of
- {ok, [PropList]} ->
- httpd_conf:validate_properties(PropList)
- catch
- exit:_ ->
- throw({error,
- {could_not_consult_proplist_file, File}})
- end
- end;
- File ->
- {ok, File}
+ case proplists:get_value(proplist_file, Config) of
+ undefined ->
+ httpd_conf:validate_properties(Config);
+ File ->
+ try file:consult(File) of
+ {ok, [PropList]} ->
+ httpd_conf:validate_properties(PropList)
+ catch
+ exit:_ ->
+ throw({error,
+ {could_not_consult_proplist_file, File}})
+ end
end.
httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
@@ -159,30 +154,8 @@ httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
- httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile);
-
-%% In this case the AcceptTimeout and Debug will only have default values...
-httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
- case httpd_conf:load(ConfigFile) of
- {ok, ConfigList} ->
- case (catch httpd_conf:validate_properties(ConfigList)) of
- {ok, Config} ->
- Address = proplists:get_value(bind_address, Config, any),
- Port = proplists:get_value(port, Config, 80),
- Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
- AcceptTimeout =
- proplists:get_value(accept_timeout, Config,
- AcceptTimeoutDef),
- Debug =
- proplists:get_value(debug, Config, DebugDef),
- httpd_child_spec([{file, ConfigFile} | Config],
- AcceptTimeout, Debug, Address, Port, Profile);
- Error ->
- Error
- end;
- Error ->
- Error
- end.
+ httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile).
+
httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
case get_fd(Port) of
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index 6b3b2c9660..05cff30243 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -167,7 +167,7 @@ reason_phrase(_) -> "Internal Server Error".
%% message
message(301,URL,_) ->
- "The document has moved <A HREF=\""++ maybe_encode(URL) ++"\">here</A>.";
+ "The document has moved <A HREF=\""++ html_encode(uri_string:normalize(URL)) ++"\">here</A>.";
message(304, _URL,_) ->
"The document has not been changed.";
message(400, none, _) ->
@@ -184,11 +184,11 @@ browser doesn't understand how to supply
the credentials required.";
message(403,RequestURI,_) ->
"You don't have permission to access " ++
- html_encode(RequestURI) ++
+ html_encode(uri_string:normalize(RequestURI)) ++
" on this server.";
message(404,RequestURI,_) ->
"The requested URL " ++
- html_encode(RequestURI) ++
+ html_encode(uri_string:normalize(RequestURI)) ++
" was not found on this server.";
message(408, Timeout, _) ->
Timeout;
@@ -212,7 +212,7 @@ message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
is_atom(Method) ->
atom_to_list(Method) ++
" to " ++
- html_encode(RequestURI) ++
+ html_encode(uri_string:normalize(RequestURI)) ++
" (" ++ HTTPVersion ++ ") not supported.";
is_list(Method) ->
Method ++
@@ -225,23 +225,9 @@ message(503, String, _ConfigDB) ->
"This service in unavailable due to: " ++ html_encode(String);
message(_, ReasonPhrase, _) ->
html_encode(ReasonPhrase).
-
-maybe_encode(URI) ->
- Decoded = try http_uri:decode(URI) of
- N -> N
- catch
- error:_ -> URI
- end,
- http_uri:encode(Decoded).
-
+
html_encode(String) ->
- try http_uri:decode(String) of
- Decoded when is_list(Decoded) ->
- http_util:html_encode(Decoded)
- catch
- _:_ ->
- http_util:html_encode(String)
- end.
+ http_util:html_encode(String).
%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
@@ -422,21 +408,26 @@ flatlength([],L) ->
%% split_path
-split_path(Path) ->
- case re:run(Path,"[\?].*\$", [{capture, first}]) of
- %% A QUERY_STRING exists!
- {match,[{Start,Length}]} ->
- {http_uri:decode(string:substr(Path,1,Start)),
- string:substr(Path,Start+1,Length)};
- %% A possible PATH_INFO exists!
- nomatch ->
- split_path(Path,[])
+split_path(URI) ->
+ case uri_string:normalize(URI, [return_map]) of
+ #{fragment := Fragment,
+ path := Path,
+ query := Query} ->
+ {Path, add_hashmark(Query, Fragment)};
+ #{path := Path,
+ query := Query} ->
+ {Path, Query};
+ #{path := Path} ->
+ split_path(Path, [])
end.
+add_hashmark(Query, Fragment) ->
+ Query ++ "#" ++ Fragment.
+
split_path([],SoFar) ->
- {http_uri:decode(lists:reverse(SoFar)),[]};
+ {lists:reverse(SoFar),[]};
split_path([$/|Rest],SoFar) ->
- Path=http_uri:decode(lists:reverse(SoFar)),
+ Path=lists:reverse(SoFar),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path,[$/|Rest]};
@@ -450,56 +441,20 @@ split_path([C|Rest],SoFar) ->
%% split_script_path
-split_script_path(Path) ->
- case split_script_path(Path, []) of
- {Script, AfterPath} ->
- {PathInfo, QueryString} = pathinfo_querystring(AfterPath),
- {Script, {PathInfo, QueryString}};
- not_a_script ->
- not_a_script
- end.
-pathinfo_querystring(Str) ->
- pathinfo_querystring(Str, []).
-pathinfo_querystring([], SoFar) ->
- {lists:reverse(SoFar), []};
-pathinfo_querystring([$?|Rest], SoFar) ->
- {lists:reverse(SoFar), Rest};
-pathinfo_querystring([C|Rest], SoFar) ->
- pathinfo_querystring(Rest, [C|SoFar]).
-
-split_script_path([$?|QueryString], SoFar) ->
- Path = http_uri:decode(lists:reverse(SoFar)),
- case file:read_file_info(Path) of
- {ok,FileInfo} when FileInfo#file_info.type =:= regular ->
- {Path, [$?|QueryString]};
- {ok, _FileInfo} ->
- not_a_script;
- {error, _Reason} ->
- not_a_script
- end;
-split_script_path([], SoFar) ->
- Path = http_uri:decode(lists:reverse(SoFar)),
- case file:read_file_info(Path) of
- {ok,FileInfo} when FileInfo#file_info.type =:= regular ->
- {Path, []};
- {ok, _FileInfo} ->
- not_a_script;
- {error, _Reason} ->
- not_a_script
- end;
-split_script_path([$/|Rest], SoFar) ->
- Path = http_uri:decode(lists:reverse(SoFar)),
- case file:read_file_info(Path) of
- {ok, FileInfo} when FileInfo#file_info.type =:= regular ->
- {Path, [$/|Rest]};
- {ok, _FileInfo} ->
- split_script_path(Rest, [$/|SoFar]);
- {error, _Reason} ->
- split_script_path(Rest, [$/|SoFar])
- end;
-split_script_path([C|Rest], SoFar) ->
- split_script_path(Rest,[C|SoFar]).
+split_script_path(URI) ->
+ case uri_string:normalize(URI, [return_map]) of
+ #{fragment := _Fragment,
+ path := _Path,
+ query := _Query} ->
+ not_a_script;
+ #{path := Path,
+ query := Query} ->
+ {Script, PathInfo} = split_path(Path, []),
+ {Script, {PathInfo, Query}};
+ #{path := Path} ->
+ split_path(Path, [])
+ end.
%% suffix
diff --git a/lib/inets/src/http_server/mod_actions.erl b/lib/inets/src/http_server/mod_actions.erl
index b5449f20ee..dcc2b87961 100644
--- a/lib/inets/src/http_server/mod_actions.erl
+++ b/lib/inets/src/http_server/mod_actions.erl
@@ -19,7 +19,7 @@
%%
%%
-module(mod_actions).
--export([do/1,load/2, store/2]).
+-export([do/1, store/2]).
-include("httpd.hrl").
-include("httpd_internal.hrl").
@@ -78,23 +78,6 @@ script(RequestURI, Method, [_ | Rest]) ->
%% Configuration
%%
-%% load
-
-load("Action "++ Action, []) ->
- case re:split(Action, " ", [{return, list}]) of
- [MimeType, CGIScript] ->
- {ok,[],{action, {MimeType, CGIScript}}};
- _ ->
- {error,?NICE(string:strip(Action)++" is an invalid Action")}
- end;
-load("Script " ++ Script,[]) ->
- case re:split(Script, " ", [{return, list}]) of
- [Method, CGIScript] ->
- {ok,[],{script, {Method, CGIScript}}};
- _ ->
- {error,?NICE(string:strip(Script)++" is an invalid Script")}
- end.
-
store({action, {MimeType, CGIScript}} = Conf, _) when is_list(MimeType),
is_list(CGIScript) ->
{ok, Conf};
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index fac59ab93c..0d1681f6ed 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -24,7 +24,6 @@
real_name/3,
real_script_name/3,
default_index/2,
- load/2,
store/2,
path/3]).
@@ -99,13 +98,12 @@ get_protocol(_) ->
%% real_name
real_name(ConfigDB, RequestURI, []) ->
- DocumentRoot = which_document_root(ConfigDB),
+ {Prefix, DocumentRoot} = which_document_root(ConfigDB),
RealName = DocumentRoot ++ RequestURI,
{ShortPath, _AfterPath} = httpd_util:split_path(RealName),
{Path, AfterPath} =
httpd_util:split_path(default_index(ConfigDB, RealName)),
- {ShortPath, Path, AfterPath};
-
+ {Prefix ++ ShortPath, Prefix ++ Path, AfterPath};
real_name(ConfigDB, RequestURI, [{MP,Replacement}| _] = Aliases)
when element(1, MP) =:= re_pattern ->
case longest_match(Aliases, RequestURI) of
@@ -200,10 +198,10 @@ append_index(RealName, [Index | Rest]) ->
path(Data, ConfigDB, RequestURI) ->
case proplists:get_value(real_name, Data) of
undefined ->
- DocumentRoot = which_document_root(ConfigDB),
- {Path, _AfterPath} =
- httpd_util:split_path(DocumentRoot ++ RequestURI),
- Path;
+ {Prefix, DocumentRoot} = which_document_root(ConfigDB),
+ {Path, _AfterPath} =
+ httpd_util:split_path(DocumentRoot ++ RequestURI),
+ Prefix ++ Path;
{Path, _AfterPath} ->
Path
end.
@@ -211,50 +209,6 @@ path(Data, ConfigDB, RequestURI) ->
%%
%% Configuration
%%
-
-%% load
-
-load("DirectoryIndex " ++ DirectoryIndex, []) ->
- DirectoryIndexes = re:split(DirectoryIndex," ", [{return, list}]),
- {ok,[], {directory_index, DirectoryIndexes}};
-load("Alias " ++ Alias, []) ->
- case re:split(Alias," ", [{return, list}]) of
- [FakeName, RealName] ->
- {ok,[],{alias,{FakeName,RealName}}};
- _ ->
- {error,?NICE(string:strip(Alias)++" is an invalid Alias")}
- end;
-load("ReWrite " ++ Rule, Acc) ->
- load_re_write(Rule, Acc, "ReWrite", re_write);
-load("ScriptAlias " ++ ScriptAlias, []) ->
- case re:split(ScriptAlias, " ", [{return, list}]) of
- [FakeName, RealName] ->
- %% Make sure the path always has a trailing slash..
- RealName1 = filename:join(filename:split(RealName)),
- {ok, [], {script_alias, {FakeName, RealName1++"/"}}};
- _ ->
- {error, ?NICE(string:strip(ScriptAlias)++
- " is an invalid ScriptAlias")}
- end;
-load("ScriptReWrite " ++ Rule, Acc) ->
- load_re_write(Rule, Acc, "ScriptReWrite", script_re_write).
-
-load_re_write(Rule0, Acc, Type, Tag) ->
- case lists:dropwhile(
- fun ($\s) -> true; ($\t) -> true; (_) -> false end,
- Rule0) of
- "" ->
- {error, ?NICE(string:strip(Rule0)++" is an invalid "++Type)};
- Rule ->
- case string:chr(Rule, $\s) of
- 0 ->
- {ok, Acc, {Tag, {Rule, ""}}};
- N ->
- {Re, [_|Replacement]} = lists:split(N-1, Rule),
- {ok, Acc, {Tag, {Re, Replacement}}}
- end
- end.
-
store({directory_index, Value} = Conf, _) when is_list(Value) ->
case is_directory_index_list(Value) of
true ->
@@ -315,7 +269,13 @@ which_port(ConfigDB) ->
httpd_util:lookup(ConfigDB, port, 80).
which_document_root(ConfigDB) ->
- httpd_util:lookup(ConfigDB, document_root, "").
+ Root = httpd_util:lookup(ConfigDB, document_root, ""),
+ case string:tokens(Root, ":") of
+ [Prefix, Path] ->
+ {Prefix ++ ":", Path};
+ [Path] ->
+ {"", Path}
+ end.
which_directory_index(ConfigDB) ->
httpd_util:lookup(ConfigDB, directory_index, []).
diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl
index fba94df176..89d6e966cb 100644
--- a/lib/inets/src/http_server/mod_auth.erl
+++ b/lib/inets/src/http_server/mod_auth.erl
@@ -22,7 +22,7 @@
%% The functions that the webbserver call on startup stop
%% and when the server traverse the modules.
--export([do/1, load/2, store/2, remove/1]).
+-export([do/1, store/2, remove/1]).
%% User entries to the gen-server.
-export([add_user/2, add_user/5, add_user/6,
@@ -127,97 +127,6 @@ do(Info) ->
%% will be returned as a ConfigList and the context will return to the
%% state it was previously.
-load("<Directory " ++ Directory,[]) ->
- Dir = string:strip(string:strip(Directory),right, $>),
- {ok,[{directory, {Dir, [{path, Dir}]}}]};
-load(eof,[{directory, {Directory, _DirData}}|_]) ->
- {error, ?NICE("Premature end-of-file in "++ Directory)};
-
-load("AuthName " ++ AuthName, [{directory, {Directory, DirData}}|Rest]) ->
- {ok, [{directory, {Directory,
- [{auth_name, string:strip(AuthName)} | DirData]}}
- | Rest ]};
-load("AuthUserFile " ++ AuthUserFile0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthUserFile = string:strip(AuthUserFile0),
- {ok, [{directory, {Directory,
- [{auth_user_file, AuthUserFile}|DirData]}} | Rest ]};
-load("AuthGroupFile " ++ AuthGroupFile0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthGroupFile = string:strip(AuthGroupFile0),
- {ok,[{directory, {Directory,
- [{auth_group_file, AuthGroupFile}|DirData]}} | Rest]};
-
-load("AuthAccessPassword " ++ AuthAccessPassword0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthAccessPassword = string:strip(AuthAccessPassword0),
- {ok,[{directory, {Directory,
- [{auth_access_password, AuthAccessPassword}|DirData]}} | Rest]};
-
-load("AuthDBType " ++ Type,
- [{directory, {Dir, DirData}}|Rest]) ->
- case string:strip(Type) of
- "plain" ->
- {ok, [{directory, {Dir, [{auth_type, plain}|DirData]}} | Rest ]};
- "mnesia" ->
- {ok, [{directory, {Dir, [{auth_type, mnesia}|DirData]}} | Rest ]};
- "dets" ->
- {ok, [{directory, {Dir, [{auth_type, dets}|DirData]}} | Rest ]};
- _ ->
- {error, ?NICE(string:strip(Type)++" is an invalid AuthDBType")}
- end;
-
-load("require " ++ Require,[{directory, {Directory, DirData}}|Rest]) ->
- case re:split(Require," ", [{return, list}]) of
- ["user" | Users] ->
- {ok,[{directory, {Directory,
- [{require_user,Users}|DirData]}} | Rest]};
- ["group"|Groups] ->
- {ok,[{directory, {Directory,
- [{require_group,Groups}|DirData]}} | Rest]};
- _ ->
- {error,?NICE(string:strip(Require) ++" is an invalid require")}
- end;
-
-load("allow " ++ Allow,[{directory, {Directory, DirData}}|Rest]) ->
- case re:split(Allow," ", [{return, list}]) of
- ["from","all"] ->
- {ok,[{directory, {Directory,
- [{allow_from,all}|DirData]}} | Rest]};
- ["from"|Hosts] ->
- {ok,[{directory, {Directory,
- [{allow_from,Hosts}|DirData]}} | Rest]};
- _ ->
- {error,?NICE(string:strip(Allow) ++" is an invalid allow")}
- end;
-
-load("deny " ++ Deny,[{directory, {Directory, DirData}}|Rest]) ->
- case re:split(Deny," ", [{return, list}]) of
- ["from", "all"] ->
- {ok,[{{directory, Directory,
- [{deny_from, all}|DirData]}} | Rest]};
- ["from"|Hosts] ->
- {ok,[{{directory, Directory,
- [{deny_from, Hosts}|DirData]}} | Rest]};
- _ ->
- {error,?NICE(string:strip(Deny) ++" is an invalid deny")}
- end;
-
-load("</Directory>",[{directory, {Directory, DirData}}|Rest]) ->
- {ok, Rest, {directory, {Directory, DirData}}};
-
-load("AuthMnesiaDB " ++ AuthMnesiaDB,
- [{directory, {Dir, DirData}}|Rest]) ->
- case string:strip(AuthMnesiaDB) of
- "On" ->
- {ok,[{directory, {Dir,[{auth_type,mnesia}|DirData]}}|Rest]};
- "Off" ->
- {ok,[{directory, {Dir,[{auth_type,plain}|DirData]}}|Rest]};
- _ ->
- {error, ?NICE(string:strip(AuthMnesiaDB) ++
- " is an invalid AuthMnesiaDB")}
- end.
-
store({directory, {Directory, DirData}}, ConfigList)
when is_list(Directory) andalso is_list(DirData) ->
try directory_config_check(Directory, DirData) of
diff --git a/lib/inets/src/http_server/mod_browser.erl b/lib/inets/src/http_server/mod_browser.erl
deleted file mode 100644
index 1e8f860746..0000000000
--- a/lib/inets/src/http_server/mod_browser.erl
+++ /dev/null
@@ -1,251 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%% ----------------------------------------------------------------------
-%%
-%% Browsers sends a string to the webbserver
-%% to identify themsevles. They are a bit nasty
-%% since the only thing that the specification really
-%% is strict about is that they shall be short
-%% some axamples:
-%%
-%% Netscape Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)
-%% Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020823 Netscape/7.0
-%% Mozilla Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827
-%% Safari Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85 (KHTML, like Gecko) Safari/85
-%% IE5 Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)
-%% Lynx Lynx/2.8.3rel.1 libwww-FM/2.142
-%%
-%% ----------------------------------------------------------------------
-
--module(mod_browser).
-
--export([do/1, test/0, getBrowser/1]).
-
-%% Remember that the order of the mozilla browsers are
-%% important since some browsers include others to behave
-%% as they were something else
--define(MOZILLA_BROWSERS,[{netscape, "netscape"},
- {opera, "opera"},
- {msie, "msie"},
- {safari, "safari"},
- {mozilla, "rv:"}]). % fallback, must be last
-
-
-%% If your operatingsystem is not recognized add it to this list.
--define(OPERATIVE_SYSTEMS,[{win3x, ["win16", "windows 3", "windows 16-bit"]},
- {win95, ["win95", "windows 95"]},
- {win98, ["win98", "windows 98"]},
- {winnt, ["winnt", "windows nt"]},
- {win2k, ["nt 5"]},
- {sunos4, ["sunos 4"]},
- {sunos5, ["sunos 5"]},
- {sun, ["sunos"]},
- {aix, ["aix"]},
- {linux, ["linux"]},
- {sco, ["sco", "unix_sv"]},
- {freebsd,["freebsd"]},
- {bsd, ["bsd"]},
- {macosx, ["mac os x"]}]).
-
--define(LYNX, lynx).
--define(MOZILLA, mozilla).
--define(EMACS, emacs).
--define(STAROFFICE, soffice).
--define(MOSAIC, mosaic).
--define(NETSCAPE, netscape).
--define(SAFARU, safari).
--define(UNKOWN, unknown).
-
--include("httpd.hrl").
-
--define(VMODULE,"BROWSER").
-
-do(Info) ->
- case proplists:get_value(status, Info#mod.data) of
- {_StatusCode, _PhraseArgs, _Reason} ->
- {proceed,Info#mod.data};
- undefined ->
- Browser = getBrowser1(Info),
- {proceed,[{'user-agent', Browser}|Info#mod.data]}
- end.
-
-getBrowser1(Info) ->
- PHead = Info#mod.parsed_header,
- case proplists:get_value("user-agent", PHead) of
- undefined ->
- undefined;
- AgentString ->
- getBrowser(AgentString)
- end.
-
-getBrowser(AgentString) ->
- LAgentString = http_util:to_lower(AgentString),
- case re:run(LAgentString,"^[^ ]*", [{capture, first}]) of
- {match,[{Start,Length}]} ->
- Browser = lists:sublist(LAgentString,Start+1,Length),
- case browserType(Browser) of
- {mozilla,Vsn} ->
- {getMozilla(LAgentString,
- ?MOZILLA_BROWSERS,{?NETSCAPE,Vsn}),
- operativeSystem(LAgentString)};
- AnyBrowser ->
- {AnyBrowser,operativeSystem(LAgentString)}
- end;
- nomatch ->
- browserType(LAgentString)
- end.
-
-browserType([$l,$y,$n,$x|Version]) ->
- {?LYNX,browserVersion(Version)};
-browserType([$m,$o,$z,$i,$l,$l,$a|Version]) ->
- {?MOZILLA,browserVersion(Version)};
-browserType([$e,$m,$a,$c,$s|Version]) ->
- {?EMACS,browserVersion(Version)};
-browserType([$s,$t,$a,$r,$o,$f,$f,$i,$c,$e|Version]) ->
- {?STAROFFICE,browserVersion(Version)};
-browserType([$m,$o,$s,$a,$i,$c|Version]) ->
- {?MOSAIC,browserVersion(Version)};
-browserType(_Unknown) ->
- unknown.
-
-
-browserVersion([$/|VsnString]) ->
- case catch list_to_float(VsnString) of
- Number when is_float(Number) ->
- Number;
- _Whatever ->
- case string:span(VsnString,"1234567890.") of
- 0 ->
- unknown;
- VLength ->
- Vsn = string:substr(VsnString,1,VLength),
- case string:tokens(Vsn,".") of
- [Number] ->
- list_to_float(Number++".0");
- [Major,Minor|_MinorMinor] ->
- list_to_float(Major++"."++Minor)
- end
- end
- end;
-browserVersion(VsnString) ->
- browserVersion([$/|VsnString]).
-
-operativeSystem(OpString) ->
- operativeSystem(OpString, ?OPERATIVE_SYSTEMS).
-
-operativeSystem(_OpString,[]) ->
- unknown;
-operativeSystem(OpString,[{RetVal,RegExps}|Rest]) ->
- case controlOperativeSystem(OpString,RegExps) of
- true ->
- RetVal;
- _ ->
- operativeSystem(OpString,Rest)
- end.
-
-controlOperativeSystem(_OpString,[]) ->
- false;
-controlOperativeSystem(OpString,[Regexp|Regexps]) ->
- case re:run(OpString,Regexp, [{capture, none}]) of
- match ->
- true;
- nomatch ->
- controlOperativeSystem(OpString,Regexps)
- end.
-
-
-%% OK this is ugly but thats the only way since
-%% all browsers dont conform to the name/vsn standard
-%% First we check if it is one of the browsers that
-%% are not the default mozillaborwser against the regexp
-%% for the different browsers. if no match, it is a mozilla
-%% browser i.e opera, netscape, ie or safari
-
-getMozilla(_AgentString,[],Default) ->
- Default;
-getMozilla(AgentString,[{Agent,AgentRegExp}|Rest],Default) ->
- case re:run(AgentString,AgentRegExp, [{capture, none}]) of
- match ->
- {Agent,getMozVersion(AgentString,AgentRegExp)};
- nomatch ->
- getMozilla(AgentString,Rest,Default)
- end.
-
-getMozVersion(AgentString, AgentRegExp) ->
- case re:run(AgentString,AgentRegExp++"[0-9\.\ \/]*",
- [{capture, first}]) of
- {match, [{Start,Length}]} when length(AgentRegExp) < Length ->
- %% Ok we got the number split it out
- RealStart = Start+1+length(AgentRegExp),
- RealLength = Length-length(AgentRegExp),
- VsnString = string:substr(AgentString,RealStart,RealLength),
- %% case string:strip(VsnString,both,$\ ) of
- case strip(VsnString) of
- [] ->
- unknown;
- [Y1,Y2,Y3,Y4,M1,M2,D1,D2] = DateVsn when
- Y1 =< $9, Y1 >= $0,
- Y2 =< $9, Y2 >= $0,
- Y3 =< $9, Y3 >= $0,
- Y4 =< $9, Y4 >= $0,
- M1 =< $9, M1 >= $0,
- M2 =< $9, M2 >= $0,
- D1 =< $9, D1 >= $0,
- D2 =< $9, D2 >= $0 ->
- list_to_integer(DateVsn);
- Vsn ->
- case string:tokens(Vsn,".") of
- [Number]->
- list_to_float(Number++".0");
- [Major,Minor|Rev] ->
- V = lists:flatten([Major,".",Minor,Rev]),
- list_to_float(V)
- end
- end;
- nomatch ->
- unknown
- end.
-
-strip(VsnString) ->
- strip2(strip1(VsnString)).
-
-strip1(VsnString) ->
- string:strip(VsnString,both,$\ ).
-
-strip2(VsnString) ->
- string:strip(VsnString,both,$/ ).
-
-test()->
- test("Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)"),
- test("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020823 Netscape/7.0"),
- test("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827"),
- test("Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4) Gecko/20020827"),
- test("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85 (KHTML, like Gecko) Safari/85"),
- test("Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)"),
- test("Lynx/2.8.3rel.1 libwww-FM/2.142"),
- ok.
-
-test(Str) ->
- Browser = getBrowser(Str),
- io:format("~n--------------------------------------------------------~n"),
- io:format("~p",[Browser]),
- io:format("~n--------------------------------------------------------~n").
-
diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl
index 2a90575e7d..ad2ee1d994 100644
--- a/lib/inets/src/http_server/mod_dir.erl
+++ b/lib/inets/src/http_server/mod_dir.erl
@@ -57,9 +57,7 @@ do_dir(Info) ->
%% Is it a directory?
case file:read_file_info(DefaultPath) of
{ok,FileInfo} when FileInfo#file_info.type == directory ->
- DecodedRequestURI =
- http_uri:decode(Info#mod.request_uri),
- case dir(DefaultPath,string:strip(DecodedRequestURI,right,$/),
+ case dir(DefaultPath,string:strip( Info#mod.request_uri,right,$/),
Info#mod.config_db) of
{ok, Dir} ->
LastModified =
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 112e74575d..00268aefec 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -27,7 +27,7 @@
-export([deliver/2]).
%% Callback API
--export([do/1, load/2, store/2]).
+-export([do/1, store/2]).
-include("httpd.hrl").
-include("httpd_internal.hrl").
@@ -83,61 +83,6 @@ do(ModData) ->
%%--------------------------------------------------------------------------
-%% load(Line, Context) -> eof | ok | {ok, NewContext} |
-%% {ok, NewContext, Directive} |
-%% {ok, NewContext, DirectiveList} | {error, Reason}
-%% Line = string()
-%% Context = NewContext = DirectiveList = [Directive]
-%% Directive = {DirectiveKey , DirectiveValue}
-%% DirectiveKey = DirectiveValue = term()
-%% Reason = term()
-%%
-%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
-%%-------------------------------------------------------------------------
-load("ErlScriptAlias " ++ ErlScriptAlias, []) ->
- try re:split(ErlScriptAlias," ", [{return, list}]) of
- [ErlName | StrModules] ->
- Modules = lists:map(fun(Str) ->
- list_to_atom(string:strip(Str))
- end, StrModules),
- {ok, [], {erl_script_alias, {ErlName, Modules}}}
- catch _:_ ->
- {error, ?NICE(string:strip(ErlScriptAlias) ++
- " is an invalid ErlScriptAlias")}
- end;
-load("EvalScriptAlias " ++ EvalScriptAlias, []) ->
- try re:split(EvalScriptAlias, " ", [{return, list}]) of
- [EvalName | StrModules] ->
- Modules = lists:map(fun(Str) ->
- list_to_atom(string:strip(Str))
- end, StrModules),
- {ok, [], {eval_script_alias, {EvalName, Modules}}}
- catch
- _:_ ->
- {error, ?NICE(string:strip(EvalScriptAlias) ++
- " is an invalid EvalScriptAlias")}
- end;
-load("ErlScriptTimeout " ++ Timeout, [])->
- case catch list_to_integer(string:strip(Timeout)) of
- TimeoutSec when is_integer(TimeoutSec) ->
- {ok, [], {erl_script_timeout, TimeoutSec}};
- _ ->
- {error, ?NICE(string:strip(Timeout) ++
- " is an invalid ErlScriptTimeout")}
- end;
-load("ErlScriptNoCache " ++ CacheArg, [])->
- case catch list_to_atom(string:strip(CacheArg)) of
- true ->
- {ok, [], {erl_script_nocache, true}};
- false ->
- {ok, [], {erl_script_nocache, false}};
- _ ->
- {error, ?NICE(string:strip(CacheArg)++
- " is an invalid ErlScriptNoCache directive")}
- end.
-
-
-%%--------------------------------------------------------------------------
%% store(Directive, DirectiveList) -> {ok, NewDirective} |
%% {ok, [NewDirective]} |
%% {error, Reason}
@@ -161,16 +106,6 @@ store({erl_script_alias, {Name, Modules}} = Conf, _)
{error, {wrong_type, {erl_script_alias, Error}}}
end;
-store({eval_script_alias, {Name, Modules}} = Conf, _)
- when is_list(Name)->
- try httpd_util:modules_validate(Modules) of
- ok ->
- {ok, Conf}
- catch
- throw:Error ->
- {error, {wrong_type, {eval_script_alias, Error}}}
- end;
-
store({erl_script_alias, Value}, _) ->
{error, {wrong_type, {erl_script_alias, Value}}};
store({erl_script_timeout, TimeoutSec}, _)
@@ -190,8 +125,6 @@ store({erl_script_nocache, Value}, _) ->
%%%========================================================================
generate_response(ModData) ->
case scheme(ModData#mod.request_uri, ModData#mod.config_db) of
- {eval, ESIBody, Modules} ->
- eval(ModData, ESIBody, Modules);
{erl, ESIBody, Modules} ->
erl(ModData, ESIBody, Modules);
no_scheme ->
@@ -201,12 +134,7 @@ generate_response(ModData) ->
scheme(RequestURI, ConfigDB) ->
case match_script(RequestURI, ConfigDB, erl_script_alias) of
no_match ->
- case match_script(RequestURI, ConfigDB, eval_script_alias) of
- no_match ->
- no_scheme;
- {EsiBody, ScriptModules} ->
- {eval, EsiBody, ScriptModules}
- end;
+ no_scheme;
{EsiBody, ScriptModules} ->
{erl, EsiBody, ScriptModules}
end.
@@ -231,10 +159,7 @@ match_esi_script(RequestURI, [{Alias,Modules} | Rest], AliasType) ->
end.
alias_match_str(Alias, erl_script_alias) ->
- "^" ++ Alias ++ "/";
-alias_match_str(Alias, eval_script_alias) ->
- "^" ++ Alias ++ "\\?".
-
+ "^" ++ Alias ++ "/".
%%------------------------ Erl mechanism --------------------------------
@@ -315,8 +240,8 @@ generate_webpage(ModData, ESIBody, Modules, Module, FunctionName,
case erl_scheme_webpage_chunk(Module, Function,
Env, Input, ModData) of
{error, erl_scheme_webpage_chunk_undefined} ->
- erl_scheme_webpage_whole(Module, Function, Env, Input,
- ModData);
+ {proceed, [{status, {404, ModData#mod.request_uri, "Not found"}}
+ | ModData#mod.data]};
ResponseResult ->
ResponseResult
end;
@@ -326,38 +251,7 @@ generate_webpage(ModData, ESIBody, Modules, Module, FunctionName,
++ ESIBody)}} | ModData#mod.data]}
end.
-%% Old API that waits for the dymnamic webpage to be totally generated
-%% before anythig is sent back to the client.
-erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) ->
- case (catch Mod:Func(Env, Input)) of
- {'EXIT',{undef, _}} ->
- {proceed, [{status, {404, ModData#mod.request_uri, "Not found"}}
- | ModData#mod.data]};
- {'EXIT',Reason} ->
- {proceed, [{status, {500, none, Reason}} |
- ModData#mod.data]};
- Response ->
- {Headers, Body} =
- httpd_esi:parse_headers(lists:flatten(Response)),
- Length = httpd_util:flatlength(Body),
- {ok, NewHeaders, StatusCode} = httpd_esi:handle_headers(Headers),
- send_headers(ModData, StatusCode,
- [{"content-length",
- integer_to_list(Length)}| NewHeaders]),
- case ModData#mod.method of
- "HEAD" ->
- {proceed, [{response, {already_sent, StatusCode, 0}} |
- ModData#mod.data]};
- _ ->
- httpd_response:send_body(ModData,
- StatusCode, Body),
- {proceed, [{response, {already_sent, StatusCode,
- Length}} |
- ModData#mod.data]}
- end
- end.
-
-%% New API that allows the dynamic wepage to be sent back to the client
+%% API that allows the dynamic wepage to be sent back to the client
%% in small chunks at the time during generation.
erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
process_flag(trap_exit, true),
@@ -369,7 +263,6 @@ erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
fun() ->
case catch Mod:Func(Self, Env, Input) of
{'EXIT', {undef,_}} ->
- %% Will force fallback on the old API
exit(erl_scheme_webpage_chunk_undefined);
{continue, _} = Continue ->
exit(Continue);
@@ -523,64 +416,3 @@ input_type([$?|_Rest]) ->
input_type([_First|Rest]) ->
input_type(Rest).
-%%------------------------ Eval mechanism --------------------------------
-
-eval(#mod{request_uri = ReqUri,
- method = "PUT",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- {proceed,[{status,{501,{"PUT", ReqUri, Version},
- ?NICE("Eval mechanism doesn't support method PUT")}}|
- Data]};
-
-eval(#mod{request_uri = ReqUri,
- method = "DELETE",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- {proceed,[{status,{501,{"DELETE", ReqUri, Version},
- ?NICE("Eval mechanism doesn't support method DELETE")}}|
- Data]};
-
-eval(#mod{request_uri = ReqUri,
- method = "POST",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- {proceed,[{status,{501,{"POST", ReqUri, Version},
- ?NICE("Eval mechanism doesn't support method POST")}}|
- Data]};
-
-eval(#mod{method = Method} = ModData, ESIBody, Modules)
- when (Method =:= "GET") orelse (Method =:= "HEAD") ->
- case is_authorized(ESIBody, Modules) of
- true ->
- case generate_webpage(ESIBody) of
- {error, Reason} ->
- {proceed, [{status, {500, none, Reason}} |
- ModData#mod.data]};
- {ok, Response} ->
- {Headers, _} =
- httpd_esi:parse_headers(lists:flatten(Response)),
- {ok, _, StatusCode} =httpd_esi:handle_headers(Headers),
- {proceed,[{response, {StatusCode, Response}} |
- ModData#mod.data]}
- end;
- false ->
- {proceed,[{status,
- {403, ModData#mod.request_uri,
- ?NICE("Client not authorized to evaluate: "
- ++ ESIBody)}} | ModData#mod.data]}
- end.
-
-generate_webpage(ESIBody) ->
- (catch erl_eval:eval_str(string:concat(ESIBody,". "))).
-
-is_authorized(_ESIBody, [all]) ->
- true;
-is_authorized(ESIBody, Modules) ->
- case re:run(ESIBody, "^[^\:(%3A)]*", [{capture, first}]) of
- {match, [{Start, Length}]} ->
- lists:member(list_to_atom(string:substr(ESIBody, Start+1, Length)),
- Modules);
- nomatch ->
- false
- end.
diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl
deleted file mode 100644
index 7b742bba24..0000000000
--- a/lib/inets/src/http_server/mod_htaccess.erl
+++ /dev/null
@@ -1,1071 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(mod_htaccess).
-
--export([do/1, load/2, store/2]).
-
--include("httpd.hrl").
--include("httpd_internal.hrl").
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Public methods that interface the eswapi %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Public method called by the webbserver to insert the data about
-% Names on accessfiles
-%----------------------------------------------------------------------
-load("AccessFileName" ++ FileNames, _Context)->
- CleanFileNames=string:strip(FileNames),
- {ok,[],{access_files,string:tokens(CleanFileNames," ")}}.
-
-store({access_files, Files} = Conf, _) when is_list(Files)->
- {ok, Conf};
-store({access_files, Value}, _) ->
- {error, {wrong_type, {access_files, Value}}}.
-
-%----------------------------------------------------------------------
-% Public method that the webbserver calls to control the page
-%----------------------------------------------------------------------
-do(Info)->
- case proplists:get_value(status, Info#mod.data) of
- {_Status_code, _PhraseArgs, _Reason}->
- {proceed,Info#mod.data};
- undefined ->
- control_path(Info)
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% The functions that start the control if there is a accessfile %%
-%% and if so controls if the dir is allowed or not %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%Info = record mod as specified in httpd.hrl
-%returns either {proceed,Info#mod.data}
-%{proceed,[{status,403....}|Info#mod.data]}
-%{proceed,[{status,401....}|Info#mod.data]}
-%{proceed,[{status,500....}|Info#mod.data]}
-%----------------------------------------------------------------------
-control_path(Info) ->
- Path = mod_alias:path(Info#mod.data,
- Info#mod.config_db,
- Info#mod.request_uri),
- case isErlScriptOrNotAccessibleFile(Path,Info) of
- true->
- {proceed,Info#mod.data};
- false->
- case getHtAccessData(Path,Info)of
- {ok,public}->
- %%There was no restrictions on the page continue
- {proceed,Info#mod.data};
- {error, _Reason} ->
- %%Something got wrong continue or quit??????????????????/
- {proceed,Info#mod.data};
- {accessData,AccessData}->
- controlAllowedMethod(Info,AccessData)
- end
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% These methods controls that the method the client used in the %%
-%% request is one of the limited %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%Control that if the accessmethod used is in the list of modes to challenge
-%
-%Info is the mod record as specified in httpd.hrl
-%AccessData is an ets table whit the data in the .htaccessfiles
-%----------------------------------------------------------------------
-controlAllowedMethod(Info,AccessData)->
- case allowedRequestMethod(Info,AccessData) of
- allow->
- %%The request didnt use one of the limited methods
- ets:delete(AccessData),
- {proceed,Info#mod.data};
- challenge->
- authenticateUser(Info,AccessData)
- end.
-
-%----------------------------------------------------------------------
-%Check the specified access method in the .htaccessfile
-%----------------------------------------------------------------------
-allowedRequestMethod(Info,AccessData)->
- case ets:lookup(AccessData,limit) of
- [{limit,all}]->
- challenge;
- [{limit,Methods}]->
- isLimitedRequestMethod(Info,Methods)
- end.
-
-
-%----------------------------------------------------------------------
-%Check the specified accessmethods in the .htaccesfile against the users
-%accessmethod
-%
-%Info is the record from the do call
-%Methods is a list of the methods specified in the .htaccessfile
-%----------------------------------------------------------------------
-isLimitedRequestMethod(Info,Methods)->
- case lists:member(Info#mod.method,Methods) of
- true->
- challenge;
- false ->
- allow
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% These methods controls that the user comes from an allowwed net %%
-%% and if so wheather its a valid user or a challenge shall be %%
-%% generated %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%The first thing to control is that the user is from a network
-%that has access to the page
-%----------------------------------------------------------------------
-authenticateUser(Info,AccessData)->
- case controlNet(Info,AccessData) of
- allow->
- %the network is ok control that it is an allowed user
- authenticateUser2(Info,AccessData);
- deny->
- %The user isnt allowed to access the pages from that network
- ets:delete(AccessData),
- {proceed,[{status,{403,Info#mod.request_uri,
- "Restricted area not allowed from your network"}}|Info#mod.data]}
- end.
-
-
-%----------------------------------------------------------------------
-%The network the user comes from is allowed to view the resources
-%control whether the user needsto supply a password or not
-%----------------------------------------------------------------------
-authenticateUser2(Info,AccessData)->
- case ets:lookup(AccessData,require) of
- [{require,AllowedUsers}]->
- case ets:lookup(AccessData,auth_name) of
- [{auth_name,Realm}]->
- authenticateUser2(Info,AccessData,Realm,AllowedUsers);
- _NoAuthName->
- ets:delete(AccessData),
- {break,[{status,{500,none,
- ?NICE("mod_htaccess:AuthName directive "
- "not specified")}}]}
- end;
- [] ->
- %%No special user is required the network is ok so let
- %%the user in
- ets:delete(AccessData),
- {proceed,Info#mod.data}
- end.
-
-
-%----------------------------------------------------------------------
-%The user must send a userId and a password to get the resource
-%Control if its already in the http-request
-%if the file with users is bad send an 500 response
-%----------------------------------------------------------------------
-authenticateUser2(Info,AccessData,Realm,AllowedUsers)->
- case authenticateUser(Info,AccessData,AllowedUsers) of
- allow ->
- ets:delete(AccessData),
- {user,Name, _Pwd} = getAuthenticatingDataFromHeader(Info),
- {proceed, [{remote_user_name,Name}|Info#mod.data]};
- challenge->
- ets:delete(AccessData),
- ReasonPhrase = httpd_util:reason_phrase(401),
- Message = httpd_util:message(401,none,Info#mod.config_db),
- {proceed,
- [{response,
- {401,
- ["WWW-Authenticate: Basic realm=\"",Realm,
- "\"\r\n\r\n","<HTML>\n<HEAD>\n<TITLE>",
- ReasonPhrase,"</TITLE>\n",
- "</HEAD>\n<BODY>\n<H1>",ReasonPhrase,
- "</H1>\n",Message,"\n</BODY>\n</HTML>\n"]}}|
- Info#mod.data]};
- deny->
- ets:delete(AccessData),
- {break,[{status,{500,none,
- ?NICE("mod_htaccess:Bad path to user "
- "or group file")}}]}
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Methods that validate the netwqork the user comes from %%
-%% according to the allowed networks %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%---------------------------------------------------------------------
-%Controls the users networkaddress agains the specifed networks to
-%allow or deny
-%
-%returns either allow or deny
-%----------------------------------------------------------------------
-controlNet(Info,AccessData)->
- UserNetwork=getUserNetworkAddress(Info),
- case getAllowDenyOrder(AccessData) of
- {_deny,[],_allow,[]}->
- allow;
- {deny,[],allow,AllowedNetworks}->
- controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
- {allow,AllowedNetworks,deny,[]}->
- controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
-
- {deny,DeniedNetworks,allow,[]}->
- controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
- {allow,[],deny,DeniedNetworks}->
- controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
-
- {deny,DeniedNetworks,allow,AllowedNetworks}->
- controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork);
- {allow,AllowedNetworks,deny,DeniedNetworks}->
- controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)
- end.
-
-
-%----------------------------------------------------------------------
-%Returns the users IP-Number
-%----------------------------------------------------------------------
-getUserNetworkAddress(Info)->
- {_Socket,Address}=(Info#mod.init_data)#init_data.peername,
- Address.
-
-
-%----------------------------------------------------------------------
-%Control the users Ip-number against the ip-numbers in the .htaccessfile
-%----------------------------------------------------------------------
-controlIfAllowed(AllowedNetworks,UserNetwork,IfAllowed,IfDenied)->
- case AllowedNetworks of
- [{allow,all}]->
- IfAllowed;
- [{deny,all}]->
- IfDenied;
- [{deny,Networks}]->
- memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed);
- [{allow,Networks}]->
- memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied);
- _Error->
- IfDenied
- end.
-
-
-%---------------------------------------------------------------------%
-%The Denycontrol isn't neccessary to preform since the allow control %
-%override the deny control %
-%---------------------------------------------------------------------%
-controlDenyAllow(_DeniedNetworks, AllowedNetworks, UserNetwork)->
- case AllowedNetworks of
- [{allow, all}]->
- allow;
- [{allow, Networks}]->
- case memberNetwork(Networks, UserNetwork) of
- true->
- allow;
- false->
- deny
- end
- end.
-
-
-%----------------------------------------------------------------------%
-%Control that the user is in the allowed list if so control that the %
-%network is in the denied list
-%----------------------------------------------------------------------%
-controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)->
- case controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny) of
- allow->
- controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow);
- deny ->
- deny
- end.
-
-%----------------------------------------------------------------------
-%Controls if the users Ipnumber is in the list of either denied or
-%allowed networks
-%----------------------------------------------------------------------
-memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)->
- case memberNetwork(Networks,UserNetwork) of
- true->
- IfTrue;
- false->
- IfFalse
- end.
-
-
-%----------------------------------------------------------------------
-%regexp match the users ip-address against the networks in the list of
-%ipadresses or subnet addresses.
-memberNetwork(Networks,UserNetwork)->
- case lists:filter(fun(Net)->
- case re:run(UserNetwork,
- formatRegexp(Net), [{capture, first}]) of
- {match,[{0,_}]}->
- true;
- _NotSubNet ->
- false
- end
- end,Networks) of
- []->
- false;
- _MemberNetWork ->
- true
- end.
-
-
-%----------------------------------------------------------------------
-%Creates a regexp from an ip-number i.e "127.0.0-> "^127[.]0[.]0.*"
-%"127.0.0.-> "^127[.]0[.]0[.].*"
-%----------------------------------------------------------------------
-formatRegexp(Net)->
- [SubNet1|SubNets]=string:tokens(Net,"."),
- NetRegexp=lists:foldl(fun(SubNet,Newnet)->
- Newnet ++ "[.]" ++SubNet
- end,"^"++SubNet1,SubNets),
- case string:len(Net)-string:rchr(Net,$.) of
- 0->
- NetRegexp++"[.].*";
- _->
- NetRegexp++".*"
- end.
-
-%----------------------------------------------------------------------
-%If the user has specified if the allow or deny check shall be preformed
-%first get that order if no order is specified take
-%allow - deny since its harder that deny - allow
-%----------------------------------------------------------------------
-getAllowDenyOrder(AccessData)->
- case ets:lookup(AccessData,order) of
- [{order,{deny,allow}}]->
- {deny,ets:lookup(AccessData,deny),
- allow,ets:lookup(AccessData,allow)};
- _DefaultOrder->
- {allow,ets:lookup(AccessData,allow),
- deny,ets:lookup(AccessData,deny)}
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% The methods that validates the user %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-%Control if there is anyu autheticating data in threquest header
-%if so it controls it against the users in the list Allowed Users
-%----------------------------------------------------------------------
-authenticateUser(Info,AccessData,AllowedUsers)->
- case getAuthenticatingDataFromHeader(Info) of
- {user,User,PassWord}->
- authenticateUser(Info,AccessData,AllowedUsers,
- {user,User,PassWord});
- {error,nouser}->
- challenge;
- {error, _BadData}->
- challenge
- end.
-
-
-%----------------------------------------------------------------------
-%Returns the Autheticating data in the http-request
-%----------------------------------------------------------------------
-getAuthenticatingDataFromHeader(Info)->
- PrsedHeader=Info#mod.parsed_header,
- case proplists:get_value("authorization", PrsedHeader) of
- undefined->
- {error,nouser};
- [$B,$a,$s,$i,$c,$\ |EncodedString] = Credentials ->
- case (catch base64:decode_to_string(EncodedString)) of
- {'EXIT',{function_clause, _}} ->
- {error, Credentials};
- UnCodedString ->
- case httpd_util:split(UnCodedString,":",2) of
- {ok,[User,PassWord]}->
- {user,User,PassWord};
- Other ->
- {error, Other}
- end
- end;
- BadCredentials ->
- {error,BadCredentials}
- end.
-
-%----------------------------------------------------------------------
-%Returns a list of all members of the allowed groups
-%----------------------------------------------------------------------
-getGroupMembers(Groups,AllowedGroups)->
- Allowed=lists:foldl(fun({group,Name,Members},AllowedMembers)->
- case lists:member(Name,AllowedGroups) of
- true->
- AllowedMembers++Members;
- false ->
- AllowedMembers
- end
- end,[],Groups),
- {ok,Allowed}.
-
-authenticateUser(Info,AccessData,{{users,[]},{groups,Groups}},User)->
- authenticateUser(Info,AccessData,{groups,Groups},User);
-authenticateUser(Info,AccessData,{{users,Users},{groups,[]}},User)->
- authenticateUser(Info,AccessData,{users,Users},User);
-
-authenticateUser(Info,AccessData,{{users,Users},{groups,Groups}},User)->
- AllowUser=authenticateUser(Info,AccessData,{users,Users},User),
- AllowGroup=authenticateUser(Info,AccessData,{groups,Groups},User),
- case {AllowGroup,AllowUser} of
- {_,allow}->
- allow;
- {allow,_}->
- allow;
- {challenge,_}->
- challenge;
- {_,challenge}->
- challenge;
- {_deny,_deny}->
- deny
- end;
-
-
-%----------------------------------------------------------------------
-%Controls that the user is a member in one of the allowed group
-%----------------------------------------------------------------------
-authenticateUser(Info,AccessData,{groups,AllowedGroups},{user,User,PassWord})->
- case getUsers(AccessData,group_file) of
- {group_data,Groups}->
- {ok, Members } = getGroupMembers(Groups,AllowedGroups),
- authenticateUser(Info,AccessData,{users,Members},
- {user,User,PassWord});
- {error, _BadData}->
- deny
- end;
-
-
-%----------------------------------------------------------------------
-%Control that the user is one of the allowed users and that the passwd is ok
-%----------------------------------------------------------------------
-authenticateUser(_Info,AccessData,{users,AllowedUsers},{user,User,PassWord})->
- case lists:member(User,AllowedUsers) of
- true->
- %Get the usernames and passwords from the file
- case getUsers(AccessData,user_file) of
- {error, _BadData}->
- deny;
- {user_data,Users}->
- %Users is a list of the users in
- %the userfile [{user,User,Passwd}]
- checkPassWord(Users,{user,User,PassWord})
- end;
- false ->
- challenge
- end.
-
-
-%----------------------------------------------------------------------
-%Control that the user User={user,"UserName","PassWd"} is
-%member of the list of Users
-%----------------------------------------------------------------------
-checkPassWord(Users,User)->
- case lists:member(User,Users) of
- true->
- allow;
- false->
- challenge
- end.
-
-
-%----------------------------------------------------------------------
-%Get the users in the specified file
-%UserOrGroup is an atom that specify if its a group file or a user file
-%i.e. group_file or user_file
-%----------------------------------------------------------------------
-getUsers({file,FileName},UserOrGroup)->
- case file:open(FileName,[read]) of
- {ok,AccessFileHandle} ->
- getUsers({stream,AccessFileHandle},[],UserOrGroup);
- {error,Reason} ->
- {error,{Reason,FileName}}
- end;
-
-
-%----------------------------------------------------------------------
-%The method that starts the lokkong for user files
-%----------------------------------------------------------------------
-
-getUsers(AccessData,UserOrGroup)->
- case ets:lookup(AccessData,UserOrGroup) of
- [{UserOrGroup,File}]->
- getUsers({file,File},UserOrGroup);
- _ ->
- {error,noUsers}
- end.
-
-
-%----------------------------------------------------------------------
-%Reads data from the filehandle File to the list FileData and when its
-%reach the end it returns the list in a tuple {user_file|group_file,FileData}
-%----------------------------------------------------------------------
-getUsers({stream,File},FileData,UserOrGroup)->
- case io:get_line(File,[]) of
- eof when UserOrGroup =:= user_file ->
- {user_data,FileData};
- eof when UserOrGroup =:= group_file ->
- {group_data,FileData};
- Line ->
- getUsers({stream,File},
- formatUser(Line,FileData,UserOrGroup),UserOrGroup)
- end.
-
-
-%----------------------------------------------------------------------
-%If the line is a comment remove it
-%----------------------------------------------------------------------
-formatUser([$#|_UserDataComment],FileData,_UserOrgroup)->
- FileData;
-
-
-%----------------------------------------------------------------------
-%The user name in the file is Username:Passwd\n
-%Remove the newline sign and split the user name in
-%UserName and Password
-%----------------------------------------------------------------------
-formatUser(UserData,FileData,UserOrGroup)->
- case string:tokens(UserData," \r\n")of
- [User| _Whitespace] when UserOrGroup =:= user_file ->
- case string:tokens(User,":") of
- [Name,PassWord]->
- [{user,Name,PassWord}|FileData];
- _Error->
- FileData
- end;
- GroupData when UserOrGroup =:= group_file ->
- parseGroupData(GroupData,FileData);
- _Error ->
- FileData
- end.
-
-
-%----------------------------------------------------------------------
-%if everything is right GroupData is on the form
-% ["groupName:", "Member1", "Member2", "Member2"
-%----------------------------------------------------------------------
-parseGroupData([GroupName|GroupData],FileData)->
- [{group,formatGroupName(GroupName),GroupData}|FileData].
-
-
-%----------------------------------------------------------------------
-%the line in the file is GroupName: Member1 Member2 .....MemberN
-%Remove the : from the group name
-%----------------------------------------------------------------------
-formatGroupName(GroupName)->
- string:strip(GroupName,right,$:).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Functions that parses the accessfiles %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%Control that the asset is a real file and not a request for an virtual
-%asset
-%----------------------------------------------------------------------
-isErlScriptOrNotAccessibleFile(Path, _Info)->
- case file:read_file_info(Path) of
- {ok,_fileInfo}->
- false;
- {error,_Reason} ->
- true
- end.
-
-
-%----------------------------------------------------------------------
-%Path=PathToTheRequestedFile=String
-%Innfo=record#mod
-%----------------------------------------------------------------------
-getHtAccessData(Path,Info)->
- HtAccessFileNames=getHtAccessFileNames(Info),
- case getData(Path,Info,HtAccessFileNames) of
- {ok,public}->
- {ok,public};
- {accessData,AccessData}->
- {accessData,AccessData};
- {error,Reason} ->
- {error,Reason}
- end.
-
-
-%----------------------------------------------------------------------
-%returns the names of the accessfiles
-%----------------------------------------------------------------------
-getHtAccessFileNames(Info)->
- case httpd_util:lookup(Info#mod.config_db,access_files) of
- undefined->
- [".htaccess"];
- Files->
- Files
- end.
-%----------------------------------------------------------------------
-%HtAccessFileNames=["accessfileName1",..."AccessFileName2"]
-%----------------------------------------------------------------------
-getData(Path,Info,HtAccessFileNames)->
- SplittedPath = re:split(Path, "/", [{return, list}]),
- getData2(HtAccessFileNames,SplittedPath,Info).
-
-%----------------------------------------------------------------------
-%Add to together the data in the Splittedpath up to the path
-%that is the alias or the document root
-%Since we do not need to control after any accessfiles before here
-%----------------------------------------------------------------------
-getData2(HtAccessFileNames,SplittedPath,Info)->
- case getRootPath(SplittedPath,Info) of
- {error,Path}->
- {error,Path};
- {ok,StartPath,RestOfSplittedPath} ->
- getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info)
- end.
-
-
-%----------------------------------------------------------------------
-%HtAccessFilenames is a list the names the accesssfiles can have
-%Path is the shortest match agains all alias and documentroot
-%rest of splitted path is a list of the parts of the path
-%Info is the mod recod from the server
-%----------------------------------------------------------------------
-getData2(HtAccessFileNames, StartPath, RestOfSplittedPath, _Info)->
- case getHtAccessFiles(HtAccessFileNames,StartPath,RestOfSplittedPath) of
- []->
- %No accessfile qiut its a public directory
- {ok,public};
- Files ->
- loadAccessFilesData(Files)
- end.
-
-
-%----------------------------------------------------------------------
-%Loads the data in the accessFiles specifiied by
-% AccessFiles=["/hoem/public/html/accefile",
-% "/home/public/html/priv/accessfile"]
-%----------------------------------------------------------------------
-loadAccessFilesData(AccessFiles)->
- loadAccessFilesData(AccessFiles,ets:new(accessData,[])).
-
-
-%----------------------------------------------------------------------
-%Returns the found data
-%----------------------------------------------------------------------
-contextToValues(AccessData)->
- case ets:lookup(AccessData,context) of
- [{context,Values}]->
- ets:delete(AccessData,context),
- insertContext(AccessData,Values),
- {accessData,AccessData};
- _Error->
- {error,errorInAccessFile}
- end.
-
-
-insertContext(_AccessData, [])->
- ok;
-
-insertContext(AccessData,[{allow,From}|Values])->
- insertDenyAllowContext(AccessData,{allow,From}),
- insertContext(AccessData,Values);
-
-insertContext(AccessData,[{deny,From}|Values])->
- insertDenyAllowContext(AccessData,{deny,From}),
- insertContext(AccessData,Values);
-
-insertContext(AccessData,[{require,{GrpOrUsr,Members}}|Values])->
- case ets:lookup(AccessData,require) of
- [] when GrpOrUsr =:= users ->
- ets:insert(AccessData,{require,{{users,Members},{groups,[]}}});
-
- [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= users ->
- ets:insert(AccessData,{require,{{users,Users++Members},
- {groups,Groups}}});
- [] when GrpOrUsr =:= groups ->
- ets:insert(AccessData,{require,{{users,[]},{groups,Members}}});
-
- [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= groups ->
- ets:insert(AccessData,{require,{{users,Users},
- {groups,Groups++Members}}})
- end,
- insertContext(AccessData,Values);
-
-
-
-%%limit and order directive need no transforming they areis just to insert
-insertContext(AccessData,[Elem|Values])->
- ets:insert(AccessData,Elem),
- insertContext(AccessData,Values).
-
-
-insertDenyAllowContext(AccessData,{AllowDeny,From})->
- case From of
- all ->
- ets:insert(AccessData,{AllowDeny,all});
- _AllowedSubnets ->
- case ets:lookup(AccessData,AllowDeny) of
- []->
- ets:insert(AccessData,{AllowDeny,From});
- [{AllowDeny,all}]->
- ok;
- [{AllowDeny,Networks}]->
- ets:insert(AccessData,{allow,Networks++From})
- end
- end.
-
-loadAccessFilesData([],AccessData)->
- %preform context to limits
- contextToValues(AccessData),
- {accessData,AccessData};
-
-%----------------------------------------------------------------------
-%Takes each file in the list and load the data to the ets table
-%AccessData
-%----------------------------------------------------------------------
-loadAccessFilesData([FileName|FileNames],AccessData)->
- case loadAccessFileData({file,FileName},AccessData) of
- overRide->
- loadAccessFilesData(FileNames,AccessData);
- noOverRide ->
- {accessData,AccessData};
- error->
- ets:delete(AccessData),
- {error,errorInAccessFile}
- end.
-
-%----------------------------------------------------------------------
-%opens the filehandle to the specified file
-%----------------------------------------------------------------------
-loadAccessFileData({file,FileName},AccessData)->
- case file:open(FileName,[read]) of
- {ok,AccessFileHandle}->
- loadAccessFileData({stream,AccessFileHandle},AccessData,[]);
- {error, _Reason} ->
- overRide
- end.
-
-%----------------------------------------------------------------------
-%%look att each line in the file and add them to the database
-%%When end of file is reached control i overrride is allowed
-%% if so return
-%----------------------------------------------------------------------
-loadAccessFileData({stream,File},AccessData,FileData)->
- case io:get_line(File,[]) of
- eof->
- insertData(AccessData,FileData),
- case ets:match_object(AccessData,{'_',error}) of
- []->
- %Case we got no error control that we can override a
- %at least some of the values
- case ets:match_object(AccessData,
- {allow_over_ride,none}) of
- []->
- overRide;
- _NoOverride->
- noOverRide
- end;
- _ ->
- error
- end;
- Line ->
- loadAccessFileData({stream,File},AccessData,
- insertLine(string:strip(Line,left),FileData))
- end.
-
-%----------------------------------------------------------------------
-%AccessData is a ets table where the previous found data is inserted
-%FileData is a list of the directives in the last parsed file
-%before insertion a control is done that the directive is allowed to
-%override
-%----------------------------------------------------------------------
-insertData(AccessData,{{context,Values},FileData})->
- insertData(AccessData,[{context,Values}|FileData]);
-
-insertData(AccessData,FileData)->
- case ets:lookup(AccessData,allow_over_ride) of
- [{allow_over_ride,all}]->
- lists:foreach(fun(Elem)->
- ets:insert(AccessData,Elem)
- end,FileData);
- []->
- lists:foreach(fun(Elem)->
- ets:insert(AccessData,Elem)
- end,FileData);
- [{allow_over_ride,Directives}] when is_list(Directives)->
- lists:foreach(fun({Key,Value}) ->
- case lists:member(Key,Directives) of
- true->
- ok;
- false ->
- ets:insert(AccessData,{Key,Value})
- end
- end,FileData);
- [{allow_over_ride,_}]->
- %Will never appear if the user
- %aint doing very strang econfig files
- ok
- end.
-%----------------------------------------------------------------------
-%Take a line in the accessfile and transform it into a tuple that
-%later can be inserted in to the ets:table
-%----------------------------------------------------------------------
-%%%Here is the alternatives that resides inside the limit context
-
-insertLine("order"++ Order, {{context, Values}, FileData})->
- {{context,[{order,getOrder(Order)}|Values]},FileData};
-%%Let the user place a tab in the beginning
-insertLine([$\t,$o,$r,$d,$e,$r|Order],{{context,Values},FileData})->
- {{context,[{order,getOrder(Order)}|Values]},FileData};
-
-insertLine("allow" ++ Allow, {{context, Values}, FileData})->
- {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
-insertLine([$\t,$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})->
- {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
-
-insertLine("deny" ++ Deny, {{context,Values}, FileData})->
- {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
-insertLine([$\t, $d,$e,$n,$y|Deny],{{context,Values},FileData})->
- {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
-
-insertLine("require" ++ Require, {{context, Values}, FileData})->
- {{context,[{require,getRequireData(Require)}|Values]},FileData};
-insertLine([$\t,$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})->
- {{context,[{require,getRequireData(Require)}|Values]},FileData};
-
-insertLine("</Limit" ++ _EndLimit, {Context,FileData})->
- [Context | FileData];
-insertLine("<Limit" ++ Limit, FileData)->
- {{context,[{limit,getLimits(Limit)}]}, FileData};
-
-insertLine([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$\ |AuthUserFile],FileData)->
- [{user_file,string:strip(AuthUserFile,right,$\n)}|FileData];
-
-insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile],
- FileData)->
- [{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData];
-
-insertLine("AllowOverRide" ++ AllowOverRide, FileData)->
- [{allow_over_ride,getAllowOverRideData(AllowOverRide)}
- | FileData];
-
-insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)->
- [{auth_name,string:strip(AuthName,right,$\n)}|FileData];
-
-insertLine("AuthType" ++ AuthType,FileData)->
- [{auth_type,getAuthorizationType(AuthType)}|FileData];
-
-insertLine(_BadDirectiveOrComment,FileData)->
- FileData.
-
-%----------------------------------------------------------------------
-%transform the Data specified about override to a form that is ieasier
-%handled later
-%Override data="all"|"md5"|"Directive1 .... DirectioveN"
-%----------------------------------------------------------------------
-
-getAllowOverRideData(OverRideData)->
- case string:tokens(OverRideData," \r\n") of
- ["all" ++ _] ->
- all;
- ["none" ++ _]->
- none;
- Directives ->
- getOverRideDirectives(Directives)
- end.
-
-getOverRideDirectives(Directives)->
- lists:map(fun(Directive)->
- transformDirective(Directive)
- end,Directives).
-transformDirective("AuthUserFile" ++ _)->
- user_file;
-transformDirective("AuthGroupFile" ++ _) ->
- group_file;
-transformDirective("AuthName" ++ _)->
- auth_name;
-transformDirective("AuthType" ++ _)->
- auth_type;
-transformDirective(_UnAllowedOverRideDirective) ->
- unallowed.
-%----------------------------------------------------------------------
-%Replace the string that specify which method to use for authentication
-%and replace it with the atom for easier mathing
-%----------------------------------------------------------------------
-getAuthorizationType(AuthType)->
- [Arg | _Crap] = string:tokens(AuthType,"\n\r\ "),
- case Arg of
- "Basic"->
- basic;
- "MD5" ->
- md5;
- _What ->
- error
- end.
-%----------------------------------------------------------------------
-%Returns a list of the specified methods to limit or the atom all
-%----------------------------------------------------------------------
-getLimits(Limits)->
- case re:split(Limits,">", [{return, list}])of
- [_NoEndOnLimit]->
- error;
- [Methods | _Crap]->
- case re:split(Methods," ", [{return, list}]) of
- [[]]->
- all;
- SplittedMethods ->
- SplittedMethods
- end
- end.
-
-
-%----------------------------------------------------------------------
-% Transform the order to prefrom deny allow control to a tuple of atoms
-%----------------------------------------------------------------------
-getOrder(Order)->
- [First | _Rest]=lists:map(fun(Part)->
- list_to_atom(Part)
- end,string:tokens(Order," \n\r")),
- case First of
- deny->
- {deny,allow};
- allow->
- {allow,deny};
- _Error->
- error
- end.
-
-%----------------------------------------------------------------------
-% The string AllowDeny is "from all" or "from Subnet1 Subnet2...SubnetN"
-%----------------------------------------------------------------------
-getAllowDenyData(AllowDeny)->
- case string:tokens(AllowDeny," \n\r") of
- [_From|AllowDenyData] when length(AllowDenyData)>=1 ->
- case lists:nth(1,AllowDenyData) of
- "all" ->
- all;
- _Hosts->
- AllowDenyData
- end;
- _ ->
- error
- end.
-%----------------------------------------------------------------------
-% Fix the string that describes who is allowed to se the page
-%----------------------------------------------------------------------
-getRequireData(Require)->
- [UserOrGroup|UserData]=string:tokens(Require," \n\r"),
- case UserOrGroup of
- "user"->
- {users,UserData};
- "group" ->
- {groups,UserData};
- _Whatever ->
- error
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Methods that collects the searchways to the accessfiles %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Get the whole path to the different accessfiles
-%----------------------------------------------------------------------
-getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)->
- getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]).
-
-getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)->
- HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/");
-
-getHtAccessFiles(_HtAccessFileNames, _Path, [], HtAccessFiles)->
- HtAccessFiles;
-getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath],
- AccessFiles)->
- getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath,
- AccessFiles ++
- accessFilesOfPath(HtAccessFileNames,Path++"/")).
-
-
-%----------------------------------------------------------------------
-%Control if therer are any accessfies in the path
-%----------------------------------------------------------------------
-accessFilesOfPath(HtAccessFileNames,Path)->
- lists:foldl(fun(HtAccessFileName,Files)->
- case file:read_file_info(Path++HtAccessFileName) of
- {ok, _}->
- [Path++HtAccessFileName|Files];
- {error,_Error} ->
- Files
- end
- end,[],HtAccessFileNames).
-
-
-%----------------------------------------------------------------------
-%Sake the splitted path and joins it up to the documentroot or the alias
-%that match first
-%----------------------------------------------------------------------
-
-getRootPath(SplittedPath, Info)->
- DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"),
- PresumtiveRootPath=
- [DocRoot|lists:map(fun({_Alias,RealPath})->
- RealPath
- end,
- httpd_util:multi_lookup(Info#mod.config_db,alias))],
- getRootPath(PresumtiveRootPath,SplittedPath,Info).
-
-
-getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)->
- getRootPath(PresumtiveRootPath,["/",Splittedpath],Info);
-
-
-getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)->
- case lists:member(Part,PresumtiveRootPath)of
- true->
- {ok,Part,[NextPart|SplittedPath]};
- false ->
- getRootPath(PresumtiveRootPath,
- [Part++"/"++NextPart|SplittedPath],Info)
- end;
-
-getRootPath(PresumtiveRootPath, [Part], _Info)->
- case lists:member(Part,PresumtiveRootPath)of
- true->
- {ok,Part,[]};
- false ->
- {error,Part}
- end.
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 41b2ab950f..54b60ee1f7 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -43,14 +43,14 @@
httpc_sup,
httpc_cookie,
- http_uri, %% Proably will by used by server also in the future
-
%% HTTP used by both client and server
http_chunk,
http_request,
http_response,
http_transport,
http_util,
+
+ http_uri, %% Deprecated
%% HTTP server:
httpd,
@@ -83,14 +83,12 @@
mod_auth_mnesia,
mod_auth_plain,
mod_auth_server,
- mod_browser,
mod_cgi,
mod_dir,
mod_disk_log,
mod_esi,
mod_get,
mod_head,
- mod_htaccess,
mod_log,
mod_range,
mod_responsecontrol,
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index 3ff3ed4e97..6492325701 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -454,7 +454,7 @@ validate_request_line() ->
validate_request_line(Config) when is_list(Config) ->
%% HTTP/0.9 only has GET requests
- ok =
+ {ok, "http://www.erlang/org"} =
httpd_request:validate("GET", "http://www.erlang/org", "HTTP/0.9"),
{error, {not_supported,
{"HEAD", "http://www.erlang/org", "HTTP/0.9"}}} =
@@ -467,43 +467,37 @@ validate_request_line(Config) when is_list(Config) ->
httpd_request:validate("POST", "http://www.erlang/org", "HTTP/0.9"),
%% HTTP/1.*
- ok = httpd_request:validate("HEAD", "http://www.erlang/org",
+ {ok, "http://www.erlang/org"} = httpd_request:validate("HEAD", "http://www.erlang/org",
"HTTP/1.1"),
- ok = httpd_request:validate("GET", "http://www.erlang/org",
+ {ok, "http://www.erlang/org"} = httpd_request:validate("GET", "http://www.erlang/org",
"HTTP/1.1"),
- ok = httpd_request:validate("POST","http://www.erlang/org",
- "HTTP/1.1"),
- ok = httpd_request:validate("TRACE","http://www.erlang/org",
+ {ok, "http://www.erlang/org"} = httpd_request:validate("POST","http://www.erlang/org",
"HTTP/1.1"),
+ {ok, "http://www.erlang/org"} = httpd_request:validate("TRACE","http://www.erlang/org",
+ "HTTP/1.1"),
{error, {not_supported,
{"FOOBAR", "http://www.erlang/org", "HTTP/1.1"}}} =
httpd_request:validate("FOOBAR", "http://www.erlang/org",
"HTTP/1.1"),
+ %%% Will work after normalization
+ Uri = "http://127.0.0.1:8888/../../../../../etc/passwd",
+ {ok, "http://127.0.0.1:8888/etc/passwd"} = httpd_request:validate("GET", Uri, "HTTP/1.1"),
- %% Attempts to get outside of server_root directory by relative links
- ForbiddenUri = "http://127.0.0.1:8888/../../../../../etc/passwd",
- {error, {bad_request, {forbidden, ForbiddenUri}}} =
- httpd_request:validate("GET", ForbiddenUri, "HTTP/1.1"),
-
- ForbiddenUri2 =
+ Uri2 =
"http://127.0.0.1:8888/././././././../../../../../etc/passwd",
- {error, {bad_request, {forbidden, ForbiddenUri2}}} =
- httpd_request:validate("GET", ForbiddenUri2, "HTTP/1.1"),
-
- HexForbiddenUri = "http://127.0.0.1:8888/%2e%2e/%2e%2e/%2e%2e/"
- "home/ingela/test.html",
- {error, {bad_request, {forbidden, HexForbiddenUri}}} =
- httpd_request:validate("GET", HexForbiddenUri, "HTTP/1.1"),
-
- NewForbiddenUri =
- "http://127.0.0.1:8888/foobar/../../../home/ingela/test.html",
- {error, {bad_request, {forbidden, NewForbiddenUri}}} =
- httpd_request:validate("GET", NewForbiddenUri, "HTTP/1.1"),
-
- NewForbiddenUri1 =
- "http://127.0.0.1:8888/../home/ingela/test.html",
- {error, {bad_request, {forbidden, NewForbiddenUri1}}} =
- httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1").
+ {ok, "http://127.0.0.1:8888/etc/passwd"} = httpd_request:validate("GET", Uri2, "HTTP/1.1"),
+
+ HexUri = "http://127.0.0.1:8888/%2e%2e/%2e%2e/%2e%2e/"
+ "home/foobar/test.html",
+ {ok, "http://127.0.0.1:8888/home/foobar/test.html"} = httpd_request:validate("GET", HexUri, "HTTP/1.1"),
+
+ NewUri =
+ "http://127.0.0.1:8888/foobar/../../../home/foobar/test.html",
+ {ok,"http://127.0.0.1:8888/home/foobar/test.html"} = httpd_request:validate("GET", NewUri, "HTTP/1.1"),
+
+ Uri1 =
+ "http://127.0.0.1:8888/../home/foobar/test.html",
+ {ok,"http://127.0.0.1:8888/home/foobar/test.html"} = httpd_request:validate("GET", Uri1, "HTTP/1.1").
%%-------------------------------------------------------------------------
check_content_length_encoding() ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 1289432f0d..da60a9a9c2 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -68,8 +68,6 @@ all() ->
{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},
{group, http_reload},
@@ -84,8 +82,7 @@ all() ->
mime_types_format,
erl_script_timeout_default,
erl_script_timeout_option,
- erl_script_timeout_proplist,
- erl_script_timeout_apache
+ erl_script_timeout_proplist
].
groups() ->
@@ -104,8 +101,6 @@ groups() ->
{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}]},
{http_logging, [], [{group, logging}]},
@@ -116,16 +111,16 @@ groups() ->
{https_not_sup, [], [{group, not_sup}]},
{https_alert, [], [tls_alert]},
{http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]},
- {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {limit, [], [content_length, max_clients_1_1]},
{custom, [], [customize, add_default]},
{reload, [], [non_disturbing_reconfiger_dies,
disturbing_reconfiger_dies,
non_disturbing_1_1,
non_disturbing_1_0,
non_disturbing_0_9,
- disturbing_1_1,
- disturbing_1_0,
- disturbing_0_9,
+ disturbing_1_1,
+ disturbing_1_0,
+ disturbing_0_9,
reload_config_file
]},
{post, [], [chunked_post, chunked_chunked_encoded_post, post_204]},
@@ -136,7 +131,6 @@ groups() ->
]},
{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
{logging, [], [disk_log_internal, disk_log_exists,
disk_log_bad_size, disk_log_bad_file]},
@@ -163,7 +157,6 @@ http_get() ->
get,
%%actions, Add configuration so that this test mod_action
esi,
- content_length,
bad_hex,
missing_CR,
max_header,
@@ -231,7 +224,7 @@ init_per_group(Group, Config0) when Group == https_basic;
catch crypto:stop(),
try crypto:start() of
ok ->
- init_ssl(Group, Config0)
+ init_ssl(Group, [{http_version, "HTTP/1.0"} | Config0])
catch
_:_ ->
{skip, "Crypto did not start"}
@@ -250,7 +243,7 @@ init_per_group(Group, Config0) when Group == http_basic;
Group == http_mime_types
->
ok = start_apps(Group),
- init_httpd(Group, [{type, ip_comm} | Config0]);
+ init_httpd(Group, [{http_version, "HTTP/1.0"}, {type, ip_comm} | Config0]);
init_per_group(http_1_1, Config) ->
[{http_version, "HTTP/1.1"} | Config];
init_per_group(http_1_0, Config) ->
@@ -262,24 +255,6 @@ init_per_group(http_0_9, Config) ->
_ ->
[{http_version, "HTTP/0.9"} | Config]
end;
-init_per_group(http_htaccess = Group, Config) ->
- Path = proplists:get_value(doc_root, Config),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, proplists:get_value(address, Config)),
- ok = start_apps(Group),
- init_httpd(Group, [{type, ip_comm} | Config]);
-init_per_group(https_htaccess = Group, Config) ->
- Path = proplists:get_value(doc_root, Config),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, proplists:get_value(address, Config)),
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- init_ssl(Group, Config)
- catch
- _:_ ->
- {skip, "Crypto did not start"}
- end;
init_per_group(auth_api, Config) ->
[{auth_prefix, ""} | Config];
init_per_group(auth_api_dets, Config) ->
@@ -306,7 +281,6 @@ end_per_group(Group, _Config) when Group == http_basic;
Group == http_auth_api;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
- Group == http_htaccess;
Group == http_security;
Group == http_reload;
Group == http_post;
@@ -319,7 +293,6 @@ end_per_group(Group, _Config) when Group == https_basic;
Group == https_auth_api;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
- Group == https_htaccess;
Group == https_security;
Group == https_reload
->
@@ -453,7 +426,8 @@ head(Config) when is_list(Config) ->
Version = proplists:get_value(http_version, Config),
Host = proplists:get_value(host, Config),
ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
- proplists:get_value(port, Config), proplists:get_value(node, Config),
+ proplists:get_value(port, Config),
+ proplists:get_value(node, Config),
http_request("HEAD /index.html ", Version, Host),
[{statuscode, head_status(Version)},
{version, Version}]).
@@ -799,139 +773,15 @@ post_204(Config) ->
ct:fail({connect_error, ConnectError,
[SockType, Host, Port, TranspOpts]})
catch
- T:E ->
+ T:E:Stk ->
ct:fail({connect_failure,
[{type, T},
{error, E},
- {stacktrace, erlang:get_stacktrace()},
+ {stacktrace, Stk},
{args, [SockType, Host, Port, TranspOpts]}]})
end.
%%-------------------------------------------------------------------------
-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 = proplists:get_value(http_version, Config),
- Host = proplists:get_value(host, Config),
- Type = proplists:get_value(type, Config),
- Port = proplists:get_value(port, Config),
- Node = proplists:get_value(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"}].
@@ -960,19 +810,6 @@ max_clients_1_1() ->
max_clients_1_1(Config) when is_list(Config) ->
do_max_clients([{http_version, "HTTP/1.1"} | Config]).
-max_clients_1_0() ->
- [{doc, "Test max clients limit"}].
-
-max_clients_1_0(Config) when is_list(Config) ->
- do_max_clients([{http_version, "HTTP/1.0"} | Config]).
-
-max_clients_0_9() ->
- [{doc, "Test max clients limit"}].
-
-max_clients_0_9(Config) when is_list(Config) ->
- do_max_clients([{http_version, "HTTP/0.9"} | Config]).
-
-
%%-------------------------------------------------------------------------
put_not_sup() ->
[{doc, "Test unhandled request"}].
@@ -1003,12 +840,6 @@ esi() ->
[{doc, "Test mod_esi"}].
esi(Config) when is_list(Config) ->
- ok = http_status("GET /eval?httpd_example:print(\"Hi!\") ",
- Config, [{statuscode, 200}]),
- ok = http_status("GET /eval?not_allowed:print(\"Hi!\") ",
- Config, [{statuscode, 403}]),
- ok = http_status("GET /eval?httpd_example:undef(\"Hi!\") ",
- Config, [{statuscode, 500}]),
ok = http_status("GET /cgi-bin/erl/httpd_example ",
Config, [{statuscode, 400}]),
ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
@@ -1590,20 +1421,20 @@ do_reconfiger_dies(Config, DisturbingType) ->
Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ BlockRequest = http_request("GET /cgi-bin/erl/httpd_example:delay ", 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
Pid = spawn(fun() -> httpd:reload_config([{server_name, "httpd_kill_" ++ Version},
- {port, Port}|
- proplists:delete(server_name, HttpdConfig)], DisturbingType)
- end),
+ {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], DisturbingType)
+ end),
monitor(process, Pid),
exit(Pid, kill),
receive
- {'DOWN', _, _, _, _} ->
- ok
+ {'DOWN', _, _, _, _} ->
+ ok
end,
inets_test_lib:close(Type, Socket),
[{server_name, "httpd_test"}] = httpd:info(Server, [server_name]).
@@ -1624,7 +1455,8 @@ disturbing(Config) when is_list(Config)->
Port = proplists:get_value(port, Config),
Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+
+ BlockRequest = http_request("GET /cgi-bin/erl/httpd_example:delay ", 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
@@ -1657,7 +1489,7 @@ non_disturbing(Config) when is_list(Config)->
Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ BlockRequest = http_request("GET /cgi-bin/erl/httpd_example:delay ", 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
@@ -1696,22 +1528,11 @@ reload_config_file(Config) when is_list(Config) ->
"{server_root,\"" ++ ServerRoot ++ "\"}," ++
"{document_root,\"" ++ proplists:get_value(doc_root, Config) ++ "\"}" ++
"].",
- NewConfigApache =
- "BindAddress localhost\n" ++
- "Port " ++ integer_to_list(Port) ++ "\n" ++
- "ServerName httpd_test_new_apache\n" ++
- "ServerRoot " ++ ServerRoot ++ "\n" ++
- "DocumentRoot " ++ proplists:get_value(doc_root, Config) ++ "\n",
-
+
%% Test Erlang term format
ok = file:write_file(HttpdConf, NewConfig),
ok = httpd:reload_config(HttpdConf, non_disturbing),
- "httpd_test_new" = proplists:get_value(server_name, httpd:info(Server)),
-
- %% Test Apache format
- ok = file:write_file(HttpdConf, NewConfigApache),
- ok = httpd:reload_config(HttpdConf, non_disturbing),
- "httpd_test_new_apache" = proplists:get_value(server_name, httpd:info(Server)).
+ "httpd_test_new" = proplists:get_value(server_name, httpd:info(Server)).
%%-------------------------------------------------------------------------
mime_types_format(Config) when is_list(Config) ->
@@ -1906,47 +1727,6 @@ erl_script_timeout_proplist(Config) when is_list(Config) ->
verify_body(Body, 3000),
inets:stop().
-erl_script_timeout_apache(Config) when is_list(Config) ->
- HttpdConf = filename:join(get_tmp_dir(Config),
- "httpd_erl_script_timeout.conf"),
- MimeTypes = filename:join(get_tmp_dir(Config),
- "erl_script_timeout_mime_types.conf"),
-
- MimeTypesConf =
- "html\n" ++
- "text/html\n",
-
- ok = file:write_file(MimeTypes, MimeTypesConf),
-
- ServerConfig =
- "Port 0\n" ++
- "ServerName localhost\n" ++
- "ServerRoot ./\n" ++
- "DocumentRoot ./\n" ++
- "BindAddress 0.0.0.0\n" ++
- "MimeTypes " ++ MimeTypes ++ "\n" ++
- "Modules mod_esi\n" ++
- "ErlScriptTimeout 8\n" ++
- "ErlScriptAlias /erl httpd_example\n",
-
- ok = file:write_file(HttpdConf, ServerConfig),
-
- inets:start(),
- {ok, Pid} = inets:start(httpd,
- [{file, HttpdConf}]),
- Info = httpd:info(Pid),
- verify_timeout(Info, 8),
-
- Port = proplists:get_value(port, Info),
-
- %% Verify: 6 =< erl_script_timeout =< 10
- Url = http_get_url(Port, 500, 6000, 4000),
-
- {ok, {_, _, Body}} = httpc:request(Url),
- ct:log("Response: ~p~n", [Body]),
- verify_body(Body, 6000),
- inets:stop().
-
tls_alert(Config) when is_list(Config) ->
SSLOpts = proplists:get_value(client_alert_conf, Config),
Port = proplists:get_value(port, Config),
@@ -1999,10 +1779,9 @@ do_max_clients(Config) ->
Type = proplists:get_value(type, Config),
Request = http_request("GET /index.html ", Version, Host),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ BlockRequest = http_request("GET /cgi_bin/erl/httpd_example:delay ", 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),
@@ -2081,7 +1860,6 @@ start_apps(Group) when Group == https_basic;
Group == https_auth_api;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
- Group == https_htaccess;
Group == https_security;
Group == https_reload;
Group == https_not_sup;
@@ -2095,7 +1873,6 @@ start_apps(Group) when Group == http_basic;
Group == http_auth_api;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
- Group == http_htaccess;
Group == http_security;
Group == http_logging;
Group == http_reload;
@@ -2151,6 +1928,7 @@ server_config(https_reload, Config) ->
[{keep_alive_timeout, 2}] ++ server_config(https, Config);
server_config(http_limit, Config) ->
Conf = [{max_clients, 1},
+ {disable_chunked_transfer_encoding_send, true},
%% Make sure option checking code is run
{max_content_length, 100000002}] ++ server_config(http, Config),
ct:pal("Received message ~p~n", [Conf]),
@@ -2160,7 +1938,9 @@ server_config(http_custom, Config) ->
server_config(https_custom, Config) ->
[{customize, ?MODULE}] ++ server_config(https, Config);
server_config(https_limit, Config) ->
- [{max_clients, 1}] ++ server_config(https, Config);
+ [{max_clients, 1},
+ {disable_chunked_transfer_encoding_send, true}
+ ] ++ server_config(https, Config);
server_config(http_basic_auth, Config) ->
ServerRoot = proplists:get_value(server_root, Config),
auth_conf(ServerRoot) ++ server_config(http, Config);
@@ -2185,10 +1965,6 @@ server_config(http_auth_api_mnesia, Config) ->
server_config(https_auth_api_mnesia, Config) ->
ServerRoot = proplists:get_value(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 = proplists:get_value(server_root, Config),
tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config);
@@ -2222,8 +1998,7 @@ server_config(http, Config) ->
{alias, {"/pics/", filename:join(ServerRoot,"icons") ++ "/"}},
{script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
{script_alias, {"/htbin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
- {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
- {eval_script_alias, {"/eval", [httpd_example, io]}}
+ {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}}
];
server_config(http_rel_path_script_alias, Config) ->
ServerRoot = proplists:get_value(server_root, Config),
@@ -2243,8 +2018,7 @@ server_config(http_rel_path_script_alias, Config) ->
{alias, {"/pics/", filename:join(ServerRoot,"icons") ++ "/"}},
{script_alias, {"/cgi-bin/", "./cgi-bin/"}},
{script_alias, {"/htbin/", "./cgi-bin/"}},
- {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
- {eval_script_alias, {"/eval", [httpd_example, io]}}
+ {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}}
];
server_config(https, Config) ->
SSLConf = proplists:get_value(ssl_conf, Config),
@@ -2309,8 +2083,7 @@ not_sup_conf() ->
[{modules, [mod_get]}].
auth_access_conf() ->
- [{modules, [mod_alias, mod_htaccess, mod_dir, mod_get, mod_head]},
- {access_files, [".htaccess"]}].
+ [{modules, [mod_alias, mod_dir, mod_get, mod_head]}].
auth_conf(Root) ->
[{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
@@ -2541,103 +2314,6 @@ create_range_data(Path) ->
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"])),
-
- 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")),
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 94d22ea76c..1d3be6de57 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -302,11 +302,13 @@ escaped_url_in_error_body(Config) when is_list(Config) ->
%% Ask for a non-existing page(1)
Path = "/<b>this_is_bold<b>",
- HTMLEncodedPath = http_util:html_encode(Path),
URL2 = uri_string:recompose(#{scheme => "http",
host => "localhost",
port => Port,
path => Path}),
+
+ #{path := EncodedPath} = uri_string:parse(URL2),
+ HTMLEncodedPath = http_util:html_encode(EncodedPath),
{ok, {404, Body3}} = httpc:request(get, {URL2, []},
[{url_encode, true},
{version, "HTTP/1.0"}],
diff --git a/lib/inets/test/httpd_bench_SUITE.erl b/lib/inets/test/httpd_bench_SUITE.erl
index 087516f56c..1cba527687 100644
--- a/lib/inets/test/httpd_bench_SUITE.erl
+++ b/lib/inets/test/httpd_bench_SUITE.erl
@@ -87,7 +87,8 @@ init_per_suite(Config) ->
{Node, Host} = setup(Config, node()),
init_ssl(Config),
[{iter, 10}, {server_node, Node}, {server_host, Host} | Config]
- catch _:_ ->
+ catch E:R:ST ->
+ ct:pal("~p:~p:~p",[E,R,ST]),
{skipped, "Benchmark machines only"}
end.
diff --git a/lib/inets/test/httpd_mod_SUITE.erl b/lib/inets/test/httpd_mod_SUITE.erl
index 5ec4e0856d..ebef7eea6c 100644
--- a/lib/inets/test/httpd_mod_SUITE.erl
+++ b/lib/inets/test/httpd_mod_SUITE.erl
@@ -53,7 +53,6 @@ groups() ->
{mod_actions, [], []},
{mod_security, [], []},
{mod_auth, [], []},
- {mod_htaccess, [], []},
{mod_cgi, [], []},
{mod_esi, [], []},
{mod_head, [], []},
@@ -66,7 +65,6 @@ all_version_groups ()->
{group, mod_actions},
{group, mod_security},
{group, mod_auth},
- {group, mod_htaccess},
{group, mod_cgi},
{group, mod_esi},
{group, mod_head}
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index b6525037b2..31a9c72e9c 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -119,11 +119,11 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, Ti
ct:fail({connect_error, ConnectError,
[SocketType, Host, Port, TranspOpts]})
catch
- T:E ->
+ T:E:Stk ->
ct:fail({connect_failure,
[{type, T},
{error, E},
- {stacktrace, erlang:get_stacktrace()},
+ {stacktrace, Stk},
{args, [SocketType, Host, Port, TranspOpts]}]})
end.
@@ -136,11 +136,11 @@ verify_request_N(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options,
ct:fail({connect_error, ConnectError,
[SocketType, Host, Port, TranspOpts]})
catch
- T:E ->
+ T:E:Stk ->
ct:fail({connect_failure,
[{type, T},
{error, E},
- {stacktrace, erlang:get_stacktrace()},
+ {stacktrace, Stk},
{args, [SocketType, Host, Port, TranspOpts]}]})
end.
@@ -388,8 +388,8 @@ is_expect(RequestStr) ->
end.
%% 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) ->
- ct:fail(content_length_error);
+check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.1\r\n\r\n", 200, "text/html", Length, _Body) when (Length =/= 274) ->
+ ct:fail({content_length_error, Length});
check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain",
_, Body) ->
case size(Body) of
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index e7964ff7f1..0ec3ce86ad 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -246,52 +246,6 @@ start_httpd(Config) when is_list(Config) ->
ok = inets:start(),
(?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
application:unset_env(inets, services),
- ok = inets:stop(),
-
- File1 = filename:join(PrivDir, "httpd_apache.conf"),
-
- {ok, Fd1} = file:open(File1, [write]),
- file:write(Fd1, "ServerName httpd_test\r\n"),
- file:write(Fd1, "ServerRoot " ++ PrivDir ++ "\r\n"),
- file:write(Fd1, "DocumentRoot " ++ PrivDir ++" \r\n"),
- file:write(Fd1, "BindAddress *|inet\r\n"),
- file:write(Fd1, "Port 0\r\n"),
- file:close(Fd1),
-
- application:load(inets),
- application:set_env(inets,
- services, [{httpd, [{file, File1}]}]),
- ok = inets:start(),
- (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- application:unset_env(inets, services),
- ok = inets:stop(),
-
- %% OLD format
- application:load(inets),
- application:set_env(inets,
- services, [{httpd, File1}]),
- ok = inets:start(),
- (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- application:unset_env(inets, services),
- ok = inets:stop(),
- ok = inets:start(),
- {error, {missing_property, server_name}} =
- inets:start(httpd, [{port, 0},
- {server_root, PrivDir},
- {document_root, PrivDir},
- {bind_address, "localhost"}]),
- {error, {missing_property, document_root}} =
- inets:start(httpd, [{port, 0},
- {server_name, "httpd_test"},
- {server_root, PrivDir},
- {bind_address, "localhost"}]),
- {error, {missing_property, server_root}} =
- inets:start(httpd, [{port, 0},
- {server_name, "httpd_test"},
- {document_root, PrivDir},
- {bind_address, "localhost"}]),
- {error, {missing_property, port}} =
- inets:start(httpd, HttpdConf),
ok = inets:stop().
%%-------------------------------------------------------------------------
@@ -337,48 +291,4 @@ httpd_reload(Config) when is_list(Config) ->
[{document_root, PrivDir}] = httpd:info(Pid0, [document_root]),
ok = inets:stop(httpd, Pid0),
- ok = inets:stop(),
-
- File = filename:join(PrivDir, "httpd_apache.conf"),
-
- {ok, Fd0} = file:open(File, [write]),
- file:write(Fd0, "ServerName httpd_test\r\n"),
- file:write(Fd0, "ServerRoot " ++ PrivDir ++ "\r\n"),
- file:write(Fd0, "DocumentRoot " ++ PrivDir ++" \r\n"),
- file:write(Fd0, "BindAddress *\r\n"),
- file:write(Fd0, "Port 0\r\n"),
- file:close(Fd0),
-
- application:load(inets),
- application:set_env(inets,
- services, [{httpd, [{file, File}]}]),
-
- ok = inets:start(),
- [Pid1] = [HttpdPid || {httpd, HttpdPid} <- inets:services()],
- [{server_name, "httpd_test"}] = httpd:info(Pid1, [server_name]),
- [{port, Port1}] = httpd:info(Pid1, [port]),
- {ok, Fd1} = file:open(File, [write]),
- file:write(Fd1, "ServerName httpd_test2\r\n"),
- file:write(Fd1, "ServerRoot " ++ PrivDir ++ "\r\n"),
- file:write(Fd1, "DocumentRoot " ++ PrivDir ++" \r\n"),
- file:write(Fd1, "BindAddress *\r\n"),
- file:write(Fd1, "Port " ++ integer_to_list(Port1) ++ "\r\n"),
- file:close(Fd1),
-
- ok = httpd:reload_config(File, non_disturbing),
- [{server_name, "httpd_test2"}] = httpd:info(Pid1, [server_name]),
-
- {ok, Fd2} = file:open(File, [write]),
- file:write(Fd2, "ServerName httpd_test\r\n"),
- file:write(Fd2, "ServerRoot " ++ PrivDir ++ "\r\n"),
- file:write(Fd2, "DocumentRoot " ++ PrivDir ++" \r\n"),
- file:write(Fd2, "BindAddress *\r\n"),
- file:write(Fd2, "Port " ++ integer_to_list(Port1) ++ "\r\n"),
- file:close(Fd2),
- ok = httpd:reload_config(File, disturbing),
- [{server_name, "httpd_test"}] = httpd:info(Pid1, [server_name]),
-
- ok = inets:stop(httpd, Pid1),
- application:unset_env(inets, services),
ok = inets:stop().
-
diff --git a/lib/jinterface/Makefile b/lib/jinterface/Makefile
index 9cf5f3e94c..dd22d743a5 100644
--- a/lib/jinterface/Makefile
+++ b/lib/jinterface/Makefile
@@ -39,3 +39,4 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index f5cba9d074..348cf0e429 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -53,14 +53,9 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APP_FILES) $(XML_REF3_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES =
-#------------------------------------------------------
-
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+NO_CHUNKS=$(XML_REF3_FILES)
+#------------------------------------------------------
JAVADOC = javadoc
JAVADOC_PKGS = com.ericsson.otp.erlang
@@ -86,75 +81,20 @@ JAVA_DOC_FILES = \
stylesheet.css \
help-doc.html
-INFO_FILE = ../../info
-JAVA_EXTRA_FILES = $(JAVA_DOC_FILES:%=$(HTMLDIR)/java/%)
-
JAVA_GEN_FILES = \
$(JAVA_FILES:%=$(JAVADOC_DEST)/$(JAVA_PKG_PATH)/%.html) \
$(JAVADOC_DEST)/$(JAVA_PKG_PATH)/package-summary.html \
$(JAVADOC_DEST)/$(JAVA_PKG_PATH)/package-tree.html \
$(JAVADOC_DEST)/$(JAVA_PKG_PATH)/package-frame.html
-
-# ----------------------------------------------------
-
-
-HTML_FILES = \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-TOP_HTML_FILES = $(INDEX_TARGET)
-
-INDEX_FILE = index.html
-INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html jdoc man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
jdoc:$(JAVA_SRC_FILES)
(cd ../../java_src;$(JAVADOC) -sourcepath . -d $(JAVADOC_DEST) \
-windowtitle $(JAVADOC_TITLE) $(JAVADOC_PKGS))
touch jdoc
-man:
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-# ----------------------------------------------------
-# 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_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR_DATA) $(HTMLDIR) "$(RELSYSDIR)/doc/html"
-
+docs: jdoc
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
index 0bf3ca2a67..26f6ffcd97 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -147,21 +147,6 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("<- ACCEPT FROM " + s);
}
-
- // get his info
- recvName(peer);
-
- // now find highest common dist value
- if (peer.proto != self.proto || self.distHigh < peer.distLow
- || self.distLow > peer.distHigh) {
- close();
- throw new IOException(
- "No common protocol found - cannot accept connection");
- }
- // highest common version: min(peer.distHigh, self.distHigh)
- peer.distChoose = peer.distHigh > self.distHigh ? self.distHigh
- : peer.distHigh;
-
doAccept();
name = peer.node();
}
@@ -953,10 +938,12 @@ public abstract class AbstractConnection extends Thread {
}
protected void doAccept() throws IOException, OtpAuthException {
+ final int send_name_tag = recvName(peer);
try {
sendStatus("ok");
final int our_challenge = genChallenge();
- sendChallenge(peer.distChoose, localNode.flags, our_challenge);
+ sendChallenge(peer.flags, localNode.flags, our_challenge);
+ recvComplement(send_name_tag);
final int her_challenge = recvChallengeReply(our_challenge);
final byte[] our_digest = genDigest(her_challenge,
localNode.cookie());
@@ -992,12 +979,14 @@ public abstract class AbstractConnection extends Thread {
System.out.println("-> MD5 CONNECT TO " + peer.host() + ":"
+ port);
}
- sendName(peer.distChoose, localNode.flags);
+ final int send_name_tag = sendName(peer.distChoose, localNode.flags,
+ localNode.creation);
recvStatus();
final int her_challenge = recvChallenge();
final byte[] our_digest = genDigest(her_challenge,
localNode.cookie());
final int our_challenge = genChallenge();
+ sendComplement(send_name_tag);
sendChallengeReply(our_challenge, our_digest);
recvChallengeAck(our_challenge);
cookieOk = true;
@@ -1070,17 +1059,31 @@ public abstract class AbstractConnection extends Thread {
return res;
}
- protected void sendName(final int dist, final int aflags)
+ protected int sendName(final int dist, final long aflags,
+ final int creation)
throws IOException {
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
final String str = localNode.node();
- obuf.write2BE(str.length() + 7); // 7 bytes + nodename
- obuf.write1(AbstractNode.NTYPE_R6);
- obuf.write2BE(dist);
- obuf.write4BE(aflags);
- obuf.write(str.getBytes());
+ int send_name_tag;
+ if (dist == 5) {
+ obuf.write2BE(1+2+4 + str.length());
+ send_name_tag = 'n';
+ obuf.write1(send_name_tag);
+ obuf.write2BE(dist);
+ obuf.write4BE(aflags);
+ obuf.write(str.getBytes());
+ }
+ else {
+ obuf.write2BE(1+8+4+2 + str.length());
+ send_name_tag = 'N';
+ obuf.write1(send_name_tag);
+ obuf.write8BE(aflags);
+ obuf.write4BE(creation);
+ obuf.write2BE(str.length());
+ obuf.write(str.getBytes());
+ }
obuf.writeToAndFlush(socket.getOutputStream());
@@ -1088,26 +1091,61 @@ public abstract class AbstractConnection extends Thread {
System.out.println("-> " + "HANDSHAKE sendName" + " flags="
+ aflags + " dist=" + dist + " local=" + localNode);
}
+ return send_name_tag;
}
- protected void sendChallenge(final int dist, final int aflags,
- final int challenge) throws IOException {
+ protected void sendComplement(final int send_name_tag)
+ throws IOException {
+
+ if (send_name_tag == 'n' &&
+ (peer.flags & AbstractNode.dFlagHandshake23) != 0) {
+ @SuppressWarnings("resource")
+ final OtpOutputStream obuf = new OtpOutputStream();
+ obuf.write2BE(1+4+4);
+ obuf.write1('c');
+ final int flagsHigh = (int)(localNode.flags >> 32);
+ obuf.write4BE(flagsHigh);
+ obuf.write4BE(localNode.creation);
+
+ obuf.writeToAndFlush(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendComplement" +
+ " flagsHigh=" + flagsHigh +
+ " creation=" + localNode.creation);
+ }
+ }
+ }
+
+ protected void sendChallenge(final long her_flags, final long our_flags,
+ final int challenge) throws IOException {
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
final String str = localNode.node();
- obuf.write2BE(str.length() + 11); // 11 bytes + nodename
- obuf.write1(AbstractNode.NTYPE_R6);
- obuf.write2BE(dist);
- obuf.write4BE(aflags);
- obuf.write4BE(challenge);
- obuf.write(str.getBytes());
+ if ((her_flags & AbstractNode.dFlagHandshake23) == 0) {
+ obuf.write2BE(1+2+4+4 + str.length());
+ obuf.write1('n');
+ obuf.write2BE(5);
+ obuf.write4BE(our_flags & 0xffffffff);
+ obuf.write4BE(challenge);
+ obuf.write(str.getBytes());
+ }
+ else {
+ obuf.write2BE(1+8+4+4+2 + str.length());
+ obuf.write1('N');
+ obuf.write8BE(our_flags);
+ obuf.write4BE(challenge);
+ obuf.write4BE(localNode.creation);
+ obuf.write2BE(str.length());
+ obuf.write(str.getBytes());
+ }
obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallenge" + " flags="
- + aflags + " dist=" + dist + " challenge=" + challenge
+ + our_flags + " challenge=" + challenge
+ " local=" + localNode);
}
}
@@ -1127,8 +1165,8 @@ public abstract class AbstractConnection extends Thread {
return tmpbuf;
}
- protected void recvName(final OtpPeer apeer) throws IOException {
-
+ protected int recvName(final OtpPeer apeer) throws IOException {
+ int send_name_tag;
String hisname = "";
try {
@@ -1137,25 +1175,31 @@ public abstract class AbstractConnection extends Thread {
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
byte[] tmpname;
final int len = tmpbuf.length;
- apeer.ntype = ibuf.read1();
- if (apeer.ntype != AbstractNode.NTYPE_R6) {
+ send_name_tag = ibuf.read1();
+ switch (send_name_tag) {
+ case 'n':
+ apeer.distLow = apeer.distHigh = ibuf.read2BE();
+ if (apeer.distLow != 5)
+ throw new IOException("Invalid handshake version");
+ apeer.flags = ibuf.read4BE();
+ tmpname = new byte[len - 7];
+ ibuf.readN(tmpname);
+ hisname = OtpErlangString.newString(tmpname);
+ break;
+ case 'N':
+ apeer.distLow = apeer.distHigh = 6;
+ apeer.flags = ibuf.read8BE();
+ if ((apeer.flags & AbstractNode.dFlagHandshake23) == 0)
+ throw new IOException("Missing DFLAG_HANDSHAKE_23");
+ apeer.creation = ibuf.read4BE();
+ int namelen = ibuf.read2BE();
+ tmpname = new byte[namelen];
+ ibuf.readN(tmpname);
+ hisname = OtpErlangString.newString(tmpname);
+ break;
+ default:
throw new IOException("Unknown remote node type");
}
- apeer.distLow = apeer.distHigh = ibuf.read2BE();
- if (apeer.distLow < 5) {
- throw new IOException("Unknown remote node type");
- }
- apeer.flags = ibuf.read4BE();
- tmpname = new byte[len - 7];
- ibuf.readN(tmpname);
- hisname = OtpErlangString.newString(tmpname);
- // Set the old nodetype parameter to indicate hidden/normal status
- // When the old handshake is removed, the ntype should also be.
- if ((apeer.flags & AbstractNode.dFlagPublished) != 0) {
- apeer.ntype = AbstractNode.NTYPE_R4_ERLANG;
- } else {
- apeer.ntype = AbstractNode.NTYPE_R4_HIDDEN;
- }
if ((apeer.flags & AbstractNode.dFlagExtendedReferences) == 0) {
throw new IOException(
@@ -1180,6 +1224,7 @@ public abstract class AbstractConnection extends Thread {
System.out.println("<- " + "HANDSHAKE" + " ntype=" + apeer.ntype
+ " dist=" + apeer.distHigh + " remote=" + apeer);
}
+ return send_name_tag;
}
protected int recvChallenge() throws IOException {
@@ -1190,14 +1235,31 @@ public abstract class AbstractConnection extends Thread {
final byte[] buf = read2BytePackage();
@SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(buf, 0);
- peer.ntype = ibuf.read1();
- if (peer.ntype != AbstractNode.NTYPE_R6) {
+ int namelen;
+ switch (ibuf.read1()) {
+ case 'n':
+ if (peer.distChoose != 5)
+ throw new IOException("Old challenge wrong version");
+ peer.distLow = peer.distHigh = ibuf.read2BE();
+ peer.flags = ibuf.read4BE();
+ if ((peer.flags & AbstractNode.dFlagHandshake23) != 0)
+ throw new IOException("Old challenge unexpected DFLAG_HANDHAKE_23");
+ challenge = ibuf.read4BE();
+ namelen = buf.length - (1+2+4+4);
+ break;
+ case 'N':
+ peer.distLow = peer.distHigh = peer.distChoose = 6;
+ peer.flags = ibuf.read8BE();
+ if ((peer.flags & AbstractNode.dFlagHandshake23) == 0)
+ throw new IOException("New challenge missing DFLAG_HANDHAKE_23");
+ challenge = ibuf.read4BE();
+ peer.creation = ibuf.read4BE();
+ namelen = ibuf.read2BE();
+ break;
+ default:
throw new IOException("Unexpected peer type");
}
- peer.distLow = peer.distHigh = ibuf.read2BE();
- peer.flags = ibuf.read4BE();
- challenge = ibuf.read4BE();
- final byte[] tmpname = new byte[buf.length - 11];
+ final byte[] tmpname = new byte[namelen];
ibuf.readN(tmpname);
final String hisname = OtpErlangString.newString(tmpname);
if (!hisname.equals(peer.node)) {
@@ -1228,6 +1290,27 @@ public abstract class AbstractConnection extends Thread {
return challenge;
}
+ protected void recvComplement(int send_name_tag) throws IOException {
+
+ if (send_name_tag == 'n' &&
+ (peer.flags & AbstractNode.dFlagHandshake23) != 0) {
+ try {
+ final byte[] tmpbuf = read2BytePackage();
+ @SuppressWarnings("resource")
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+ if (ibuf.read1() != 'c')
+ throw new IOException("Not a complement tag");
+
+ final long flagsHigh = ibuf.read4BE();
+ peer.flags |= flagsHigh << 32;
+ peer.creation = ibuf.read4BE();
+
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ }
+ }
+ }
+
protected void sendChallengeReply(final int challenge, final byte[] digest)
throws IOException {
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 c3f71a84f0..fa6db9a046 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -74,10 +74,7 @@ public class AbstractNode implements OtpTransportFactory {
static String defaultCookie = null;
final OtpTransportFactory transportFactory;
- // Node types
static final int NTYPE_R6 = 110; // 'n' post-r5, all nodes
- static final int NTYPE_R4_ERLANG = 109; // 'm' Only for source compatibility
- static final int NTYPE_R4_HIDDEN = 104; // 'h' Only for source compatibility
// Node capability flags
static final int dFlagPublished = 1;
@@ -96,17 +93,19 @@ public class AbstractNode implements OtpTransportFactory {
static final int dFlagUtf8Atoms = 0x10000;
static final int dFlagMapTag = 0x20000;
static final int dFlagBigCreation = 0x40000;
+ static final int dFlagHandshake23 = 0x1000000;
int ntype = NTYPE_R6;
int proto = 0; // tcp/ip
- int distHigh = 5; // Cannot talk to nodes before R6
+ int distHigh = 6;
int distLow = 5; // Cannot talk to nodes before R6
int creation = 0;
- int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
+ long flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
| dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
| dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag
| dFlagExportPtrTag
- | dFlagBigCreation;
+ | dFlagBigCreation
+ | dFlagHandshake23;
/* initialize hostname and default cookie */
static {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index ee616f3d7e..bcbb206db6 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -67,8 +67,11 @@ JARFILE= OtpErlang.jar
# ----------------------------------------------------
# Programs and Flags
# ----------------------------------------------------
-
-JAR= jar
+ifeq ($(TARGET),win32)
+ JAR=jar.exe
+else
+ JAR=jar
+endif
CLASSPATH = $(JAVA_SRC_ROOT)
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
index fffb8475d3..008ee9727e 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -74,8 +74,9 @@ public class OtpEpmd {
private static final byte port4req = (byte) 122;
private static final byte port4resp = (byte) 119;
- private static final byte publish4req = (byte) 120;
- private static final byte publish4resp = (byte) 121;
+ private static final byte ALIVE2_REQ = (byte) 120;
+ private static final byte ALIVE2_RESP = (byte) 121;
+ private static final byte ALIVE2_X_RESP = (byte) 118;
private static final byte names4req = (byte) 110;
private static int traceLevel = 0;
@@ -287,7 +288,7 @@ public class OtpEpmd {
obuf.write2BE(node.alive().length() + 13);
- obuf.write1(publish4req);
+ obuf.write1(ALIVE2_REQ);
obuf.write2BE(node.port());
obuf.write1(node.type());
@@ -322,10 +323,11 @@ public class OtpEpmd {
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
final int response = ibuf.read1();
- if (response == publish4resp) {
+ if (response == ALIVE2_RESP || response == ALIVE2_X_RESP) {
final int result = ibuf.read1();
if (result == 0) {
- node.creation = ibuf.read2BE();
+ node.creation = (response == ALIVE2_RESP
+ ? ibuf.read2BE() : ibuf.read4BE());
if (traceLevel >= traceThreshold) {
System.out.println("<- OK");
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
index 9cbd735751..3abdf9535f 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
@@ -27,7 +27,6 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object>
// don't change this!
private static final long serialVersionUID = 1664394142301803659L;
- private final int tag;
private final String node;
private final int id;
private final int serial;
@@ -45,7 +44,6 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object>
public OtpErlangPid(final OtpLocalNode self) {
final OtpErlangPid p = self.createPid();
- tag = p.tag;
id = p.id;
serial = p.serial;
creation = p.creation;
@@ -67,7 +65,6 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object>
throws OtpErlangDecodeException {
final OtpErlangPid p = buf.read_pid();
- tag = p.tag;
node = p.node();
id = p.id();
serial = p.serial();
@@ -118,7 +115,6 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object>
*/
protected OtpErlangPid(final int tag, final String node, final int id,
final int serial, final int creation) {
- this.tag = tag;
this.node = node;
if (tag == OtpExternal.pidTag) {
this.id = id & 0x7fff; // 15 bits
@@ -133,7 +129,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object>
}
protected int tag() {
- return tag;
+ return OtpExternal.newPidTag;
}
/**
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
index 79b5d2736c..c8648d7aa3 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
@@ -26,7 +26,6 @@ public class OtpErlangPort extends OtpErlangObject {
// don't change this!
private static final long serialVersionUID = 4037115468007644704L;
- private final int tag;
private final String node;
private final int id;
private final int creation;
@@ -43,7 +42,6 @@ public class OtpErlangPort extends OtpErlangObject {
private OtpErlangPort(final OtpSelf self) {
final OtpErlangPort p = self.createPort();
- tag = p.tag;
id = p.id;
creation = p.creation;
node = p.node;
@@ -64,7 +62,6 @@ public class OtpErlangPort extends OtpErlangObject {
throws OtpErlangDecodeException {
final OtpErlangPort p = buf.read_port();
- tag = p.tag;
node = p.node();
id = p.id();
creation = p.creation();
@@ -105,7 +102,6 @@ public class OtpErlangPort extends OtpErlangObject {
*/
public OtpErlangPort(final int tag, final String node, final int id,
final int creation) {
- this.tag = tag;
this.node = node;
if (tag == OtpExternal.portTag) {
this.id = id & 0xfffffff; // 28 bits
@@ -118,7 +114,7 @@ public class OtpErlangPort extends OtpErlangObject {
}
protected int tag() {
- return tag;
+ return OtpExternal.newPortTag;
}
/**
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
index 2165397013..2bf8d9a56b 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
@@ -28,7 +28,6 @@ public class OtpErlangRef extends OtpErlangObject {
// don't change this!
private static final long serialVersionUID = -7022666480768586521L;
- private final int tag;
private final String node;
private final int creation;
@@ -49,7 +48,6 @@ public class OtpErlangRef extends OtpErlangObject {
public OtpErlangRef(final OtpLocalNode self) {
final OtpErlangRef r = self.createRef();
- tag = r.tag;
ids = r.ids;
creation = r.creation;
node = r.node;
@@ -70,7 +68,6 @@ public class OtpErlangRef extends OtpErlangObject {
throws OtpErlangDecodeException {
final OtpErlangRef r = buf.read_ref();
- tag = r.tag;
node = r.node();
creation = r.creation();
@@ -90,7 +87,6 @@ public class OtpErlangRef extends OtpErlangObject {
* another arbitrary number.
*/
public OtpErlangRef(final String node, final int id, final int creation) {
- this.tag = OtpExternal.newRefTag;
this.node = node;
ids = new int[1];
ids[0] = id & 0x3ffff; // 18 bits
@@ -138,7 +134,6 @@ public class OtpErlangRef extends OtpErlangObject {
*/
public OtpErlangRef(final int tag, final String node, final int[] ids,
final int creation) {
- this.tag = tag;
this.node = node;
// use at most 3 words
@@ -162,7 +157,7 @@ public class OtpErlangRef extends OtpErlangObject {
}
protected int tag() {
- return tag;
+ return OtpExternal.newerRefTag;
}
/**
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 6d81ce630b..8cc5b3c21d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -239,6 +239,20 @@ public class OtpInputStream extends ByteArrayInputStream {
}
/**
+ * Read a eight byte big endian integer from the stream.
+ *
+ * @return the bytes read, converted from big endian to a long integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public long read8BE() throws OtpErlangDecodeException {
+ long high = read4BE();
+ long low = read4BE();
+ return (high << 32) | (low & 0xffffffff);
+ }
+
+ /**
* Read a two byte little endian integer from the stream.
*
* @return the bytes read, converted from little endian to an integer.
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index 5e777c1164..917e5baf3a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -713,7 +713,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
*/
public void write_pid(final String node, final int id, final int serial,
final int creation) {
- write1(OtpExternal.pidTag);
+ write1(OtpExternal.newPidTag);
write_atom(node);
write4BE(id & 0x7fff); // 15 bits
write4BE(serial & 0x1fff); // 13 bits
@@ -727,20 +727,11 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* the pid
*/
public void write_pid(OtpErlangPid pid) {
- write1(pid.tag());
+ write1(OtpExternal.newPidTag);
write_atom(pid.node());
write4BE(pid.id());
write4BE(pid.serial());
- switch (pid.tag()) {
- case OtpExternal.pidTag:
- write1(pid.creation());
- break;
- case OtpExternal.newPidTag:
- write4BE(pid.creation());
- break;
- default:
- throw new AssertionError("Invalid pid tag " + pid.tag());
- }
+ write4BE(pid.creation());
}
@@ -758,7 +749,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* be used.
*/
public void write_port(final String node, final int id, final int creation) {
- write1(OtpExternal.portTag);
+ write1(OtpExternal.newPortTag);
write_atom(node);
write4BE(id & 0xfffffff); // 28 bits
write1(creation & 0x3); // 2 bits
@@ -771,19 +762,10 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* the port.
*/
public void write_port(OtpErlangPort port) {
- write1(port.tag());
+ write1(OtpExternal.newPortTag);
write_atom(port.node());
write4BE(port.id());
- switch (port.tag()) {
- case OtpExternal.portTag:
- write1(port.creation());
- break;
- case OtpExternal.newPortTag:
- write4BE(port.creation());
- break;
- default:
- throw new AssertionError("Invalid port tag " + port.tag());
- }
+ write4BE(port.creation());
}
/**
@@ -829,7 +811,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
arity = 3; // max 3 words in ref
}
- write1(OtpExternal.newRefTag);
+ write1(OtpExternal.newerRefTag);
// how many id values
write2BE(arity);
@@ -857,24 +839,12 @@ public class OtpOutputStream extends ByteArrayOutputStream {
int[] ids = ref.ids();
int arity = ids.length;
- write1(ref.tag());
+ write1(OtpExternal.newerRefTag);
write2BE(arity);
write_atom(ref.node());
+ write4BE(ref.creation());
- switch (ref.tag()) {
- case OtpExternal.newRefTag:
- write1(ref.creation());
- write4BE(ids[0] & 0x3ffff); // first word gets truncated to 18 bits
- break;
- case OtpExternal.newerRefTag:
- write4BE(ref.creation());
- write4BE(ids[0]); // full first word
- break;
- default:
- throw new AssertionError("Invalid ref tag " + ref.tag());
- }
-
- for (int i = 1; i < arity; i++) {
+ for (int i = 0; i < arity; i++) {
write4BE(ids[i]);
}
}
diff --git a/lib/kernel/Makefile b/lib/kernel/Makefile
index b956f5eaf5..5ab8ac63b9 100644
--- a/lib/kernel/Makefile
+++ b/lib/kernel/Makefile
@@ -34,3 +34,5 @@ SPECIAL_TARGETS =
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index fe3dc9dab5..9b004b3781 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(KERNEL_VSN)
APPLICATION=kernel
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -46,6 +41,7 @@ XML_REF3_FILES = application.xml \
erl_epmd.xml \
erl_prim_loader_stub.xml \
erlang_stub.xml \
+ erpc.xml \
error_handler.xml \
error_logger.xml \
file.xml \
@@ -67,6 +63,7 @@ XML_REF3_FILES = application.xml \
net_adm.xml \
net_kernel.xml \
os.xml \
+ pg.xml \
pg2.xml \
rpc.xml \
seq_trace.xml \
@@ -96,86 +93,8 @@ XML_FILES = \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES)\
$(XML_REF6_FILES) $(XML_APPLICATION_FILES)
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
-
-# ----------------------------------------------------
-# FIGURES
-# ----------------------------------------------------
-# In order to update the figures you have to have both dia
-# and imagemagick installed.
-# The generated .png file must be committed.
-
-update_png:
- dia --export=logger_arch.eps logger_arch.dia
- convert logger_arch.eps -resize 65% logger_arch.png
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-SPECS_ESRC = ../../src
-
-SPECS_FLAGS = -I../../include
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%: %
- $(INSTALL_DATA) $< $@
-
-docs: man pdf html
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: images $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES)
-
-images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
-
-info:
- @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)"
- @echo "XML_REF3_ESOCK_FILES: $(XML_REF3_ESOCK_FILES)"
- @echo "XML_REF3_FILES: $(XML_REF3_FILES)"
- @echo "XML_REF4_FILES: $(XML_REF4_FILES)"
- @echo "XML_REF6_FILES: $(XML_REF6_FILES)"
- @echo "XML_PART_FILES: $(XML_PART_FILES)"
- @echo "XML_CHAPTER_FILES: $(XML_CHAPTER_FILES)"
- @echo "BOOK_FILES: $(BOOK_FILES)"
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN4DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~ *.eps
-
$(SPECDIR)/specs_erl_prim_loader_stub.xml:
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_prim_loader_stub
@@ -189,24 +108,17 @@ $(SPECDIR)/specs_zlib_stub.xml:
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module zlib_stub
+NO_CHUNKS = erl_prim_loader_stub.xml erlang_stub.xml init_stub.xml zlib_stub.xml
# ----------------------------------------------------
-# Release Target
+# FIGURES
# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
+# In order to update the figures you have to have both dia
+# and imagemagick installed.
+# The generated .png file must be committed.
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+update_png:
+ dia --export=logger_arch.eps logger_arch.dia
+ convert logger_arch.eps -resize 65% logger_arch.png
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index 4aa9e8b9d2..45538d3239 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -315,6 +315,9 @@ zip:create("mnesia-4.4.7.ez",
<name name="load_error_rsn"/>
</datatype>
<datatype>
+ <name name="module_status"/>
+ </datatype>
+ <datatype>
<name name="prepared_code"/>
<desc><p>An opaque term holding prepared code.</p></desc>
</datatype>
@@ -702,6 +705,21 @@ ok = code:finish_loading(Prepared),
</desc>
</func>
<func>
+ <name name="all_available" arity="0" since="OTP @OTP-16494@"/>
+ <fsummary>Get all available modules.</fsummary>
+ <type name="loaded_filename"/>
+ <type name="loaded_ret_atoms"/>
+ <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute
+ filename.</type_desc>
+ <desc>
+ <p>Returns a list of tuples <c>{<anno>Module</anno>, <anno>Filename</anno>,
+ <anno>Loaded</anno>}</c> for all available modules. A module is considered
+ to be available if it either is loaded or would be loaded if called.
+ <c><anno>Filename</anno></c> is normally the absolute filename, as described for
+ <seealso marker="#is_loaded/1"><c>is_loaded/1</c></seealso>.</p>
+ </desc>
+ </func>
+ <func>
<name name="all_loaded" arity="0" since=""/>
<fsummary>Get all loaded modules.</fsummary>
<type name="loaded_filename"/>
@@ -718,6 +736,7 @@ ok = code:finish_loading(Prepared),
<func>
<name name="which" arity="1" since=""/>
<fsummary>The object code file of a module.</fsummary>
+ <type name="loaded_filename"/>
<type name="loaded_ret_atoms"/>
<desc>
<p>If the module is not loaded, this function searches the code
@@ -750,6 +769,22 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]),
</desc>
</func>
<func>
+ <name name="get_doc" arity="1" since="OTP @OTP-16406@"/>
+ <fsummary>Gets the documentation for a module.</fsummary>
+ <desc>
+ <p>Searches the code path for a documentation chunk
+ and returns ut if available. If no documentation chunk
+ can be found the function tries to generate documentation
+ from the debug information in the module. If no debug
+ information is available, this function will return
+ <c>{error,missing}</c>.
+ </p>
+ <p>For more information about the documentation chunk see
+ <seealso marker="erl_docgen:doc_storage">Documentation Storage</seealso>
+ in Erl_Docgen's User's Guide.</p>
+ </desc>
+ </func>
+ <func>
<name name="root_dir" arity="0" since=""/>
<fsummary>Root directory of Erlang/OTP.</fsummary>
<desc>
@@ -901,10 +936,20 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]),
</desc>
</func>
<func>
+ <name name="module_status" arity="0" since="OTP 23.0"/>
+ <fsummary>Return the statuses of all loaded modules.</fsummary>
+ <type name="module_status"/>
+ <desc>
+ <p>See <seealso marker="#module_status/1"><c>module_status/1</c></seealso> and <seealso marker="#all_loaded/0"><c>all_loaded/0</c></seealso> for details.</p>
+ </desc>
+ </func>
+ <func>
<name name="module_status" arity="1" since="OTP 20.0"/>
- <fsummary>Return the status of the module in relation to object file on disk.</fsummary>
+ <fsummary>Return the status of a module or modules in relation to the
+ object files on disk.</fsummary>
+ <type name="module_status"/>
<desc>
- <p>Returns:</p>
+ <p>The status of a module can be one of:</p>
<taglist>
<tag><c>not_loaded</c></tag>
<item><p>If <c><anno>Module</anno></c> is not currently loaded.</p></item>
diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml
index e308b06f3c..4bfe9cb0db 100644
--- a/lib/kernel/doc/src/disk_log.xml
+++ b/lib/kernel/doc/src/disk_log.xml
@@ -127,6 +127,10 @@
functions fail. The corresponding terms (not the binaries)
are returned when <c>chunk/2,3</c> is called.
</p>
+ <note><p>
+ The distributed disk log feature has been deprecated. This
+ feature has also been scheduled for removal in OTP 24.
+ </p></note>
<p>A collection of open disk logs with the same name running on
different nodes is said to be a <em>distributed disk log</em>
if requests made to any of the logs are automatically made to
@@ -609,6 +613,10 @@
the current node, <c><anno>Dist</anno></c> has the value <c>local</c>,
otherwise all nodes where the log is distributed
are returned as a list.</p>
+ <warning><p>
+ The distributed disk log feature has been deprecated. This
+ feature has also been scheduled for removal in OTP 24.
+ </p></warning>
</item>
</taglist>
<p>The following pairs are returned for all logs opened in
@@ -871,7 +879,11 @@
adding members to a distributed disk log.
Defaults to <c>[]</c>, which means that
the log is local on the current node.
- </p>
+ </p>
+ <warning><p>
+ The distributed disk log feature has been deprecated. This
+ feature has also been scheduled for removal in OTP 24.
+ </p></warning>
</item>
<tag><c>{notify, boolean()}</c><marker id="notify"></marker></tag>
<item>
diff --git a/lib/kernel/doc/src/erpc.xml b/lib/kernel/doc/src/erpc.xml
new file mode 100644
index 0000000000..43e25b016b
--- /dev/null
+++ b/lib/kernel/doc/src/erpc.xml
@@ -0,0 +1,513 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2020</year><year>2020</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>erpc</title>
+ <prepared>Rickard Green</prepared>
+ <docno>1</docno>
+ <date>2020-02-20</date>
+ <rev>A</rev>
+ </header>
+ <module since="OTP @OTP-13450@">erpc</module>
+ <modulesummary>Enhanced Remote Procedure Call</modulesummary>
+ <description>
+ <p>
+ This module provide services similar to Remote Procedure Calls.
+ A remote procedure call is a method to call a function on a remote
+ node and collect the answer. It is used for collecting information
+ on a remote node, or for running a function with some specific side
+ effects on the remote node.
+ </p>
+ <p>
+ This is an enhanced subset of the operations provided by the
+ <seealso marker="rpc"><c>rpc</c></seealso> module. Enhanced in the
+ sense that it makes it possible to distinguish between returned
+ value, raised exceptions, and other errors. <c>erpc</c> also has
+ better performance and scalability than the original <c>rpc</c>
+ implementation. However, current <c>rpc</c> module will utilize
+ <c>erpc</c> in order to also provide these properties when
+ possible.
+ </p>
+ <p>
+ In order for an <c>erpc</c> operation to succeed, the remote
+ node also needs to support <c>erpc</c>. Typically only ordinary
+ Erlang nodes as of OTP 23 have <c>erpc</c> support.
+ </p>
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="request_id"/>
+ <desc>
+ <p>
+ An opaque type of call request identifiers. For more
+ information see
+ <seealso marker="#send_request/4"><c>send_request/4</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+
+ <func>
+ <name name="call" arity="4" since="OTP @OTP-13450@"/>
+ <name name="call" arity="5" since="OTP @OTP-13450@"/>
+ <fsummary>Evaluate a function call on a node.</fsummary>
+ <desc>
+ <p>
+ Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns
+ the corresponding value <c><anno>Result</anno></c>.
+ <c><anno>Timeout</anno></c> is an integer representing
+ the timeout in milliseconds or the atom <c>infinity</c>.
+ </p>
+ <p>The call <c>erpc:call(<anno>Node</anno>, <anno>Module</anno>,
+ <anno>Function</anno>, <anno>Args</anno>)</c> is equivalent
+ to the call <c>erpc:call(<anno>Node</anno>, <anno>Module</anno>,
+ <anno>Function</anno>, <anno>Args</anno>, infinity)</c></p>
+ <p>
+ The <c>call()</c> function only returns if the applied
+ function successfully returned without raising any uncaught
+ exceptions, the operation did not time out, and no failures
+ occurred. In all other cases an exception is raised. The
+ following exceptions, listed by exception class, can
+ currently be raised by <c>erpc:call()</c>:
+ </p>
+ <taglist>
+ <tag><c>throw</c></tag>
+ <item><p>
+ The applied function called <c>throw(Value)</c>
+ and did not catch this exception. The exception
+ reason <c>Value</c> equals the argument passed to
+ <c>throw/1</c>.
+ </p></item>
+
+ <tag><c>exit</c></tag>
+ <item><p>
+ Exception reason:
+ </p>
+ <taglist>
+ <tag><c>{exception, ExitReason}</c></tag>
+ <item><p>
+ The applied function called <c>exit(ExitReason)</c>
+ and did not catch this exception. The exit
+ reason <c>ExitReason</c> equals the argument passed
+ to <c>exit/1</c>.
+ </p></item>
+ <tag><c>{signal, ExitReason}</c></tag>
+ <item><p>
+ The process that applied the function received an
+ exit signal and terminated due to this signal. The
+ process terminated with exit reason <c>ExitReason</c>.
+ </p></item>
+ </taglist>
+ </item>
+
+ <tag><c>error</c></tag>
+ <item><p>
+ Exception reason:
+ </p>
+ <taglist>
+
+ <tag><c>{exception, ErrorReason, StackTrace}</c></tag>
+ <item><p>
+ A runtime error occurred which raised and error
+ exception while applying the function,
+ and the applied function did not catch the
+ exception. The error reason <c>ErrorReason</c>
+ indicates the type of error that occurred.
+ <c>StackTrace</c> is formatted as when caught in a
+ <c>try/catch</c> construct. The <c>StackTrace</c>
+ is limited to the applied function and functions
+ called by it.
+ </p></item>
+
+ <tag><c>{erpc, ERpcErrorReason}</c></tag>
+ <item><p>
+ The <c>erpc</c> operation failed. The following
+ <c>ERpcErrorReason</c>s are the most common ones:
+ </p>
+
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item>
+ <p>If any one of these are true:</p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Module</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Function</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Args</anno></c> is not a proper list
+ of terms.</p></item>
+ <item><p><c><anno>Timeout</anno></c> is not the
+ atom <c>infinity</c> or an integer in valid
+ range.</p></item>
+ </list>
+ </item>
+
+ <tag><c>noconnection</c></tag>
+ <item><p>
+ The connection to <c>Node</c> was lost or could
+ not be established. The function may or may not
+ be applied.
+ </p></item>
+
+ <tag><c>system_limit</c></tag>
+ <item><p>
+ The <c>erpc</c> operation failed due to some system
+ limit being reached. This typically due to failure
+ to create a process on the remote node <c>Node</c>,
+ but can be other things as well.
+ </p></item>
+
+ <tag><c>timeout</c></tag>
+ <item><p>
+ The <c>erpc</c> operation timed out. The function may
+ or may not be applied.
+ </p></item>
+
+ <tag><c>notsup</c></tag>
+ <item><p>
+ The remote node <c>Node</c> does not support
+ this <c>erpc</c> operation.
+ </p>
+ </item>
+
+ </taglist>
+ </item>
+
+ </taglist>
+ </item>
+ </taglist>
+
+ <p>
+ If the <c>erpc</c> operation fails, but it is unknown if
+ the function is/will be applied (that is, a timeout or
+ a connection loss), the caller will not receive any
+ further information about the result if/when the applied
+ function completes. If the applied function explicitly
+ communicates with the calling process, such communication
+ may, of course, reach the calling process.
+ </p>
+
+ <note>
+ <p>
+ You cannot make <em>any</em> assumptions about the
+ process that will perform the <c>apply()</c>. It may
+ be the calling process itself, a server, or a freshly
+ spawned process.
+ </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="cast" arity="4" since="OTP @OTP-13450@"/>
+ <fsummary>Evaluate a function call on a node ignoring the result.</fsummary>
+ <desc>
+ <p>
+ Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>)</c> on node
+ <c><anno>Node</anno></c>. No response is delivered to the
+ calling process. <c>erpc:cast()</c> returns immediately
+ after the cast request has been sent. Any failures beside
+ bad arguments are silently ignored.
+ </p>
+ <p><c>erpc:cast/4</c> fails with an <c>{erpc, badarg}</c>
+ <c>error</c> exception if:</p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Module</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Function</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Args</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ <note>
+ <p>
+ You cannot make <em>any</em> assumptions about the
+ process that will perform the <c>apply()</c>. It may
+ be a server, or a freshly spawned process.
+ </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="check_response" arity="2" since="OTP @OTP-13450@"/>
+ <fsummary>Check if a message is a response corresponding to a
+ previously sent call request.</fsummary>
+ <desc>
+ <p>
+ Check if a message is a response to a <c>call</c> request
+ previously made by the calling process using
+ <seealso marker="#send_request/4"><c>erpc:send_request/4</c></seealso>.
+ <c><anno>RequestId</anno></c> should be the value returned
+ from the previously made <c>erpc:send_request()</c> call,
+ and the corresponding response should not already have been
+ received and handled to completion by <c>erpc:check_response()</c>,
+ <seealso marker="#receive_response/2"><c>erpc:receive_response()</c></seealso>, or
+ <seealso marker="#wait_response/2"><c>erpc:wait_response()</c></seealso>.
+ <c><anno>Message</anno></c> is the message to check.
+ </p>
+ <p>
+ If <c><anno>Message</anno></c> does not correspond to the
+ response, the atom <c>no_response</c> is returned. If
+ <c><anno>Message</anno></c> corresponds to the response, the
+ <c>call</c> operation is completed and either the result is
+ returned as <c>{response, Result}</c> where <c>Result</c>
+ corresponds to the value returned from the applied function
+ or an exception is raised. The exceptions that can be raised
+ corresponds to the same exceptions as can be raised by
+ <seealso marker="#call/4"><c>erpc:call/4</c></seealso>.
+ That is, no <c>{erpc, timeout}</c> <c>error</c> exception
+ can be raised.
+ </p>
+ <p>
+ If the <c>erpc</c> operation fails, but it is unknown if
+ the function is/will be applied (that is, a connection loss),
+ the caller will not receive any further information about the
+ result if/when the applied function completes. If the applied
+ function explicitly communicates with the calling process,
+ such communication may, of course, reach the calling process.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="multicall" arity="4" since="OTP @OTP-13450@"/>
+ <name name="multicall" arity="5" since="OTP @OTP-13450@"/>
+ <fsummary>Evaluate a function call on a number of nodes.</fsummary>
+ <type name="caught_call_exception"/>
+ <type name="stack_item"/>
+ <desc>
+ <p>
+ Performs multiple <c>call</c> operations in parallel
+ on multiple nodes. The result is returned as a list where
+ the result from each node is placed at the same position
+ as the node name is placed in <c><anno>Nodes</anno></c>.
+ Each item in the resulting list is formatted as either:
+ </p>
+ <taglist>
+ <tag><c>{ok, Result}</c></tag>
+ <item><p>
+ The <c>call</c> operation for this specific node
+ returned <c>Result</c>.
+ </p></item>
+ <tag><c>{Class, ExceptionReason}</c></tag>
+ <item><p>
+ The <c>call</c> operation for this specific node
+ raised an exception of class <c>Class</c> with
+ exception reason <c>ExceptionReason</c>. These
+ corresponds the the exceptions that
+ <seealso marker="#call/5"><c>erpc:call/5</c></seealso>
+ can raise.
+ </p></item>
+ </taglist>
+ <p>
+ The call <c>erpc:multicall(<anno>Nodes</anno>, <anno>Module</anno>,
+ <anno>Function</anno>, <anno>Args</anno>)</c> is equivalent
+ to the call <c>erpc:multicall(<anno>Nodes</anno>, <anno>Module</anno>,
+ <anno>Function</anno>, <anno>Args</anno>, infinity)</c>. These
+ calls are also equivalent to calling <c>my_multicall(Nodes, Module,
+ Function, Args)</c> if one disregards performance:
+ </p>
+ <pre>
+my_multicall(Nodes, Module, Function, Args) ->
+ ReqIds = lists:map(fun (Node) ->
+ <seealso marker="#send_request/4">erpc:send_request(Node, Module, Function, Args)</seealso>
+ end,
+ Nodes),
+ lists:map(fun (ReqId) ->
+ try
+ {ok, <seealso marker="#receive_response/2">erpc:receive_response(ReqId, infinity)</seealso>}
+ catch
+ Class:Reason ->
+ {Class, Reason}
+ end
+ end,
+ ReqIds).
+</pre>
+
+ <p>
+ The <c><anno>Timeout</anno></c> value in milliseconds
+ sets an upper time limit for all <c>call</c> operations
+ to complete.
+ </p>
+
+ <p>
+ If an <c>erpc</c> operation fails, but it is unknown if
+ the function is/will be applied (that is, a timeout or
+ connection loss), the caller will not receive any
+ further information about the result if/when the applied
+ function completes. If the applied function communicates
+ with the calling process, such communication may, of
+ course, reach the calling process.
+ </p>
+ </desc>
+
+ </func>
+
+ <func>
+ <name name="receive_response" arity="1" since="OTP @OTP-13450@"/>
+ <name name="receive_response" arity="2" since="OTP @OTP-13450@"/>
+ <fsummary>Receive a call response corresponding to a
+ previously sent call request.</fsummary>
+ <desc>
+ <p>
+ Receive a response to a <c>call</c> request previously
+ made by the calling process using
+ <seealso marker="#send_request/4"><c>erpc:send_request/4</c></seealso>.
+ <c><anno>RequestId</anno></c> should be the value returned from
+ the previously made <c>erpc:send_request()</c> call, and
+ the corresponding response should not already have been received
+ and handled to completion by
+ <seealso marker="#check_response/2"><c>erpc:check_response()</c></seealso>,
+ <c>erpc:receive_response()</c>, or
+ <seealso marker="#wait_response/2"><c>erpc:wait_response()</c></seealso>.
+ <c><anno>Timeout</anno></c> equals the timeout time in milliseconds
+ or the atom <c>infinity</c>. The <c>call</c> operation is completed
+ once the <c>erpc:receive_response()</c> call returns or raise an
+ exception.
+ </p>
+ <p>
+ The call <c>erpc:receive_response(<anno>RequestId</anno>)</c> is
+ equivalent to the call
+ <c>erpc:receive_response(<anno>RequestId</anno>, infinity)</c>.
+ </p>
+ <p>
+ A call to the function
+ <c>my_call(Node, Module, Function, Args, Timeout)</c>
+ below is equivalent to the call
+ <seealso marker="#call/5"><c>erpc:call(Node, Module, Function, Args,
+ Timeout)</c></seealso> if one disregards performance. <c>erpc:call()</c>
+ can utilize a message queue optimization which removes the need to scan
+ the whole message queue which the combination
+ <c>erpc:send_request()/erpc:receive_response()</c> cannot.
+ </p>
+ <pre>
+my_call(Node, Module, Function, Args, Timeout) ->
+ RequestId = <seealso marker="#send_request/4">erpc:send_request(Node, Module, Function, Args)</seealso>,
+ erpc:receive_response(RequestId, Timeout).
+</pre>
+ <p>
+ If the <c>erpc</c> operation fails, but it is unknown if
+ the function is/will be applied (that is, a timeout, or
+ a connection loss), the caller will not receive any
+ further information about the result if/when the applied
+ function completes. If the applied function explicitly
+ communicates with the calling process, such communication
+ may, of course, reach the calling process.
+ </p>
+
+ <p>
+ <c>erpc:receive_response()</c> will return or raise exceptions the
+ same way as <seealso marker="#call/5"><c>erpc:call/5</c></seealso>
+ does.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="send_request" arity="4" since="OTP @OTP-13450@"/>
+ <fsummary>Send a request to evaluate a function call on a node.</fsummary>
+ <desc>
+ <p>
+ Send an asynchronous <c>call</c> request to the node
+ <c><anno>Node</anno></c>. <c>erpc:send_request()</c>
+ returns a request identifier that later is to be passed
+ as argument to either
+ <seealso marker="#receive_response/1"><c>erpc:receive_response()</c></seealso>,
+ <seealso marker="#wait_response/1"><c>erpc:wait_response()</c></seealso>,
+ or,
+ <seealso marker="#check_response/2"><c>erpc:check_response()</c></seealso>
+ in order to get the response of the call request.
+ </p>
+ <p><c>erpc:send_request()</c> fails with an <c>{erpc, badarg}</c>
+ <c>error</c> exception if:</p>
+ <list>
+ <item><p><c><anno>Node</anno></c> is not a valid
+ node name atom.</p></item>
+ <item><p><c><anno>Module</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Function</anno></c> is not an atom.</p></item>
+ <item><p><c><anno>Args</anno></c> is not a proper list
+ of terms.</p></item>
+ </list>
+ </desc>
+ </func>
+
+ <func>
+ <name name="wait_response" arity="1" since="OTP @OTP-13450@"/>
+ <name name="wait_response" arity="2" since="OTP @OTP-13450@"/>
+ <fsummary>Wait or poll for a call response corresponding to a previously
+ sent call request.</fsummary>
+ <desc>
+ <p>
+ Wait or poll for a response message to a <c>call</c> request
+ previously made by the calling process using
+ <seealso marker="#send_request/4"><c>erpc:send_request/4</c></seealso>.
+ <c><anno>RequestId</anno></c> should be the value returned from
+ the previously made <c>erpc:send_request()</c> call, and the
+ corresponding response should not already have been received and handled
+ to completion by
+ <seealso marker="#check_response/2"><c>erpc:check_response()</c></seealso>,
+ <seealso marker="#receive_response/2"><c>erpc:receive_response()</c></seealso>,
+ or <c>erpc:wait_response()</c>. <c><anno>WaitTime</anno></c> equals the
+ time to wait in milliseconds (or the atom <c>infinity</c>) during the wait.
+ </p>
+ <p>
+ The call <c>erpc:wait_response(<anno>RequestId</anno>)</c> is equivalent
+ to the call <c>erpc:wait_response(<anno>RequestId</anno>, 0)</c>. That is,
+ poll for a response message to a <c>call</c> request previously made by
+ the calling process.
+ </p>
+ <p>
+ If no response is received before <c><anno>WaitTime</anno></c> milliseconds,
+ the atom <c>no_response</c> is returned. It is valid to continue waiting
+ for a response as many times as needed up until a response has
+ been received and completed by <c>erpc:check_response()</c>,
+ <c>erpc:receive_response()</c>, or <c>erpc:wait_response()</c>. If a
+ response is received, the <c>call</c> operation is completed and either
+ the result is returned as <c>{response, Result}</c> where <c>Result</c>
+ corresponds to the value returned from the applied function or an
+ exception is raised. The exceptions that can be raised corresponds to the
+ same exceptions as can be raised by
+ <seealso marker="#call/4"><c>erpc:call/4</c></seealso>.
+ That is, no <c>{erpc, timeout}</c> <c>error</c> exception can be raised.
+ </p>
+ <p>
+ If the <c>erpc</c> operation fails, but it is unknown if
+ the function is/will be applied (that is, a too large wait time
+ value, or a connection loss), the caller will not receive any
+ further information about the result if/when the applied function
+ completes. If the applied function explicitly communicates with the
+ calling process, such communication may, of course, reach the
+ calling process.
+ </p>
+ </desc>
+ </func>
+
+ </funcs>
+</erlref>
+
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index fc25e83d40..c4073f13a2 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -202,10 +202,7 @@
<desc>
<p><c>allocate/3</c> can be used to preallocate space for a file.</p>
<p>This function only succeeds in platforms that provide this
- feature. When it succeeds, space is preallocated for the file but
- the file size might not be updated. This behaviour depends on the
- preallocation implementation. To guarantee that the file size is updated,
- truncate the file to the new size.</p>
+ feature.</p>
</desc>
</func>
<func>
@@ -939,6 +936,10 @@ f.txt: {person, "kalle", 25}.
support for POSIX <c>O_SYNC</c> or equivalent, use of the <c>sync</c>
flag causes <c>open</c> to return <c>{error, enotsup}</c>.</p>
</item>
+ <tag><c>directory</c></tag>
+ <item>
+ <p>Allows <c>open</c> to work on directories.</p>
+ </item>
</taglist>
<p>Returns:</p>
<taglist>
@@ -953,10 +954,9 @@ f.txt: {person, "kalle", 25}.
</item>
</taglist>
<p><c><anno>IoDevice</anno></c> is really the pid of the process that
- handles the file. This process is linked to the process
- that originally opened the file. If any process to which
- the <c><anno>IoDevice</anno></c> is linked terminates, the file is
- closed and the process itself is terminated.
+ handles the file. This process monitors the process that originally
+ opened the file (the owner process). If the owner process terminates,
+ the file is closed and the process itself terminates too.
An <c><anno>IoDevice</anno></c> returned from this call can be used
as an argument to the I/O functions (see
<seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p>
@@ -985,8 +985,10 @@ f.txt: {person, "kalle", 25}.
</item>
<tag><c>enotdir</c></tag>
<item>
- <p>A component of the filename is not a directory. On some
- platforms, <c>enoent</c> is returned instead.</p>
+ <p>A component of the filename is not a directory, or the
+ filename itself is not a directory if <c>directory</c>
+ mode was specified. On some platforms, <c>enoent</c> is
+ returned instead.</p>
</item>
<tag><c>enospc</c></tag>
<item>
@@ -1438,7 +1440,11 @@ f.txt: {person, "kalle", 25}.
break this module's atomicity guarantees as it can race with a
concurrent call to
<seealso marker="#write_file_info/2"><c>write_file_info/1,2</c>
- </seealso></p>
+ </seealso>.</p>
+ <p>This option has no effect when the function is
+ given an I/O device instead of a file name. Use
+ <seealso marker="#open/2"><c>open/2</c></seealso> with the
+ <c>raw</c> mode to obtain a file descriptor first.</p>
<note>
<p>As file times are stored in POSIX time on most OS, it is faster to
query file information with option <c>posix</c>.</p>
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index 61ac1485c1..d9194da4f2 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -165,7 +165,7 @@
<pre>
#sctp_assoc_change{
state = atom(),
- error = atom(),
+ error = integer(),
outbound_streams = integer(),
inbound_streams = integer(),
assoc_id = assoc_id()
@@ -208,7 +208,9 @@ connect(Socket, Ip, Port>,
<tag><c>shutdown_comp</c></tag>
<item></item>
</taglist>
- <p>Field <c>error</c> can provide more detailed diagnostics.</p>
+ <p>Field <c>error</c> can provide more detailed diagnostics.
+ The <c>error</c> field value can be converted into a string using
+ <seealso marker="#error_string/1"><c>error_string/1</c></seealso>.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 14819aa938..9ecadc5498 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -305,4 +305,3 @@
</func>
</funcs>
</erlref>
-
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 7f9609d5c1..7ad3d15cd6 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -414,6 +414,15 @@ MaxT = TickTime + TickTime / 4</code>
using this service.</p>
<p>Defaults to <c>false</c>.</p>
</item>
+ <tag><c>start_pg = true | false</c></tag>
+ <item>
+ <marker id="start_pg"></marker>
+ <p>Starts the default <c>pg</c> scope server (see
+ <seealso marker="pg"><c>pg(3)</c></seealso>) if
+ the parameter is <c>true</c>. This parameter is to be set to
+ <c>true</c> in an embedded system that uses this service.</p>
+ <p>Defaults to <c>false</c>.</p>
+ </item>
<tag><c>start_pg2 = true | false</c></tag>
<item>
<marker id="start_pg2"></marker>
@@ -556,6 +565,7 @@ erl -kernel logger '[{handler,default,logger_std_h,#{formatter=>{logger_formatte
<seealso marker="logger"><c>logger(3)</c></seealso>,
<seealso marker="net_kernel"><c>net_kernel(3)</c></seealso>,
<seealso marker="os"><c>os(3)</c></seealso>,
+ <seealso marker="pg"><c>pg(3)</c></seealso>,
<seealso marker="pg2"><c>pg2(3)</c></seealso>,
<seealso marker="rpc"><c>rpc(3)</c></seealso>,
<seealso marker="seq_trace"><c>seq_trace(3)</c></seealso>,
diff --git a/lib/kernel/doc/src/net.xml b/lib/kernel/doc/src/net.xml
index d60e1af311..d7732e714a 100644
--- a/lib/kernel/doc/src/net.xml
+++ b/lib/kernel/doc/src/net.xml
@@ -122,7 +122,7 @@
<funcs>
<func>
- <name name="gethostname" arity="0"/>
+ <name name="gethostname" arity="0" since="OTP 22.0"/>
<fsummary>Get hostname.</fsummary>
<desc>
<p>Returns the name of the current host.</p>
diff --git a/lib/kernel/doc/src/pg.xml b/lib/kernel/doc/src/pg.xml
new file mode 100644
index 0000000000..f04d9561e9
--- /dev/null
+++ b/lib/kernel/doc/src/pg.xml
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<!-- %ExternalCopyright% -->
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2020</year><year>2020</year>
+ <holder>Maxim Fedorov, WhatsApp Inc.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>pg</title>
+ <prepared>maximfca@gmail.com</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev>A</rev>
+ <file>pg.xml</file>
+ </header>
+ <module since="OTP 23.0">pg</module>
+ <modulesummary>Distributed named process groups.</modulesummary>
+ <description>
+ <p>This module implements process groups. A message can be sent
+ to one, some, or all group members.</p>
+
+ <p>Up until OTP 17 there used to exist an experimental <c>pg</c>
+ module in <c>stdlib</c>. This <c>pg</c> module is not the same
+ module as that experimental <c>pg</c> module, and only share
+ the same module name.</p>
+
+ <p>A group of processes can be accessed by a common name. For
+ example, if there is a group named <c>foobar</c>, there can be a
+ set of processes (which can be located on different nodes) that
+ are all members of the group <c>foobar</c>. There are no special
+ functions for sending a message to the group. Instead, client
+ functions are to be written with the functions
+ <seealso marker="#get_members/1"><c>get_members/1</c></seealso> and
+ <seealso marker="#get_local_members/1"><c>get_local_members/1</c></seealso>
+ to determine which processes are members of the group.
+ Then the message can be sent to one or more group members.</p>
+ <p>If a member terminates, it is automatically removed from the group.</p>
+
+ <p>A process may join multiple groups. It may join the same group multiple times.
+ It is only allowed to join processes running on local node.
+ </p>
+
+ <p>Process Groups implement strong eventual consistency.
+ Unlike <seealso marker="pg2"><c>pg2</c></seealso>, that provides
+ strong ordering guarantees, Process Groups membership view may temporarily
+ diverge. For example, when processes on <c>node1</c> and <c>node2</c>
+ join concurrently, <c>node3</c> and <c>node4</c> may receive updates in
+ a different order.</p>
+
+ <p> Membership view is not transitive. If <c>node1</c> is not directly
+ connected to <c>node2</c>, they will not see each other groups. But if
+ both are connected to <c>node3</c>, <c>node3</c> will have the full view. </p>
+
+ <p>Groups are automatically created when any process joins,
+ and are removed when all processes leave the group. Non-existing group is
+ considered empty (containing no processes).</p>
+
+ <p>Process groups can be organised into multiple scopes. Scopes are
+ completely independent of each other. A process may join any
+ number of groups in any number of scopes. Scopes are designed to
+ decouple single mesh into a set of overlay networks, reducing
+ amount of traffic required to propagate group membership
+ information. Default scope <c>pg</c> is started automatically
+ when <seealso marker="kernel_app#start_pg"><c>kernel(6)</c></seealso>
+ is configured to do so.
+ </p>
+
+ <note><p>
+ Scope name is used to register process locally, and to name an ETS table.
+ If there is another process registered under this name, or another ETS table
+ exists, scope fails to start.</p>
+ <p>Local membership is not preserved if scope process exits and
+ restarts. This behaviour is different from
+ <seealso marker="pg2"><c>pg2</c></seealso>, that recovers
+ local membership from remote nodes.
+ </p></note>
+
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="group"/>
+ <desc><p>The identifier of a process group.</p></desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+
+ <func>
+ <name name="start_link" arity="0" since="OTP 23.0"/>
+ <fsummary>Start the default <c>pg</c> scope.</fsummary>
+ <desc>
+ <p>Starts the default <c>pg</c> scope within supervision tree.
+ Kernel may be configured to do it automatically, see
+ <seealso marker="kernel_app#start_pg"><c>kernel(6)</c></seealso>
+ configuration manual.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start" arity="1" since="OTP 23.0"/>
+ <name name="start_link" arity="1" since="OTP 23.0"/>
+ <fsummary>Start additional scope.</fsummary>
+ <desc>
+ <p>Starts additional scope.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="join" arity="2" since="OTP 23.0"/>
+ <name name="join" arity="3" since="OTP 23.0"/>
+ <fsummary>Join a process or a list of processes to a group.</fsummary>
+ <desc>
+ <p>Joins single process or multiple processes to the
+ group <c>Name</c>. A process can join a group many times and
+ must then leave the group the same number of times.</p>
+ <p><c>PidOrPids</c> may contain the same process multiple times.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="leave" arity="2" since="OTP 23.0"/>
+ <name name="leave" arity="3" since="OTP 23.0"/>
+ <fsummary>Make a process leave a group.</fsummary>
+ <desc>
+ <p>Makes the process <c>PidOrPids</c> leave the group <c>Name</c>.
+ If the process is not a member of the group, <c>not_joined</c> is
+ returned.</p>
+ <p>When list of processes is passed as <c>PidOrPids</c>, function
+ returns <c>not_joined</c> only when all processes of the list
+ are not joined.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get_local_members" arity="1" since="OTP 23.0"/>
+ <name name="get_local_members" arity="2" since="OTP 23.0"/>
+ <fsummary>Return all local processes in a group.</fsummary>
+ <desc>
+ <p>Returns all processes running on the local node in the
+ group <c>Name</c>. Processes are returned in no specific order.
+ This function is optimised for speed.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get_members" arity="1" since="OTP 23.0"/>
+ <name name="get_members" arity="2" since="OTP 23.0"/>
+ <fsummary>Return all processes in a group.</fsummary>
+ <desc>
+ <p>Returns all processes in the group <c>Name</c>.
+ Processes are returned in no specific order.
+ This function is optimised for speed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="which_groups" arity="0" since="OTP 23.0"/>
+ <name name="which_groups" arity="1" since="OTP 23.0"/>
+ <fsummary>Return a list of all known groups.</fsummary>
+ <desc>
+ <p>Returns a list of all known groups.</p>
+ </desc>
+ </func>
+
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="kernel_app"><c>kernel(6)</c></seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml
index 058d711756..510dea0b92 100644
--- a/lib/kernel/doc/src/pg2.xml
+++ b/lib/kernel/doc/src/pg2.xml
@@ -35,6 +35,16 @@
<module since="">pg2</module>
<modulesummary>Distributed named process groups.</modulesummary>
<description>
+ <warning>
+ <p>
+ The <c>pg2</c> module is deprecated as of OTP 23 and scheduled
+ for removal in OTP 24. You are advised to replace the usage of
+ <c>pg2</c> with <seealso marker="kernel:pg">pg</seealso>.
+ <c>pg</c> has a similar API, but with an implementation that
+ is more scalable. See the documentation of <c>pg</c> for more
+ information about differences.
+ </p>
+ </warning>
<p>This module implements process groups. Each message can be sent
to one, some, or all group members.</p>
<p>A group of processes can be accessed by a common name. For
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index 9df51dee22..9127157eb5 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -43,6 +43,7 @@
<xi:include href="erl_epmd.xml"/>
<xi:include href="erl_prim_loader_stub.xml"/>
<xi:include href="erlang_stub.xml"/>
+ <xi:include href="erpc.xml"/>
<xi:include href="error_handler.xml"/>
<xi:include href="error_logger.xml"/>
<xi:include href="file.xml"/>
@@ -64,6 +65,7 @@
<xi:include href="net_adm.xml"/>
<xi:include href="net_kernel.xml"/>
<xi:include href="os.xml"/>
+ <xi:include href="pg.xml"/>
<xi:include href="pg2.xml"/>
<xi:include href="rpc.xml"/>
<xi:include href="seq_trace.xml"/>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index 0e07d334d8..03bfc97c5b 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -37,13 +37,29 @@
a function on a remote node and collect the answer. It is used
for collecting information on a remote node, or for running a
function with some specific side effects on the remote node.</p>
+ <note><p>
+ <c>rpc:call()</c> and friends makes it quite hard to distinguish
+ between successful results, raised exceptions, and other errors.
+ This cannot be changed due to compatibility reasons. As of OTP 23,
+ a new module <seealso marker="erpc"><c>erpc</c></seealso> was
+ introduced in order to provide an API that makes it possible
+ to distingush between the different results. The <c>erpc</c>
+ module provides a subset (however, the central subset) of the
+ functionality available in the <c>rpc</c> module. The <c>erpc</c>
+ implementation also provides a more scalable implementation with
+ better performance than the original <c>rpc</c> implementation.
+ However, since the introduction of <c>erpc</c>, the <c>rpc</c>
+ module implements large parts of its central functionality using
+ <c>erpc</c>, so the <c>rpc</c> module wont not suffer scalability
+ wise and performance wise compared to <c>erpc</c>.
+ </p></note>
</description>
<datatypes>
<datatype>
<name name="key"/>
<desc>
- <p>As returned by
+ <p>Opaque value returned by
<seealso marker="#async_call/4"><c>async_call/4</c></seealso>.</p>
</desc>
</datatype>
@@ -87,7 +103,17 @@
<seealso marker="#nb_yield/1"><c>nb_yield/1,2</c></seealso>
to retrieve the value of evaluating <c>apply(<anno>Module</anno>,
<anno>Function</anno>, <anno>Args</anno>)</c> on node
- <c><anno>Node</anno></c>.</p>
+ <c><anno>Node</anno></c>.</p>
+ <note>
+ <p>
+ If you want the ability to distinguish between results,
+ you may want to consider using the
+ <seealso marker="erpc#send_request/4"><c>erpc:send_request()</c></seealso>
+ function from the <c>erpc</c> module instead. This also
+ gives you the ability retrieve the results in other useful
+ ways.
+ </p>
+ </note>
<note>
<p><seealso marker="#yield/1"><c>yield/1</c></seealso> and
<seealso marker="#nb_yield/1"><c>nb_yield/1,2</c></seealso>
@@ -99,34 +125,34 @@
<func>
<name name="block_call" arity="4" since=""/>
- <fsummary>Evaluate a function call on a node in the RPC server's
- context.</fsummary>
- <desc>
- <p>Same as <seealso marker="#call/4"><c>call/4</c></seealso>,
- but the RPC server at <c><anno>Node</anno></c> does
- not create a separate process to handle the call. Thus,
- this function can be used if the intention of the call is to
- block the RPC server from any other incoming requests until
- the request has been handled. The function can also be used
- for efficiency reasons when very small fast functions are
- evaluated, for example, BIFs that are guaranteed not to
- suspend.</p>
- <p>See the note in <seealso marker="#call/4"><c>call/4</c></seealso>
- for more details of the return value.</p>
+ <fsummary>Evaluate a function call on a node.</fsummary>
+ <desc>
+ <p>
+ The same as calling
+ <seealso marker="#block_call/5"><c>rpc:block_call(<anno>Node</anno>,
+ <anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>, infinity)</c></seealso>.
+ </p>
</desc>
</func>
<func>
<name name="block_call" arity="5" since=""/>
- <fsummary>Evaluate a function call on a node in the RPC server's
- context.</fsummary>
+ <fsummary>Evaluate a function call on a node.</fsummary>
<desc>
- <p>Same as
- <seealso marker="#block_call/4"><c>block_call/4</c></seealso>,
- but with a time-out value in the same manner as
- <seealso marker="#call/5"><c>call/5</c></seealso>.</p>
- <p>See the note in <seealso marker="#call/4"><c>call/4</c></seealso>
- for more details of the return value.</p>
+ <p>
+ The same as calling
+ <seealso marker="#call/5"><c>rpc:call(<anno>Node</anno>,
+ <anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>, <anno>Timeout</anno>)</c></seealso> with
+ the exception that it also blocks other <c>rpc:block_call()</c>
+ operations from executing concurrently on the node
+ <c><anno>Node</anno></c>.
+ </p>
+ <warning><p>
+ Note that it also blocks other operations than just
+ <c>rpc:block_call()</c> operations, so use it with care.
+ </p></warning>
</desc>
</func>
@@ -135,9 +161,38 @@
<fsummary>Evaluate a function call on a node.</fsummary>
<desc>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns
+ the corresponding value <c><anno>Res</anno></c>, or
+ <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.
+ The same as calling
+ <seealso marker="#call/5"><c>rpc:call(<anno>Node</anno>,
+ <anno>Module</anno>, <anno>Function</anno>,
+ <anno>Args</anno>, infinity)</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="call" arity="5" since=""/>
+ <fsummary>Evaluate a function call on a node.</fsummary>
+ <desc>
+ <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns
the corresponding value <c><anno>Res</anno></c>, or
- <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.</p>
+ <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.
+ <c><anno>Timeout</anno></c> is
+ a time-out value in milliseconds. If the call times out,
+ <c><anno>Reason</anno></c> is <c>timeout</c>.</p>
+ <p>If the reply arrives after the call times out, no message
+ contaminates the caller's message queue.</p>
+ <note>
+ <p>
+ If you want the ability to distinguish between results,
+ you may want to consider using the
+ <seealso marker="erpc#call/4"><c>erpc:call()</c></seealso>
+ function from the <c>erpc</c> module instead.
+ </p>
+ </note>
<note>
<p>Here follows the details of what exactly is returned.</p>
<p><c>{badrpc, <anno>Reason</anno>}</c> will be returned in the
@@ -159,28 +214,14 @@
<strong>not</strong> match <c>{'EXIT',_}</c>.</item>
</list>
</note>
- </desc>
- </func>
-
- <func>
- <name name="call" arity="5" since=""/>
- <fsummary>Evaluate a function call on a node.</fsummary>
- <desc>
- <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
- <anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns
- the corresponding value <c><anno>Res</anno></c>, or
- <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.
- <c><anno>Timeout</anno></c> is
- a time-out value in milliseconds. If the call times out,
- <c><anno>Reason</anno></c> is <c>timeout</c>.
- See the note in <seealso marker="#call/4"><c>call/4</c></seealso>
- for more details of the return value.</p>
- <p>If the reply arrives after the call times out, no message
- contaminates the caller's message queue, as this
- function spawns off a middleman process to act as (a void)
- destination for such an orphan reply. This feature also makes
- this function more expensive than <c>call/4</c> at
- the caller's end.</p>
+ <note>
+ <p>
+ You cannot make <em>any</em> assumptions about the
+ process that will perform the <c>apply()</c>. It may
+ be the calling process itself, an <c>rpc</c> server,
+ another server, or a freshly spawned process.
+ </p>
+ </note>
</desc>
</func>
@@ -194,6 +235,14 @@
process is not suspended until the evaluation is complete, as
is the case with
<seealso marker="#call/4"><c>call/4,5</c></seealso>.</p>
+ <note>
+ <p>
+ You cannot make <em>any</em> assumptions about the
+ process that will perform the <c>apply()</c>. It may
+ be an <c>rpc</c> server, another server, or a
+ freshly spawned process.
+ </p>
+ </note>
</desc>
</func>
@@ -226,7 +275,7 @@
<anno>Name</anno>, <anno>Msg</anno>)</c>.</p>
</desc>
</func>
-
+
<func>
<name name="multi_server_call" arity="3" since=""/>
<fsummary>Interact with the servers on a number of nodes.</fsummary>
@@ -311,6 +360,14 @@
{ResL, _} = rpc:multicall(code, load_binary, [Mod, File, Bin]),
%% and then maybe check the ResL list.</code>
+ <note>
+ <p>
+ If you want the ability to distinguish between results,
+ you may want to consider using the
+ <seealso marker="erpc#multicall/4"><c>erpc:multicall()</c></seealso>
+ function from the <c>erpc</c> module instead.
+ </p>
+ </note>
</desc>
</func>
diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml
index 9aef748594..059f03593f 100644
--- a/lib/kernel/doc/src/seq_trace.xml
+++ b/lib/kernel/doc/src/seq_trace.xml
@@ -29,10 +29,11 @@
<rev>A</rev>
</header>
<module since="">seq_trace</module>
- <modulesummary>Sequential tracing of messages.</modulesummary>
+ <modulesummary>Sequential tracing of information transfers.</modulesummary>
<description>
- <p>Sequential tracing makes it possible to trace all messages
- resulting from one initial message. Sequential tracing is
+ <p>Sequential tracing makes it possible to trace information
+ flows between processes resulting from one initial transfer
+ of information. Sequential tracing is
independent of the ordinary tracing in Erlang, which
is controlled by the <c>erlang:trace/3</c> BIF. For more information
about what sequential tracing is and how it can be used, see section
@@ -104,13 +105,13 @@ seq_trace:set_token(OldToken), % activate the trace token again
<tag><c>set_token(send, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
- enables/disables tracing on message sending. Default is
+ enables/disables tracing on information sending. Default is
<c>false</c>.</p>
</item>
<tag><c>set_token('receive', <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
- enables/disables tracing on message reception. Default is
+ enables/disables tracing on information reception. Default is
<c>false</c>.</p>
</item>
<tag><c>set_token(print, <anno>Bool</anno>)</c></tag>
@@ -257,12 +258,26 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
<tag><c>{send, Serial, From, To, Message}</c></tag>
<item>
<p>Used when a process <c>From</c> with its trace token flag
- <c>send</c> set to <c>true</c> has sent a message.</p>
+ <c>send</c> set to <c>true</c> has sent information. <c>To</c>
+ may be a process identifier, a registered name on a node
+ represented as <c>{NameAtom, NodeAtom}</c>, or a node name
+ represented as an atom. <c>From</c> may be a process identifier
+ or a node name represented as an atom. <c>Message</c> contains
+ the information passed along in this information transfer. If
+ the transfer is done via message passing, it is the actual
+ message.
+ </p>
</item>
<tag><c>{'receive', Serial, From, To, Message}</c></tag>
<item>
- <p>Used when a process <c>To</c> receives a message with a
- trace token that has flag <c>'receive'</c> set to <c>true</c>.</p>
+ <p>Used when a process <c>To</c> receives information with a
+ trace token that has flag <c>'receive'</c> set to <c>true</c>.
+ <c>To</c> may be a process identifier, or a node name
+ represented as an atom. <c>From</c> may be a process identifier
+ or a node name represented as an atom. <c>Message</c> contains
+ the information passed along in this information transfer. If
+ the transfer is done via message passing, it is the actual
+ message.</p>
</item>
<tag><c>{print, Serial, From, _, Info}</c></tag>
<item>
@@ -276,7 +291,7 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
where:</p>
<list type="bulleted">
<item><p>Integer <c>PreviousSerial</c> denotes the serial
- counter passed in the last received message that carried a trace
+ counter passed in the last received information that carried a trace
token. If the process is the first in a new sequential trace,
<c>PreviousSerial</c> is set to the value of the process internal
"trace clock".</p></item>
@@ -290,22 +305,32 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
<section>
<marker id="whatis"></marker>
<title>Sequential Tracing</title>
- <p>Sequential tracing is a way to trace a sequence of messages sent
- between different local or remote processes, where the sequence
- is initiated by a single message. In short, it works as follows:</p>
+ <p>Sequential tracing is a way to trace a sequence of information
+ transfers between different local or remote processes, where the
+ sequence is initiated by a single transfer. The typical information
+ transfer is an ordinary Erlang message passed between two processes,
+ but information is transferred also in other ways. In short, it works
+ as follows:</p>
<p>Each process has a <em>trace token</em>, which can be empty or
not empty. When not empty, the trace token can be seen as
the tuple <c>{Label, Flags, Serial, From}</c>. The trace token is
- passed invisibly with each message.</p>
+ passed invisibly when information is passed between processes.
+ In most cases the information is passed in ordinary messages
+ between processes, but information is also passed between processes
+ by other means. For example, by spawning a new process. An information
+ transfer between two processes is represented by a send event and a
+ receive event regardless of how it is passed.
+ </p>
<p>To start a sequential trace, the user must explicitly set
- the trace token in the process that will send the first message
+ the trace token in the process that will send the first information
in a sequence.</p>
<p>The trace token of a process is set each time the process
+ receives information. This is typically when the process
matches a message in a receive statement, according to the trace
token carried by the received message, empty or not.</p>
<p>On each Erlang node, a process can be set as the <em>system tracer</em>.
This process will receive trace messages each time
- a message with a trace token is sent or received (if the trace
+ information with a trace token is sent or received (if the trace
token flag <c>send</c> or <c>'receive'</c> is set). The system
tracer can then print each trace event, write it to a file, or
whatever suitable.</p>
@@ -321,11 +346,58 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
</section>
<section>
+ <title>Different Information Transfers</title>
+ <p>
+ Information flows between processes in a lot of different
+ ways. Not all flows of information will be covered by
+ sequential tracing. One example is information passed via
+ ETS tables. Below is a list of information paths that are
+ covered by sequential tracing:</p>
+ <taglist>
+ <tag>Message Passing</tag>
+ <item><p>
+ All ordinary messages passed between Erlang processes.
+ </p></item>
+ <tag>Exit signals</tag>
+ <item><p>
+ An exit signal is represented as an <c>{'EXIT', Pid, Reason}</c>
+ tuple.
+ </p></item>
+ <tag>Process Spawn</tag>
+ <item><p>
+ A process spawn is represented as multiple information
+ transfers. At least one spawn request and one spawn reply. The
+ actual amount of information transfers depends on what type
+ of spawn it is and may also change in future implementations.
+ Note that this is more or less an internal protocol that you
+ are peeking at. The spawn request will be represented as a
+ tuple with the first element containing the atom
+ <c>spawn_request</c>, but this is more or less all that you
+ can depend on.
+ </p></item>
+ </taglist>
+ <note>
+ <p>
+ If you do ordinary <c>send</c> or <c>receive</c>
+ trace on the system, you will only see ordinary message
+ passing, not the other information transfers listed above.
+ </p>
+ </note>
+ <note>
+ <p>
+ When a send event and corresponding receive event do not
+ both correspond to ordinary Erlang messages, the <c>Message</c>
+ part of the trace messages may not be identical. This since
+ all information not necessarily are available when generating
+ the trace messages.
+ </p>
+ </note>
+ </section>
+
+ <section>
<title>Trace Token</title>
- <p>Each process has a current trace token. Initially, the token is
- empty. When the process sends a message to another process, a
- copy of the current token is sent "invisibly" along with
- the message.</p>
+ <p>Each process has a current trace token which is "invisibly" passed
+ from the parent process on creation of the process.</p>
<p>The current token of a process is set in one of the following two
ways:</p>
<list type="bulleted">
@@ -334,7 +406,9 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
<c>seq_trace:set_token/1,2</c></p>
</item>
<item>
- <p>When a message is received</p>
+ <p>When information is received. This is typically when
+ a received message is matched out in a receive expression,
+ but also when information is received in other ways.</p>
</item>
</list>
<p>In both cases, the current token is set. In particular, if
@@ -354,12 +428,16 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
<p>The algorithm for updating <c>Serial</c> can be described as
follows:</p>
<p>Let each process have two counters, <c>prev_cnt</c> and
- <c>curr_cnt</c>, both are set to <c>0</c> when a process is created.
- The counters are updated at the following occasions:</p>
+ <c>curr_cnt</c>, both are set to <c>0</c> when a process is created
+ outside of a trace sequence. The counters are updated at the following
+ occasions:</p>
<list type="bulleted">
<item>
- <p><em>When the process is about to send a message and the trace token
- is not empty.</em></p>
+ <p><em>When the process is about to pass along information to
+ another process and the trace token is not empty.</em> This
+ typically occurs when sending a message, but also, for example,
+ when spawning another process.
+ </p>
<p>Let the serial of the trace token be <c>tprev</c> and
<c>tcurr</c>.</p>
<pre>
@@ -367,7 +445,7 @@ curr_cnt := curr_cnt + 1
tprev := prev_cnt
tcurr := curr_cnt</pre>
<p>The trace token with <c>tprev</c> and <c>tcurr</c> is then
- passed along with the message.</p>
+ passed along with the information passed to the other process.</p>
</item>
<item>
<p><em>When the process calls</em> <c>seq_trace:print(Label, Info)</c>,
@@ -376,8 +454,9 @@ tcurr := curr_cnt</pre>
<p>The algorithm is the same as for send above.</p>
</item>
<item>
- <p><em>When a message is received and contains a non-empty trace
- token.</em></p>
+ <p><em>When information is received that also contains a non-empty
+ trace token. For example, when a message is matched out in a
+ receive expression, or when a new process is spawned.</em></p>
<p>The process trace token is set to the trace token from
the message.</p>
<p>Let the serial of the trace token be <c>tprev</c> and
@@ -487,9 +566,9 @@ tracer() ->
print_trace(Label,TraceInfo,false);
{seq_trace,Label,TraceInfo,Ts} ->
print_trace(Label,TraceInfo,Ts);
- Other -> ignore
+ _Other -> ignore
end,
- tracer().
+ tracer().
print_trace(Label,TraceInfo,false) ->
io:format("~p:",[Label]),
@@ -504,7 +583,7 @@ print_trace({'receive',Serial,From,To,Message}) ->
io:format("~p Received ~p FROM ~p WITH~n~p~n",
[To,Serial,From,Message]);
print_trace({send,Serial,From,To,Message}) ->
- io:format("~p Sent ~p TO ~p WITH~n~p~n",
+ io:format("~p Sent ~p TO ~p WITH~n~p~n",
[From,Serial,To,Message]).</code>
<p>The code that creates a process that runs this tracer function
and sets that process as the system tracer can look like this:</p>
diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml
index 9e258910db..00f6f04218 100644
--- a/lib/kernel/doc/src/specs.xml
+++ b/lib/kernel/doc/src/specs.xml
@@ -9,6 +9,7 @@
<xi:include href="../specs/specs_erl_epmd.xml"/>
<xi:include href="../specs/specs_erl_prim_loader_stub.xml"/>
<xi:include href="../specs/specs_erlang_stub.xml"/>
+ <xi:include href="../specs/specs_erpc.xml"/>
<xi:include href="../specs/specs_error_handler.xml"/>
<xi:include href="../specs/specs_error_logger.xml"/>
<xi:include href="../specs/specs_file.xml"/>
@@ -30,6 +31,7 @@
<xi:include href="../specs/specs_net_adm.xml"/>
<xi:include href="../specs/specs_net_kernel.xml"/>
<xi:include href="../specs/specs_os.xml"/>
+ <xi:include href="../specs/specs_pg.xml"/>
<xi:include href="../specs/specs_pg2.xml"/>
<xi:include href="../specs/specs_rpc.xml"/>
<xi:include href="../specs/specs_seq_trace.xml"/>
diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
index bc2e07ddf5..f4eb12818d 100644
--- a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
+++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
@@ -400,6 +400,10 @@ is_node_name(_Node) ->
hs_data_common(DistCtrl) ->
TickHandler = call_ctrlr(DistCtrl, tick_handler),
Socket = call_ctrlr(DistCtrl, socket),
+ RejectFlags = case init:get_argument(gen_tcp_dist_reject_flags) of
+ {ok,[[Flags]]} -> list_to_integer(Flags);
+ _ -> #hs_data{}#hs_data.reject_flags
+ end,
#hs_data{f_send = send_fun(),
f_recv = recv_fun(),
f_setopts_pre_nodeup = setopts_pre_nodeup_fun(),
@@ -410,7 +414,8 @@ hs_data_common(DistCtrl) ->
mf_setopts = setopts_fun(DistCtrl, Socket),
mf_getopts = getopts_fun(DistCtrl, Socket),
mf_getstat = getstat_fun(DistCtrl, Socket),
- mf_tick = tick_fun(DistCtrl, TickHandler)}.
+ mf_tick = tick_fun(DistCtrl, TickHandler),
+ reject_flags = RejectFlags}.
%%% ------------------------------------------------------------
%%% Distribution controller processes
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index f06fc328d7..3cc825fca6 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -44,7 +44,18 @@
-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000).
%% -define(DFLAG_NO_MAGIC, 16#200000). %% Used internally only
-define(DFLAG_EXIT_PAYLOAD, 16#400000).
--define(DFLAG_FRAGMENTS, 16#800000).
+-define(DFLAG_FRAGMENTS, 16#00800000).
+-define(DFLAG_HANDSHAKE_23, 16#01000000).
+-define(DFLAG_RESERVED, 16#fe000000).
+-define(DFLAG_SPAWN, 16#100000000).
%% Also update dflag2str() in ../src/dist_util.erl
%% when adding flags...
+
+
+-define(ERL_DIST_VER_5, 5). % OTP-22 or (much) older
+-define(ERL_DIST_VER_6, 6). % OTP-23 (or maybe newer?)
+
+-define(ERL_DIST_VER_LOW, ?ERL_DIST_VER_5).
+-define(ERL_DIST_VER_HIGH, ?ERL_DIST_VER_6).
+
diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl
index 56f775f060..05c7eee795 100644
--- a/lib/kernel/include/dist_util.hrl
+++ b/lib/kernel/include/dist_util.hrl
@@ -84,7 +84,10 @@
f_handshake_complete, %% Notify handshake complete
add_flags, %% dflags to add
reject_flags, %% dflags not to use (not all can be rejected)
- require_flags %% dflags that are required
+ require_flags, %% dflags that are required
+
+ %% New in kernel-@master@ (OTP-23.0)
+ other_creation
}).
diff --git a/lib/kernel/include/eep48.hrl b/lib/kernel/include/eep48.hrl
new file mode 100644
index 0000000000..2ce9a1430a
--- /dev/null
+++ b/lib/kernel/include/eep48.hrl
@@ -0,0 +1,14 @@
+-define(NATIVE_FORMAT,<<"application/erlang+html">>).
+-define(CURR_DOC_VERSION, {1,0,0}).
+-record(docs_v1, {anno,
+ beam_language = erlang,
+ format = ?NATIVE_FORMAT,
+ module_doc,
+ metadata = #{ otp_doc_vsn => ?CURR_DOC_VERSION },
+ docs}).
+
+-record(docs_v1_entry, {kind_name_arity,
+ anno,
+ signature,
+ doc,
+ metadata}).
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 2d2b84c206..a5e24c9d67 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -73,6 +73,7 @@ MODULES = \
erl_epmd \
erl_reply \
erl_signal_handler \
+ erpc \
erts_debug \
error_handler \
error_logger \
@@ -128,6 +129,7 @@ MODULES = \
net_adm \
net_kernel \
os \
+ pg \
pg2 \
ram_file \
rpc \
@@ -147,7 +149,8 @@ MODULES = \
HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \
../include/dist.hrl ../include/dist_util.hrl \
- ../include/net_address.hrl ../include/logger.hrl
+ ../include/net_address.hrl ../include/logger.hrl ../include/eep48.hrl
+
INTERNAL_HRL_FILES= application_master.hrl disk_log.hrl \
erl_epmd.hrl file_int.hrl hipe_ext_format.hrl \
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index 7715dca7c6..3e1a49733a 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -37,6 +37,9 @@
-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3, init_starter/4, get_loaded/1]).
+%% logger callback
+-export([format_log/1, format_log/2]).
+
%% Test exports, only to be used from the test suites
-export([test_change_apps/2]).
@@ -1804,7 +1807,7 @@ check_conf() ->
case init:get_argument(config) of
{ok, Files} ->
{ok, lists:foldl(
- fun([File], Env) ->
+ fun(File, Env) ->
BFName = filename:basename(File,".config"),
FName = filename:join(filename:dirname(File),
BFName ++ ".config"),
@@ -1836,7 +1839,7 @@ check_conf() ->
{error, {Line, _Mod, Str}} ->
throw({error, {FName, Line, Str}})
end
- end, [], Files)};
+ end, [], lists:append(Files))};
_ -> {ok, []}
end.
@@ -1930,9 +1933,12 @@ info_started(Name, Node) ->
report=>[{application, Name},
{started_at, Node}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun application_controller:format_log/2,
logger_formatter=>#{title=>"PROGRESS REPORT"},
- error_logger=>#{tag=>info_report,type=>progress}}).
+ error_logger=>#{tag=>info_report,
+ type=>progress,
+ report_cb=>
+ fun application_controller:format_log/1}}).
info_exited(Name, Reason, Type) ->
?LOG_NOTICE(#{label=>{application_controller,exit},
@@ -1940,8 +1946,140 @@ info_exited(Name, Reason, Type) ->
{exited, Reason},
{type, Type}]},
#{domain=>[otp],
- report_cb=>fun logger:format_otp_report/1,
- error_logger=>#{tag=>info_report,type=>std_info}}).
+ report_cb=>fun application_controller:format_log/2,
+ error_logger=>#{tag=>info_report,
+ type=>std_info,
+ report_cb=>
+ fun application_controller:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(LogReport) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(LogReport, Depth), FormatOpts).
+
+limit_report(LogReport, unlimited) ->
+ LogReport;
+limit_report(#{label:={application_controller,progress},
+ report:=[{application,_}=Application,
+ {started_at,Node}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Application,
+ {started_at,io_lib:limit_term(Node, Depth)}]};
+limit_report(#{label:={application_controller,exit},
+ report:=[{application,_}=Application,
+ {exited,Reason},{type,Type}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Application,
+ {exited,io_lib:limit_term(Reason, Depth)},
+ {type,io_lib:limit_term(Type, Depth)}]}.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={application_controller,progress},
+ report:=[{application,Name},{started_at,Node}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = "Application: "++P++". Started at: "++P++".",
+ Args =
+ case Depth of
+ unlimited ->
+ [Name,Node];
+ _ ->
+ [Name,Depth,Node,Depth]
+ end,
+ {Format,Args};
+format_log_single(#{label:={application_controller,exit},
+ report:=[{application,Name},
+ {exited,Reason},
+ {type,Type}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Application: ",P,". Exited: ",P,
+ ". Type: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Name,Reason,Type];
+ _ ->
+ [Name,Depth,Reason,Depth,Type,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={application_controller,progress},
+ report:=[{application,Name},
+ {started_at,Node}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" application: ",P,"~n",
+ " started_at: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Name,Node];
+ _ ->
+ [Name,Depth,Node,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={application_controller,exit},
+ report:=[{application,Name},
+ {exited,Reason},
+ {type,Type}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" application: ",P,"~n",
+ " exited: ",P,"~n",
+ " type: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Name,Reason,Type];
+ _ ->
+ [Name,Depth,Reason,Depth,Type,Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%%-----------------------------------------------------------------
%% Reply to all processes waiting this application to be started.
diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl
index 4d18daf9e4..1f4a507766 100644
--- a/lib/kernel/src/auth.erl
+++ b/lib/kernel/src/auth.erl
@@ -24,7 +24,11 @@
%% Old documented interface - deprecated
-export([is_auth/1, cookie/0, cookie/1, node_cookie/1, node_cookie/2]).
--deprecated([{is_auth,1}, {cookie,'_'}, {node_cookie, '_'}]).
+-deprecated([{is_auth,1,"use net_adm:ping/1 instead"},
+ {cookie,0,"use erlang:get_cookie/0 instead"},
+ {cookie,1,"use erlang:set_cookie/2 instead"},
+ {node_cookie, '_',
+ "use erlang:set_cookie/2 and net_adm:ping/1 instead"}]).
%% New interface - meant for internal use within kernel only
-export([get_cookie/0, get_cookie/1,
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index 964ede9bc9..71a20231d4 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -20,6 +20,7 @@
-module(code).
-include_lib("kernel/include/logger.hrl").
+-include("eep48.hrl").
%% This is the interface module to the code server. It also contains
%% some implementation details. See also related modules: code_*.erl
@@ -44,6 +45,7 @@
soft_purge/1,
is_loaded/1,
all_loaded/0,
+ all_available/0,
stop/0,
root_dir/0,
lib_dir/0,
@@ -68,18 +70,21 @@
rehash/0,
start_link/0,
which/1,
+ get_doc/1,
where_is_file/1,
where_is_file/2,
set_primary_archive/4,
clash/0,
+ module_status/0,
module_status/1,
modified_modules/0,
get_mode/0]).
--deprecated({rehash,0,next_major_release}).
+-deprecated({rehash,0,"the code path cache feature has been removed"}).
-export_type([load_error_rsn/0, load_ret/0]).
-export_type([prepared_code/0]).
+-export_type([module_status/0]).
-include_lib("kernel/include/file.hrl").
@@ -218,6 +223,53 @@ get_object_code(Mod) when is_atom(Mod) -> call({get_object_code, Mod}).
Loaded :: loaded_filename().
all_loaded() -> call(all_loaded).
+-spec all_available() -> [{Module, Filename, Loaded}] when
+ Module :: string(),
+ Filename :: loaded_filename(),
+ Loaded :: boolean().
+all_available() ->
+ case code:get_mode() of
+ interactive ->
+ all_available(get_path(), #{});
+ embedded ->
+ all_available([], #{})
+ end.
+all_available([Path|Tail], Acc) ->
+ case erl_prim_loader:list_dir(Path) of
+ {ok, Files} ->
+ all_available(Tail, all_available(Path, Files, Acc));
+ _Error ->
+ all_available(Tail, Acc)
+ end;
+all_available([], AllModules) ->
+ AllLoaded = [{atom_to_list(M),Path,true} || {M,Path} <- all_loaded()],
+ AllAvailable =
+ maps:fold(
+ fun(File, Path, Acc) ->
+ [{filename:rootname(File), filename:append(Path, File), false} | Acc]
+ end, [], AllModules),
+ OrderFun = fun F({A,_,_},{B,_,_}) ->
+ F(A,B);
+ F(A,B) ->
+ A =< B
+ end,
+ lists:umerge(OrderFun, lists:sort(OrderFun, AllLoaded), lists:sort(OrderFun, AllAvailable)).
+
+all_available(Path, [File | T], Acc) ->
+ case filename:extension(File) of
+ ".beam" ->
+ case maps:is_key(File, Acc) of
+ false ->
+ all_available(Path, T, Acc#{ File => Path });
+ true ->
+ all_available(Path, T, Acc)
+ end;
+ _Else ->
+ all_available(Path, T, Acc)
+ end;
+all_available(_Path, [], Acc) ->
+ Acc.
+
-spec stop() -> no_return().
stop() -> call(stop).
@@ -734,7 +786,7 @@ start_get_mode() ->
-spec which(Module) -> Which when
Module :: module(),
- Which :: file:filename() | loaded_ret_atoms() | non_existing.
+ Which :: loaded_filename() | non_existing.
which(Module) when is_atom(Module) ->
case is_loaded(Module) of
false ->
@@ -783,6 +835,94 @@ where_is_file(Tail, File, Path, Files) ->
where_is_file(Tail, File)
end.
+-spec get_doc(Mod) -> {ok, Res} | {error, Reason} when
+ Mod :: module(),
+ Res :: #docs_v1{},
+ Reason :: non_existing | missing | file:posix().
+get_doc(Mod) when is_atom(Mod) ->
+ case which(Mod) of
+ preloaded ->
+ Fn = filename:join([code:lib_dir(erts),"ebin",atom_to_list(Mod) ++ ".beam"]),
+ get_doc_chunk(Fn, Mod);
+ Error when is_atom(Error) ->
+ {error, Error};
+ Fn ->
+ get_doc_chunk(Fn, Mod)
+ end.
+
+get_doc_chunk(Filename, Mod) when is_atom(Mod) ->
+ case beam_lib:chunks(Filename, ["Docs"]) of
+ {error,beam_lib,{missing_chunk,_,_}} ->
+ case get_doc_chunk(Filename, atom_to_list(Mod)) of
+ {error,missing} ->
+ get_doc_chunk_from_ast(Filename);
+ Error ->
+ Error
+ end;
+ {error,beam_lib,{file_error,_Filename,enoent}} ->
+ get_doc_chunk(Filename, atom_to_list(Mod));
+ {ok, {Mod, [{"Docs",Bin}]}} ->
+ binary_to_term(Bin)
+ end;
+get_doc_chunk(Filename, Mod) ->
+ case filename:dirname(Filename) of
+ Filename ->
+ {error,missing};
+ Dir ->
+ ChunkFile = filename:join([Dir,"doc","chunks",Mod ++ ".chunk"]),
+ case file:read_file(ChunkFile) of
+ {ok, Bin} ->
+ {ok, binary_to_term(Bin)};
+ {error,enoent} ->
+ get_doc_chunk(Dir, Mod);
+ {error,Reason} ->
+ {error,Reason}
+ end
+ end.
+
+get_doc_chunk_from_ast(Filename) ->
+ case beam_lib:chunks(Filename, [abstract_code]) of
+ {error,beam_lib,{missing_chunk,_,_}} ->
+ {error,missing};
+ {ok, {_Mod, [{abstract_code,
+ {raw_abstract_v1, AST}}]}} ->
+ Docs = get_function_docs_from_ast(AST),
+ {ok, #docs_v1{ anno = 0, beam_language = erlang,
+ module_doc = none,
+ metadata = #{ generated => true, otp_doc_vsn => ?CURR_DOC_VERSION },
+ docs = Docs }};
+ {ok, {_Mod, [{abstract_code,no_abstract_code}]}} ->
+ {error,missing};
+ Error ->
+ Error
+ end.
+
+get_function_docs_from_ast(AST) ->
+ lists:flatmap(fun(E) -> get_function_docs_from_ast(E, AST) end, AST).
+get_function_docs_from_ast({function,Ln,Name,Arity,_Code}, AST) ->
+ Signature = io_lib:format("~p/~p",[Name,Arity]),
+ Anno = erl_anno:new(Ln),
+ Specs = lists:filter(fun({attribute,_Ln,spec,{FA,_}}) ->
+ case FA of
+ {F,A} ->
+ F =:= Name andalso A =:= Arity;
+ {_, F, A} ->
+ F =:= Name andalso A =:= Arity
+ end;
+ (_) -> false
+ end, AST),
+ SpecMd = case Specs of
+ [S] -> #{ spec => [S] };
+ [] -> #{}
+ end,
+ FnDocs = [],
+ Md = SpecMd#{},
+ [{{function, Name, Arity}, Anno, [unicode:characters_to_binary(Signature)],
+ #{ <<"en">> => FnDocs },
+ Md#{}}];
+get_function_docs_from_ast(_, _) ->
+ [].
+
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
FileInfo :: file:file_info(),
@@ -804,7 +944,7 @@ set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo,
{error, _Reason} = Error ->
Error
end.
-
+
%% Search the entire path system looking for name clashes
-spec clash() -> 'ok'.
@@ -915,8 +1055,19 @@ load_all_native_1([{Mod,BeamFilename}|T], ChunkTag) ->
load_all_native_1([], _) ->
ok.
+-type module_status() :: not_loaded | loaded | modified | removed.
+
+%% Returns the list of all loaded modules and their current status
+-spec module_status() -> [{module(), module_status()}].
+module_status() ->
+ module_status([M || {M, _} <- all_loaded()]).
+
%% Returns the status of the module in relation to object file on disk.
--spec module_status(Module :: module()) -> not_loaded | loaded | modified | removed.
+-spec module_status (Module :: module() | [module()]) ->
+ module_status() | [{module(), module_status()}].
+module_status(Modules) when is_list(Modules) ->
+ PathFiles = path_files(),
+ [{M, module_status(M, PathFiles)} || M <- Modules];
module_status(Module) ->
module_status(Module, code:get_path()).
@@ -991,9 +1142,7 @@ get_beam_chunk(Path, Chunk) ->
%% Returns a list of all modules modified on disk.
-spec modified_modules() -> [module()].
modified_modules() ->
- PathFiles = path_files(),
- [M || {M, _} <- code:all_loaded(),
- module_status(M, PathFiles) =:= modified].
+ [M || {M, modified} <- module_status()].
%% prefetch the directory contents of code path directories
path_files() ->
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 5469d8694c..8ef54dd0e1 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -93,7 +93,7 @@ init(Ref, Parent, [Root,Mode]) ->
root = Root,
path = Path,
moddb = Db,
- namedb = init_namedb(Path),
+ namedb = create_namedb(Path, Root),
mode = Mode},
Parent ! {Ref,{ok,self()}},
@@ -265,8 +265,8 @@ handle_call({add_paths,Where,Dirs0}, _From,
{reply,Resp,S#state{path=Path}};
handle_call({set_path,PathList}, _From,
- #state{path=Path0,namedb=Namedb}=S) ->
- {Resp,Path,NewDb} = set_path(PathList, Path0, Namedb),
+ #state{root=Root,path=Path0,namedb=Namedb}=S) ->
+ {Resp,Path,NewDb} = set_path(PathList, Path0, Namedb, Root),
{reply,Resp,S#state{path=Path,namedb=NewDb}};
handle_call({del_path,Name}, _From,
@@ -755,12 +755,12 @@ update(Dir, NameDb) ->
%%
%% Set a completely new path.
%%
-set_path(NewPath0, OldPath, NameDb) ->
+set_path(NewPath0, OldPath, NameDb, Root) ->
NewPath = normalize(NewPath0),
case check_path(NewPath) of
{ok, NewPath2} ->
ets:delete(NameDb),
- NewDb = init_namedb(NewPath2),
+ NewDb = create_namedb(NewPath2, Root),
{true, NewPath2, NewDb};
Error ->
{Error, OldPath, NameDb}
@@ -788,11 +788,27 @@ normalize(Other) ->
%% Handle a table of name-directory pairs.
%% The priv_dir/1 and lib_dir/1 functions will have
%% an O(1) lookup.
-init_namedb(Path) ->
- Db = ets:new(code_names,[private]),
+create_namedb(Path, Root) ->
+ Db = ets:new(code_names,[named_table, public]),
init_namedb(lists:reverse(Path), Db),
+
+ case lookup_name("erts", Db) of
+ {ok, _, _, _} ->
+ %% erts is part of code path
+ ok;
+ false ->
+ %% No erts in code path, check if this is a source
+ %% repo and if so use that.
+ ErtsDir = filename:join(Root, "erts"),
+ case erl_prim_loader:read_file_info(ErtsDir) of
+ error ->
+ ok;
+ _ ->
+ do_insert_name("erts", ErtsDir, Db)
+ end
+ end,
Db.
-
+
init_namedb([P|Path], Db) ->
insert_dir(P, Db),
init_namedb(Path, Db);
@@ -997,7 +1013,6 @@ lookup_name(Name, Db) ->
_ -> false
end.
-
%%
%% Fetch a directory.
%%
diff --git a/lib/kernel/src/disk_log_server.erl b/lib/kernel/src/disk_log_server.erl
index 78c15d0ad8..2e22f28b14 100644
--- a/lib/kernel/src/disk_log_server.erl
+++ b/lib/kernel/src/disk_log_server.erl
@@ -38,6 +38,12 @@
-record(state, {pending = [] :: [#pending{}]}).
+-compile({nowarn_deprecated_function, [{pg2, create, 1}]}).
+-compile({nowarn_deprecated_function, [{pg2, join, 2}]}).
+-compile({nowarn_deprecated_function, [{pg2, leave, 2}]}).
+-compile({nowarn_deprecated_function, [{pg2, which_groups, 0}]}).
+-compile({nowarn_deprecated_function, [{pg2, get_members, 1}]}).
+
%%%-----------------------------------------------------------------
%%% This module implements the disk_log server. Its primary purpose
%%% is to keep the ets table 'disk_log_names' updated and to handle
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index d8571c01be..3b9d88da6a 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -27,7 +27,7 @@
%%-compile(export_all).
-export([handshake_we_started/1, handshake_other_started/1,
- strict_order_flags/0,
+ strict_order_flags/0, rejectable_flags/0,
start_timer/1, setup_timer/2,
reset_timer/1, cancel_timer/1,
is_node_name/1, split_node/1, is_allowed/2,
@@ -70,6 +70,8 @@
-define(u32(X3,X2,X1,X0),
(((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))).
+-define(CREATION_UNKNOWN,0).
+
-record(tick, {read = 0,
write = 0,
tick = 0,
@@ -120,6 +122,10 @@ dflag2str(?DFLAG_EXIT_PAYLOAD) ->
"EXIT_PAYLOAD";
dflag2str(?DFLAG_FRAGMENTS) ->
"FRAGMENTS";
+dflag2str(?DFLAG_HANDSHAKE_23) ->
+ "HANDSHAKE_23";
+dflag2str(?DFLAG_SPAWN) ->
+ "SPAWN";
dflag2str(_) ->
"UNKNOWN".
@@ -152,6 +158,11 @@ strict_order_flags() ->
EDF = erts_internal:get_dflags(),
EDF#erts_dflags.strict_order.
+-spec rejectable_flags() -> integer().
+rejectable_flags() ->
+ EDF = erts_internal:get_dflags(),
+ EDF#erts_dflags.rejectable.
+
make_this_flags(RequestType, AddFlags, RejectFlags, OtherNode,
#erts_dflags{}=EDF) ->
case RejectFlags band (bnot EDF#erts_dflags.rejectable) of
@@ -174,30 +185,35 @@ handshake_other_started(#hs_data{request_type=ReqType,
AddFlgs = convert_flags(AddFlgs0),
RejFlgs = convert_flags(RejFlgs0),
ReqFlgs = convert_flags(ReqFlgs0),
- {PreOtherFlags,Node,Version} = recv_name(HSData0),
+ {PreOtherFlags,Node,Creation,SendNameVersion} = recv_name(HSData0),
EDF = erts_internal:get_dflags(),
PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF),
- ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags),
- HSData = HSData0#hs_data{this_flags=ChosenFlags,
- other_flags=ChosenFlags,
- other_version=Version,
- other_node=Node,
- other_started=true,
- add_flags=AddFlgs,
- reject_flags=RejFlgs,
- require_flags=ReqFlgs},
- check_dflags(HSData, EDF),
- ?debug({"MD5 connection from ~p (V~p)~n",
- [Node, HSData#hs_data.other_version]}),
- mark_pending(HSData),
+ HSData1 = HSData0#hs_data{this_flags=PreThisFlags,
+ other_flags=PreOtherFlags,
+ other_version=flags_to_version(PreOtherFlags),
+ other_node=Node,
+ other_started=true,
+ other_creation=Creation,
+ add_flags=AddFlgs,
+ reject_flags=RejFlgs,
+ require_flags=ReqFlgs},
+ check_dflags(HSData1, EDF),
+ ?debug({"MD5 connection from ~p~n", [Node]}),
+ mark_pending(HSData1),
{MyCookie,HisCookie} = get_cookies(Node),
ChallengeA = gen_challenge(),
- send_challenge(HSData, ChallengeA),
- reset_timer(HSData#hs_data.timer),
- ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie),
- send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)),
+ send_challenge(HSData1, ChallengeA),
+ reset_timer(HSData1#hs_data.timer),
+ HSData2 = recv_complement(HSData1, SendNameVersion),
+ check_dflags(HSData2, EDF),
+ ChosenFlags = adjust_flags(HSData2#hs_data.this_flags,
+ HSData2#hs_data.other_flags),
+ HSData3 = HSData2#hs_data{this_flags = ChosenFlags,
+ other_flags = ChosenFlags},
+ ChallengeB = recv_challenge_reply(HSData3, ChallengeA, MyCookie),
+ send_challenge_ack(HSData3, gen_digest(ChallengeB, HisCookie)),
?debug({dist_util, self(), accept_connection, Node}),
- connection(HSData);
+ connection(HSData3);
handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
handshake_other_started(convert_old_hsdata(OldHsData)).
@@ -363,16 +379,19 @@ handshake_we_started(#hs_data{request_type=ReqType,
add_flags = AddFlgs,
reject_flags = RejFlgs,
require_flags = ReqFlgs},
- send_name(HSData),
+ SendNameVersion = send_name(HSData),
recv_status(HSData),
- {PreOtherFlags,ChallengeA} = recv_challenge(HSData),
+ {PreOtherFlags, ChallengeA, Creation} = recv_challenge(HSData),
ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags),
NewHSData = HSData#hs_data{this_flags = ChosenFlags,
other_flags = ChosenFlags,
- other_started = false},
+ other_started = false,
+ other_version = flags_to_version(PreOtherFlags),
+ other_creation = Creation},
check_dflags(NewHSData, EDF),
MyChallenge = gen_challenge(),
{MyCookie,HisCookie} = get_cookies(Node),
+ send_complement(NewHSData, SendNameVersion),
send_challenge_reply(NewHSData,MyChallenge,
gen_digest(ChallengeA,HisCookie)),
reset_timer(NewHSData#hs_data.timer),
@@ -393,6 +412,16 @@ convert_flags(Flags) when is_integer(Flags) ->
convert_flags(_Undefined) ->
0.
+flags_to_version(Flags) ->
+ case Flags band ?DFLAG_HANDSHAKE_23 of
+ 0 ->
+ ?ERL_DIST_VER_5;
+ ?DFLAG_HANDSHAKE_23 ->
+ ?ERL_DIST_VER_6
+ end.
+
+
+
%% --------------------------------------------------------------
%% The connection has been established.
%% --------------------------------------------------------------
@@ -470,15 +499,14 @@ get_cookies(Node) ->
%% No error return; either succeeds or terminates the process.
do_setnode(#hs_data{other_node = Node, socket = Socket,
other_flags = Flags, other_version = Version,
- f_getll = GetLL}) ->
+ f_getll = GetLL,
+ other_creation = Creation}) ->
case GetLL(Socket) of
{ok,Port} ->
- ?trace("setnode(md5,~p ~p ~p)~n",
- [Node, Port, {publish_type(Flags),
- '(', Flags, ')',
- Version}]),
+ ?trace("setnode: node=~p port=~p flags=~p(~p) ver=~p creation=~p~n",
+ [Node, Port, Flags, publish_type(Flags), Version, Creation]),
try
- erlang:setnode(Node, Port, {Flags, Version, '', ''})
+ erlang:setnode(Node, Port, {Flags, Version, Creation})
catch
error:system_limit ->
error_msg("** Distribution system limit reached, "
@@ -585,21 +613,77 @@ send_name(#hs_data{socket = Socket, this_node = Node,
f_send = FSend,
this_flags = Flags,
other_version = Version}) ->
- ?trace("send_name: node=~w, version=~w\n",
- [Node,Version]),
- ?to_port(FSend, Socket,
- [$n, ?int16(Version), ?int32(Flags), atom_to_list(Node)]).
+ NameBin = atom_to_binary(Node, latin1),
+ if Version =:= undefined;
+ Version =:= ?ERL_DIST_VER_5 ->
+ %% We treat "5" the same as 'undefined' as there are
+ %% custom made epmd modules out there with a hardcoded "5".
+ %%
+ %% Send old 'n' message but with DFLAG_HANDSHAKE_23
+ %% Old nodes will ignore DFLAG_HANDSHAKE_23 and reply old 'n' challenge.
+ %% New nodes will see DFLAG_HANDSHAKE_23 and reply new 'N' challenge.
+ ?trace("send_name: 'n' node=~p, version=~w\n",
+ [Node, ?ERL_DIST_VER_5]),
+ _ = ?to_port(FSend, Socket,
+ [<<$n, ?ERL_DIST_VER_5:16, Flags:32>>, NameBin]),
+ ?ERL_DIST_VER_5;
+
+ is_integer(Version), Version >= ?ERL_DIST_VER_6 ->
+ Creation = erts_internal:get_creation(),
+ NameLen = byte_size(NameBin),
+ ?trace("send_name: 'N' node=~p creation=~w\n",
+ [Node, Creation]),
+ _ = ?to_port(FSend, Socket,
+ [<<$N, Flags:64, Creation:32, NameLen:16>>, NameBin]),
+ ?ERL_DIST_VER_6
+ end.
send_challenge(#hs_data{socket = Socket, this_node = Node,
- other_version = Version,
- this_flags = Flags,
+ this_flags = ThisFlags,
+ other_flags = OtherFlags,
f_send = FSend},
Challenge ) ->
- ?trace("send: challenge=~w version=~w\n",
- [Challenge,Version]),
- ?to_port(FSend, Socket, [$n,?int16(Version), ?int32(Flags),
- ?int32(Challenge),
- atom_to_list(Node)]).
+ case OtherFlags band ?DFLAG_HANDSHAKE_23 of
+ 0 ->
+ %% Reply with old 'n' message
+ ?trace("send: 'n' challenge=~w\n", [Challenge]),
+
+ ?to_port(FSend, Socket, [<<$n,
+ ?ERL_DIST_VER_5:16, % echo same Version back
+ ThisFlags:32,
+ Challenge:32>>,
+ atom_to_list(Node)]);
+
+ ?DFLAG_HANDSHAKE_23 ->
+ %% Reply with new 'N' message
+ Creation = erts_internal:get_creation(),
+ NodeName = atom_to_binary(Node, latin1),
+ NameLen = byte_size(NodeName),
+ ?trace("send: 'N' challenge=~w creation=~w\n",
+ [Challenge,Creation]),
+ ?to_port(FSend, Socket, [<<$N,
+ ThisFlags:64,
+ Challenge:32,
+ Creation:32,
+ NameLen:16>>, NodeName])
+ end.
+
+send_complement(#hs_data{socket = Socket,
+ f_send = FSend,
+ this_flags = Flags,
+ other_flags = Flags},
+ SendNameVersion) ->
+ if SendNameVersion =:= ?ERL_DIST_VER_5,
+ (Flags band ?DFLAG_HANDSHAKE_23) =/= 0 ->
+ %% We sent an old 'n' name message and need to complement
+ %% with creation value.
+ Creation = erts_internal:get_creation(),
+ FlagsHigh = Flags bsr 32,
+ ?trace("send_complement: 'c' flags_high=~w creation=~w\n", [FlagsHigh,Creation]),
+ ?to_port(FSend, Socket, [<<$c, FlagsHigh:32, Creation:32>>]);
+ true->
+ ok % no complement msg needed
+ end.
send_challenge_reply(#hs_data{socket = Socket, f_send = FSend},
Challenge, Digest) ->
@@ -614,31 +698,50 @@ send_challenge_ack(#hs_data{socket = Socket, f_send = FSend},
%%
-%% Get the name of the other side.
+%% Receive first handshake message sent from connecting side.
%% Close the connection if invalid data.
-%% The IP address sent is not interesting (as in the old
-%% tcp_drv.c which used it to detect simultaneous connection
-%% attempts).
%%
recv_name(#hs_data{socket = Socket, f_recv = Recv} = HSData) ->
case Recv(Socket, 0, infinity) of
- {ok,
- [$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4
- | OtherNode] = Data} ->
- case is_node_name(OtherNode) of
- true ->
- Flags = ?u32(Flag1, Flag2, Flag3, Flag4),
- Version = ?u16(VersionA,VersionB),
- is_allowed(HSData, Flags, OtherNode, Version);
- false ->
- ?shutdown(Data)
- end;
+ {ok, [$n | _] = Data} ->
+ recv_name_old(HSData, Data);
+ {ok, [$N | _] = Data} ->
+ recv_name_new(HSData, Data);
_ ->
?shutdown(no_node)
end.
-is_node_name(OtherNodeName) ->
- case string:split(OtherNodeName, "@", all) of
+recv_name_old(HSData,
+ [$n, V1, V0, F3, F2, F1, F0 | Node] = Data) ->
+ <<_Version:16>> = <<V1,V0>>,
+ <<Flags:32>> = <<F3,F2,F1,F0>>,
+ ?trace("recv_name: 'n' node=~p version=~w\n", [Node, _Version]),
+ case is_node_name(Node) of
+ true ->
+ check_allowed(HSData, Node),
+ {Flags, list_to_atom(Node), ?CREATION_UNKNOWN, ?ERL_DIST_VER_5};
+ false ->
+ ?shutdown(Data)
+ end.
+
+recv_name_new(HSData,
+ [$N, F7,F6,F5,F4,F3,F2,F1,F0, Cr3,Cr2,Cr1,Cr0,
+ NL1, NL0 | Rest] = Data) ->
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ <<Creation:32>> = <<Cr3,Cr2,Cr1,Cr0>>,
+ <<NameLen:16>> = <<NL1,NL0>>,
+ {Node, _Residue} = lists:split(NameLen, Rest),
+ ?trace("recv_name: 'N' node=~p creation=~w\n", [Node, Creation]),
+ case is_node_name(Node) of
+ true ->
+ check_allowed(HSData, Node),
+ {Flags, list_to_atom(Node), Creation, ?ERL_DIST_VER_6};
+ false ->
+ ?shutdown(Data)
+ end.
+
+is_node_name(NodeName) ->
+ case string:split(NodeName, "@", all) of
[Name,Host] ->
(not string:is_empty(Name))
andalso (not string:is_empty(Host));
@@ -674,12 +777,12 @@ split_node(Node) ->
%% with allow-node-scheme. An empty allowed list
%% allows all nodes.
%%
-is_allowed(#hs_data{allowed = []}, Flags, Node, Version) ->
- {Flags,list_to_atom(Node),Version};
-is_allowed(#hs_data{allowed = Allowed} = HSData, Flags, Node, Version) ->
+check_allowed(#hs_data{allowed = []}, _Node) ->
+ ok;
+check_allowed(#hs_data{allowed = Allowed} = HSData, Node) ->
case is_allowed(Node, Allowed) of
true ->
- {Flags,list_to_atom(Node),Version};
+ ok;
false ->
send_status(HSData#hs_data{other_node = Node}, not_allowed),
error_msg("** Connection attempt from "
@@ -753,25 +856,91 @@ publish_type(Flags) ->
end.
%% wait for challenge after connect
-recv_challenge(#hs_data{socket=Socket,other_node=Node,
- other_version=Version,f_recv=Recv}) ->
+recv_challenge(#hs_data{socket=Socket, f_recv=Recv}=HSData) ->
case Recv(Socket, 0, infinity) of
- {ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
- Flags = ?u32(Fl1,Fl2,Fl3,Fl4),
- try {list_to_existing_atom(Ns),?u16(V1,V0)} of
- {Node,Version} ->
- Challenge = ?u32(CA3,CA2,CA1,CA0),
- ?trace("recv: node=~w, challenge=~w version=~w\n",
- [Node, Challenge,Version]),
- {Flags,Challenge};
- _ ->
- ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns})
- catch
- error:badarg ->
- ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns})
- end;
+ {ok, [$n | _]=Msg} ->
+ recv_challenge_old(HSData, Msg);
+ {ok,[$N | _]=Msg} ->
+ recv_challenge_new(HSData, Msg);
Other ->
- ?shutdown2(no_node, {recv_challenge_failed, Other})
+ ?shutdown2(no_node, {recv_challenge_failed, Other})
+ end.
+
+recv_challenge_old(#hs_data{other_node=Node},
+ [$n, V1,V0, F3,F2,F1,F0, C3,C2,C1,C0 | Ns]=Msg) ->
+ <<_Version:16>> = <<V1,V0>>,
+ <<Flags:32>> = <<F3,F2,F1,F0>>,
+ <<Challenge:32>> = <<C3,C2,C1,C0>>,
+ ?trace("recv: 'n' node=~p, challenge=~w version=~w\n",
+ [Ns, Challenge, _Version]),
+ try {list_to_existing_atom(Ns), Flags band ?DFLAG_HANDSHAKE_23} of
+ {Node, 0} ->
+ {Flags, Challenge, ?CREATION_UNKNOWN};
+ _ ->
+ ?shutdown2(no_node, {recv_challenge_failed, version, Msg})
+ catch
+ error:badarg ->
+ ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns})
+ end;
+recv_challenge_old(_, Other) ->
+ ?shutdown2(no_node, {recv_challenge_failed, Other}).
+
+recv_challenge_new(#hs_data{other_node=Node},
+ [$N,
+ F7,F6,F5,F4,F3,F2,F1,F0,
+ Ch3,Ch2,Ch1,Ch0,
+ Cr3,Cr2,Cr1,Cr0,
+ NL1,NL0 | Rest] = Msg) ->
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ <<Challenge:32>> = <<Ch3,Ch2,Ch1,Ch0>>,
+ <<Creation:32>> = <<Cr3,Cr2,Cr1,Cr0>>,
+ <<NameLen:16>> = <<NL1,NL0>>,
+ {Ns, _Residue} =
+ try
+ lists:split(NameLen, Rest)
+ catch
+ error:badarg ->
+ ?shutdown2(no_node, {recv_challenge_failed, no_node, Msg})
+ end,
+ ?trace("recv: 'N' node=~p, challenge=~w creation=~w\n",
+ [Ns, Challenge, Creation]),
+
+ case Flags band ?DFLAG_HANDSHAKE_23 of
+ ?DFLAG_HANDSHAKE_23 ->
+ try list_to_existing_atom(Ns) of
+ Node ->
+ {Flags, Challenge, Creation};
+ _ ->
+ ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns})
+ catch
+ error:badarg ->
+ ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns})
+ end;
+ 0 ->
+ ?shutdown2(no_node, {recv_challenge_failed, version, Msg})
+ end;
+recv_challenge_new(_, Other) ->
+ ?shutdown2(no_node, {recv_challenge_failed, Other}).
+
+
+recv_complement(#hs_data{socket = Socket,
+ f_recv = Recv,
+ other_flags = Flags} = HSData,
+ SendNameVersion) ->
+ if SendNameVersion =:= ?ERL_DIST_VER_5,
+ (Flags band ?DFLAG_HANDSHAKE_23) =/= 0 ->
+ case Recv(Socket, 0, infinity) of
+ {ok, [$c, F7,F6,F5,F4, Cr3,Cr2,Cr1,Cr0]} ->
+ <<FlagsHigh:32>> = <<F7,F6,F5,F4>>,
+ <<Creation:32>> = <<Cr3,Cr2,Cr1,Cr0>>,
+ ?trace("recv_complement: creation=~w\n", [Creation]),
+ HSData#hs_data{other_creation = Creation,
+ other_flags = Flags bor (FlagsHigh bsl 32)};
+ Other ->
+ ?shutdown2(no_node, {recv_complement_failed, Other})
+ end;
+ true ->
+ HSData
end.
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index ac4fe2b78b..fecb1cd3e0 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -29,14 +29,16 @@
-define(port_please_failure2(Term), noop).
-endif.
+-include("dist.hrl").
+
-ifndef(erlang_daemon_port).
-define(erlang_daemon_port, 4369).
-endif.
-ifndef(epmd_dist_high).
--define(epmd_dist_high, 4370).
+-define(epmd_dist_high, ?ERL_DIST_VER_HIGH).
-endif.
-ifndef(epmd_dist_low).
--define(epmd_dist_low, 4370).
+-define(epmd_dist_low, ?ERL_DIST_VER_LOW).
-endif.
%% External exports
@@ -347,6 +349,13 @@ wait_for_reg_reply(Socket, SoFar) ->
receive
{tcp, Socket, Data0} ->
case SoFar ++ Data0 of
+ [$v, Result, A, B, C, D] ->
+ case Result of
+ 0 ->
+ {alive, Socket, ?u32(A, B, C, D)};
+ _ ->
+ {error, duplicate_name}
+ end;
[$y, Result, A, B] ->
case Result of
0 ->
diff --git a/lib/kernel/src/erpc.erl b/lib/kernel/src/erpc.erl
new file mode 100644
index 0000000000..a73598c019
--- /dev/null
+++ b/lib/kernel/src/erpc.erl
@@ -0,0 +1,473 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Author: Rickard Green
+%%
+
+-module(erpc).
+
+%% Exported API
+
+-export([call/4,
+ call/5,
+ cast/4,
+ send_request/4,
+ receive_response/1,
+ receive_response/2,
+ wait_response/1,
+ wait_response/2,
+ check_response/2,
+ multicall/4,
+ multicall/5]).
+
+-export_type([request_id/0]).
+
+%% Internal exports (also used by the 'rpc' module)
+
+-export([execute_call/4,
+ execute_call/3,
+ is_arg_error/4,
+ trim_stack/4,
+ call_result/4]).
+
+%%------------------------------------------------------------------------
+
+-compile({inline,[{result,4}]}). %% Nicer error stack trace...
+
+-define(MAX_INT_TIMEOUT, 4294967295).
+-define(TIMEOUT_TYPE, 0..?MAX_INT_TIMEOUT | 'infinity').
+-define(IS_VALID_TMO_INT(TI_), (is_integer(TI_)
+ andalso (0 =< TI_)
+ andalso (TI_ =< ?MAX_INT_TIMEOUT))).
+-define(IS_VALID_TMO(T_), ((T_ == infinity) orelse ?IS_VALID_TMO_INT(T_))).
+
+%%------------------------------------------------------------------------
+%% Exported API
+%%------------------------------------------------------------------------
+
+-spec call(Node, Module, Function, Args) -> Result when
+ Node :: node(),
+ Module :: atom(),
+ Function :: atom(),
+ Args :: [term()],
+ Result :: term().
+
+call(N, M, F, A) ->
+ call(N, M, F, A, infinity).
+
+-dialyzer([{nowarn_function, call/5}, no_return]).
+
+-spec call(Node, Module, Function, Args, Timeout) -> Result when
+ Node :: node(),
+ Module :: atom(),
+ Function :: atom(),
+ Args :: [term()],
+ Timeout :: ?TIMEOUT_TYPE,
+ Result :: term().
+
+call(N, M, F, A, infinity) when node() =:= N, %% Optimize local call
+ is_atom(M),
+ is_atom(F),
+ is_list(A) ->
+ try
+ {return, Return} = execute_call(M,F,A),
+ Return
+ catch
+ exit:Reason ->
+ exit({exception, Reason});
+ error:Reason:Stack ->
+ case is_arg_error(Reason, M, F, A) of
+ true ->
+ error({?MODULE, Reason});
+ false ->
+ ErpcStack = trim_stack(Stack, M, F, A),
+ error({exception, Reason, ErpcStack})
+ end
+ end;
+call(N, _M, _F, _A, infinity) when node() =:= N ->
+ error({?MODULE, badarg});
+call(N, M, F, A, T) when ?IS_VALID_TMO(T) ->
+ try
+ Res = make_ref(),
+ ReqId = erlang:spawn_request(N, ?MODULE, execute_call,
+ [Res, M, F, A],
+ [{reply, error_only},
+ monitor]),
+ receive
+ {spawn_reply, ReqId, error, Reason} ->
+ result(spawn_reply, ReqId, Res, Reason);
+ {'DOWN', ReqId, process, _Pid, Reason} ->
+ result(down, ReqId, Res, Reason)
+ after T ->
+ result(timeout, ReqId, Res, undefined)
+ end
+ catch
+ error:badarg ->
+ error({?MODULE, badarg})
+ end;
+call(_N, _M, _F, _A, _T) ->
+ error({?MODULE, badarg}).
+
+%% Asynchronous call
+
+-opaque request_id() :: {reference(), reference()}.
+
+-spec send_request(Node, Module, Function, Args) -> RequestId when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ RequestId :: request_id().
+
+send_request(N, M, F, A) ->
+ try
+ Res = make_ref(),
+ ReqId = erlang:spawn_request(N, ?MODULE, execute_call,
+ [Res, M, F, A],
+ [{reply, error_only},
+ monitor]),
+ {Res, ReqId}
+ catch
+ error:badarg ->
+ error({?MODULE, badarg})
+ end.
+
+-spec receive_response(RequestId) -> Result when
+ RequestId :: request_id(),
+ Result :: term().
+
+receive_response({Res, ReqId} = RId) when is_reference(Res),
+ is_reference(ReqId) ->
+ receive_response(RId, infinity);
+receive_response(_) ->
+ error({?MODULE, badarg}).
+
+-dialyzer([{nowarn_function, receive_response/2}, no_return]).
+
+-spec receive_response(RequestId, Timeout) -> Result when
+ RequestId :: request_id(),
+ Timeout :: ?TIMEOUT_TYPE,
+ Result :: term().
+
+receive_response({Res, ReqId}, Tmo) when is_reference(Res),
+ is_reference(ReqId),
+ ?IS_VALID_TMO(Tmo) ->
+ receive
+ {spawn_reply, ReqId, error, Reason} ->
+ result(spawn_reply, ReqId, Res, Reason);
+ {'DOWN', ReqId, process, _Pid, Reason} ->
+ result(down, ReqId, Res, Reason)
+ after Tmo ->
+ result(timeout, ReqId, Res, undefined)
+ end;
+receive_response(_, _) ->
+ error({?MODULE, badarg}).
+
+-spec wait_response(RequestId) -> {'response', Result} | 'no_response' when
+ RequestId :: request_id(),
+ Result :: term().
+
+wait_response({Res, ReqId} = RId) when is_reference(Res),
+ is_reference(ReqId) ->
+ wait_response(RId, 0).
+
+-dialyzer([{nowarn_function, wait_response/2}, no_return]).
+
+-spec wait_response(RequestId, WaitTime) ->
+ {'response', Result} | 'no_response' when
+ RequestId :: request_id(),
+ WaitTime :: ?TIMEOUT_TYPE,
+ Result :: term().
+
+wait_response({Res, ReqId}, WT) when is_reference(Res),
+ is_reference(ReqId),
+ ?IS_VALID_TMO(WT) ->
+ receive
+ {spawn_reply, ReqId, error, Reason} ->
+ result(spawn_reply, ReqId, Res, Reason);
+ {'DOWN', ReqId, process, _Pid, Reason} ->
+ {response, result(down, ReqId, Res, Reason)}
+ after WT ->
+ no_response
+ end;
+wait_response(_, _) ->
+ error({?MODULE, badarg}).
+
+-dialyzer([{nowarn_function, check_response/2}, no_return]).
+
+-spec check_response(Message, RequestId) ->
+ {'response', Result} | 'no_response' when
+ Message :: term(),
+ RequestId :: request_id(),
+ Result :: term().
+
+check_response({spawn_reply, ReqId, error, Reason},
+ {Res, ReqId}) when is_reference(Res),
+ is_reference(ReqId) ->
+ result(spawn_reply, ReqId, Res, Reason);
+check_response({'DOWN', ReqId, process, _Pid, Reason},
+ {Res, ReqId}) when is_reference(Res),
+ is_reference(ReqId) ->
+ {response, result(down, ReqId, Res, Reason)};
+check_response(_Msg, {Res, ReqId}) when is_reference(Res),
+ is_reference(ReqId) ->
+ no_response;
+check_response(_, _) ->
+ error({?MODULE, badarg}).
+
+-type stack_item() ::
+ {Module :: atom(),
+ Function :: atom(),
+ Arity :: arity() | (Args :: [term()]),
+ Location :: [{file, Filename :: string()} |
+ {line, Line :: pos_integer()}]}.
+
+-type caught_call_exception() ::
+ {throw, Throw :: term()}
+ | {exit, {exception, Reason :: term()}}
+ | {error, {exception, Reason :: term(), StackTrace :: [stack_item()]}}
+ | {exit, {signal, Reason :: term()}}
+ | {error, {?MODULE, Reason :: term()}}.
+
+
+-spec multicall(Nodes, Module, Function, Args) -> Result when
+ Nodes :: [atom()],
+ Module :: atom(),
+ Function :: atom(),
+ Args :: [term()],
+ Result :: [{ok, ReturnValue :: term()} | caught_call_exception()].
+
+multicall(Ns, M, F, A) ->
+ multicall(Ns, M, F, A, infinity).
+
+-spec multicall(Nodes, Module, Function, Args, Timeout) -> Result when
+ Nodes :: [atom()],
+ Module :: atom(),
+ Function :: atom(),
+ Args :: [term()],
+ Timeout :: ?TIMEOUT_TYPE,
+ Result :: [{ok, ReturnValue :: term()} | caught_call_exception()].
+
+multicall(Ns, M, F, A, T) when ?IS_VALID_TMO(T) ->
+ EndTime = if T == infinity ->
+ infinity;
+ T == 0 ->
+ erlang:monotonic_time(millisecond);
+ T == ?MAX_INT_TIMEOUT ->
+ erlang:monotonic_time(millisecond)
+ + ?MAX_INT_TIMEOUT;
+ true ->
+ Start = erlang:monotonic_time(),
+ NTmo = erlang:convert_time_unit(T,
+ millisecond,
+ native),
+ erlang:convert_time_unit(Start + NTmo - 1,
+ native,
+ millisecond) + 1
+ end,
+ try
+ ReqIds = mcall_send_requests(Ns, M, F, A, []),
+ mcall_wait_replies(ReqIds, [], EndTime)
+ catch
+ error:{?MODULE, badarg} = Reason ->
+ error(Reason)
+ end;
+multicall(_Ns, _M, _F, _A, _T) ->
+ error({?MODULE, badarg}).
+
+-spec cast(Node, Module, Function, Args) -> ok when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+
+cast(Node, Mod, Fun, Args) ->
+ %% Fire and forget...
+ try
+ _ = erlang:spawn_request(Node, Mod, Fun, Args, [{reply, no}]),
+ ok
+ catch
+ error:badarg ->
+ error({?MODULE, badarg})
+ end.
+
+%%------------------------------------------------------------------------
+%% Exported internals
+%%------------------------------------------------------------------------
+
+%% Note that most of these are used by 'rpc' as well...
+
+execute_call(Ref, M, F, A) ->
+ Reply = try
+ {Ref, return, apply(M, F, A)}
+ catch
+ throw:Reason ->
+ {Ref, throw, Reason};
+ exit:Reason ->
+ {Ref, exit, Reason};
+ error:Reason:Stack ->
+ case is_arg_error(Reason, M, F, A) of
+ true ->
+ {Ref, error, {?MODULE, Reason}};
+ false ->
+ ErpcStack = trim_stack(Stack, M, F, A),
+ {Ref, error, Reason, ErpcStack}
+ end
+ end,
+ exit(Reply).
+
+execute_call(M,F,A) ->
+ {return, apply(M, F, A)}.
+
+call_result(Type, ReqId, Res, Reason) ->
+ result(Type, ReqId, Res, Reason).
+
+is_arg_error(badarg, M, F, A) ->
+ try
+ true = is_atom(M),
+ true = is_atom(F),
+ true = is_integer(length(A)),
+ false
+ catch
+ error:badarg ->
+ true
+ end;
+is_arg_error(system_limit, _M, _F, A) ->
+ try
+ apply(?MODULE, nonexisting, A),
+ false
+ catch
+ error:system_limit -> true;
+ _:_ -> false
+ end;
+is_arg_error(_R, _M, _F, _A) ->
+ false.
+
+trim_stack([{?MODULE, execute_call, _, _} | _], M, F, A) ->
+ [{M, F, A, []}];
+trim_stack([{M, F, A, _} = SF, {?MODULE, execute_call, _, _} | _], M, F, A) ->
+ [SF];
+trim_stack(S, M, F, A) ->
+ try
+ trim_stack_aux(S, M, F, A)
+ catch
+ throw:use_all -> S
+ end.
+
+%%------------------------------------------------------------------------
+%% Internals
+%%------------------------------------------------------------------------
+
+trim_stack_aux([], _M, _F, _A) ->
+ throw(use_all);
+trim_stack_aux([{M, F, AL, _} = SF, {?MODULE, execute_call, _, _} | _],
+ M, F, A) when AL == length(A) ->
+ [SF];
+trim_stack_aux([{?MODULE, execute_call, _, _} | _], M, F, A) ->
+ [{M, F, length(A), []}];
+trim_stack_aux([SF|SFs], M, F, A) ->
+ [SF|trim_stack_aux(SFs, M, F, A)].
+
+call_abandon(ReqId) ->
+ case erlang:spawn_request_abandon(ReqId) of
+ true -> true;
+ false -> erlang:demonitor(ReqId, [info])
+ end.
+
+-dialyzer([{nowarn_function, result/4}, no_return]).
+
+-spec result('down', ReqId, Res, Reason) -> term() when
+ ReqId :: reference(),
+ Res :: reference(),
+ Reason :: term();
+ ('spawn_reply', ReqId, Res, Reason) -> no_return() when
+ ReqId :: reference(),
+ Res :: reference(),
+ Reason :: term();
+ ('timeout', ReqId, Res, Reason) -> term() when
+ ReqId :: reference(),
+ Res :: reference(),
+ Reason :: term().
+
+result(down, _ReqId, Res, {Res, return, Return}) ->
+ Return;
+result(down, _ReqId, Res, {Res, throw, Throw}) ->
+ throw(Throw);
+result(down, _ReqId, Res, {Res, exit, Exit}) ->
+ exit({exception, Exit});
+result(down, _ReqId, Res, {Res, error, Error, Stack}) ->
+ error({exception, Error, Stack});
+result(down, _ReqId, Res, {Res, error, {?MODULE, _} = ErpcErr}) ->
+ error(ErpcErr);
+result(down, _ReqId, _Res, noconnection) ->
+ error({?MODULE, noconnection});
+result(down, _ReqId, _Res, Reason) ->
+ exit({signal, Reason});
+result(spawn_reply, _ReqId, _Res, Reason) ->
+ error({?MODULE, Reason});
+result(timeout, ReqId, Res, _Reason) ->
+ case call_abandon(ReqId) of
+ true ->
+ error({?MODULE, timeout});
+ false ->
+ %% Spawn error or DOWN has arrived. Return
+ %% a result instead of a timeout since we
+ %% just got the result...
+ receive
+ {spawn_reply, ReqId, error, Reason} ->
+ result(spawn_reply, ReqId, Res, Reason);
+ {'DOWN', ReqId, process, _Pid, Reason} ->
+ result(down, ReqId, Res, Reason)
+ after
+ 0 ->
+ %% Invalid request id...
+ error({?MODULE, badarg})
+ end
+ end.
+
+mcall_send_requests([], _M, _F, _A, RIDs) ->
+ RIDs;
+mcall_send_requests([N|Ns], M, F, A, RIDs) ->
+ RID = send_request(N, M, F, A),
+ mcall_send_requests(Ns, M, F, A, [RID|RIDs]);
+mcall_send_requests(_, _M, _F, _A, _RIDs) ->
+ error({?MODULE, badarg}).
+
+mcall_wait_replies([], Replies, _Tmo) ->
+ Replies;
+mcall_wait_replies([RID|RIDs], Replies, infinity) ->
+ Reply = mcall_wait_reply(RID, infinity),
+ mcall_wait_replies(RIDs, [Reply|Replies], infinity);
+mcall_wait_replies([RID|RIDs], Replies, EndTime) ->
+ Now = erlang:monotonic_time(millisecond),
+ Tmo = case EndTime - Now of
+ T when T =< 0 -> 0;
+ T -> T
+ end,
+ Reply = mcall_wait_reply(RID, Tmo),
+ mcall_wait_replies(RIDs, [Reply|Replies], EndTime).
+
+mcall_wait_reply(RID, Tmo) ->
+ try
+ {ok, receive_response(RID, Tmo)}
+ catch
+ Class:Reason ->
+ {Class, Reason}
+ end.
+
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
index e324be5290..37771f4e60 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.erl
@@ -147,15 +147,15 @@ do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag}}=Meta) ->
_ ->
%% From logger call which added error_logger data to
%% obtain backwards compatibility with error_logger:*_msg/1,2
- case maps:get(report_cb,Meta,fun logger:format_report/1) of
+ case get_report_cb(Meta) of
RCBFun when is_function(RCBFun,1) ->
try RCBFun(Msg) of
{F,A} when is_list(F), is_list(A) ->
{F,A};
Other ->
{"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]}
- catch C:R ->
- {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]}
+ catch C:R:S ->
+ {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R,S}]}
end;
RCBFun when is_function(RCBFun,2) ->
try RCBFun(Msg,#{depth=>get_format_depth(),
@@ -216,6 +216,13 @@ fix_warning_type(error,std_warning) -> std_error;
fix_warning_type(info,std_warning) -> std_info;
fix_warning_type(_,Type) -> Type.
+get_report_cb(#{?MODULE:=#{report_cb:=RBFun}}) ->
+ RBFun;
+get_report_cb(#{report_cb:=RBFun}) ->
+ RBFun;
+get_report_cb(_) ->
+ fun logger:format_report/1.
+
%%-----------------------------------------------------------------
%% These two simple old functions generate events tagged 'error'
%% Used for simple messages; error or information.
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index e6a30d0b92..da86679532 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -40,6 +40,15 @@
lc_graph/0, lc_graph_to_dot/2, lc_graph_merge/2,
alloc_blocks_size/1]).
+%% Reroutes calls to the given MFA to error_handler:breakpoint/3
+%%
+%% Note that this is potentially unsafe as compiled code may assume that the
+%% targeted function returns a specific type, triggering undefined behavior if
+%% this function were to return something else.
+%%
+%% For reference, the debugger avoids the issue by purging the affected module
+%% and interpreting all functions in the module, ensuring that no assumptions
+%% are made with regard to return or argument types.
-spec breakpoint(MFA, Flag) -> non_neg_integer() when
MFA :: {Module :: module(),
Function :: atom(),
@@ -92,7 +101,7 @@ copy_shared(_) ->
-spec get_internal_state(W) -> term() when
W :: reds_left | node_and_dist_references | monitoring_nodes
- | next_pid | 'DbTable_words' | check_io_debug
+ | next_pid | 'DbTable_words' | check_io_debug | lc_graph
| process_info_args | processes | processes_bif_info
| max_atom_out_cache_index | nbalance | available_internal_state
| force_heap_frags | memory
@@ -517,43 +526,38 @@ alloc_blocks_size_1([], _Type, 0) ->
undefined;
alloc_blocks_size_1([{_Type, false} | Rest], Type, Acc) ->
alloc_blocks_size_1(Rest, Type, Acc);
-alloc_blocks_size_1([{Type, Instances} | Rest], Type, Acc0) ->
- F = fun ({instance, _, L}, Acc) ->
+alloc_blocks_size_1([{_Type, Instances} | Rest], Type, Acc) ->
+ F = fun ({instance, _, L}, Acc0) ->
MBCSPool = case lists:keyfind(mbcs_pool, 1, L) of
{_, Pool} -> Pool;
false -> []
end,
{_,MBCS} = lists:keyfind(mbcs, 1, L),
{_,SBCS} = lists:keyfind(sbcs, 1, L),
- Acc +
- sum_block_sizes(MBCSPool) +
- sum_block_sizes(MBCS) +
- sum_block_sizes(SBCS)
+ Acc1 = sum_block_sizes(MBCSPool, Type, Acc0),
+ Acc2 = sum_block_sizes(MBCS, Type, Acc1),
+ sum_block_sizes(SBCS, Type, Acc2)
end,
- alloc_blocks_size_1(Rest, Type, lists:foldl(F, Acc0, Instances));
-alloc_blocks_size_1([{_Type, Instances} | Rest], Type, Acc0) ->
- F = fun ({instance, _, L}, Acc) ->
- Acc + sum_foreign_sizes(Type, L)
- end,
- alloc_blocks_size_1(Rest, Type, lists:foldl(F, Acc0, Instances));
+ alloc_blocks_size_1(Rest, Type, lists:foldl(F, Acc, Instances));
alloc_blocks_size_1([], _Type, Acc) ->
Acc.
-sum_foreign_sizes(Type, L) ->
- case lists:keyfind(mbcs_pool, 1, L) of
- {_,Pool} ->
- {_,ForeignBlocks} = lists:keyfind(foreign_blocks, 1, Pool),
- case lists:keyfind(Type, 1, ForeignBlocks) of
- {_,TypeSizes} -> sum_block_sizes(TypeSizes);
- false -> 0
- end;
- _ ->
- 0
- end.
+sum_block_sizes([{blocks, List} | Rest], Type, Acc) ->
+ sum_block_sizes(Rest, Type, sum_block_sizes_1(List, Type, Acc));
+sum_block_sizes([_ | Rest], Type, Acc) ->
+ sum_block_sizes(Rest, Type, Acc);
+sum_block_sizes([], _Type, Acc) ->
+ Acc.
+
+sum_block_sizes_1([{Type, L} | Rest], Type, Acc0) ->
+ Acc = lists:foldl(fun({size, Sz,_,_}, Sz0) -> Sz0+Sz;
+ ({size, Sz}, Sz0) -> Sz0+Sz;
+ (_, Sz) -> Sz
+ end, Acc0, L),
+ sum_block_sizes_1(Rest, Type, Acc);
+sum_block_sizes_1([_ | Rest], Type, Acc) ->
+ sum_block_sizes_1(Rest, Type, Acc);
+sum_block_sizes_1([], _Type, Acc) ->
+ Acc.
+
-sum_block_sizes(Blocks) ->
- lists:foldl(
- fun({blocks_size, Sz,_,_}, Sz0) -> Sz0+Sz;
- ({blocks_size, Sz}, Sz0) -> Sz0+Sz;
- (_, Sz) -> Sz
- end, 0, Blocks).
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 1d4e37196c..cde03ce1c4 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -239,20 +239,30 @@ make_dir(Name) ->
del_dir(Name) ->
check_and_call(del_dir, [file_name(Name)]).
--spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name_all(),
+-spec read_file_info(File) -> {ok, FileInfo} | {error, Reason} when
+ File :: name_all() | io_device(),
FileInfo :: file_info(),
Reason :: posix() | badarg.
+read_file_info(IoDevice)
+ when is_pid(IoDevice); is_record(IoDevice, file_descriptor) ->
+ read_file_info(IoDevice, []);
+
read_file_info(Name) ->
check_and_call(read_file_info, [file_name(Name)]).
--spec read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name_all(),
+-spec read_file_info(File, Opts) -> {ok, FileInfo} | {error, Reason} when
+ File :: name_all() | io_device(),
Opts :: [file_info_option()],
FileInfo :: file_info(),
Reason :: posix() | badarg.
+read_file_info(IoDevice, Opts) when is_pid(IoDevice), is_list(Opts) ->
+ file_request(IoDevice, {read_handle_info, Opts});
+
+read_file_info(#file_descriptor{module = Module} = Handle, Opts) when is_list(Opts) ->
+ Module:read_handle_info(Handle, Opts);
+
read_file_info(Name, Opts) when is_list(Opts) ->
Args = [file_name(Name), Opts],
case check_args(Args) of
@@ -460,7 +470,7 @@ raw_write_file_info(Name, #file_info{} = Info) ->
-spec open(File, Modes) -> {ok, IoDevice} | {error, Reason} when
File :: Filename | iodata(),
Filename :: name_all(),
- Modes :: [mode() | ram],
+ Modes :: [mode() | ram | directory],
IoDevice :: io_device(),
Reason :: posix() | badarg | system_limit.
@@ -545,7 +555,7 @@ allocate(#file_descriptor{module = Module} = Handle, Offset, Length) ->
| {no_translation, unicode, latin1}.
read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 ->
- case io:request(File, {get_chars, '', Sz}) of
+ case io:request(File, {get_chars, latin1, '', Sz}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
Other ->
@@ -566,7 +576,7 @@ read(_, _) ->
| {no_translation, unicode, latin1}.
read_line(File) when (is_pid(File) orelse is_atom(File)) ->
- case io:request(File, {get_line, ''}) of
+ case io:request(File, {get_line, latin1, ''}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
Other ->
@@ -1143,7 +1153,7 @@ path_script(Path, File, Bs) ->
{ok, IoDevice, FullName} | {error, Reason} when
Path :: [Dir :: name_all()],
Filename :: name_all(),
- Modes :: [mode()],
+ Modes :: [mode() | directory],
IoDevice :: io_device(),
FullName :: filename_all(),
Reason :: posix() | badarg | system_limit.
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index 34d5497a4a..c03fbb548a 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -314,6 +314,14 @@ file_request(truncate,
Reply ->
std_reply(Reply, State)
end;
+file_request({read_handle_info, Opts},
+ #state{handle=Handle}=State) ->
+ case ?CALL_FD(Handle, read_handle_info, [Opts]) of
+ {error,Reason}=Reply ->
+ {stop,Reason,Reply,State};
+ Reply ->
+ {reply,Reply,State}
+ end;
file_request(Unknown,
#state{}=State) ->
Reason = {request, Unknown},
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 3875074d74..ff6674cd08 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -909,7 +909,7 @@ handle_info({nodeup, Node}, S0) when S0#state.connect_all ->
end;
handle_info({whereis, Name, From}, S) ->
- do_whereis(Name, From),
+ _ = do_whereis(Name, From),
{noreply, S};
handle_info(known, S) ->
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index 5625ae6eb7..8410c1a4b5 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@ get_pids([], _Found, false) ->
start_shell({Mod,Func,Args}) ->
start_shell1(Mod, Func, Args);
start_shell({Node,Mod,Func,Args}) ->
- start_shell1(net, call, [Node,Mod,Func,Args]);
+ start_shell1(rpc, call, [Node,Mod,Func,Args]);
start_shell(Shell) when is_atom(Shell) ->
start_shell1(Shell, start, []);
start_shell(Shell) when is_function(Shell) ->
@@ -795,12 +795,6 @@ save_line({stack, U, _L, D}, Line) ->
{stack, U, Line, D}.
get_lines(Ls) -> get_all_lines(Ls).
-%get_lines({stack, U, {}, []}) ->
-% U;
-%get_lines({stack, U, {}, D}) ->
-% tl(lists:reverse(D, U));
-%get_lines({stack, U, L, D}) ->
-% get_lines({stack, U, {}, [L|D]}).
%% There's a funny behaviour whenever the line stack doesn't have a "\n"
%% at its end -- get_lines() seemed to work on the assumption it *will* be
diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl
index 9745848992..1fab2ba14e 100644
--- a/lib/kernel/src/group_history.erl
+++ b/lib/kernel/src/group_history.erl
@@ -44,7 +44,7 @@ load() ->
wait_for_kernel_safe_sup(),
case history_status() of
enabled ->
- case open_log() of
+ try open_log() of
{ok, ?LOG_NAME} ->
read_full_log(?LOG_NAME);
{repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} ->
@@ -68,6 +68,10 @@ load() ->
handle_open_error(Reason),
disable_history(),
[]
+ catch
+ % disk_log shut down abruptly, possibly because
+ % the node is shutting down. Ignore it.
+ exit:_ -> []
end;
_ ->
[]
@@ -127,11 +131,15 @@ repair_log(Name) ->
%% Return whether the shell history is enabled or not
-spec history_status() -> enabled | disabled.
history_status() ->
- case is_user() orelse application:get_env(kernel, shell_history) of
- true -> disabled; % don't run for user proc
- {ok, enabled} -> enabled;
- undefined -> ?DEFAULT_STATUS;
- _ -> disabled
+ %% Don't run for user proc or if the emulator's tearing down
+ Skip = is_user() orelse not init_running(),
+ case application:get_env(kernel, shell_history) of
+ {ok, enabled} when not Skip ->
+ enabled;
+ undefined when not Skip ->
+ ?DEFAULT_STATUS;
+ _ ->
+ disabled
end.
%% Return whether the user process is running this
@@ -142,6 +150,14 @@ is_user() ->
_ -> false
end.
+%% Return if the system is running (not stopping)
+-spec init_running() -> boolean().
+init_running() ->
+ case init:get_status() of
+ {stopping, _} -> false;
+ _ -> true
+ end.
+
%% Open a disk_log file while ensuring the required path is there.
open_log() ->
Opts = log_options(),
@@ -322,7 +338,7 @@ show_unexpected_warning({M,F,A}, Term) ->
show_unexpected_close_warning() ->
show('$#erlang-history-unexpected-close',
- "The shell log file has mysteriousy closed. Ignoring "
+ "The shell log file has mysteriously closed. Ignoring "
"currently unread history.~n", []).
show_size_warning(_Current, _New) ->
diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl
index 630ef5d2f7..fbccc787a2 100644
--- a/lib/kernel/src/inet_db.erl
+++ b/lib/kernel/src/inet_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -229,12 +229,14 @@ set_edns(Version) -> res_option(edns, Version).
set_udp_payload_size(Size) -> res_option(udp_payload_size, Size).
-set_resolv_conf(Fname) -> res_option(resolv_conf, Fname).
+set_resolv_conf(Fname) when is_list(Fname) ->
+ res_option(resolv_conf, Fname).
-set_hosts_file(Fname) -> res_option(hosts_file, Fname).
+set_hosts_file(Fname) when is_list(Fname) ->
+ res_option(hosts_file, Fname).
get_hosts_file() ->
- get_rc_hosts([], [], inet_hosts_file_byname).
+ get_rc_hosts([], [], inet_hosts_file_byaddr).
%% set socks options
set_socks_server(Server) -> call({set_socks_server, Server}).
@@ -318,7 +320,7 @@ get_rc() ->
get_rc([K | Ks], Ls) ->
case K of
- hosts -> get_rc_hosts(Ks, Ls, inet_hosts_byname);
+ hosts -> get_rc_hosts(Ks, Ls, inet_hosts_byaddr);
domain -> get_rc(domain, res_domain, "", Ks, Ls);
nameservers -> get_rc_ns(db_get(res_ns),nameservers,Ks,Ls);
alt_nameservers -> get_rc_ns(db_get(res_alt_ns),alt_nameservers,Ks,Ls);
@@ -371,18 +373,11 @@ get_rc_ns([], _Tag, Ks, Ls) ->
get_rc(Ks, Ls).
get_rc_hosts(Ks, Ls, Tab) ->
- case lists:keysort(3, ets:tab2list(Tab)) of
+ case ets:tab2list(Tab) of
[] -> get_rc(Ks, Ls);
- [{N,_,IP}|Hosts] -> get_rc_hosts(Ks, Ls, IP, Hosts, [N])
+ Hosts -> get_rc(Ks, [ [{host, IP, Names} || {{_Fam, IP}, Names} <- Hosts] | Ls])
end.
-get_rc_hosts(Ks, Ls, IP, [], Ns) ->
- get_rc(Ks, [{host,IP,lists:reverse(Ns)}|Ls]);
-get_rc_hosts(Ks, Ls, IP, [{N,_,IP}|Hosts], Ns) ->
- get_rc_hosts(Ks, Ls, IP, Hosts, [N|Ns]);
-get_rc_hosts(Ks, Ls, IP, [{N,_,NewIP}|Hosts], Ns) ->
- [{host,IP,lists:reverse(Ns)}|get_rc_hosts(Ks, Ls, NewIP, Hosts, [N])].
-
%%
%% Resolver options
%%
@@ -502,49 +497,26 @@ socks_option(noproxy) -> db_get(socks5_noproxy).
gethostname() -> db_get(hostname).
res_update_conf() ->
- res_update(res_resolv_conf, res_resolv_conf_tm, res_resolv_conf_info,
- set_resolv_conf_tm, fun set_resolv_conf/1).
+ res_update(resolv_conf, res_resolv_conf_tm).
res_update_hosts() ->
- res_update(res_hosts_file, res_hosts_file_tm, res_hosts_file_info,
- set_hosts_file_tm, fun set_hosts_file/1).
+ res_update(hosts_file, res_hosts_file_tm).
-res_update(Tag, TagTm, TagInfo, TagSetTm, SetFun) ->
+res_update(Option, TagTm) ->
case db_get(TagTm) of
undefined -> ok;
- TM ->
+ Tm ->
case times() of
- Now when Now >= TM + ?RES_FILE_UPDATE_TM ->
- case db_get(Tag) of
- undefined ->
- SetFun("");
- "" ->
- SetFun("");
- File ->
- case erl_prim_loader:read_file_info(File) of
- {ok, Finfo0} ->
- Finfo =
- Finfo0#file_info{access = undefined,
- atime = undefined},
- case db_get(TagInfo) of
- Finfo ->
- call({TagSetTm, Now});
- _ ->
- SetFun(File)
- end;
- _ ->
- call({TagSetTm, Now}),
- error
- end
- end;
+ Now when Now >= Tm + ?RES_FILE_UPDATE_TM ->
+ %% Enough time has passed - request server to update
+ res_option(Option, Tm);
_ -> ok
end
end.
db_get(Name) ->
- case ets:lookup(inet_db, Name) of
- [] -> undefined;
- [{_,Val}] -> Val
+ try ets:lookup_element(inet_db, Name, 2)
+ catch error:badarg -> undefined
end.
add_rr(RR) ->
@@ -853,12 +825,10 @@ init([]) ->
reset_db(Db),
CacheOpts = [public, bag, {keypos,#dns_rr.domain}, named_table],
Cache = ets:new(inet_cache, CacheOpts),
- BynameOpts = [protected, bag, named_table, {keypos,1}],
- ByaddrOpts = [protected, bag, named_table, {keypos,3}],
- HostsByname = ets:new(inet_hosts_byname, BynameOpts),
- HostsByaddr = ets:new(inet_hosts_byaddr, ByaddrOpts),
- HostsFileByname = ets:new(inet_hosts_file_byname, BynameOpts),
- HostsFileByaddr = ets:new(inet_hosts_file_byaddr, ByaddrOpts),
+ HostsByname = ets:new(inet_hosts_byname, [named_table]),
+ HostsByaddr = ets:new(inet_hosts_byaddr, [named_table]),
+ HostsFileByname = ets:new(inet_hosts_file_byname, [named_table]),
+ HostsFileByaddr = ets:new(inet_hosts_file_byaddr, [named_table]),
{ok, #state{db = Db,
cache = Cache,
hosts_byname = HostsByname,
@@ -868,29 +838,31 @@ init([]) ->
cache_timer = init_timer() }}.
reset_db(Db) ->
- ets:insert(Db, {hostname, []}),
- ets:insert(Db, {res_ns, []}),
- ets:insert(Db, {res_alt_ns, []}),
- ets:insert(Db, {res_search, []}),
- ets:insert(Db, {res_domain, ""}),
- ets:insert(Db, {res_lookup, []}),
- ets:insert(Db, {res_recurse, true}),
- ets:insert(Db, {res_usevc, false}),
- ets:insert(Db, {res_id, 0}),
- ets:insert(Db, {res_retry, ?RES_RETRY}),
- ets:insert(Db, {res_timeout, ?RES_TIMEOUT}),
- ets:insert(Db, {res_inet6, false}),
- ets:insert(Db, {res_edns, false}),
- ets:insert(Db, {res_udp_payload_size, ?DNS_UDP_PAYLOAD_SIZE}),
- ets:insert(Db, {cache_size, ?CACHE_LIMIT}),
- ets:insert(Db, {cache_refresh_interval,?CACHE_REFRESH}),
- ets:insert(Db, {socks5_server, ""}),
- ets:insert(Db, {socks5_port, ?IPPORT_SOCKS}),
- ets:insert(Db, {socks5_methods, [none]}),
- ets:insert(Db, {socks5_noproxy, []}),
- ets:insert(Db, {tcp_module, ?DEFAULT_TCP_MODULE}),
- ets:insert(Db, {udp_module, ?DEFAULT_UDP_MODULE}),
- ets:insert(Db, {sctp_module, ?DEFAULT_SCTP_MODULE}).
+ ets:insert(
+ Db,
+ [{hostname, []},
+ {res_ns, []},
+ {res_alt_ns, []},
+ {res_search, []},
+ {res_domain, ""},
+ {res_lookup, []},
+ {res_recurse, true},
+ {res_usevc, false},
+ {res_id, 0},
+ {res_retry, ?RES_RETRY},
+ {res_timeout, ?RES_TIMEOUT},
+ {res_inet6, false},
+ {res_edns, false},
+ {res_udp_payload_size, ?DNS_UDP_PAYLOAD_SIZE},
+ {cache_size, ?CACHE_LIMIT},
+ {cache_refresh_interval,?CACHE_REFRESH},
+ {socks5_server, ""},
+ {socks5_port, ?IPPORT_SOCKS},
+ {socks5_methods, [none]},
+ {socks5_noproxy, []},
+ {tcp_module, ?DEFAULT_TCP_MODULE},
+ {udp_module, ?DEFAULT_UDP_MODULE},
+ {sctp_module, ?DEFAULT_SCTP_MODULE}]).
%%----------------------------------------------------------------------
%% Func: handle_call/3
@@ -908,22 +880,7 @@ reset_db(Db) ->
handle_call(Request, From, #state{db=Db}=State) ->
case Request of
{load_hosts_file,IPNmAs} when is_list(IPNmAs) ->
- NIPs =
- lists:flatten(
- [ [{N,
- if tuple_size(IP) =:= 4 -> inet;
- tuple_size(IP) =:= 8 -> inet6
- end,IP} || N <- [Nm|As]]
- || {IP,Nm,As} <- IPNmAs]),
- Byname = State#state.hosts_file_byname,
- Byaddr = State#state.hosts_file_byaddr,
- ets:delete_all_objects(Byname),
- ets:delete_all_objects(Byaddr),
- %% Byname has lowercased names while Byaddr keep the name casing.
- %% This is to be able to reconstruct the original
- %% /etc/hosts entry.
- ets:insert(Byname, [{tolower(N),Type,IP} || {N,Type,IP} <- NIPs]),
- ets:insert(Byaddr, NIPs),
+ load_hosts_list(IPNmAs, State#state.hosts_file_byname, State#state.hosts_file_byaddr),
{reply, ok, State};
{add_host,{A,B,C,D}=IP,[N|As]=Names}
@@ -969,7 +926,7 @@ handle_call(Request, From, #state{db=Db}=State) ->
case res_check_option(Opt, El) of
true ->
Optname = res_optname(Opt),
- [{_,Es}] = ets:lookup(Db, Optname),
+ Es = ets:lookup_element(Db, Optname, 2),
NewEs = case Op of
ins -> [E | lists_delete(E, Es)];
add -> lists_delete(E, Es) ++ El;
@@ -1003,12 +960,12 @@ handle_call(Request, From, #state{db=Db}=State) ->
Option, Fname, res_resolv_conf_tm, res_resolv_conf_info,
undefined, From, State);
- {res_set, hosts_file=Option, Fname} ->
+ {res_set, hosts_file=Option, Fname_or_Tm} ->
handle_set_file(
- Option, Fname, res_hosts_file_tm, res_hosts_file_info,
- fun (Bin) ->
+ Option, Fname_or_Tm, res_hosts_file_tm, res_hosts_file_info,
+ fun (File, Bin) ->
case inet_parse:hosts(
- Fname, {chars,Bin}) of
+ File, {chars,Bin}) of
{ok,Opts} ->
[{load_hosts_file,Opts}];
_ -> error
@@ -1016,12 +973,12 @@ handle_call(Request, From, #state{db=Db}=State) ->
end,
From, State);
%%
- {res_set, resolv_conf=Option, Fname} ->
+ {res_set, resolv_conf=Option, Fname_or_Tm} ->
handle_set_file(
- Option, Fname, res_resolv_conf_tm, res_resolv_conf_info,
- fun (Bin) ->
+ Option, Fname_or_Tm, res_resolv_conf_tm, res_resolv_conf_info,
+ fun (File, Bin) ->
case inet_parse:resolv(
- Fname, {chars,Bin}) of
+ File, {chars,Bin}) of
{ok,Opts} ->
Search =
lists:foldl(
@@ -1075,13 +1032,13 @@ handle_call(Request, From, #state{db=Db}=State) ->
{reply, ok, State};
{add_socks_methods, Ls} ->
- [{_,As}] = ets:lookup(Db, socks5_methods),
+ As = ets:lookup_element(Db, socks5_methods, 2),
As1 = lists_subtract(As, Ls),
ets:insert(Db, {socks5_methods, As1 ++ Ls}),
{reply, ok, State};
{del_socks_methods, Ls} ->
- [{_,As}] = ets:lookup(Db, socks5_methods),
+ As = ets:lookup_element(Db, socks5_methods, 2),
As1 = lists_subtract(As, Ls),
case lists:member(none, As1) of
false -> ets:insert(Db, {socks5_methods, As1 ++ [none]});
@@ -1095,12 +1052,12 @@ handle_call(Request, From, #state{db=Db}=State) ->
{add_socks_noproxy, {{A,B,C,D},{MA,MB,MC,MD}}}
when ?ip(A,B,C,D), ?ip(MA,MB,MC,MD) ->
- [{_,As}] = ets:lookup(Db, socks5_noproxy),
+ As = ets:lookup_element(Db, socks5_noproxy, 2),
ets:insert(Db, {socks5_noproxy, As++[{{A,B,C,D},{MA,MB,MC,MD}}]}),
{reply, ok, State};
{del_socks_noproxy, {A,B,C,D}=IP} when ?ip(A,B,C,D) ->
- [{_,As}] = ets:lookup(Db, socks5_noproxy),
+ As = ets:lookup_element(Db, socks5_noproxy, 2),
ets:insert(Db, {socks5_noproxy, lists_keydelete(IP, 1, As)}),
{reply, ok, State};
@@ -1194,68 +1151,283 @@ terminate(_Reason, State) ->
%%% Internal functions
%%%----------------------------------------------------------------------
-handle_set_file(Option, Fname, TagTm, TagInfo, ParseFun, From,
- #state{db=Db}=State) ->
+handle_set_file(
+ Option, Tm, TagTm, TagInfo, ParseFun, From, #state{db=Db}=State)
+ when is_integer(Tm) ->
+ %%
+ %% Maybe update file content
+ %%
+ try ets:lookup_element(Db, TagTm, 2) of
+ Tm ->
+ %% Current update request
+ File = ets:lookup_element(Db, res_optname(Option), 2),
+ Finfo = ets:lookup_element(Db, TagInfo, 2),
+ handle_update_file(
+ Finfo, File, TagTm, TagInfo, ParseFun, From, State);
+ _ ->
+ %% Late request - ignore update
+ {reply, ok, State}
+ catch error:badarg ->
+ %% Option no longer set - ignore update
+ {reply, ok, State}
+ end;
+handle_set_file(
+ Option, Fname, TagTm, TagInfo, ParseFun, From, #state{db=Db}=State) ->
case res_check_option(Option, Fname) of
true when Fname =:= "" ->
+ %% Delete file content and monitor
ets:insert(Db, {res_optname(Option), Fname}),
ets:delete(Db, TagInfo),
ets:delete(Db, TagTm),
- handle_set_file(ParseFun, <<>>, From, State);
+ handle_set_file(ParseFun, Fname, <<>>, From, State);
true when ParseFun =:= undefined ->
+ %% Set file name and monitor
File = filename:flatten(Fname),
ets:insert(Db, {res_optname(Option), File}),
ets:insert(Db, {TagInfo, undefined}),
- TimeZero = - (?RES_FILE_UPDATE_TM + 1), % Early enough
+ TimeZero = times() - (?RES_FILE_UPDATE_TM + 1), % Early enough
ets:insert(Db, {TagTm, TimeZero}),
{reply,ok,State};
true ->
+ %% Set file name and monitor, read content
File = filename:flatten(Fname),
ets:insert(Db, {res_optname(Option), File}),
- Bin =
- case erl_prim_loader:read_file_info(File) of
- {ok, Finfo0} ->
- Finfo = Finfo0#file_info{access = undefined,
- atime = undefined},
- ets:insert(Db, {TagInfo, Finfo}),
- ets:insert(Db, {TagTm, times()}),
- case erl_prim_loader:get_file(File) of
- {ok, B, _} -> B;
- _ -> <<>>
- end;
- _ ->
- ets:insert(Db, {TagInfo, undefined}),
- TimeZero = - (?RES_FILE_UPDATE_TM + 1), % Early enough
- ets:insert(Db, {TagTm, TimeZero}),
- <<>>
- end,
- handle_set_file(ParseFun, Bin, From, State);
+ handle_update_file(
+ undefined, File, TagTm, TagInfo, ParseFun, From, State);
false -> {reply,error,State}
end.
-handle_set_file(ParseFun, Bin, From, State) ->
- case ParseFun(Bin) of
+handle_set_file(ParseFun, File, Bin, From, State) ->
+ case ParseFun(File, Bin) of
error ->
{reply,error,State};
Opts ->
handle_rc_list(Opts, From, State)
end.
+handle_update_file(
+ Finfo, File, TagTm, TagInfo, ParseFun, From, #state{db = Db} = State) ->
+ %%
+ %% Update file content if file has been updated
+ %%
+ case erl_prim_loader:read_file_info(File) of
+ {ok, Finfo} ->
+ %% No file update - we are done
+ {reply, ok, State};
+ {ok, Finfo_1} ->
+ %% File updated - read content
+ ets:insert(Db, {TagInfo, Finfo_1}),
+ ets:insert(Db, {TagTm, times()}),
+ Bin =
+ case erl_prim_loader:get_file(File) of
+ {ok, B, _} -> B;
+ _ -> <<>>
+ end,
+ handle_set_file(ParseFun, File, Bin, From, State);
+ _ ->
+ %% No file - clear content and reset monitor
+ ets:insert(Db, {TagInfo, undefined}),
+ ets:insert(Db, {TagTm, times()}),
+ handle_set_file(ParseFun, File, <<>>, From, State)
+ end.
+
%% Byname has lowercased names while Byaddr keep the name casing.
%% This is to be able to reconstruct the original /etc/hosts entry.
do_add_host(Byname, Byaddr, Names, Type, IP) ->
- do_del_host(Byname, Byaddr, IP),
- ets:insert(Byname, [{tolower(N),Type,IP} || N <- Names]),
- ets:insert(Byaddr, [{N,Type,IP} || N <- Names]),
+ Nms = [tolower(Nm) || Nm <- Names],
+ add_ip_bynms(Byname, Type, IP, Nms, Names),
+ Key = {Type, IP},
+ try ets:lookup_element(Byaddr, Key, 2) of
+ Names_0 ->
+ %% Delete IP address from byname entries
+ NmsSet = % Set of new tolower(Name)s
+ lists:foldl(
+ fun (Nm, Set) ->
+ maps:put(Nm, [], Set)
+ end, #{}, Nms),
+ del_ip_bynms(
+ Byname, Type, IP,
+ [Nm || Nm <- [tolower(Name) || Name <- Names_0],
+ not maps:is_key(Nm, NmsSet)])
+ catch error:badarg ->
+ ok
+ end,
+ %% Replace the entry in the byaddr table
+ ets:insert(Byaddr, {Key, Names}),
ok.
do_del_host(Byname, Byaddr, IP) ->
- _ =
- [ets:delete_object(Byname, {tolower(Name),Type,Addr}) ||
- {Name,Type,Addr} <- ets:lookup(Byaddr, IP)],
- ets:delete(Byaddr, IP),
+ Fam = inet_family(IP),
+ Key = {Fam, IP},
+ try ets:lookup_element(Byaddr, Key, 2) of
+ Names ->
+ %% Delete IP address from byname entries
+ del_ip_bynms(
+ Byname, Fam, IP,
+ [tolower(Name) || Name <- Names]),
+ %% Delete from byaddr table
+ true = ets:delete(Byaddr, Key),
+ ok
+ catch error:badarg ->
+ ok
+ end.
+
+
+add_ip_bynms(Byname, Fam, IP, Nms, Names) ->
+ lists:foreach(
+ fun (Nm) ->
+ Key = {Fam, Nm},
+ case ets:lookup(Byname, Key) of
+ [{_Key, [IP | _] = IPs, _Names_1}] ->
+ %% Replace names in the byname entry
+ true =
+ ets:insert(
+ Byname,
+ {Key, IPs, Names});
+ [{_Key, IPs, Names_0}] ->
+ case lists:member(IP, IPs) of
+ true ->
+ ok;
+ false ->
+ %% Add the IP address
+ true =
+ ets:insert(
+ Byname,
+ {Key, IPs ++ [IP], Names_0})
+ end;
+ [] ->
+ %% Create a new byname entry
+ true =
+ ets:insert(Byname, {Key, [IP], Names})
+ end
+ end, Nms).
+
+del_ip_bynms(Byname, Fam, IP, Nms) ->
+ lists:foreach(
+ fun (Nm) ->
+ Key = {Fam, Nm},
+ case ets:lookup(Byname, Key) of
+ [{_Key, [IP], _Names}] ->
+ %% Delete whole entry
+ true = ets:delete(Byname, Key);
+ [{_Key, IPs_0, Names_0}] ->
+ case lists:member(IP, IPs_0) of
+ true ->
+ %% Delete the IP address from list
+ IPs = lists:delete(IP, IPs_0),
+ true =
+ ets:insert(
+ Byname, {Key, IPs, Names_0});
+ false ->
+ ok
+ end;
+ [] ->
+ ok
+ end
+ end, Nms).
+
+
+inet_family(T) when tuple_size(T) =:= 4 -> inet;
+inet_family(T) when tuple_size(T) =:= 8 -> inet6.
+
+
+%% Hosts = [ {IP, Name, Aliases}, ... ]
+%% ByaddrMap = #{ {Fam, IP} := rev(Names) }
+%% BynameMap = #{ {Fam, tolower(Name)} := {rev([IP, ...]), Names}}
+
+%% Synchronises internal tables with .hosts/aliases file
+load_hosts_list(Hosts, Byname, Byaddr) ->
+ %% Create byaddr and byname maps
+ {ByaddrMap, BynameMap} = load_hosts_list(Hosts),
+ %% Insert or overwrite existing keys
+ ets:insert(
+ Byaddr,
+ [{Addr, lists:reverse(NamesR)}
+ || {Addr, NamesR} <- maps:to_list(ByaddrMap)]),
+ ets:insert(
+ Byname,
+ [{Fam_Nm, lists:reverse(IPsR), Names}
+ || {Fam_Nm, {IPsR, Names}} <- maps:to_list(BynameMap)]),
+ %% Delete no longer existing keys
+ ets_clean_map_keys(Byaddr, ByaddrMap),
+ ets_clean_map_keys(Byname, BynameMap).
+
+load_hosts_list(Hosts) ->
+ load_hosts_list_byaddr(Hosts, #{}, []).
+
+load_hosts_list_byaddr(
+ [], ByaddrMap, Addrs) ->
+ %% Now for the byname table...
+ load_hosts_list_byname(lists:reverse(Addrs), ByaddrMap, #{});
+%% Traverse hosts list, create byaddr map and insertion order list
+load_hosts_list_byaddr(
+ [{IP, Name, Aliases} | Hosts], ByaddrMap, Addrs) ->
+ Addr = {inet_family(IP), IP},
+ case ByaddrMap of
+ #{Addr := NamesR} ->
+ %% Concatenate names to existing IP address entry
+ load_hosts_list_byaddr(
+ Hosts,
+ ByaddrMap#{Addr := lists:reverse(Aliases, [Name | NamesR])},
+ Addrs);
+ #{} ->
+ %% First entry for an IP address
+ load_hosts_list_byaddr(
+ Hosts,
+ ByaddrMap#{Addr => lists:reverse(Aliases, [Name])},
+ [Addr | Addrs])
+ end.
+
+%% Traverse in insertion order from byaddr pass
+load_hosts_list_byname(
+ [], ByaddrMap, BynameMap) ->
+ {ByaddrMap, BynameMap};
+load_hosts_list_byname(
+ [{Fam, IP} = Addr | Addrs], ByaddrMap, BynameMap) ->
+ Names = lists:reverse(maps:get(Addr, ByaddrMap)),
+ %% Traverse all names for this IP address
+ load_hosts_list_byname(
+ Addrs, ByaddrMap,
+ load_hosts_list_byname(Fam, IP, BynameMap, Names, Names)).
+
+load_hosts_list_byname(_Fam, _IP, BynameMap, _Names_0, []) ->
+ BynameMap;
+load_hosts_list_byname(
+ Fam, IP, BynameMap, Names_0, [Name | Names]) ->
+ Key = {Fam, tolower(Name)},
+ case BynameMap of
+ #{Key := {IPsR, Names_1}} ->
+ %% Add IP address to existing name entry
+ load_hosts_list_byname(
+ Fam, IP,
+ BynameMap#{Key := {[IP | IPsR], Names_1}},
+ Names_0, Names);
+ #{} ->
+ %% First entry for a name
+ load_hosts_list_byname(
+ Fam, IP,
+ BynameMap#{Key => {[IP], Names_0}},
+ Names_0, Names)
+ end.
+
+ets_clean_map_keys(Tab, Map) ->
+ true = ets:safe_fixtable(Tab, true),
+ ets_clean_map_keys(Tab, Map, ets:first(Tab)),
+ true = ets:safe_fixtable(Tab, false),
ok.
+%%
+ets_clean_map_keys(_Tab, _Map, '$end_of_table') ->
+ ok;
+ets_clean_map_keys(Tab, Map, Key) ->
+ case maps:is_key(Key, Map) of
+ true ->
+ ets_clean_map_keys(Tab, Map, ets:next(Tab, Key));
+ false ->
+ true = ets:delete(Tab, Key),
+ ets_clean_map_keys(Tab, Map, ets:next(Tab, Key))
+ end.
+
%% Loop over .inetrc option list and call handle_call/3 for each
%%
@@ -1383,8 +1555,9 @@ cache_rr(_Db, Cache, RR) ->
ets:insert(Cache, RR).
times() ->
- erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time),
- native, second).
+ erlang:monotonic_time(second).
+ %% erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time),
+ %% native, second).
%% lookup and remove old entries
@@ -1518,12 +1691,12 @@ do_refresh_cache(Key, CacheDb, Now, OldestT) ->
%% -------------------------------------------------------------------
alloc_entry(Db, CacheDb, TM) ->
CurSize = ets:info(CacheDb, size),
- case ets:lookup(Db, cache_size) of
- [{cache_size, Size}] when Size =< CurSize, Size > 0 ->
+ case ets:lookup_element(Db, cache_size, 2) of
+ Size when Size =< CurSize, Size > 0 ->
alloc_entry(CacheDb, CurSize, TM, trunc(Size * 0.1) + 1);
- [{cache_size, Size}] when Size =< 0 ->
+ Size when Size =< 0 ->
false;
- _ ->
+ _Size ->
true
end.
diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl
index fc653bf0d3..6dce48cf42 100644
--- a/lib/kernel/src/inet_hosts.erl
+++ b/lib/kernel/src/inet_hosts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -41,12 +41,10 @@ gethostbyname(_) -> {error, formerr}.
gethostbyname(Name, Type) when is_list(Name), is_atom(Type) ->
%% Byname has lowercased names while Byaddr keep the name casing.
%% This is to be able to reconstruct the original /etc/hosts entry.
- N = inet_db:tolower(Name),
- case gethostbyname(N, Type, inet_hosts_byname, inet_hosts_byaddr) of
+ Nm = inet_db:tolower(Name),
+ case gethostbyname(Nm, Type, inet_hosts_byname) of
false ->
- case gethostbyname(N, Type,
- inet_hosts_file_byname,
- inet_hosts_file_byaddr) of
+ case gethostbyname(Nm, Type, inet_hosts_file_byname) of
false -> {error,nxdomain};
Hostent -> {ok,Hostent}
end;
@@ -56,15 +54,12 @@ gethostbyname(Name, Type) when is_atom(Name), is_atom(Type) ->
gethostbyname(atom_to_list(Name), Type);
gethostbyname(_, _) -> {error, formerr}.
-gethostbyname(Name, Type, Byname, Byaddr) ->
+gethostbyname(Nm, Type, Byname) ->
inet_db:res_update_hosts(),
- case [I || [I] <- ets:match(Byname, {Name,Type,'$1'})] of
+ case ets:lookup(Byname, {Type, Nm}) of
[] -> false;
- [IP|_]=IPs ->
- %% Use the primary IP address to generate aliases
- [Nm|As] = [N || [N] <- ets:match(Byaddr,
- {'$1',Type,IP})],
- make_hostent(Nm, IPs, As, Type)
+ [{_, IPs, [Primary | Aliases]}] ->
+ make_hostent(Primary, IPs, Aliases, Type)
end.
@@ -97,13 +92,12 @@ gethostbyaddr(IP, Type) ->
gethostbyaddr(IP, Type, Byaddr) ->
inet_db:res_update_hosts(),
- case [N || [N] <- ets:match(Byaddr, {'$1',Type,IP})] of
+ case ets:lookup(Byaddr, {Type, IP}) of
[] -> false;
- [Nm|As] -> make_hostent(Nm, [IP], As, Type)
+ [{_, [Primary | Aliases]}] -> make_hostent(Primary, [IP], Aliases, Type)
end.
-
make_hostent(Name, Addrs, Aliases, inet) ->
#hostent {
h_name = Name,
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 35c0c3f88e..f7f6a3b497 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -36,6 +36,7 @@
erl_distribution,
erl_reply,
erl_signal_handler,
+ erpc,
error_handler,
error_logger,
file,
@@ -103,6 +104,7 @@
inet_tcp,
inet_udp,
inet_sctp,
+ pg,
pg2,
raw_file_io,
raw_file_io_compressed,
@@ -143,12 +145,14 @@
ddll_server,
erl_epmd,
inet_db,
+ pg,
pg2]},
{applications, []},
{env, [{logger_level, notice},
- {logger_sasl_compatible, false}
+ {logger_sasl_compatible, false},
+ {shell_docs_ansi,auto}
]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-10.6", "stdlib-3.5", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-@OTP-15251@", "stdlib-@OTP-15251@", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index 8877ceea8e..83f3fbecd5 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -64,7 +64,7 @@ config_change(Changed, New, Removed) ->
%%% (file,code, | erl_dist (A)| | safe_sup (1)|
%%% rpc, ...) ------------- -------------
%%% | |
-%%% (net_kernel, (disk_log, pg2,
+%%% (net_kernel, (disk_log, pg,
%%% auth, ...) ...)
%%%
%%% The rectangular boxes are supervisors. All supervisors except
@@ -180,7 +180,7 @@ init(safe) ->
Boot = start_boot_server(),
DiskLog = start_disk_log(),
- Pg2 = start_pg2(),
+ Pg = start_pg2() ++ start_pg(),
%% Run the on_load handlers for all modules that have been
%% loaded so far. Running them at this point means that
@@ -188,7 +188,7 @@ init(safe) ->
%% (and in particular call code:priv_dir/1 or code:lib_dir/1).
init:run_on_load_handlers(),
- {ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}.
+ {ok, {SupFlags, Boot ++ DiskLog ++ Pg}}.
start_distribution() ->
Rpc = #{id => rex,
@@ -279,6 +279,19 @@ start_disk_log() ->
[]
end.
+start_pg() ->
+ case application:get_env(kernel, start_pg) of
+ {ok, true} ->
+ [#{id => pg,
+ start => {pg, start_link, []},
+ restart => permanent,
+ shutdown => 1000,
+ type => worker,
+ modules => [pg]}];
+ _ ->
+ []
+ end.
+
start_pg2() ->
case application:get_env(kernel, start_pg2) of
{ok, true} ->
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index fd02cf67bf..e51407f482 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -256,9 +256,8 @@ log(Level, FunOrFormat, Args, Metadata) ->
-spec allow(Level,Module) -> boolean() when
Level :: level(),
Module :: module().
-allow(Level,Module) when ?IS_LEVEL(Level), is_atom(Module) ->
- logger_config:allow(?LOGGER_TABLE,Level,Module).
-
+allow(Level,Module) when is_atom(Module) ->
+ logger_config:allow(Level,Module).
-spec macro_log(Location,Level,StringOrReport) -> ok when
Location :: location(),
@@ -571,8 +570,8 @@ set_application_level(App,Level) ->
{error, {not_loaded, App}}
end.
--spec unset_application_level(Application) -> ok | {error, not_loaded} when
- Application :: atom().
+-spec unset_application_level(Application) ->
+ ok | {error, {not_loaded, Application}} when Application :: atom().
unset_application_level(App) ->
case application:get_key(App, modules) of
{ok, Modules} ->
@@ -595,7 +594,7 @@ get_module_level(Modules) when is_list(Modules) ->
Module :: module(),
Level :: level() | all | none.
get_module_level() ->
- logger_config:get_module_level(?LOGGER_TABLE).
+ logger_config:get_module_level().
%%%-----------------------------------------------------------------
%%% Misc
@@ -1027,14 +1026,14 @@ get_logger_env(App) ->
%%%-----------------------------------------------------------------
%%% Internal
do_log(Level,Msg,#{mfa:={Module,_,_}}=Meta) ->
- case logger_config:allow(?LOGGER_TABLE,Level,Module) of
+ case logger_config:allow(Level,Module) of
true ->
log_allowed(#{},Level,Msg,Meta);
false ->
ok
end;
do_log(Level,Msg,Meta) ->
- case logger_config:allow(?LOGGER_TABLE,Level) of
+ case logger_config:allow(Level) of
true ->
log_allowed(#{},Level,Msg,Meta);
false ->
@@ -1051,7 +1050,7 @@ do_log(Level,Msg,Meta) ->
Meta :: metadata().
log_allowed(Location,Level,{Fun,FunArgs},Meta) when is_function(Fun,1) ->
try Fun(FunArgs) of
- Msg={Format,Args} when is_list(Format), is_list(Args) ->
+ Msg={Format,Args} when ?IS_FORMAT(Format), is_list(Args) ->
log_allowed(Location,Level,Msg,Meta);
Report when ?IS_REPORT(Report) ->
log_allowed(Location,Level,Report,Meta);
@@ -1086,7 +1085,7 @@ log_allowed(Location,Level,Msg,Meta0) when is_map(Meta0) ->
do_log_allowed(Level,{Format,Args}=Msg,Meta,Tid)
when ?IS_LEVEL(Level),
- is_list(Format),
+ ?IS_FORMAT(Format),
is_list(Args),
is_map(Meta) ->
logger_backend:log_allowed(#{level=>Level,msg=>Msg,meta=>Meta},Tid);
diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl
index 432c671afd..7003d2268e 100644
--- a/lib/kernel/src/logger_backend.erl
+++ b/lib/kernel/src/logger_backend.erl
@@ -56,6 +56,7 @@ call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) ->
error,{removed_failing_handler,Id}),
?LOG_INTERNAL(
debug,
+ Log1,
[{logger,removed_failing_handler},
{handler,{Id,Module}},
{log_event,Log1},
@@ -68,6 +69,7 @@ call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) ->
{error,Reason} ->
?LOG_INTERNAL(
debug,
+ Log1,
[{logger,remove_handler_failed},
{reason,Reason}])
end
@@ -119,6 +121,7 @@ handle_filter_failed({Id,_}=Filter,Owner,Log,Reason) ->
ok ->
logger:internal_log(error,{removed_failing_filter,Id}),
?LOG_INTERNAL(debug,
+ Log,
[{logger,removed_failing_filter},
{filter,Filter},
{owner,Owner},
diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl
index af8ebfd4e9..4495790c30 100644
--- a/lib/kernel/src/logger_config.erl
+++ b/lib/kernel/src/logger_config.erl
@@ -21,120 +21,142 @@
-export([new/1,delete/2,
exist/2,
- allow/2,allow/3,
+ allow/1,allow/2,
get/2, get/3,
create/3, set/3,
- set_module_level/3,unset_module_level/2,
- get_module_level/1,cache_module_level/2,
+ set_module_level/2,unset_module_level/1,
+ get_module_level/0,
level_to_int/1]).
-include("logger_internal.hrl").
+-compile({inline,[get_primary_level/0,level_to_int/1]}).
+
+-define(LEVEL_TO_CACHE(Level),Level + 16#10).
+-define(PRIMARY_TO_CACHE(Level),Level).
+-define(IS_CACHED(Level),(Level =< ?LOG_ALL)).
+-define(CACHE_TO_LEVEL(Level),if ?IS_CACHED(Level) -> Level; true -> Level - 16#10 end).
+
+-define(IS_MODULE(Module),is_atom(Module) andalso Module =/= ?PRIMARY_KEY).
+
new(Name) ->
_ = ets:new(Name,[set,protected,named_table,
{read_concurrency,true},
{write_concurrency,true}]),
ets:whereis(Name).
-delete(Tid,Id) ->
- ets:delete(Tid,table_key(Id)).
-
-allow(Tid,Level,Module) ->
- LevelInt = level_to_int(Level),
- try ets:lookup(Tid,Module) of
- [{Module,{ModLevel,cached}}] when is_integer(ModLevel),
- LevelInt =< ModLevel ->
- true;
- [{Module,ModLevel}] when is_integer(ModLevel),
- LevelInt =< ModLevel ->
- true;
- [] ->
- logger_server:cache_module_level(Module),
- allow(Tid,Level);
- _ ->
- false
- catch error:badarg ->
- true
- end.
-
-allow(Tid,Level) ->
- try ets:lookup_element(Tid,?PRIMARY_KEY,2) of
- GlobalLevelInt ->
- level_to_int(Level) =< GlobalLevelInt
- catch error:badarg ->
- true
- end.
+delete(Tid,What) ->
+ persistent_term:put({?MODULE,table_key(What)},undefined),
+ ets:delete(Tid,table_key(What)).
+
+%% Optimized for speed.
+allow(Level,Module) ->
+ ModLevel =
+ case persistent_term:get({?MODULE,Module},undefined) of
+ undefined ->
+ %% This is where the module cache takes place. We insert the module level
+ %% plus 16 into the pt and then when looking it up we need to do a check
+ %% and subtraction.
+ %% The reason why we do this dance and not just wrap it all in a tuple
+ %% is because updates of immediates (i.e. small ints in this case)
+ %% is cheap even in pt, so we cannot put any complex terms in there.
+ IntLevel = get_primary_level(),
+ persistent_term:put({?MODULE,Module},?PRIMARY_TO_CACHE(IntLevel)),
+ IntLevel;
+ IntLevel ->
+ ?CACHE_TO_LEVEL(IntLevel)
+ end,
+ less_or_equal_level(Level,ModLevel).
+
+allow(Level) ->
+ PrimaryLevelInt = get_primary_level(),
+ less_or_equal_level(Level,PrimaryLevelInt).
+
+less_or_equal_level(emergency,ModLevel) -> ?EMERGENCY =< ModLevel;
+less_or_equal_level(alert,ModLevel) -> ?ALERT =< ModLevel;
+less_or_equal_level(critical,ModLevel) -> ?CRITICAL =< ModLevel;
+less_or_equal_level(error,ModLevel) -> ?ERROR =< ModLevel;
+less_or_equal_level(warning,ModLevel) -> ?WARNING =< ModLevel;
+less_or_equal_level(notice,ModLevel) -> ?NOTICE =< ModLevel;
+less_or_equal_level(info,ModLevel) -> ?INFO =< ModLevel;
+less_or_equal_level(debug,ModLevel) -> ?DEBUG =< ModLevel.
exist(Tid,What) ->
ets:member(Tid,table_key(What)).
+get_primary_level() ->
+ persistent_term:get({?MODULE,?PRIMARY_KEY},?NOTICE).
+
get(Tid,What) ->
case ets:lookup(Tid,table_key(What)) of
- [{_,_,Config}] ->
- {ok,Config};
- [{_,Config}] when What=:=proxy ->
+ [{_,Config}] ->
{ok,Config};
[] ->
{error,{not_found,What}}
end.
get(Tid,What,Level) ->
- MS = [{{table_key(What),'$1','$2'},
- [{'>=','$1',level_to_int(Level)}],
- ['$2']}],
- case ets:select(Tid,MS) of
- [] -> error;
- [Data] -> {ok,Data}
+ TableKey = table_key(What),
+ case persistent_term:get({?MODULE,TableKey},undefined) of
+ undefined ->
+ %% The handler is not installed at the moment
+ {error,{not_found,What}};
+ ConfLevel ->
+ case less_or_equal_level(Level,ConfLevel) of
+ true ->
+ get(Tid, What);
+ false ->
+ error
+ end
end.
create(Tid,proxy,Config) ->
ets:insert(Tid,{table_key(proxy),Config});
create(Tid,What,Config) ->
LevelInt = level_to_int(maps:get(level,Config)),
- ets:insert(Tid,{table_key(What),LevelInt,Config}).
+ ok = persistent_term:put({?MODULE,table_key(What)}, LevelInt),
+ ets:insert(Tid,{table_key(What),Config}).
set(Tid,proxy,Config) ->
ets:insert(Tid,{table_key(proxy),Config}),
ok;
set(Tid,What,Config) ->
LevelInt = level_to_int(maps:get(level,Config)),
- %% Should do this only if the level has actually changed. Possibly
- %% overwrite instead of delete?
+ ok = persistent_term:put({?MODULE,table_key(What)}, LevelInt),
case What of
primary ->
- _ = ets:select_delete(Tid,[{{'_',{'$1',cached}},
- [{'=/=','$1',LevelInt}],
- [true]}]),
+ [persistent_term:put(Key,?PRIMARY_TO_CACHE(LevelInt))
+ || {{?MODULE,Module} = Key,Level} <- persistent_term:get(),
+ ?IS_MODULE(Module), ?IS_CACHED(Level)],
ok;
_ ->
ok
end,
- ets:update_element(Tid,table_key(What),[{2,LevelInt},{3,Config}]),
+ ets:insert(Tid,{table_key(What),Config}),
ok.
-set_module_level(Tid,Modules,Level) ->
+set_module_level(Modules,Level) ->
LevelInt = level_to_int(Level),
- [ets:insert(Tid,{Module,LevelInt}) || Module <- Modules],
+ [persistent_term:put({?MODULE,Module},?LEVEL_TO_CACHE(LevelInt)) || Module <- Modules],
ok.
-%% should possibly overwrite instead of delete?
-unset_module_level(Tid,all) ->
- MS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[true]}],
- _ = ets:select_delete(Tid,MS),
+%% We overwrite instead of delete because that is more efficient
+%% when using persistent_term
+unset_module_level(all) ->
+ PrimaryLevel = get_primary_level(),
+ [persistent_term:put(Key, ?PRIMARY_TO_CACHE(PrimaryLevel))
+ || {{?MODULE, Module} = Key,_} <- persistent_term:get(), ?IS_MODULE(Module)],
ok;
-unset_module_level(Tid,Modules) ->
- [ets:delete(Tid,Module) || Module <- Modules],
+unset_module_level(Modules) ->
+ PrimaryLevel = get_primary_level(),
+ [persistent_term:put({?MODULE,Module}, ?PRIMARY_TO_CACHE(PrimaryLevel)) || Module <- Modules],
ok.
-get_module_level(Tid) ->
- MS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[{{'$1','$2'}}]}],
- Modules = ets:select(Tid,MS),
- lists:sort([{M,int_to_level(L)} || {M,L} <- Modules]).
-
-cache_module_level(Tid,Module) ->
- GlobalLevelInt = ets:lookup_element(Tid,?PRIMARY_KEY,2),
- ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}),
- ok.
+get_module_level() ->
+ lists:sort(
+ [{Module,int_to_level(?CACHE_TO_LEVEL(Level))}
+ || {{?MODULE, Module},Level} <- persistent_term:get(),
+ ?IS_MODULE(Module), not ?IS_CACHED(Level)]).
level_to_int(none) -> ?LOG_NONE;
level_to_int(emergency) -> ?EMERGENCY;
diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl
index 8696adbd72..0a145b16d5 100644
--- a/lib/kernel/src/logger_formatter.erl
+++ b/lib/kernel/src/logger_formatter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -323,7 +323,7 @@ timestamp_to_datetimemicro(SysTime,Config) when is_integer(SysTime) ->
{Date,Time,Micro,UtcStr}.
format_mfa({M,F,A},_) when is_atom(M), is_atom(F), is_integer(A) ->
- atom_to_list(M)++":"++atom_to_list(F)++"/"++integer_to_list(A);
+ io_lib:fwrite("~tw:~tw/~w", [M, F, A]);
format_mfa({M,F,A},Config) when is_atom(M), is_atom(F), is_list(A) ->
format_mfa({M,F,length(A)},Config);
format_mfa(MFA,Config) ->
diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl
index 16946ff97c..fb150d181b 100644
--- a/lib/kernel/src/logger_h_common.erl
+++ b/lib/kernel/src/logger_h_common.erl
@@ -392,26 +392,26 @@ do_log_to_binary(Log,Config) ->
String = try_format(Log,Formatter,FormatterConfig),
try string_to_binary(String)
catch C2:R2:S2 ->
- ?LOG_INTERNAL(debug,[{formatter_error,Formatter},
- {config,FormatterConfig},
- {log_event,Log},
- {bad_return_value,String},
- {catched,{C2,R2,S2}}]),
- <<"FORMATTER ERROR: bad return value">>
+ ?LOG_INTERNAL(debug,Log,[{formatter_error,Formatter},
+ {config,FormatterConfig},
+ {log_event,Log},
+ {bad_return_value,String},
+ {catched,{C2,R2,S2}}]),
+ <<"FORMATTER ERROR: bad return value\n">>
end.
try_format(Log,Formatter,FormatterConfig) ->
try Formatter:format(Log,FormatterConfig)
catch
C:R:S ->
- ?LOG_INTERNAL(debug,[{formatter_crashed,Formatter},
- {config,FormatterConfig},
- {log_event,Log},
- {reason,
- {C,R,logger:filter_stacktrace(?MODULE,S)}}]),
+ ?LOG_INTERNAL(debug,Log,[{formatter_crashed,Formatter},
+ {config,FormatterConfig},
+ {log_event,Log},
+ {reason,
+ {C,R,logger:filter_stacktrace(?MODULE,S)}}]),
case {?DEFAULT_FORMATTER,#{}} of
{Formatter,FormatterConfig} ->
- "DEFAULT FORMATTER CRASHED";
+ "DEFAULT FORMATTER CRASHED\n";
{DefaultFormatter,DefaultConfig} ->
try_format(Log#{msg=>{"FORMATTER CRASH: ~tp",
[maps:get(msg,Log)]}},
diff --git a/lib/kernel/src/logger_handler_watcher.erl b/lib/kernel/src/logger_handler_watcher.erl
index b75c74c643..6f622b169c 100644
--- a/lib/kernel/src/logger_handler_watcher.erl
+++ b/lib/kernel/src/logger_handler_watcher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -51,42 +51,25 @@ register_handler(Id,Pid) ->
%%% gen_server callbacks
%%%===================================================================
--spec init(Args :: term()) -> {ok, State :: term()} |
- {ok, State :: term(), Timeout :: timeout()} |
- {ok, State :: term(), hibernate} |
- {stop, Reason :: term()} |
- ignore.
+-spec init(Args :: term()) -> {ok, State :: term()}.
init([]) ->
process_flag(trap_exit, true),
{ok, #state{handlers=[]}}.
-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->
- {reply, Reply :: term(), NewState :: term()} |
- {reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} |
- {reply, Reply :: term(), NewState :: term(), hibernate} |
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), Timeout :: timeout()} |
- {noreply, NewState :: term(), hibernate} |
- {stop, Reason :: term(), Reply :: term(), NewState :: term()} |
- {stop, Reason :: term(), NewState :: term()}.
+ {reply, ok, NewState :: term()}.
handle_call({register,Id,Pid}, _From, #state{handlers=Hs}=State) ->
Ref = erlang:monitor(process,Pid),
Hs1 = lists:keystore(Id,1,Hs,{Id,Ref}),
{reply, ok, State#state{handlers=Hs1}}.
-spec handle_cast(Request :: term(), State :: term()) ->
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), Timeout :: timeout()} |
- {noreply, NewState :: term(), hibernate} |
- {stop, Reason :: term(), NewState :: term()}.
+ {noreply, NewState :: term()}.
handle_cast(_Request, State) ->
{noreply, State}.
-spec handle_info(Info :: timeout() | term(), State :: term()) ->
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), Timeout :: timeout()} |
- {noreply, NewState :: term(), hibernate} |
- {stop, Reason :: normal | term(), NewState :: term()}.
+ {noreply, NewState :: term()}.
handle_info({'DOWN',Ref,process,_,shutdown}, #state{handlers=Hs}=State) ->
case lists:keytake(Ref,2,Hs) of
{value,{Id,Ref},Hs1} ->
@@ -108,6 +91,6 @@ handle_info(_Other,State) ->
{noreply,State}.
-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term(),
- State :: term()) -> any().
+ State :: term()) -> ok.
terminate(_Reason, _State) ->
ok.
diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl
index c2b2d419e7..0a5c4c944f 100644
--- a/lib/kernel/src/logger_internal.hrl
+++ b/lib/kernel/src/logger_internal.hrl
@@ -41,14 +41,14 @@
-define(DEFAULT_LOGGER_CALL_TIMEOUT, infinity).
--define(LOG_INTERNAL(Level,Report),?DO_LOG_INTERNAL(Level,[Report])).
--define(LOG_INTERNAL(Level,Format,Args),?DO_LOG_INTERNAL(Level,[Format,Args])).
--define(DO_LOG_INTERNAL(Level,Data),
+-define(LOG_INTERNAL(Level,Log,Report),
+ ?DO_LOG_INTERNAL(Level,Log,[Report])).
+-define(LOG_INTERNAL(Level,Log,Format,Args),
+ ?DO_LOG_INTERNAL(Level,Log,[Format,Args])).
+-define(DO_LOG_INTERNAL(Level,Log,Data),
case logger:allow(Level,?MODULE) of
true ->
- %% Spawn this to avoid deadlocks
- _ = spawn(logger,macro_log,[?LOCATION,Level|Data]++
- [logger:add_default_metadata(#{})]),
+ _ = logger_server:do_internal_log(Level,?LOCATION,Log,Data),
ok;
false ->
ok
@@ -91,7 +91,7 @@
-define(IS_LEVEL_ALL(L),
?IS_LEVEL(L) orelse
L=:=all orelse
- L=:=none ).
+ L=:=none ).
-define(IS_MSG(Msg),
((is_tuple(Msg) andalso tuple_size(Msg)==2)
@@ -107,3 +107,6 @@
-define(IS_STRING(String),
(is_list(String) orelse is_binary(String))).
+
+-define(IS_FORMAT(Format),
+ (?IS_STRING(Format) orelse is_atom(Format))).
diff --git a/lib/kernel/src/logger_olp.erl b/lib/kernel/src/logger_olp.erl
index 8365383fe2..cf9b480341 100644
--- a/lib/kernel/src/logger_olp.erl
+++ b/lib/kernel/src/logger_olp.erl
@@ -77,7 +77,7 @@ load({_Name,Pid,ModeRef},Msg) ->
%% synchronous instead of asynchronous (slows down the tempo of a
%% process causing much load). If the process is choked, drop mode
%% is set and no message is sent.
- try ?get_mode(ModeRef) of
+ case get_mode(ModeRef) of
async ->
gen_server:cast(Pid, {'$olp_load',Msg});
sync ->
@@ -86,17 +86,11 @@ load({_Name,Pid,ModeRef},Msg) ->
ok;
_Other ->
%% dropped or {error,busy}
- ?observe(_Name,{dropped,1}),
- ok
+ ?observe(_Name,{dropped,1})
end;
drop ->
?observe(_Name,{dropped,1})
- catch
- %% if the ETS table doesn't exist (maybe because of a
- %% process restart), we can only drop the event
- _:_ -> ?observe(_Name,{dropped,1})
- end,
- ok.
+ end.
-spec info(Olp) -> map() | {error, busy} when
Olp :: atom() | pid() | olp_ref().
@@ -147,8 +141,8 @@ restart(Fun) ->
catch C:R:S ->
{error,{restart_failed,Fun,C,R,S}}
end,
- ?LOG_INTERNAL(debug,[{logger_olp,restart},
- {result,Result}]),
+ ?LOG_INTERNAL(debug,#{},[{logger_olp,restart},
+ {result,Result}]),
ok.
-spec get_ref() -> olp_ref().
@@ -174,40 +168,32 @@ init([Name,Module,Args,Options]) ->
?start_observation(Name),
- try ets:new(Name, [public]) of
- ModeRef ->
- OlpRef = {Name,self(),ModeRef},
- put(olp_ref,OlpRef),
- try Module:init(Args) of
- {ok,CBState} ->
- ?set_mode(ModeRef, async),
- T0 = ?timestamp(),
- proc_lib:init_ack({ok,self(),OlpRef}),
- %% Storing options in state to avoid copying
- %% (sending) the option data with each message
- State0 = ?merge_with_stats(
- Options#{id => Name,
- idle=> true,
- module => Module,
- mode_ref => ModeRef,
- mode => async,
- last_qlen => 0,
- last_load_ts => T0,
- burst_win_ts => T0,
- burst_msg_count => 0,
- cb_state => CBState}),
- State = reset_restart_flag(State0),
- gen_server:enter_loop(?MODULE, [], State);
- Error ->
- _ = ets:delete(ModeRef),
- unregister(Name),
- proc_lib:init_ack(Error)
- catch
- _:Error ->
- _ = ets:delete(ModeRef),
- unregister(Name),
- proc_lib:init_ack(Error)
- end
+ ModeRef = {?MODULE, Name},
+ OlpRef = {Name,self(),ModeRef},
+ put(olp_ref,OlpRef),
+ try Module:init(Args) of
+ {ok,CBState} ->
+ set_mode(ModeRef, async),
+ T0 = ?timestamp(),
+ proc_lib:init_ack({ok,self(),OlpRef}),
+ %% Storing options in state to avoid copying
+ %% (sending) the option data with each message
+ State0 = ?merge_with_stats(
+ Options#{id => Name,
+ idle=> true,
+ module => Module,
+ mode_ref => ModeRef,
+ mode => async,
+ last_qlen => 0,
+ last_load_ts => T0,
+ burst_win_ts => T0,
+ burst_msg_count => 0,
+ cb_state => CBState}),
+ State = reset_restart_flag(State0),
+ gen_server:enter_loop(?MODULE, [], State);
+ Error ->
+ unregister(Name),
+ proc_lib:init_ack(Error)
catch
_:Error ->
unregister(Name),
@@ -274,11 +260,11 @@ handle_cast(Msg, #{module:=Module, cb_state:=CBState} = State) ->
{stop, Reason, State#{cb_state=>CBState1}}
end.
-handle_info(timeout, #{mode_ref:=_ModeRef, mode:=Mode} = State) ->
+handle_info(timeout, #{mode_ref:=ModeRef} = State) ->
State1 = notify(idle,State),
State2 = maybe_notify_mode_change(async,State1),
{noreply, State2#{idle => true,
- mode => ?change_mode(_ModeRef, Mode, async),
+ mode => set_mode(ModeRef, async),
burst_msg_count => 0}};
handle_info(Msg, #{module := Module, cb_state := CBState} = State) ->
case try_callback_call(Module,handle_info,[Msg, CBState]) of
@@ -357,7 +343,7 @@ do_load(Msg, CallOrCast, State) ->
%% this function is called by do_load/3 after an overload check
%% has been performed, where QLen > FlushQLen
-flush(T1, State=#{id := _Name, mode := Mode, last_load_ts := _T0, mode_ref := ModeRef}) ->
+flush(T1, State=#{id := _Name, last_load_ts := _T0, mode_ref := ModeRef}) ->
%% flush load messages in the mailbox (a limited number in order
%% to not cause long delays)
NewFlushed = flush_load(?FLUSH_MAX_N),
@@ -378,7 +364,7 @@ flush(T1, State=#{id := _Name, mode := Mode, last_load_ts := _T0, mode_ref := Mo
State3 = ?update_max_qlen(QLen1,State2),
State4 = maybe_notify_mode_change(async,State3),
{dropped,?update_other(flushed,FLUSHED,NewFlushed,
- State4#{mode => ?change_mode(ModeRef,Mode,async),
+ State4#{mode => set_mode(ModeRef,async),
last_qlen => QLen1,
last_load_ts => T1})}.
@@ -507,11 +493,11 @@ check_load(State = #{id:=_Name, mode_ref := ModeRef, mode := Mode,
%% be dropped on the client side (never sent to
%% the olp process).
IncDrops = if Mode == drop -> 0; true -> 1 end,
- {?change_mode(ModeRef, Mode, drop), IncDrops,0};
+ {set_mode(ModeRef, drop), IncDrops,0};
QLen >= SyncModeQLen ->
- {?change_mode(ModeRef, Mode, sync), 0,0};
+ {set_mode(ModeRef, sync), 0,0};
true ->
- {?change_mode(ModeRef, Mode, async), 0,0}
+ {set_mode(ModeRef, async), 0,0}
end,
State1 = ?update_other(drops,DROPS,_NewDrops,State),
State2 = ?update_max_qlen(QLen,State1),
@@ -576,7 +562,7 @@ flush_load(N, Limit) ->
{log,_,_,_,_} ->
flush_load(N+1, Limit);
{log,_,_,_} ->
- flush_load(N+1, Limit)
+ flush_load(N+1, Limit)
after
0 -> N
end.
@@ -587,6 +573,11 @@ overload_levels_ok(Options) ->
FQL = maps:get(flush_qlen, Options, ?FLUSH_QLEN),
(DMQL > 1) andalso (SMQL =< DMQL) andalso (DMQL =< FQL).
+get_mode(Ref) -> persistent_term:get(Ref, async).
+
+set_mode(Ref, M) ->
+ true = is_atom(M), persistent_term:put(Ref, M), M.
+
maybe_notify_mode_change(drop,#{mode:=Mode0}=State)
when Mode0=/=drop ->
notify({mode_change,Mode0,drop},State);
diff --git a/lib/kernel/src/logger_olp.hrl b/lib/kernel/src/logger_olp.hrl
index d68b5c048d..5ee3f4dd1c 100644
--- a/lib/kernel/src/logger_olp.hrl
+++ b/lib/kernel/src/logger_olp.hrl
@@ -72,25 +72,8 @@
-define(timestamp(), erlang:monotonic_time(microsecond)).
--define(get_mode(Tid),
- case ets:lookup(Tid, mode) of
- [{mode,M}] -> M;
- _ -> async
- end).
-
--define(set_mode(Tid, M),
- begin ets:insert(Tid, {mode,M}), M end).
-
--define(change_mode(Tid, M0, M1),
- if M0 == M1 ->
- M0;
- true ->
- ets:insert(Tid, {mode,M1}),
- M1
- end).
-
-define(max(X1, X2),
- if
+ if
X2 == undefined -> X1;
X2 > X1 -> X2;
true -> X1
diff --git a/lib/kernel/src/logger_proxy.erl b/lib/kernel/src/logger_proxy.erl
index 6ab8e3e4c5..ead2d74f37 100644
--- a/lib/kernel/src/logger_proxy.erl
+++ b/lib/kernel/src/logger_proxy.erl
@@ -151,13 +151,13 @@ notify({mode_change,Mode0,Mode1},State) ->
true ->
ok
end,
- ?LOG_INTERNAL(notice,"~w switched from ~w to ~w mode",[?MODULE,Mode0,Mode1]),
+ ?LOG_INTERNAL(notice,#{},"~w switched from ~w to ~w mode",[?MODULE,Mode0,Mode1]),
State;
notify({flushed,Flushed},State) ->
- ?LOG_INTERNAL(notice, "~w flushed ~w log events",[?MODULE,Flushed]),
+ ?LOG_INTERNAL(notice,#{},"~w flushed ~w log events",[?MODULE,Flushed]),
State;
notify(restart,State) ->
- ?LOG_INTERNAL(notice, "~w restarted", [?MODULE]),
+ ?LOG_INTERNAL(notice,#{},"~w restarted", [?MODULE]),
State;
notify(_Note,State) ->
State.
@@ -167,7 +167,7 @@ notify(_Note,State) ->
try_log(Args) ->
try apply(logger,log,Args)
catch C:R:S ->
- ?LOG_INTERNAL(debug,[{?MODULE,log_failed},
- {log,Args},
- {reason,{C,R,S}}])
+ ?LOG_INTERNAL(debug,#{},[{?MODULE,log_failed},
+ {log,Args},
+ {reason,{C,R,S}}])
end.
diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl
index 722246e82c..fbf3b48d9b 100644
--- a/lib/kernel/src/logger_server.erl
+++ b/lib/kernel/src/logger_server.erl
@@ -25,13 +25,13 @@
-export([start_link/0, add_handler/3, remove_handler/1,
add_filter/2, remove_filter/2,
set_module_level/2, unset_module_level/0,
- unset_module_level/1, cache_module_level/1,
+ unset_module_level/1,
set_config/2, set_config/3,
update_config/2, update_config/3,
update_formatter_config/2]).
%% Helper
--export([diff_maps/2]).
+-export([diff_maps/2,do_internal_log/4]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -104,9 +104,6 @@ unset_module_level(Modules) when is_list(Modules) ->
unset_module_level(Modules) ->
{error,{not_a_list_of_modules,Modules}}.
-cache_module_level(Module) ->
- gen_server:cast(?SERVER,{cache_module_level,Module}).
-
set_config(Owner,Key,Value) ->
case sanity_check(Owner,Key,Value) of
ok ->
@@ -325,18 +322,15 @@ handle_call({update_formatter_config,HandlerId,NewFConfig},_From,
{error,{not_found,HandlerId}}
end,
{reply,Reply,State};
-handle_call({set_module_level,Modules,Level}, _From, #state{tid=Tid}=State) ->
- Reply = logger_config:set_module_level(Tid,Modules,Level),
+handle_call({set_module_level,Modules,Level}, _From, State) ->
+ Reply = logger_config:set_module_level(Modules,Level),
{reply,Reply,State};
-handle_call({unset_module_level,Modules}, _From, #state{tid=Tid}=State) ->
- Reply = logger_config:unset_module_level(Tid,Modules),
+handle_call({unset_module_level,Modules}, _From, State) ->
+ Reply = logger_config:unset_module_level(Modules),
{reply,Reply,State}.
handle_cast({async_req_reply,_Ref,_Reply} = Reply,State) ->
- call_h_reply(Reply,State);
-handle_cast({cache_module_level,Module}, #state{tid=Tid}=State) ->
- logger_config:cache_module_level(Tid,Module),
- {noreply, State}.
+ call_h_reply(Reply,State).
%% Interface for those who can't call the API - e.g. the emulator, or
%% places related to code loading.
@@ -359,12 +353,14 @@ handle_info(Unexpected,State) when element(1,Unexpected) == 'EXIT' ->
%% The simple handler will send an 'EXIT' message when it is replaced
%% We may as well ignore all 'EXIT' messages that we get
?LOG_INTERNAL(debug,
+ #{},
[{logger,got_unexpected_message},
{process,?SERVER},
{message,Unexpected}]),
{noreply,State};
handle_info(Unexpected,State) ->
?LOG_INTERNAL(info,
+ #{},
[{logger,got_unexpected_message},
{process,?SERVER},
{message,Unexpected}]),
@@ -550,6 +546,7 @@ call_h(Module, Function, Args, DefRet) ->
_ ->
ST = logger:filter_stacktrace(?MODULE,S),
?LOG_INTERNAL(error,
+ #{},
[{logger,callback_crashed},
{process,?SERVER},
{reason,{C,R,ST}}]),
@@ -592,6 +589,7 @@ call_h_reply({'DOWN',Ref,_Proc,Pid,Reason}, #state{ async_req = {Ref,_PostFun,_F
%% to the spawned process. It is only here to make sure that the logger_server does
%% not deadlock if that happens.
?LOG_INTERNAL(error,
+ #{},
[{logger,process_exited},
{process,Pid},
{reason,Reason}]),
@@ -600,6 +598,7 @@ call_h_reply({'DOWN',Ref,_Proc,Pid,Reason}, #state{ async_req = {Ref,_PostFun,_F
State);
call_h_reply(Unexpected,State) ->
?LOG_INTERNAL(info,
+ #{},
[{logger,got_unexpected_message},
{process,?SERVER},
{message,Unexpected}]),
@@ -615,3 +614,18 @@ diffs([{K,V1}|T1],[{K,V2}|T2],D1,D2) ->
diffs(T1,T2,D1#{K=>V1},D2#{K=>V2});
diffs([],[],D1,D2) ->
{D1,D2}.
+
+do_internal_log(Level,Location,Log,[Report] = Data) ->
+ do_internal_log(Level,Location,Log,Data,{report,Report});
+do_internal_log(Level,Location,Log,[Fmt,Args] = Data) ->
+ do_internal_log(Level,Location,Log,Data,{Fmt,Args}).
+do_internal_log(Level,Location,Log,Data,Msg) ->
+ Meta = logger:add_default_metadata(maps:merge(Location,maps:get(meta,Log,#{}))),
+ %% Spawn these to avoid deadlocks
+ case Log of
+ #{ meta := #{ internal_log_event := true } } ->
+ _ = spawn(logger_simple_h,log,[#{level=>Level,msg=>Msg,meta=>Meta},#{}]);
+ _ ->
+ _ = spawn(logger,macro_log,[Location,Level|Data]++
+ [Meta#{internal_log_event=>true}])
+ end.
diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl
index a0d51dba25..72dfe8a899 100644
--- a/lib/kernel/src/logger_simple_h.erl
+++ b/lib/kernel/src/logger_simple_h.erl
@@ -61,15 +61,20 @@ log(#{meta:=#{error_logger:=#{tag:=info_report,type:=Type}}},_Config)
%% Skip info reports that are not 'std_info' (ref simple logger in
%% error_logger)
ok;
-log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) ->
+log(#{msg:=_,meta:=#{time:=_}=M}=Log,_Config) ->
_ = case whereis(?MODULE) of
undefined ->
%% Is the node on the way down? Real emergency?
%% Log directly from client just to get it out
- do_log(
- #{level=>error,
- msg=>{report,{error,simple_handler_process_dead}},
- meta=>#{time=>logger:timestamp()}}),
+ case maps:get(internal_log_event, M, false) of
+ false ->
+ do_log(
+ #{level=>error,
+ msg=>{report,{error,simple_handler_process_dead}},
+ meta=>#{time=>logger:timestamp()}});
+ true ->
+ ok
+ end,
do_log(Log);
_ ->
?MODULE ! {log,Log}
diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl
index 6641d99776..dbbd6fbc1f 100644
--- a/lib/kernel/src/logger_std_h.erl
+++ b/lib/kernel/src/logger_std_h.erl
@@ -330,13 +330,22 @@ open_log_file(HandlerName,#{type:=file,
end.
close_log_file(#{fd:=Fd}) ->
- _ = file:datasync(Fd),
+ _ = file:datasync(Fd), %% file:datasync may return error as it will flush the delayed_write buffer
_ = file:close(Fd),
ok;
close_log_file(_) ->
ok.
-
+%% A special close that closes the FD properly when the delayed write close failed
+delayed_write_close(#{fd:=Fd}) ->
+ case file:close(Fd) of
+ %% We got an error while closing, could be a delayed write failing
+ %% So we close again in order to make sure the file is closed.
+ {error, _} ->
+ file:close(Fd);
+ Res ->
+ Res
+ end.
%%%-----------------------------------------------------------------
%%% File control process
@@ -556,10 +565,9 @@ maybe_rotate_file(AddSize,#{rotation:=#{size:=RotSize,
maybe_rotate_file(_Bin,State) ->
State.
-rotate_file(#{fd:=Fd0,file_name:=FileName,modes:=Modes,rotation:=Rotation}=State) ->
+rotate_file(#{file_name:=FileName,modes:=Modes,rotation:=Rotation}=State) ->
State1 = sync_dev(State),
- _ = file:close(Fd0),
- _ = file:close(Fd0),
+ _ = delayed_write_close(State),
rotate_files(FileName,maps:get(count,Rotation),maps:get(compress,Rotation)),
case file:open(FileName,Modes) of
{ok,Fd} ->
diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl
index fcc78d6971..aceb903bb6 100644
--- a/lib/kernel/src/net.erl
+++ b/lib/kernel/src/net.erl
@@ -69,12 +69,12 @@
]).
--deprecated({call, 4, eventually}).
--deprecated({cast, 4, eventually}).
--deprecated({broadcast, 3, eventually}).
--deprecated({ping, 1, eventually}).
--deprecated({relay, 1, eventually}).
--deprecated({sleep, 1, eventually}).
+-deprecated({call, 4, "use rpc:call/4 instead"}).
+-deprecated({cast, 4, "use rpc:cast/4 instead"}).
+-deprecated({broadcast, 3, "use rpc:eval_everywhere/3 instead"}).
+-deprecated({ping, 1, "use net_adm:ping/1 instead"}).
+-deprecated({relay, 1, "use slave:relay/1 instead"}).
+-deprecated({sleep, 1, "use 'receive after T -> ok end' instead"}).
-type ifaddrs_flag() :: up | broadcast | debug | loopback | pointopoint |
diff --git a/lib/kernel/src/pg.erl b/lib/kernel/src/pg.erl
new file mode 100644
index 0000000000..0668dd1f79
--- /dev/null
+++ b/lib/kernel/src/pg.erl
@@ -0,0 +1,507 @@
+%%
+%%
+%% Copyright WhatsApp Inc. and its affiliates. All rights reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%%
+%%-------------------------------------------------------------------
+%%
+%% @author Maxim Fedorov <maximfca@gmail.com>
+%% Process Groups with eventually consistent membership.
+%%
+%% Differences (compared to pg2):
+%% * non-existent and empty group treated the same (empty list of pids),
+%% thus create/1 and delete/1 have no effect (and not implemented).
+%% which_groups() return only non-empty groups
+%% * no cluster lock required, and no dependency on global
+%% * all join/leave operations require local process (it's not possible to join
+%% a process from a different node)
+%% * multi-join: join/leave several processes with a single call
+%%
+%% Why empty groups are not supported:
+%% Unlike a process, group does not have originating node. So it's possible
+%% that during net split one node deletes the group, that still exists for
+%% another partition. pg2 will recover the group, as soon as net
+%% split converges, which is quite unexpected.
+%%
+%% Exchange protocol:
+%% * when pg process starts, it broadcasts
+%% 'discover' message to all nodes in the cluster
+%% * when pg server receives 'discover', it responds with 'sync' message
+%% containing list of groups with all local processes, and starts to
+%% monitor process that sent 'discover' message (assuming it is a part
+%% of an overlay network)
+%% * every pg process monitors 'nodeup' messages to attempt discovery for
+%% nodes that are (re)joining the cluster
+%%
+%% Leave/join operations:
+%% * processes joining the group are monitored on the local node
+%% * when process exits (without leaving groups prior to exit), local
+%% instance of pg scoped process detects this and sends 'leave' to
+%% all nodes in an overlay network (no remote monitoring done)
+%% * all leave/join operations are serialised through pg server process
+%%
+-module(pg).
+
+%% API: default scope
+-export([
+ start_link/0,
+
+ join/2,
+ leave/2,
+ get_members/1,
+ get_local_members/1,
+ which_groups/0,
+ which_local_groups/0
+]).
+
+%% Scoped API: overlay networks
+-export([
+ start/1,
+ start_link/1,
+
+ join/3,
+ leave/3,
+ get_members/2,
+ get_local_members/2,
+ which_groups/1,
+ which_local_groups/1
+]).
+
+%% gen_server callbacks
+-export([
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2
+]).
+
+%% Types
+-type group() :: any().
+
+%% Default scope started by kernel app
+-define(DEFAULT_SCOPE, ?MODULE).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server and links it to calling process.
+%% Uses default scope, which is the same as as the module name.
+-spec start_link() -> {ok, pid()} | {error, any()}.
+start_link() ->
+ start_link(?DEFAULT_SCOPE).
+
+%% @doc
+%% Starts the server outside of supervision hierarchy.
+-spec start(Scope :: atom()) -> {ok, pid()} | {error, any()}.
+start(Scope) when is_atom(Scope) ->
+ gen_server:start({local, Scope}, ?MODULE, [Scope], []).
+
+%% @doc
+%% Starts the server and links it to calling process.
+%% Scope name is supplied.
+-spec start_link(Scope :: atom()) -> {ok, pid()} | {error, any()}.
+start_link(Scope) when is_atom(Scope) ->
+ gen_server:start_link({local, Scope}, ?MODULE, [Scope], []).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Joins a single process
+%% Group is created automatically.
+%% Process must be local to this node.
+-spec join(Group :: group(), PidOrPids :: pid() | [pid()]) -> ok.
+join(Group, PidOrPids) ->
+ join(?DEFAULT_SCOPE, Group, PidOrPids).
+
+-spec join(Scope :: atom(), Group :: group(), PidOrPids :: pid() | [pid()]) -> ok.
+join(Scope, Group, PidOrPids) ->
+ Node = node(),
+ is_list(PidOrPids) andalso [error({nolocal, Pid}) || Pid <- PidOrPids, node(Pid) =/= Node orelse not is_pid(Pid)],
+ gen_server:call(Scope, {join_local, Group, PidOrPids}, infinity).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Single process leaving the group.
+%% Process must be local to this node.
+-spec leave(Group :: group(), PidOrPids :: pid() | [pid()]) -> ok.
+leave(Group, PidOrPids) ->
+ leave(?DEFAULT_SCOPE, Group, PidOrPids).
+
+-spec leave(Scope :: atom(), Group :: group(), Pid :: pid() | [pid()]) -> ok | not_joined.
+leave(Scope, Group, PidOrPids) ->
+ Node = node(),
+ is_list(PidOrPids) andalso [error({nolocal, Pid}) || Pid <- PidOrPids, node(Pid) =/= Node orelse not is_pid(Pid)],
+ gen_server:call(Scope, {leave_local, Group, PidOrPids}, infinity).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Returns all processes in a group
+-spec get_members(Group :: group()) -> [pid()].
+get_members(Group) ->
+ get_members(?DEFAULT_SCOPE, Group).
+
+-spec get_members(Scope :: atom(), Group :: group()) -> [pid()].
+get_members(Scope, Group) ->
+ try
+ ets:lookup_element(Scope, Group, 2)
+ catch
+ error:badarg ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Returns processes in a group, running on local node.
+-spec get_local_members(Group :: group()) -> [pid()].
+get_local_members(Group) ->
+ get_local_members(?DEFAULT_SCOPE, Group).
+
+-spec get_local_members(Scope :: atom(), Group :: group()) -> [pid()].
+get_local_members(Scope, Group) ->
+ try
+ ets:lookup_element(Scope, Group, 3)
+ catch
+ error:badarg ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Returns a list of all known groups.
+-spec which_groups() -> [Group :: group()].
+which_groups() ->
+ which_groups(?DEFAULT_SCOPE).
+
+-spec which_groups(Scope :: atom()) -> [Group :: group()].
+which_groups(Scope) when is_atom(Scope) ->
+ [G || [G] <- ets:match(Scope, {'$1', '_', '_'})].
+
+%%--------------------------------------------------------------------
+%% @private
+%% Returns a list of groups that have any local processes joined.
+-spec which_local_groups() -> [Group :: group()].
+which_local_groups() ->
+ which_local_groups(?DEFAULT_SCOPE).
+
+-spec which_local_groups(Scope :: atom()) -> [Group :: group()].
+which_local_groups(Scope) when is_atom(Scope) ->
+ ets:select(Scope, [{{'$1', '_', '$2'}, [{'=/=', '$2', []}], ['$1']}]).
+
+%%--------------------------------------------------------------------
+%% Internal implementation
+
+%% gen_server implementation
+-record(state, {
+ %% ETS table name, and also the registered process name (self())
+ scope :: atom(),
+ %% monitored local processes and groups they joined
+ monitors = #{} :: #{pid() => {MRef :: reference(), Groups :: [group()]}},
+ %% remote node: scope process monitor and map of groups to pids for fast sync routine
+ nodes = #{} :: #{pid() => {reference(), #{group() => [pid()]}}}
+}).
+
+-type state() :: #state{}.
+
+-spec init([Scope :: atom()]) -> {ok, state()}.
+init([Scope]) ->
+ ok = net_kernel:monitor_nodes(true),
+ %% discover all nodes in the cluster
+ broadcast([{Scope, Node} || Node <- nodes()], {discover, self()}),
+ Scope = ets:new(Scope, [set, protected, named_table, {read_concurrency, true}]),
+ {ok, #state{scope = Scope}}.
+
+-spec handle_call(Call :: {join_local, Group :: group(), Pid :: pid()}
+ | {leave_local, Group :: group(), Pid :: pid()},
+ From :: {pid(),Tag :: any()},
+ State :: state()) -> {reply, ok | not_joined, state()}.
+
+handle_call({join_local, Group, PidOrPids}, _From, #state{scope = Scope, monitors = Monitors, nodes = Nodes} = State) ->
+ NewMons = join_monitors(PidOrPids, Group, Monitors),
+ join_local_group(Scope, Group, PidOrPids),
+ broadcast(maps:keys(Nodes), {join, self(), Group, PidOrPids}),
+ {reply, ok, State#state{monitors = NewMons}};
+
+handle_call({leave_local, Group, PidOrPids}, _From, #state{scope = Scope, monitors = Monitors, nodes = Nodes} = State) ->
+ case leave_monitors(PidOrPids, Group, Monitors) of
+ Monitors ->
+ {reply, not_joined, State};
+ NewMons ->
+ leave_local_group(Scope, Group, PidOrPids),
+ broadcast(maps:keys(Nodes), {leave, self(), PidOrPids, [Group]}),
+ {reply, ok, State#state{monitors = NewMons}}
+ end;
+
+handle_call(_Request, _From, _S) ->
+ error(badarg).
+
+-spec handle_cast(
+ {sync, Peer :: pid(), Groups :: [{group(), [pid()]}]},
+ State :: state()) -> {noreply, state()}.
+
+handle_cast({sync, Peer, Groups}, #state{scope = Scope, nodes = Nodes} = State) ->
+ {noreply, State#state{nodes = handle_sync(Scope, Peer, Nodes, Groups)}};
+
+handle_cast(_, _State) ->
+ error(badarg).
+
+-spec handle_info(
+ {discover, Peer :: pid()} |
+ {join, Peer :: pid(), group(), pid() | [pid()]} |
+ {leave, Peer :: pid(), pid() | [pid()], [group()]} |
+ {'DOWN', reference(), process, pid(), term()} |
+ {nodedown, node()} | {nodeup, node()}, State :: state()) -> {noreply, state()}.
+
+%% remote pid or several pids joining the group
+handle_info({join, Peer, Group, PidOrPids}, #state{scope = Scope, nodes = Nodes} = State) ->
+ join_remote(Scope, Group, PidOrPids),
+ % store remote group => pids map for fast sync operation
+ {MRef, RemoteGroups} = maps:get(Peer, Nodes),
+ NewRemoteGroups = join_remote_map(Group, PidOrPids, RemoteGroups),
+ {noreply, State#state{nodes = Nodes#{Peer => {MRef, NewRemoteGroups}}}};
+
+%% remote pid leaving (multiple groups at once)
+handle_info({leave, Peer, PidOrPids, Groups}, #state{scope = Scope, nodes = Nodes} = State) ->
+ _ = leave_remote(Scope, PidOrPids, Groups),
+ {MRef, RemoteMap} = maps:get(Peer, Nodes),
+ NewRemoteMap = lists:foldl(
+ fun (Group, Acc) ->
+ case maps:get(Group, Acc) of
+ PidOrPids ->
+ Acc;
+ [PidOrPids] ->
+ Acc;
+ Existing when is_pid(PidOrPids) ->
+ Acc#{Group => lists:delete(PidOrPids, Existing)};
+ Existing ->
+ Acc#{Group => Existing-- PidOrPids}
+ end
+ end, RemoteMap, Groups),
+ {noreply, State#state{nodes = Nodes#{Peer => {MRef, NewRemoteMap}}}};
+
+%% we're being discovered, let's exchange!
+handle_info({discover, Peer}, #state{scope = Scope, nodes = Nodes} = State) ->
+ gen_server:cast(Peer, {sync, self(), all_local_pids(Scope)}),
+ %% do we know who is looking for us?
+ case maps:is_key(Peer, Nodes) of
+ true ->
+ {noreply, State};
+ false ->
+ MRef = monitor(process, Peer),
+ erlang:send(Peer, {discover, self()}, [noconnect]),
+ {noreply, State#state{nodes = Nodes#{Peer => {MRef, #{}}}}}
+ end;
+
+%% handle local process exit
+handle_info({'DOWN', MRef, process, Pid, _Info}, #state{scope = Scope, monitors = Monitors, nodes = Nodes} = State) when node(Pid) =:= node() ->
+ case maps:take(Pid, Monitors) of
+ error ->
+ %% this can only happen when leave request and 'DOWN' are in pg queue
+ {noreply, State};
+ {{MRef, Groups}, NewMons} ->
+ [leave_local_group(Scope, Group, Pid) || Group <- Groups],
+ %% send update to all nodes
+ broadcast(maps:keys(Nodes), {leave, self(), Pid, Groups}),
+ {noreply, State#state{monitors = NewMons}}
+ end;
+
+%% handle remote node down or leaving overlay network
+handle_info({'DOWN', MRef, process, Pid, _Info}, #state{scope = Scope, nodes = Nodes} = State) ->
+ {{MRef, RemoteMap}, NewNodes} = maps:take(Pid, Nodes),
+ _ = maps:map(fun (Group, Pids) -> leave_remote(Scope, Pids, [Group]) end, RemoteMap),
+ {noreply, State#state{nodes = NewNodes}};
+
+%% nodedown: ignore, and wait for 'DOWN' signal for monitored process
+handle_info({nodedown, _Node}, State) ->
+ {noreply, State};
+
+%% nodeup: discover if remote node participates in the overlay network
+handle_info({nodeup, Node}, #state{scope = Scope} = State) ->
+ {Scope, Node} ! {discover, self()},
+ {noreply, State};
+
+handle_info(_Info, _State) ->
+ error(badarg).
+
+-spec terminate(Reason :: any(), State :: state()) -> true.
+terminate(_Reason, #state{scope = Scope}) ->
+ true = ets:delete(Scope).
+
+%%--------------------------------------------------------------------
+%% Internal implementation
+
+%% Override all knowledge of the remote node with information it sends
+%% to local node. Current implementation must do the full table scan
+%% to remove stale pids (just as for 'nodedown').
+handle_sync(Scope, Peer, Nodes, Groups) ->
+ %% can't use maps:get() because it evaluates 'default' value first,
+ %% and in this case monitor() call has side effect.
+ {MRef, RemoteGroups} =
+ case maps:find(Peer, Nodes) of
+ error ->
+ {monitor(process, Peer), #{}};
+ {ok, MRef0} ->
+ MRef0
+ end,
+ %% sync RemoteMap and transform ETS table
+ _ = sync_groups(Scope, RemoteGroups, Groups),
+ Nodes#{Peer => {MRef, maps:from_list(Groups)}}.
+
+sync_groups(Scope, RemoteGroups, []) ->
+ %% leave all missing groups
+ [leave_remote(Scope, Pids, [Group]) || {Group, Pids} <- maps:to_list(RemoteGroups)];
+sync_groups(Scope, RemoteGroups, [{Group, Pids} | Tail]) ->
+ case maps:take(Group, RemoteGroups) of
+ {Pids, NewRemoteGroups} ->
+ sync_groups(Scope, NewRemoteGroups, Tail);
+ {OldPids, NewRemoteGroups} ->
+ [{Group, AllOldPids, LocalPids}] = ets:lookup(Scope, Group),
+ %% should be really rare...
+ AllNewPids = Pids ++ AllOldPids -- OldPids,
+ true = ets:insert(Scope, {Group, AllNewPids, LocalPids}),
+ sync_groups(Scope, NewRemoteGroups, Tail);
+ error ->
+ join_remote(Scope, Group, Pids),
+ sync_groups(Scope, RemoteGroups, Tail)
+ end.
+
+join_monitors(Pid, Group, Monitors) when is_pid(Pid) ->
+ case maps:find(Pid, Monitors) of
+ {ok, {MRef, Groups}} ->
+ maps:put(Pid, {MRef, [Group | Groups]}, Monitors);
+ error ->
+ MRef = erlang:monitor(process, Pid),
+ Monitors#{Pid => {MRef, [Group]}}
+ end;
+join_monitors([], _Group, Monitors) ->
+ Monitors;
+join_monitors([Pid | Tail], Group, Monitors) ->
+ join_monitors(Tail, Group, join_monitors(Pid, Group, Monitors)).
+
+join_local_group(Scope, Group, Pid) when is_pid(Pid) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, [Pid | All], [Pid | Local]});
+ [] ->
+ ets:insert(Scope, {Group, [Pid], [Pid]})
+ end;
+join_local_group(Scope, Group, Pids) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, Pids ++ All, Pids ++ Local});
+ [] ->
+ ets:insert(Scope, {Group, Pids, Pids})
+ end.
+
+join_remote(Scope, Group, Pid) when is_pid(Pid) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, [Pid | All], Local});
+ [] ->
+ ets:insert(Scope, {Group, [Pid], []})
+ end;
+join_remote(Scope, Group, Pids) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, Pids ++ All, Local});
+ [] ->
+ ets:insert(Scope, {Group, Pids, []})
+ end.
+
+join_remote_map(Group, Pid, RemoteGroups) when is_pid(Pid) ->
+ maps:update_with(Group, fun (List) -> [Pid | List] end, [Pid], RemoteGroups);
+join_remote_map(Group, Pids, RemoteGroups) ->
+ maps:update_with(Group, fun (List) -> Pids ++ List end, Pids, RemoteGroups).
+
+leave_monitors(Pid, Group, Monitors) when is_pid(Pid) ->
+ case maps:find(Pid, Monitors) of
+ {ok, {MRef, [Group]}} ->
+ erlang:demonitor(MRef),
+ maps:remove(Pid, Monitors);
+ {ok, {MRef, Groups}} ->
+ case lists:member(Group, Groups) of
+ true ->
+ maps:put(Pid, {MRef, lists:delete(Group, Groups)}, Monitors);
+ false ->
+ Monitors
+ end;
+ _ ->
+ Monitors
+ end;
+leave_monitors([], _Group, Monitors) ->
+ Monitors;
+leave_monitors([Pid | Tail], Group, Monitors) ->
+ leave_monitors(Tail, Group, leave_monitors(Pid, Group, Monitors)).
+
+leave_local_group(Scope, Group, Pid) when is_pid(Pid) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, [Pid], [Pid]}] ->
+ ets:delete(Scope, Group);
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, lists:delete(Pid, All), lists:delete(Pid, Local)});
+ [] ->
+ %% rare race condition when 'DOWN' from monitor stays in msg queue while process is leave-ing.
+ true
+ end;
+leave_local_group(Scope, Group, Pids) ->
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ case All -- Pids of
+ [] ->
+ ets:delete(Scope, Group);
+ NewAll ->
+ ets:insert(Scope, {Group, NewAll, Local -- Pids})
+ end;
+ [] ->
+ true
+ end.
+
+leave_remote(Scope, Pid, Groups) when is_pid(Pid) ->
+ _ = [
+ case ets:lookup(Scope, Group) of
+ [{Group, [Pid], []}] ->
+ ets:delete(Scope, Group);
+ [{Group, All, Local}] ->
+ ets:insert(Scope, {Group, lists:delete(Pid, All), Local});
+ [] ->
+ true
+ end ||
+ Group <- Groups];
+leave_remote(Scope, Pids, Groups) ->
+ _ = [
+ case ets:lookup(Scope, Group) of
+ [{Group, All, Local}] ->
+ case All -- Pids of
+ [] when Local =:= [] ->
+ ets:delete(Scope, Group);
+ NewAll ->
+ ets:insert(Scope, {Group, NewAll, Local})
+ end;
+ [] ->
+ true
+ end ||
+ Group <- Groups].
+
+all_local_pids(Scope) ->
+ %% selector: ets:fun2ms(fun({N,_,L}) when L =/=[] -> {N,L}end).
+ ets:select(Scope, [{{'$1','_','$2'},[{'=/=','$2',[]}],[{{'$1','$2'}}]}]).
+
+%% Works as gen_server:abcast(), but accepts a list of processes
+%% instead of nodes list.
+broadcast([], _Msg) ->
+ ok;
+broadcast([Dest | Tail], Msg) ->
+ %% do not use 'nosuspend', as it will lead to missing
+ %% join/leave messages when dist buffer is full
+ erlang:send(Dest, Msg, [noconnect]),
+ broadcast(Tail, Msg).
diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl
index c4732f37ee..d02bbeccdf 100644
--- a/lib/kernel/src/pg2.erl
+++ b/lib/kernel/src/pg2.erl
@@ -25,6 +25,10 @@
-export([start/0,start_link/0,init/1,handle_call/3,handle_cast/2,handle_info/2,
terminate/2]).
+-deprecated([{'_','_',
+ "the 'pg2' module is deprecated and scheduled for removal "
+ "in OTP 24; use 'pg' instead."}]).
+
%%% As of R13B03 monitors are used instead of links.
%%%
diff --git a/lib/kernel/src/raw_file_io_compressed.erl b/lib/kernel/src/raw_file_io_compressed.erl
index f6ac6eaffc..1a6de3a4eb 100644
--- a/lib/kernel/src/raw_file_io_compressed.erl
+++ b/lib/kernel/src/raw_file_io_compressed.erl
@@ -21,7 +21,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -118,6 +119,9 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) ->
sendfile(_,_,_,_,_,_,_,_) ->
{error, enotsup}.
+read_handle_info(Fd, Opts) ->
+ wrap_call(Fd, [Opts]).
+
wrap_call(Fd, Command) ->
{_Owner, Pid} = get_fd_data(Fd),
try gen_statem:call(Pid, Command, infinity) of
diff --git a/lib/kernel/src/raw_file_io_delayed.erl b/lib/kernel/src/raw_file_io_delayed.erl
index 5644717aaa..f9b89270f1 100644
--- a/lib/kernel/src/raw_file_io_delayed.erl
+++ b/lib/kernel/src/raw_file_io_delayed.erl
@@ -23,7 +23,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -304,6 +305,9 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) ->
sendfile(_,_,_,_,_,_,_,_) ->
{error, enotsup}.
+read_handle_info(Fd, Opts) ->
+ wrap_call(Fd, [Opts]).
+
wrap_call(Fd, Command) ->
#{ pid := Pid } = get_fd_data(Fd),
try gen_statem:call(Pid, Command, infinity) of
diff --git a/lib/kernel/src/raw_file_io_list.erl b/lib/kernel/src/raw_file_io_list.erl
index 2e16e63f0e..e4fe434e13 100644
--- a/lib/kernel/src/raw_file_io_list.erl
+++ b/lib/kernel/src/raw_file_io_list.erl
@@ -21,7 +21,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -126,3 +127,7 @@ sendfile(Fd, Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags) ->
Args = [Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags],
PrivateFd = Fd#file_descriptor.data,
?CALL_FD(PrivateFd, sendfile, Args).
+
+read_handle_info(Fd, Opts) ->
+ PrivateFd = Fd#file_descriptor.data,
+ ?CALL_FD(PrivateFd, read_handle_info, [Opts]).
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index d197de942f..22893b3468 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -19,6 +19,8 @@
%%
-module(rpc).
+%% -define(SERVER_SIDE_ERPC_IS_MANDATORY, yes).
+
%% General rpc, broadcast,multicall, promise and parallel evaluator
%% facility
@@ -26,6 +28,7 @@
%% a separate module.
-define(NAME, rex).
+-define(TAB_NAME, rex_nodes_observer).
-behaviour(gen_server).
@@ -61,12 +64,23 @@
-export_type([key/0]).
+%% Removed functions
+
+-removed([{safe_multi_server_call,2,"use rpc:multi_server_call/2 instead"},
+ {safe_multi_server_call,3,"use rpc:multi_server_call/3 instead"}]).
+
%%------------------------------------------------------------------------
-type state() :: map().
%%------------------------------------------------------------------------
+-define(MAX_INT_TIMEOUT, 4294967295).
+-define(TIMEOUT_TYPE, 0..?MAX_INT_TIMEOUT | 'infinity').
+-define(IS_VALID_TMO_INT(TI_), (is_integer(TI_)
+ andalso (0 =< TI_)
+ andalso (TI_ =< ?MAX_INT_TIMEOUT))).
+-define(IS_VALID_TMO(T_), ((T_ == infinity) orelse ?IS_VALID_TMO_INT(T_))).
%% The rex server may receive a huge amount of
%% messages. Make sure that they are stored off heap to
@@ -101,7 +115,7 @@ stop(Rpc) ->
init([]) ->
process_flag(trap_exit, true),
- {ok, maps:new()}.
+ {ok, #{nodes_observer => start_nodes_observer()}}.
-spec handle_call(term(), term(), state()) ->
{'noreply', state()} |
@@ -113,13 +127,23 @@ handle_call({call, Mod, Fun, Args, Gleader}, To, S) ->
handle_call({block_call, Mod, Fun, Args, Gleader}, _To, S) ->
MyGL = group_leader(),
set_group_leader(Gleader),
- Reply =
- case catch apply(Mod,Fun,Args) of
- {'EXIT', _} = Exit ->
- {badrpc, Exit};
- Other ->
- Other
- end,
+ Reply = try
+ {return, Return} = erpc:execute_call(Mod, Fun, Args),
+ Return
+ catch
+ throw:Result ->
+ Result;
+ exit:Reason ->
+ {'EXIT', Reason};
+ error:Reason:Stack ->
+ case erpc:is_arg_error(Reason, Mod, Fun, Args) of
+ true ->
+ {'EXIT', Reason};
+ false ->
+ RpcStack = erpc:trim_stack(Stack, Mod, Fun, Args),
+ {'EXIT', {Reason, RpcStack}}
+ end
+ end,
group_leader(MyGL, self()), % restore
{reply, Reply, S};
handle_call(stop, _To, S) ->
@@ -140,6 +164,8 @@ handle_cast(_, S) ->
-spec handle_info(term(), state()) -> {'noreply', state()}.
+handle_info({'DOWN', M, process, P, _}, #{nodes_observer := {P,M}} = S) ->
+ {noreply, S#{nodes_observer => start_nodes_observer()}};
handle_info({'DOWN', _, process, Caller, normal}, S) ->
{noreply, maps:remove(Caller, S)};
handle_info({'DOWN', _, process, Caller, Reason}, S) ->
@@ -169,6 +195,9 @@ handle_info({From, {send, Name, Msg}}, S) ->
handle_info({From, {call,Mod,Fun,Args,Gleader}}, S) ->
%% Special for hidden C node's, uugh ...
handle_call_call(Mod, Fun, Args, Gleader, {From,?NAME}, S);
+handle_info({From, features_request}, S) ->
+ From ! {features_reply, node(), [erpc]},
+ {noreply, S};
handle_info(_, S) ->
{noreply, S}.
@@ -254,9 +283,72 @@ proxy_user_flush() ->
end,
proxy_user_flush().
+start_nodes_observer() ->
+ Init = fun () ->
+ process_flag(priority, high),
+ process_flag(trap_exit, true),
+ Tab = ets:new(?TAB_NAME,
+ [{read_concurrency, true},
+ protected]),
+ persistent_term:put(?TAB_NAME, Tab),
+ ok = net_kernel:monitor_nodes(true),
+ lists:foreach(fun (N) ->
+ self() ! {nodeup, N}
+ end,
+ [node()|nodes()]),
+ nodes_observer_loop(Tab)
+ end,
+ spawn_monitor(Init).
+
+nodes_observer_loop(Tab) ->
+ receive
+ {nodeup, N} ->
+ {?NAME, N} ! {self(), features_request};
+ {nodedown, N} ->
+ ets:delete(Tab, N);
+ {features_reply, N, FeatureList} ->
+ try
+ SpawnRpc = lists:member(erpc, FeatureList),
+ ets:insert(Tab, {N, SpawnRpc})
+ catch
+ _:_ -> ets:insert(Tab, {N, false})
+ end;
+ _ ->
+ ignore
+ end,
+ nodes_observer_loop(Tab).
+
+-dialyzer([{nowarn_function, node_has_feature/2}, no_match]).
+
+-spec node_has_feature(Node :: atom(), Feature :: term()) -> boolean().
+
+node_has_feature(N, erpc) when N == node() ->
+ true;
+node_has_feature(N, erpc) ->
+ try
+ Tab = persistent_term:get(?TAB_NAME),
+ ets:lookup_element(Tab, N, 2)
+ catch
+ _:_ -> false
+ end;
+node_has_feature(_N, _Feature) ->
+ false.
%% THE rpc client interface
+%% Call
+
+-define(RPCIFY(ERPC_),
+ try ERPC_ of
+ {'EXIT', _} = BadRpc_ ->
+ {badrpc, BadRpc_};
+ Result_ ->
+ Result_
+ catch
+ Class_:Reason_ ->
+ callify_exception(Class_, Reason_)
+ end).
+
-spec call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when
Node :: node(),
Module :: module(),
@@ -265,10 +357,8 @@ proxy_user_flush() ->
Res :: term(),
Reason :: term().
-call(N,M,F,A) when node() =:= N -> %% Optimize local call
- local_call(M, F, A);
call(N,M,F,A) ->
- do_call(N, {call,M,F,A,group_leader()}, infinity).
+ call(N,M,F,A,infinity).
-spec call(Node, Module, Function, Args, Timeout) ->
Res | {badrpc, Reason} when
@@ -278,14 +368,23 @@ call(N,M,F,A) ->
Args :: [term()],
Res :: term(),
Reason :: term(),
- Timeout :: timeout().
+ Timeout :: ?TIMEOUT_TYPE.
-call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
- local_call(M,F,A);
call(N,M,F,A,infinity) ->
- do_call(N, {call,M,F,A,group_leader()}, infinity);
-call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 ->
- do_call(N, {call,M,F,A,group_leader()}, Timeout).
+ case ?RPCIFY(erpc:call(N, M, F, A, infinity)) of
+ {badrpc, notsup} ->
+ call_fallback(N, M, F, A, infinity, undefined);
+ Res ->
+ Res
+ end;
+call(N,M,F,A,T) when ?IS_VALID_TMO_INT(T) ->
+ Start = erlang:monotonic_time(),
+ case ?RPCIFY(erpc:call(N, M, F, A, T)) of
+ {badrpc, notsup} ->
+ call_fallback(N, M, F, A, T, Start);
+ Res ->
+ Res
+ end.
-spec block_call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when
Node :: node(),
@@ -295,10 +394,8 @@ call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 ->
Res :: term(),
Reason :: term().
-block_call(N,M,F,A) when node() =:= N -> %% Optimize local call
- local_call(M,F,A);
block_call(N,M,F,A) ->
- do_call(N, {block_call,M,F,A,group_leader()}, infinity).
+ do_srv_call(N, {block_call,M,F,A,group_leader()}, infinity).
-spec block_call(Node, Module, Function, Args, Timeout) ->
Res | {badrpc, Reason} when
@@ -308,24 +405,58 @@ block_call(N,M,F,A) ->
Args :: [term()],
Res :: term(),
Reason :: term(),
- Timeout :: timeout().
-
-block_call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call
- local_call(M, F, A);
-block_call(N,M,F,A,infinity) ->
- do_call(N, {block_call,M,F,A,group_leader()}, infinity);
-block_call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 ->
- do_call(N, {block_call,M,F,A,group_leader()}, Timeout).
-
-local_call(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
- case catch apply(M, F, A) of
- {'EXIT',_}=V -> {badrpc, V};
- Other -> Other
+ Timeout :: ?TIMEOUT_TYPE.
+
+block_call(N,M,F,A,Timeout) when is_atom(N),
+ is_atom(M),
+ is_list(A),
+ ?IS_VALID_TMO(Timeout) ->
+ do_srv_call(N, {block_call,M,F,A,group_leader()}, Timeout).
+
+
+%% call() implementation utilizing erpc:call()...
+
+callify_exception(throw, {'EXIT', _} = BadRpc) ->
+ {badrpc, BadRpc};
+callify_exception(throw, Return) ->
+ Return;
+callify_exception(exit, {exception, Exit}) ->
+ {badrpc, {'EXIT', Exit}};
+callify_exception(exit, {signal, Reason}) ->
+ {badrpc, {'EXIT', Reason}};
+callify_exception(exit, Reason) ->
+ exit(Reason);
+callify_exception(error, {exception, Error, Stack}) ->
+ {badrpc, {'EXIT', {Error, Stack}}};
+callify_exception(error, {erpc, noconnection}) ->
+ {badrpc, nodedown};
+callify_exception(error, {erpc, timeout}) ->
+ {badrpc, timeout};
+callify_exception(error, {erpc, notsup}) ->
+ {badrpc, notsup};
+callify_exception(error, {erpc, Error}) ->
+ {badrpc, {'EXIT', Error}};
+callify_exception(error, Reason) ->
+ error(Reason).
+
+call_result(Type, ReqId, Res, Reason) ->
+ ?RPCIFY(erpc:call_result(Type, ReqId, Res, Reason)).
+
+call_fallback(N, M, F, A, infinity, _S) ->
+ do_srv_call(N, {call,M,F,A,group_leader()}, infinity);
+call_fallback(N, M, F, A, T, S) ->
+ Now = erlang:monotonic_time(),
+ Used = erlang:convert_time_unit(Now-S, native, millisecond),
+ case T - Used of
+ Timeout when Timeout =< 0 ->
+ {badrpc, timeout};
+ Timeout ->
+ do_srv_call(N, {call,M,F,A,group_leader()}, Timeout)
end.
-do_call(Node, Request, infinity) ->
+do_srv_call(Node, Request, infinity) ->
rpc_check(catch gen_server:call({?NAME,Node}, Request, infinity));
-do_call(Node, Request, Timeout) ->
+do_srv_call(Node, Request, Timeout) ->
Tag = make_ref(),
{Receiver,Mref} =
erlang:spawn_monitor(
@@ -346,6 +477,7 @@ do_call(Node, Request, Timeout) ->
end.
rpc_check_t({'EXIT', {timeout,_}}) -> {badrpc, timeout};
+rpc_check_t({'EXIT', {timeout_value,_}}) -> {badrpc, badarg};
rpc_check_t(X) -> rpc_check(X).
rpc_check({'EXIT', {{nodedown,_},_}}) ->
@@ -395,11 +527,14 @@ server_call(Node, Name, ReplyWrapper, Msg)
Function :: atom(),
Args :: [term()].
-cast(Node, Mod, Fun, Args) when Node =:= node() ->
- catch spawn(Mod, Fun, Args),
- true;
cast(Node, Mod, Fun, Args) ->
- gen_server:cast({?NAME,Node}, {cast,Mod,Fun,Args,group_leader()}),
+ _ = case node_has_feature(Node, erpc) of
+ false ->
+ gen_server:cast({?NAME,Node},
+ {cast,Mod,Fun,Args,group_leader()});
+ true ->
+ erpc:cast(Node, Mod, Fun, Args)
+ end,
true.
@@ -464,8 +599,11 @@ eval_everywhere(Mod, Fun, Args) ->
Args :: [term()].
eval_everywhere(Nodes, Mod, Fun, Args) ->
- gen_server:abcast(Nodes, ?NAME, {cast,Mod,Fun,Args,group_leader()}).
-
+ lists:foreach(fun (Node) ->
+ cast(Node, Mod, Fun, Args)
+ end,
+ Nodes),
+ abcast.
send_nodes([Node|Tail], Name, Msg, Monitors) when is_atom(Node) ->
Monitor = start_monitor(Node, Name),
@@ -489,7 +627,6 @@ start_monitor(Node, Name) ->
{Node,erlang:monitor(process, {Name, Node})}
end.
-
%% Call apply(M,F,A) on all nodes in parallel
-spec multicall(Module, Function, Args) -> {ResL, BadNodes} when
Module :: module(),
@@ -512,7 +649,7 @@ multicall(M, F, A) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Timeout :: timeout(),
+ Timeout :: ?TIMEOUT_TYPE,
ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
@@ -527,23 +664,187 @@ multicall(M, F, A, Timeout) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Timeout :: timeout(),
+ Timeout :: ?TIMEOUT_TYPE,
ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
-multicall(Nodes, M, F, A, infinity)
- when is_list(Nodes), is_atom(M), is_atom(F), is_list(A) ->
- do_multicall(Nodes, M, F, A, infinity);
-multicall(Nodes, M, F, A, Timeout)
- when is_list(Nodes), is_atom(M), is_atom(F), is_list(A), is_integer(Timeout),
- Timeout >= 0 ->
+multicall(Nodes, M, F, A, Timeout) when is_list(Nodes), is_atom(M),
+ is_atom(F), is_list(A),
+ ?IS_VALID_TMO(Timeout) ->
do_multicall(Nodes, M, F, A, Timeout).
+mc_requests(_Res, [], _M, _F, _A, ReqMap) ->
+ ReqMap;
+mc_requests(Res, [N|Ns], M, F, A, ReqMap) ->
+ ReqId = erlang:spawn_request(N, erpc, execute_call,
+ [Res, M, F, A],
+ [{reply_tag, {spawn_reply, Res, N}},
+ monitor]),
+ mc_requests(Res, Ns, M, F, A, ReqMap#{N => {spawn_request, ReqId}}).
+
+mc_spawn_replies(_Res, 0, ReqMap, _MFA, _EndTime) ->
+ ReqMap;
+mc_spawn_replies(Res, Outstanding, ReqMap, MFA, EndTime) ->
+ Timeout = mc_timeout(EndTime),
+ receive
+ {{spawn_reply, Res, _}, _, _, _} = Reply ->
+ NewReqMap = mc_handle_spawn_reply(Reply, ReqMap, MFA, EndTime),
+ mc_spawn_replies(Res, Outstanding-1, NewReqMap, MFA, EndTime)
+ after
+ Timeout ->
+ ReqMap
+ end.
+
+mc_handle_spawn_reply({{spawn_reply, _Res, Node}, ReqId, ok, Pid},
+ ReqMap, _MFA, _EndTime) ->
+ ReqMap#{Node => {spawn, ReqId, Pid}};
+mc_handle_spawn_reply({{spawn_reply, _Res, Node}, _ReqId, error, notsup},
+ ReqMap, MFA, infinity) ->
+ {M, F, A} = MFA,
+ SrvReqId = gen_server:send_request({?NAME, Node},
+ {call, M,F,A,
+ group_leader()}),
+ ReqMap#{Node => {server, SrvReqId}};
+mc_handle_spawn_reply({{spawn_reply, Res, Node}, _ReqId, error, notsup},
+ ReqMap, MFA, EndTime) ->
+ {M, F, A} = MFA,
+ {Pid, Mon} = spawn_monitor(fun () ->
+ process_flag(trap_exit, true),
+ Request = {call, M,F,A,
+ group_leader()},
+ Timeout = mc_timeout(EndTime),
+ Result = gen_server:call({?NAME, Node},
+ Request,
+ Timeout),
+ exit({Res, Result})
+ end),
+ ReqMap#{Node => {spawn_server, Mon, Pid}};
+mc_handle_spawn_reply({{spawn_reply, _Res, Node}, _ReqId, error, noconnection},
+ ReqMap, _MFA, _EndTime) ->
+ ReqMap#{Node => {error, badnode}};
+mc_handle_spawn_reply({{spawn_reply, _Res, Node}, _ReqId, error, Reason},
+ ReqMap, _MFA, _EndTime) ->
+ ReqMap#{Node => {error, {badrpc, {'EXIT', Reason}}}}.
+
+mc_timeout(infinity) ->
+ infinity;
+mc_timeout(EndTime) when is_integer(EndTime) ->
+ Now = erlang:monotonic_time(),
+ case erlang:convert_time_unit(EndTime - Now,
+ native,
+ millisecond) + 1 of
+ Ms when Ms >= 0 ->
+ Ms;
+ _ ->
+ 0
+ end.
+
+mc_results(_Res, [], OkAcc, ErrAcc, _ReqMap, _MFA, _EndTime) ->
+ {lists:reverse(OkAcc), lists:reverse(ErrAcc)};
+mc_results(Res, [N|Ns] = Nodes, OkAcc, ErrAcc, ReqMap, MFA, EndTime) ->
+ case maps:get(N, ReqMap) of
+ {error, badnode} ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap, MFA, EndTime);
+ {error, BadRpc} ->
+ mc_results(Res, Ns, [BadRpc|OkAcc], ErrAcc, ReqMap, MFA, EndTime);
+ {spawn_request, ReqId} ->
+ %% We timed out waiting for spawn replies...
+ case erlang:spawn_request_abandon(ReqId) of
+ true ->
+ %% Timed out request...
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap, MFA, EndTime);
+ false ->
+ %% Reply has been delivered now; handle it...
+ receive
+ {{spawn_reply, Res, _}, _, _, _} = Reply ->
+ NewReqMap = mc_handle_spawn_reply(Reply, ReqMap, MFA,
+ EndTime),
+ mc_results(Res, Nodes, OkAcc, ErrAcc, NewReqMap,
+ MFA, EndTime)
+ after 0 ->
+ error(internal_error)
+ end
+ end;
+ {spawn, ReqId, Pid} ->
+ Timeout = mc_timeout(EndTime),
+ receive
+ {'DOWN', ReqId, process, Pid, Reason} ->
+ case call_result(down, ReqId, Res, Reason) of
+ {badrpc, nodedown} ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap,
+ MFA, EndTime);
+ CallRes ->
+ mc_results(Res, Ns, [CallRes|OkAcc], ErrAcc,
+ ReqMap, MFA, EndTime)
+ end
+ after
+ Timeout ->
+ case erlang:demonitor(ReqId, [info]) of
+ true ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap,
+ MFA, EndTime);
+ false ->
+ receive
+ {'DOWN', ReqId, process, Pid, Reason} ->
+ case call_result(down, ReqId, Res, Reason) of
+ {badrpc, nodedown} ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc],
+ ReqMap, MFA, EndTime);
+ CallRes ->
+ mc_results(Res, Ns, [CallRes|OkAcc],
+ ErrAcc, ReqMap, MFA,
+ EndTime)
+ end
+ after 0 ->
+ error(internal_error)
+ end
+ end
+ end;
+ {spawn_server, Mon, Pid} ->
+ %% Old node with timeout on the call...
+ Result = receive
+ {'DOWN', Mon, process, Pid, {Res, CallRes}} ->
+ rpc_check(CallRes);
+ {'DOWN', Mon, process, Pid, Reason} ->
+ rpc_check_t({'EXIT',Reason})
+ end,
+ case Result of
+ {badrpc, BadRpcReason} when BadRpcReason == timeout;
+ BadRpcReason == nodedown ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap, MFA, EndTime);
+ _ ->
+ mc_results(Res, Ns, [Result|OkAcc], ErrAcc, ReqMap, MFA, EndTime)
+ end;
+ {server, ReqId} ->
+ %% Old node without timeout on the call...
+ case gen_server:wait_response(ReqId, infinity) of
+ {reply, Reply} ->
+ Result = rpc_check(Reply),
+ mc_results(Res, Ns, [Result|OkAcc], ErrAcc, ReqMap, MFA, EndTime);
+ {error, {noconnection, _}} ->
+ mc_results(Res, Ns, OkAcc, [N|ErrAcc], ReqMap, MFA, EndTime);
+ {error, {Reason, _}} ->
+ BadRpc = {badrpc, {'EXIT', Reason}},
+ mc_results(Res, Ns, [BadRpc|OkAcc], ErrAcc, ReqMap, MFA, EndTime)
+ end
+ end.
+
do_multicall(Nodes, M, F, A, Timeout) ->
- {Rep,Bad} = gen_server:multi_call(Nodes, ?NAME,
- {call, M,F,A, group_leader()},
- Timeout),
- {lists:map(fun({_,R}) -> R end, Rep), Bad}.
+ Res = make_ref(),
+ EndTime = if Timeout == infinity ->
+ infinity;
+ true ->
+ Now = erlang:monotonic_time(),
+ Tmo = erlang:convert_time_unit(Timeout,
+ millisecond,
+ native),
+ Now + Tmo
+ end,
+ MFA = {M, F, A},
+ ReqMap0 = mc_requests(Res, Nodes, M, F, A, #{}),
+ ReqMap1 = mc_spawn_replies(Res, maps:size(ReqMap0), ReqMap0,
+ MFA, EndTime),
+ mc_results(Res, Nodes, [], [], ReqMap1, MFA, EndTime).
%% Send Msg to Name on all nodes, and collect the answers.
@@ -603,7 +904,75 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) ->
%% rpc's towards the same node. I.e. it returns immediately and
%% it returns a Key that can be used in a subsequent yield(Key).
--opaque key() :: pid().
+-ifdef(SERVER_SIDE_ERPC_IS_MANDATORY).
+
+%%
+%% Use this more efficient implementation of async_call()
+%% when 'erpc' support can be made mandatory for server
+%% side. It should be possible to make it mandatory in
+%% OTP 25.
+%%
+
+-opaque key() :: erpc:request_id().
+
+-spec async_call(Node, Module, Function, Args) -> Key when
+ Node :: node(),
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Key :: key().
+
+async_call(Node, Mod, Fun, Args) ->
+ try
+ erpc:send_request(Node, Mod, Fun, Args)
+ catch
+ error:{erpc, badarg} ->
+ error(badarg)
+ end.
+
+-spec yield(Key) -> Res | {badrpc, Reason} when
+ Key :: key(),
+ Res :: term(),
+ Reason :: term().
+
+yield(Key) ->
+ ?RPCIFY(erpc:receive_response(Key)).
+
+-spec nb_yield(Key, Timeout) -> {value, Val} | timeout when
+ Key :: key(),
+ Timeout :: ?TIMEOUT_TYPE,
+ Val :: (Res :: term()) | {badrpc, Reason :: term()}.
+
+nb_yield(Key, Tmo) ->
+ case ?RPCIFY(erpc:wait_response(Key, Tmo)) of
+ no_response ->
+ timeout;
+ {response, {'EXIT', _} = BadRpc} ->
+ %% RPCIFY() cannot handle this case...
+ {value, BadRpc};
+ {response, R} ->
+ {value, R};
+ BadRpc ->
+ %% An exception converted by RPCIFY()...
+ {value, BadRpc}
+ end.
+
+-spec nb_yield(Key) -> {value, Val} | timeout when
+ Key :: key(),
+ Val :: (Res :: term()) | {badrpc, Reason :: term()}.
+
+nb_yield(Key) ->
+ nb_yield(Key, 0).
+
+-else.
+
+%%
+%% Currently used implementation for async_call(). When
+%% 'erpc' support can be required for async_call()
+%% replace with the implementation above...
+%%
+
+-opaque key() :: {pid(), reference()}.
-spec async_call(Node, Module, Function, Args) -> Key when
Node :: node(),
@@ -613,49 +982,59 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) ->
Key :: key().
async_call(Node, Mod, Fun, Args) ->
- ReplyTo = self(),
- spawn(
- fun() ->
- R = call(Node, Mod, Fun, Args), %% proper rpc
- ReplyTo ! {self(), {promise_reply, R}} %% self() is key
- end).
+ Caller = self(),
+ spawn_monitor(fun() ->
+ process_flag(trap_exit, true),
+ R = call(Node, Mod, Fun, Args),
+ exit({call_result, Caller, R})
+ end).
-spec yield(Key) -> Res | {badrpc, Reason} when
Key :: key(),
Res :: term(),
Reason :: term().
-yield(Key) when is_pid(Key) ->
+yield({Pid, Ref} = Key) when is_pid(Pid),
+ is_reference(Ref) ->
{value,R} = do_yield(Key, infinity),
R.
-spec nb_yield(Key, Timeout) -> {value, Val} | timeout when
Key :: key(),
- Timeout :: timeout(),
+ Timeout :: ?TIMEOUT_TYPE,
Val :: (Res :: term()) | {badrpc, Reason :: term()}.
-nb_yield(Key, infinity=Inf) when is_pid(Key) ->
+nb_yield({Pid, Ref} = Key, infinity=Inf) when is_pid(Pid),
+ is_reference(Ref) ->
do_yield(Key, Inf);
-nb_yield(Key, Timeout) when is_pid(Key), is_integer(Timeout), Timeout >= 0 ->
- do_yield(Key, Timeout).
+nb_yield({Pid, Ref} = Key, Tmo) when is_pid(Pid),
+ is_reference(Ref),
+ is_integer(Tmo),
+ Tmo >= 0 ->
+ do_yield(Key, Tmo).
-spec nb_yield(Key) -> {value, Val} | timeout when
Key :: key(),
Val :: (Res :: term()) | {badrpc, Reason :: term()}.
-nb_yield(Key) when is_pid(Key) ->
+nb_yield({Pid, Ref} = Key) when is_pid(Pid),
+ is_reference(Ref) ->
do_yield(Key, 0).
--spec do_yield(pid(), timeout()) -> {'value', _} | 'timeout'.
+-spec do_yield({pid(), reference()}, ?TIMEOUT_TYPE) -> {'value', _} | 'timeout'.
-do_yield(Key, Timeout) ->
+do_yield({Proxy, Mon}, Tmo) ->
+ Me = self(),
receive
- {Key,{promise_reply,R}} ->
- {value,R}
- after Timeout ->
+ {'DOWN', Mon, process, Proxy, {call_result, Me, R}} ->
+ {value,R};
+ {'DOWN', Mon, process, Proxy, Reason} ->
+ {value, {badrpc, {'EXIT', Reason}}}
+ after Tmo ->
timeout
end.
+-endif.
%% A parallel network evaluator
%% ArgL === [{M,F,Args},........]
diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl
index f0bd1fabe9..a9f46126ef 100644
--- a/lib/kernel/src/seq_trace.erl
+++ b/lib/kernel/src/seq_trace.erl
@@ -20,12 +20,13 @@
-module(seq_trace).
--define(SEQ_TRACE_SEND, 1). %(1 << 0)
--define(SEQ_TRACE_RECEIVE, 2). %(1 << 1)
--define(SEQ_TRACE_PRINT, 4). %(1 << 2)
--define(SEQ_TRACE_NOW_TIMESTAMP, 8). %(1 << 3)
--define(SEQ_TRACE_STRICT_MON_TIMESTAMP, 16). %(1 << 4)
--define(SEQ_TRACE_MON_TIMESTAMP, 32). %(1 << 5)
+%% Don't forget to update seq_trace_SUITE after changing these.
+-define(SEQ_TRACE_SEND, 1). %(1 << 0)
+-define(SEQ_TRACE_RECEIVE, 2). %(1 << 1)
+-define(SEQ_TRACE_PRINT, 4). %(1 << 2)
+-define(SEQ_TRACE_NOW_TIMESTAMP, 8). %(1 << 3)
+-define(SEQ_TRACE_STRICT_MON_TIMESTAMP, 16). %(1 << 4)
+-define(SEQ_TRACE_MON_TIMESTAMP, 32). %(1 << 5)
-export([set_token/1,
set_token/2,
@@ -39,7 +40,8 @@
%%---------------------------------------------------------------------------
--type flag() :: 'send' | 'receive' | 'print' | 'timestamp' | 'monotonic_timestamp' | 'strict_monotonic_timestamp'.
+-type flag() :: 'send' | 'receive' | 'print' | 'timestamp' |
+ 'monotonic_timestamp' | 'strict_monotonic_timestamp'.
-type component() :: 'label' | 'serial' | flag().
-type value() :: (Label :: term())
| {Previous :: non_neg_integer(),
diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl
index 5a3487a9ba..81520dd841 100644
--- a/lib/kernel/src/user.erl
+++ b/lib/kernel/src/user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -537,54 +537,6 @@ get_line_doit(Prompt, Port, Q, Accu, Enc) ->
binrev(L, T) ->
list_to_binary(lists:reverse(L, T)).
-%% is_cr_at(Pos,Bin) ->
-%% case Bin of
-%% <<_:Pos/binary,$\r,_/binary>> ->
-%% true;
-%% _ ->
-%% false
-%% end.
-
-%% collect_line_bin_re(Bin,_Data,Stack,_) ->
-%% case re:run(Bin,<<"\n">>) of
-%% nomatch ->
-%% X = byte_size(Bin)-1,
-%% case is_cr_at(X,Bin) of
-%% true ->
-%% <<D:X/binary,_/binary>> = Bin,
-%% [<<$\r>>,D|Stack];
-%% false ->
-%% [Bin|Stack]
-%% end;
-%% {match,[{Pos,1}]} ->
-%% PosPlus = Pos + 1,
-%% case Stack of
-%% [] ->
-%% case is_cr_at(Pos - 1,Bin) of
-%% false ->
-%% <<Head:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop, Head, Tail};
-%% true ->
-%% PosMinus = Pos - 1,
-%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin,
-%% {stop, binrev([],[Head,$\n]),Tail}
-%% end;
-%% [<<$\r>>|Stack1] when Pos =:= 0 ->
-
-%% <<_:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop,binrev(Stack1, [$\n]),Tail};
-%% _ ->
-%% case is_cr_at(Pos - 1,Bin) of
-%% false ->
-%% <<Head:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop,binrev(Stack, [Head]),Tail};
-%% true ->
-%% PosMinus = Pos - 1,
-%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin,
-%% {stop, binrev(Stack,[Head,$\n]),Tail}
-%% end
-%% end
-%% end.
%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding)
%% Gets characters from the input port until the applied function
%% returns {stop,Result,RestBuf}. Does not block output until input
@@ -618,9 +570,6 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) ->
{Port, eof} ->
put(eof, true),
{ok, eof, queue:new()};
- %%{io_request,From,ReplyAs,Request} when is_pid(From) ->
- %% get_chars_req(Prompt, M, F, Xa, Port, queue:new(), State,
- %% Request, From, ReplyAs);
{io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) ->
do_io_request(Req, From, ReplyAs, Port,
queue:new()), %Keep Q over this call
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index bd1590ee8f..57fcc74729 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
MODULES= \
+ erpc_SUITE \
rpc_SUITE \
pdict_SUITE \
bif_SUITE \
@@ -85,6 +86,7 @@ MODULES= \
logger_test_lib \
net_SUITE \
os_SUITE \
+ pg_SUITE \
pg2_SUITE \
seq_trace_SUITE \
wrap_log_reader_SUITE \
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 1ab554db7c..aebbd7735d 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -39,7 +39,8 @@
distr_changed_tc1/1, distr_changed_tc2/1,
ensure_started/1, ensure_all_started/1,
shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1,
- config_relative_paths/1]).
+ config_relative_paths/1, handle_many_config_files/1,
+ format_log_1/1, format_log_2/1]).
-define(TESTCASE, testcase_name).
-define(testcase, proplists:get_value(?TESTCASE, Config)).
@@ -59,7 +60,7 @@ all() ->
set_env, set_env_persistent, set_env_errors,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout,
shutdown_deadlock, config_relative_paths,
- persistent_env].
+ persistent_env, handle_many_config_files, format_log_1, format_log_2].
groups() ->
[{reported_bugs, [],
@@ -2076,6 +2077,31 @@ persistent_env(Conf) when is_list(Conf) ->
%% Clean up
ok = application:unload(appinc).
+
+%% Test more than one config file defined by one -config parameter:
+handle_many_config_files(Conf) when is_list(Conf) ->
+
+ %% Write a config file
+ Dir = proplists:get_value(priv_dir, Conf),
+ {ok, Fd} = file:open(filename:join(Dir, "sys.config"), [write]),
+ io:format(Fd, "[].~n", []),
+ file:close(Fd),
+ NodeName = node_name(n1, Conf),
+ Config = filename:join(Dir, "sys"),
+
+ %% Node will be started with two -config
+ %% First one has one argument and second one has two arguments.
+ {ok, Node} = start_node(
+ NodeName,
+ Config,
+ " -config " ++ Config ++ " " ++ Config
+ ),
+ case rpc:call(Node, init, get_argument, [config]) of
+ {ok, [[Config], [Config, Config]]} -> ok;
+ {ok, [[Config], [Config], [Config]]} -> ok %% This happens on windows
+ end,
+ stop_node_nice(Node).
+
%%%-----------------------------------------------------------------
%%% Tests the 'shutdown_func' kernel config parameter
%%%-----------------------------------------------------------------
@@ -3008,6 +3034,188 @@ distr_changed_prep(Conf) when is_list(Conf) ->
{value, {kernel, OldKernel}} = lists:keysearch(kernel, 1, OldEnv),
{OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {Ncp1, Ncp2, Ncp3}, Config2}.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Application = my_application,
+ Error = exit,
+ Node = Term,
+ Report = #{label=>{application_controller,Error},
+ report=>[{application,Application},
+ {exited,Term},
+ {type,Term}]},
+ {F1, A1} = application_controller:format_log(Report),
+ FExpected1 = " application: ~tp~n"
+ " exited: ~tp~n"
+ " type: ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Application,Term,Term] = A1,
+
+ Progress = #{label=>{application_controller,progress},
+ report=>[{application,Application},{started_at,Node}]},
+ {PF1,PA1} = application_controller:format_log(Progress),
+ PFExpected1 = " application: ~tp~n started_at: ~tp~n",
+ ct:log("PF1: ~ts~nPA1: ~tp", [PF1,PA1]),
+ PFExpected1 = PF1,
+ [Application,Node] = PA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = application_controller:format_log(Report),
+ FExpected2 = " application: ~tP~n"
+ " exited: ~tP~n"
+ " type: ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ [Application,Depth,Limited,Depth,Limited,Depth] = A2,
+
+ {PF2,PA2} = application_controller:format_log(Progress),
+ PFExpected2 = " application: ~tP~n started_at: ~tP~n",
+ ct:log("PF2: ~ts~nPA2: ~tp", [PF2,PA2]),
+ PFExpected2 = PF2,
+ [Application,Depth,Limited,Depth] = PA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Application = my_application,
+ Error = exit,
+ Node = Term,
+ Report = #{label=>{application_controller,Error},
+ report=>[{application,Application},
+ {exited,Term},
+ {type,Term}]},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " application: my_application\n"
+ " exited: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " type: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Progress = #{label=>{application_controller,progress},
+ report=>[{application,Application},{started_at,Node}]},
+ PStr1 = flatten_format_log(Progress, FormatOpts1),
+ PL1 = length(PStr1),
+ PExpected1 = " application: my_application\n"
+ " started_at: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("PStr1: ~ts", [PStr1]),
+ ct:log("length(PStr1): ~p", [PL1]),
+ true = PExpected1 =:= PStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " application: my_application\n"
+ " exited: [1,2,3,4,5,6,7,8,9|...]\n"
+ " type: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ PStr2 = flatten_format_log(Progress, FormatOpts2),
+ PL2 = length(PStr2),
+ PExpected2 = " application: my_application\n"
+ " started_at: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("PStr2: ~ts", [PStr2]),
+ ct:log("length(PStr2): ~p", [PL2]),
+ true = PExpected2 =:= PStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " application: my_application\n"
+ " exited: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " type: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = Expected3 =:= Str3,
+
+ PFormatOpts3 = #{chars_limit=>80},
+ PStr3 = flatten_format_log(Progress, PFormatOpts3),
+ PL3 = length(PStr3),
+ PExpected3 = " application: my_application\n"
+ " started_at:",
+ ct:log("PStr3: ~ts", [PStr3]),
+ ct:log("length(PStr3): ~p", [PL3]),
+ true = lists:prefix(PExpected3, PStr3),
+ true = PL3 < PL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+
+ Expected4 = "Application: my_application. "
+ "Exited: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Type: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ PStr4 = flatten_format_log(Progress, FormatOpts4),
+ PL4 = length(PStr4),
+ PExpected4 = "Application: my_application. "
+ "Started at: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("PStr4: ~ts", [PStr4]),
+ ct:log("length(PStr4): ~p", [PL4]),
+ true = PExpected4 =:= PStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Application: my_application. "
+ "Exited: [1,2,3,4,5,6,7,8,9|...]. "
+ "Type: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ PStr5 = flatten_format_log(Progress, FormatOpts5),
+ PL5 = length(PStr5),
+ PExpected5 = "Application: my_application. "
+ "Started at: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("PStr5: ~ts", [PStr5]),
+ ct:log("length(PStr5): ~p", [PL5]),
+ true = PExpected5 =:= PStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>100},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Application: my_application. Exited:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ PFormatOpts6 = #{single_line=>true, chars_limit=>60},
+ PStr6 = flatten_format_log(Progress, PFormatOpts6),
+ PL6 = length(PStr6),
+ PExpected6 = "Application: my_application. Started at:",
+ ct:log("PStr6: ~ts", [PStr6]),
+ ct:log("length(PStr6): ~p", [PL6]),
+ true = lists:prefix(PExpected6, PStr6),
+ true = PL6 < PL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(application_controller:format_log(Report, Format)).
%%% Copied from init_SUITE.erl.
is_real_system(KernelVsn, StdlibVsn) ->
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 131e3fed34..ed1976d912 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -26,7 +26,7 @@
-export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1,
replace_path/1, load_file/1, load_abs/1, ensure_loaded/1,
delete/1, purge/1, purge_many_exits/0, purge_many_exits/1,
- soft_purge/1, is_loaded/1, all_loaded/1,
+ soft_purge/1, is_loaded/1, all_loaded/1, all_available/1,
load_binary/1, dir_req/1, object_code/1, set_path_file/1,
upgrade/0, upgrade/1,
sticky_dir/1, pa_pz_option/1, add_del_path/1,
@@ -61,7 +61,7 @@ all() ->
[set_path, get_path, add_path, add_paths, del_path,
replace_path, load_file, load_abs, ensure_loaded,
delete, purge, purge_many_exits, soft_purge, is_loaded, all_loaded,
- load_binary, dir_req, object_code, set_path_file,
+ all_available, load_binary, dir_req, object_code, set_path_file,
upgrade,
sticky_dir, pa_pz_option, add_del_path, dir_disappeared,
ext_mod_dep, clash, where_is_file,
@@ -507,6 +507,47 @@ all_unique([]) -> ok;
all_unique([_]) -> ok;
all_unique([{X,_}|[{Y,_}|_]=T]) when X < Y -> all_unique(T).
+all_available(Config) when is_list(Config) ->
+ case test_server:is_cover() of
+ true -> {skip,"Cover is running"};
+ false -> all_available_1(Config)
+ end.
+
+all_available_1(Config) ->
+
+ %% Add an ez dir to make sure the modules in there are found
+ DDir = proplists:get_value(data_dir,Config)++"clash/",
+ true = code:add_path(DDir++"foobar-0.1.ez/foobar-0.1/ebin"),
+
+ Available = code:all_available(),
+
+ %% Test that baz and blarg that are part of the .ez archive are found
+ {value, _} =
+ lists:search(fun({Name,_,Loaded}) -> not Loaded andalso Name =:= "baz" end, Available),
+ {value, _} =
+ lists:search(fun({Name,_,Loaded}) -> not Loaded andalso Name =:= "blarg" end, Available),
+
+ %% Test that all loaded are part of all available
+ Loaded = [{atom_to_list(M),P,true} || {M,P} <- code:all_loaded()],
+ [] = Loaded -- Available,
+
+ {value, {ModStr,_Path,false} = NotLoaded} =
+ lists:search(fun({Name,_,Loaded}) -> not is_atom(Name) end, Available),
+ ct:log("Testing with ~p",[NotLoaded]),
+
+ Mod = list_to_atom(ModStr),
+
+ %% Test that the module is actually not loaded
+ false = code:is_loaded(Mod),
+
+ %% Load it
+ Mod:module_info(),
+
+ {value, {ModStr,_Path,true}} =
+ lists:search(fun({Name,_,_}) -> Name =:= ModStr end, code:all_available()),
+
+ ok.
+
load_binary(Config) when is_list(Config) ->
TestDir = test_dir(),
File = TestDir ++ "/code_b_test" ++ code:objfile_extension(),
@@ -1883,6 +1924,11 @@ module_status() ->
loaded = code:module_status(erlang), % preloaded
loaded = code:module_status(?MODULE), % normal known loaded
+ %% module_status/0 returns status for each loaded module
+ true = (lists:sort([{M, code:module_status(M)}
+ || {M, _} <- code:all_loaded()])
+ =:= lists:sort(code:module_status())),
+
non_existing = code:which(?TESTMOD), % verify dummy name not in path
code:purge(?TESTMOD), % ensure no previous version in memory
code:delete(?TESTMOD),
@@ -1895,6 +1941,11 @@ module_status() ->
"" = code:which(?TESTMOD), % verify empty string for source file
loaded = code:module_status(?TESTMOD),
+ %% module_status/1 also accepts a list of modules
+ [] = code:module_status([]),
+ [{erlang, loaded},{?MODULE,loaded},{?TESTMOD,loaded}] =
+ code:module_status([erlang, ?MODULE, ?TESTMOD]),
+
%% deleting generated code
true = code:delete(?TESTMOD),
non_existing = code:which(?TESTMOD), % verify still not in path
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index c3a022df0a..70661637cc 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -20,6 +20,7 @@
-module(erl_distribution_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/dist.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
@@ -41,7 +42,9 @@
monitor_nodes_combinations/1,
monitor_nodes_cleanup/1,
monitor_nodes_many/1,
- dist_ctrl_proc_smoke/1]).
+ monitor_nodes_down_up/1,
+ dist_ctrl_proc_smoke/1,
+ dist_ctrl_proc_reject/1]).
%% Performs the test at another node.
-export([get_socket_priorities/0,
@@ -53,7 +56,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
--export([dist_cntrlr_output_test/2]).
+-export([dist_cntrlr_output_test_size/2]).
-export([pinger/1]).
@@ -72,6 +75,7 @@ suite() ->
all() ->
[dist_ctrl_proc_smoke,
+ dist_ctrl_proc_reject,
tick, tick_change, nodenames, hostnames, illegal_nodenames,
connect_node,
hidden_node, setopts,
@@ -85,13 +89,16 @@ groups() ->
monitor_nodes_node_type, monitor_nodes_misc,
monitor_nodes_otp_6481, monitor_nodes_errors,
monitor_nodes_combinations, monitor_nodes_cleanup,
- monitor_nodes_many]}].
+ monitor_nodes_many,
+ monitor_nodes_down_up]}].
init_per_suite(Config) ->
+ start_gen_tcp_dist_test_type_server(),
Config.
end_per_suite(_Config) ->
[slave:stop(N) || N <- nodes()],
+ kill_gen_tcp_dist_test_type_server(),
ok.
init_per_group(_GroupName, Config) ->
@@ -275,7 +282,7 @@ test_node(Name, Illigal, ExtraArgs) ->
end,
net_kernel:monitor_nodes(true),
BinCommand = unicode:characters_to_binary(Command, utf8),
- Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
+ _Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
Node = list_to_atom(Name),
receive
{nodeup, Node} ->
@@ -1456,89 +1463,268 @@ monitor_nodes_many(DCfg, _Config) ->
MonNodeState = monitor_node_state(),
ok.
+%% Test order of messages nodedown and nodeup.
+monitor_nodes_down_up(Config) when is_list(Config) ->
+ [An] = get_nodenames(1, monitor_nodeup),
+ {ok, A} = ct_slave:start(An),
+
+ try
+ monitor_nodes_yoyo(A)
+ after
+ catch ct_slave:stop(A)
+ end.
+
+monitor_nodes_yoyo(A) ->
+ net_kernel:monitor_nodes(true),
+ Papa = self(),
+
+ %% Spawn lots of processes doing one erlang:monitor_node(A,true) each
+ %% just to get lots of other monitors to fire when connection goes down
+ %% and thereby give time for {nodeup,A} to race before {nodedown,A}.
+ NodeMonCnt = 10000,
+ NodeMons = [my_spawn_opt(fun F() ->
+ monitor_node = receive_any(),
+ monitor_node(A, true),
+ Papa ! ready,
+ {nodedown, A} = receive_any(),
+ F()
+ end,
+ [link, monitor, {priority, low}])
+ ||
+ _ <- lists:seq(1, NodeMonCnt)],
+
+ %% Spawn message spamming process to trigger new connection setups
+ %% as quick as possible.
+ Spammer = my_spawn_opt(fun F() ->
+ {dummy, A} ! trigger_auto_connect,
+ F()
+ end,
+ [link, monitor]),
+
+ %% Now bring connection down and verify we get {nodedown,A} before {nodeup,A}.
+ Yoyos = 20,
+ [begin
+ [P ! monitor_node || P <- NodeMons],
+ [receive ready -> ok end || _ <- NodeMons],
+
+ Owner = get_conn_owner(A),
+ exit(Owner, kill),
+
+ {nodedown, A} = receive_any(),
+ {nodeup, A} = receive_any()
+ end
+ || _ <- lists:seq(1,Yoyos)],
+
+ unlink(Spammer),
+ exit(Spammer, die),
+ receive {'DOWN',_,process,Spammer,_} -> ok end,
+
+ [begin unlink(P), exit(P, die) end || P <- NodeMons],
+ [receive {'DOWN',_,process,P,_} -> ok end || P <- NodeMons],
+
+ net_kernel:monitor_nodes(false),
+ ok.
+
+receive_any() ->
+ receive_any(infinity).
+
+receive_any(Timeout) ->
+ receive
+ M -> M
+ after
+ Timeout -> timeout
+ end.
+
+my_spawn_opt(Fun, Opts) ->
+ case spawn_opt(Fun, Opts) of
+ {Pid, _Mref} -> Pid;
+ Pid -> Pid
+ end.
+
+get_conn_owner(Node) ->
+ {ok, NodeInfo} = net_kernel:node_info(Node),
+ {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo),
+ Owner.
+
dist_ctrl_proc_smoke(Config) when is_list(Config) ->
+ dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME)).
+
+dist_ctrl_proc_reject(Config) when is_list(Config) ->
+ ToReject = combinations(dist_util:rejectable_flags()),
+ lists:map(fun(Flags) ->
+ ct:log("Try to reject ~p",[Flags]),
+ dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME),
+ "-gen_tcp_dist_reject_flags " ++ integer_to_list(Flags))
+ end, ToReject).
+
+combinations([H | T]) ->
+ lists:flatten([[(1 bsl H) bor C || C <- combinations(T)] | combinations(T)]);
+combinations([]) ->
+ [0];
+combinations(BitField) ->
+ lists:sort(combinations(bits(BitField, 0))).
+
+bits(0, _) ->
+ [];
+bits(BitField, Cnt) when BitField band 1 == 1 ->
+ [Cnt | bits(BitField bsr 1, Cnt + 1)];
+bits(BitField, Cnt) ->
+ bits(BitField bsr 1, Cnt + 1).
+
+dist_ctrl_proc_test(Nodes) ->
+ dist_ctrl_proc_test(Nodes,"").
+
+dist_ctrl_proc_test([Name1,Name2], Extra) ->
ThisNode = node(),
- [Name1, Name2] = get_nodenames(2, dist_ctrl_proc_example_smoke),
- GetSizeArg = " -gen_tcp_dist_output_loop "
- ++ atom_to_list(?MODULE) ++ " "
- ++ "dist_cntrlr_output_test",
+ GenTcpOptProlog = "-proto_dist gen_tcp "
+ "-gen_tcp_dist_output_loop " ++ atom_to_list(?MODULE) ++ " " ++
+ "dist_cntrlr_output_test_size " ++ Extra,
{ok, Node1} = start_node("", Name1, "-proto_dist gen_tcp"),
- {ok, Node2} = start_node("", Name2, "-proto_dist gen_tcp" ++ GetSizeArg),
- pong = rpc:call(Node1, net_adm, ping, [Node2]),
- NL1 = lists:sort([ThisNode, Node2]),
- NL2 = lists:sort([ThisNode, Node1]),
- NL1 = lists:sort(rpc:call(Node1, erlang, nodes, [])),
- NL2 = lists:sort(rpc:call(Node2, erlang, nodes, [])),
+ {ok, Node2} = start_node("", Name2, GenTcpOptProlog),
+ NL = lists:sort([ThisNode, Node1, Node2]),
+ wait_until(fun () ->
+ NL == lists:sort([node()|nodes()])
+ end),
+ wait_until(fun () ->
+ NL == lists:sort([rpc:call(Node1,erlang, node, [])
+ | rpc:call(Node1, erlang, nodes, [])])
+ end),
+ wait_until(fun () ->
+ NL == lists:sort([rpc:call(Node2,erlang, node, [])
+ | rpc:call(Node2, erlang, nodes, [])])
+ end),
+
+ smoke_communicate(Node1, gen_tcp_dist, dist_cntrlr_output_loop),
+ smoke_communicate(Node2, erl_distribution_SUITE, dist_cntrlr_output_loop_size),
+ stop_node(Node1),
+ stop_node(Node2),
+ ok.
+
+smoke_communicate(Node, OLoopMod, OLoopFun) ->
%% Verify that we actually are executing the distribution
%% module we expect and also massage message passing over
- %% it a bit...
- Ps1 = rpc:call(Node1, erlang, processes, []),
- try
- lists:foreach(
- fun (P) ->
- case rpc:call(Node1, erlang, process_info, [P, current_stacktrace]) of
- undefined ->
- ok;
- {current_stacktrace, StkTrace} ->
- lists:foreach(fun ({gen_tcp_dist,
- dist_cntrlr_output_loop,
- 2, _}) ->
- io:format("~p ~p~n", [P, StkTrace]),
- throw(found_it);
- (_) ->
- ok
- end, StkTrace)
- end
- end, Ps1),
- exit({missing, dist_cntrlr_output_loop})
- catch
- throw:found_it -> ok
- end,
-
- Ps2 = rpc:call(Node2, erlang, processes, []),
+ %% the connection a bit...
+ Ps = rpc:call(Node, erlang, processes, []),
try
lists:foreach(
fun (P) ->
- case rpc:call(Node2, erlang, process_info, [P, current_stacktrace]) of
+ case rpc:call(Node, erlang, process_info, [P, current_stacktrace]) of
undefined ->
ok;
{current_stacktrace, StkTrace} ->
- lists:foreach(fun ({erl_distribution_SUITE,
- dist_cntrlr_output_loop,
- 2, _}) ->
+ lists:foreach(fun ({Mod, Fun, 2, _}) when Mod == OLoopMod,
+ Fun == OLoopFun ->
io:format("~p ~p~n", [P, StkTrace]),
throw(found_it);
(_) ->
ok
end, StkTrace)
end
- end, Ps2),
- exit({missing, dist_cntrlr_output_loop})
+ end, Ps),
+ exit({missing, {OLoopMod, OLoopFun}})
catch
throw:found_it -> ok
end,
-
- stop_node(Node1),
- stop_node(Node2),
+ Bin = list_to_binary(lists:duplicate(1000,100)),
+ BitStr = <<0:7999>>,
+ List = [[Bin], atom, [BitStr|Bin], make_ref(), [[[BitStr|"hopp"]]],
+ 4711, 111122222211111111111111,"hej", fun () -> ok end, BitStr,
+ self(), fun erlang:node/1],
+ Pid = spawn_link(Node, fun () -> receive {From1, Msg1} -> From1 ! Msg1 end,
+ receive {From2, Msg2} -> From2 ! Msg2 end
+ end),
+ R = make_ref(),
+ Pid ! {self(), [R, List]},
+ receive [R, L1] -> List = L1 end,
+
+ %% Send a huge message in order to trigger message fragmentation if enabled
+ FragBin = <<0:(2*(1024*64*8))>>,
+ Pid ! {self(), [R, List, FragBin]},
+ receive [R, L2, B] -> List = L2, FragBin = B end,
+
+ unlink(Pid),
+ exit(Pid, kill),
ok.
%% Misc. functions
run_dist_configs(Func, Config) ->
- GetSizeArg = " -gen_tcp_dist_output_loop "
- ++ atom_to_list(?MODULE) ++ " "
- ++ "dist_cntrlr_output_test",
+ GetOptProlog = "-proto_dist gen_tcp -gen_tcp_dist_output_loop "
+ ++ atom_to_list(?MODULE) ++ " ",
+ GenTcpDistTest = case get_gen_tcp_dist_test_type() of
+ default ->
+ {"gen_tcp_dist", "-proto_dist gen_tcp"};
+ size ->
+ {"gen_tcp_dist (get_size)",
+ GetOptProlog ++ "dist_cntrlr_output_test_size"}
+ end,
lists:map(fun ({DCfgName, DCfg}) ->
io:format("~n~n=== Running ~s configuration ===~n~n",
[DCfgName]),
Func(DCfg, Config)
end,
- [{"default", ""},
- {"gen_tcp_dist", "-proto_dist gen_tcp"},
- {"gen_tcp_dist (get_size)", "-proto_dist gen_tcp" ++ GetSizeArg}]).
+ [{"default", ""}, GenTcpDistTest]).
+
+start_gen_tcp_dist_test_type_server() ->
+ Me = self(),
+ Go = make_ref(),
+ io:format("STARTING: gen_tcp_dist_test_type_server~n",[]),
+ {P, M} = spawn_monitor(fun () ->
+ register(gen_tcp_dist_test_type_server, self()),
+ Me ! Go,
+ gen_tcp_dist_test_type_server()
+ end),
+ receive
+ Go ->
+ ok;
+ {'DOWN', M, process, P, _} ->
+ start_gen_tcp_dist_test_type_server()
+ end.
+
+kill_gen_tcp_dist_test_type_server() ->
+ case whereis(gen_tcp_dist_test_type_server) of
+ undefined ->
+ ok;
+ Pid ->
+ exit(Pid,kill),
+ %% Sync death, before continuing...
+ false = erlang:is_process_alive(Pid)
+ end.
+
+gen_tcp_dist_test_type_server() ->
+ Type = case abs(erlang:monotonic_time(second)) rem 2 of
+ 0 -> default;
+ 1 -> size
+ end,
+ gen_tcp_dist_test_type_server(Type).
+
+gen_tcp_dist_test_type_server(Type) ->
+ receive
+ {From, Ref} ->
+ From ! {Ref, Type},
+ NewType = case Type of
+ default -> size;
+ size -> default
+ end,
+ gen_tcp_dist_test_type_server(NewType)
+ end.
+
+get_gen_tcp_dist_test_type() ->
+ Ref = make_ref(),
+ try
+ gen_tcp_dist_test_type_server ! {self(), Ref},
+ receive
+ {Ref, Type} ->
+ Type
+ end
+ catch
+ error:badarg ->
+ start_gen_tcp_dist_test_type_server(),
+ get_gen_tcp_dist_test_type()
+ end.
-dist_cntrlr_output_test(DHandle, Socket) ->
+dist_cntrlr_output_test_size(DHandle, Socket) ->
false = erlang:dist_ctrl_get_opt(DHandle, get_size),
false = erlang:dist_ctrl_set_opt(DHandle, get_size, true),
true = erlang:dist_ctrl_get_opt(DHandle, get_size),
@@ -1546,27 +1732,33 @@ dist_cntrlr_output_test(DHandle, Socket) ->
false = erlang:dist_ctrl_get_opt(DHandle, get_size),
false = erlang:dist_ctrl_set_opt(DHandle, get_size, true),
true = erlang:dist_ctrl_get_opt(DHandle, get_size),
- dist_cntrlr_output_loop(DHandle, Socket).
-
-dist_cntrlr_send_data(DHandle, Socket) ->
+ dist_cntrlr_output_loop_size(DHandle, Socket).
+
+dist_cntrlr_output_loop_size(DHandle, Socket) ->
+ receive
+ dist_data ->
+ %% Outgoing data from this node...
+ dist_cntrlr_send_data_size(DHandle, Socket);
+ _ ->
+ ok %% Drop garbage message...
+ end,
+ dist_cntrlr_output_loop_size(DHandle, Socket).
+
+dist_cntrlr_send_data_size(DHandle, Socket) ->
case erlang:dist_ctrl_get_data(DHandle) of
none ->
erlang:dist_ctrl_get_data_notification(DHandle);
{Size, Data} ->
+ ok = ensure_iovec(Data),
Size = erlang:iolist_size(Data),
ok = gen_tcp:send(Socket, Data),
- dist_cntrlr_send_data(DHandle, Socket)
+ dist_cntrlr_send_data_size(DHandle, Socket)
end.
-dist_cntrlr_output_loop(DHandle, Socket) ->
- receive
- dist_data ->
- %% Outgoing data from this node...
- dist_cntrlr_send_data(DHandle, Socket);
- _ ->
- ok %% Drop garbage message...
- end,
- dist_cntrlr_output_loop(DHandle, Socket).
+ensure_iovec([]) ->
+ ok;
+ensure_iovec([X|Y]) when is_binary(X) ->
+ ensure_iovec(Y).
monitor_node_state() ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -1593,7 +1785,7 @@ print_my_messages() ->
sleep(T) -> receive after T * 1000 -> ok end.
-start_node(DCfg, Name, Param, this) ->
+start_node(_DCfg, Name, Param, this) ->
NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
start_node(DCfg, Name, Param, "this") ->
@@ -1671,3 +1863,5 @@ block_emu(Ms) ->
Res = erts_debug:set_internal_state(block, Ms),
erts_debug:set_internal_state(available_internal_state, false),
Res.
+
+
diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl
index 8256444bdc..ca4511a19b 100644
--- a/lib/kernel/test/erl_distribution_wb_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl
@@ -28,15 +28,6 @@
-export([init_per_testcase/2, end_per_testcase/2, whitebox/1,
switch_options/1, missing_compulsory_dflags/1]).
-%% 1)
-%%
-%% Connections are now always set up symmetrically with respect to
-%% publication. If connecting node doesn't send DFLAG_PUBLISHED
-%% the other node wont send DFLAG_PUBLISHED. If the connecting
-%% node send DFLAG_PUBLISHED but the other node doesn't send
-%% DFLAG_PUBLISHED, the connecting node should consider its
-%% DFLAG_PUBLISHED as dropped, i.e the connecting node wont be
-%% published on the other node.
-define(to_port(Socket, Data),
case inet_tcp:send(Socket, Data) of
@@ -47,6 +38,9 @@
R
end).
+-define(DIST_VER_HIGH, 6).
+-define(DIST_VER_LOW, 5).
+
-define(DFLAG_PUBLISHED,1).
-define(DFLAG_ATOM_CACHE,2).
-define(DFLAG_EXTENDED_REFERENCES,4).
@@ -57,15 +51,19 @@
-define(DFLAG_NEW_FUN_TAGS,16#80).
-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
-define(DFLAG_UTF8_ATOMS, 16#10000).
+-define(DFLAG_BIG_CREATION, 16#40000).
+-define(DFLAG_HANDSHAKE_23, 16#01000000).
%% From R9 and forward extended references is compulsory
%% From R10 and forward extended pids and ports are compulsory
%% From R20 and forward UTF8 atoms are compulsory
%% From R21 and forward NEW_FUN_TAGS is compulsory (no more tuple fallback {fun, ...})
+%% From R23 and forward BIG_CREATION is compulsory
-define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor
?DFLAG_EXTENDED_PIDS_PORTS bor
?DFLAG_UTF8_ATOMS bor
- ?DFLAG_NEW_FUN_TAGS)).
+ ?DFLAG_NEW_FUN_TAGS bor
+ ?DFLAG_BIG_CREATION)).
-define(PASS_THROUGH, $p).
@@ -131,9 +129,18 @@ whitebox(Config) when is_list(Config) ->
{ok, Node} = start_node(?MODULE,""),
Cookie = erlang:get_cookie(),
{_,Host} = split(node()),
- ok = pending_up_md5(Node, join(ccc,Host), Cookie),
- ok = simultaneous_md5(Node, join('A',Host), Cookie),
- ok = simultaneous_md5(Node, join(zzzzzzzzzzzzzz,Host), Cookie),
+ [begin
+ io:format("Test OurVersion=~p and TrustEpmd=~p\n",
+ [OurVersion, TrustEpmd]),
+ ok = pending_up_md5(Node, join(ccc,Host), OurVersion,
+ TrustEpmd, Cookie),
+ ok = simultaneous_md5(Node, join('A',Host), OurVersion,
+ TrustEpmd, Cookie),
+ ok = simultaneous_md5(Node, join(zzzzzzzzzzzzzz,Host),
+ OurVersion, TrustEpmd, Cookie)
+ end
+ || OurVersion <- lists:seq(?DIST_VER_LOW, ?DIST_VER_HIGH),
+ TrustEpmd <- [true, false]],
stop_node(Node),
ok.
@@ -202,17 +209,22 @@ test_switch_active_and_packet() ->
%%
%% Handshake tests
%%
-pending_up_md5(Node,OurName,Cookie) ->
+pending_up_md5(Node,OurName,OurVersion,TrustEpmd,Cookie) ->
{NA,NB} = split(Node),
- {port,PortNo,_} = erl_epmd:port_please(NA,NB),
+ {port,PortNo,EpmdSaysVersion} = erl_epmd:port_please(NA,NB),
{ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
- send_name(SocketA,OurName,5),
+ AssumedVersion = case TrustEpmd of
+ true -> EpmdSaysVersion;
+ false -> ?DIST_VER_LOW
+ end,
+ SentNameMsg = send_name(SocketA,OurName, OurVersion, AssumedVersion),
ok = recv_status(SocketA),
- {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1)
+ {Node,ChallengeMsg,HisChallengeA} = recv_challenge(SocketA,OurVersion),
OurChallengeA = gen_challenge(),
OurDigestA = gen_digest(HisChallengeA, Cookie),
+ send_complement(SocketA, SentNameMsg, ChallengeMsg, OurVersion),
send_challenge_reply(SocketA, OurChallengeA, OurDigestA),
ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie),
%%%
@@ -224,13 +236,14 @@ pending_up_md5(Node,OurName,Cookie) ->
{ok, SocketB} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
- send_name(SocketB,OurName,5),
+ SentNameMsg = send_name(SocketB,OurName, OurVersion, AssumedVersion),
alive = recv_status(SocketB),
send_status(SocketB, true),
gen_tcp:close(SocketA),
- {hidden,Node,5,HisChallengeB} = recv_challenge(SocketB), % See 1)
+ {Node,ChallengeMsg,HisChallengeB} = recv_challenge(SocketB,OurVersion),
OurChallengeB = gen_challenge(),
OurDigestB = gen_digest(HisChallengeB, Cookie),
+ send_complement(SocketB, SentNameMsg, ChallengeMsg, OurVersion),
send_challenge_reply(SocketB, OurChallengeB, OurDigestB),
ok = recv_challenge_ack(SocketB, OurChallengeB, Cookie),
%%%
@@ -246,7 +259,7 @@ pending_up_md5(Node,OurName,Cookie) ->
gen_tcp:close(SocketB),
ok.
-simultaneous_md5(Node, OurName, Cookie) when OurName < Node ->
+simultaneous_md5(Node, OurName, OurVersion, TrustEpmd, Cookie) when OurName < Node ->
pong = net_adm:ping(Node),
LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of
{ok, Socket} ->
@@ -254,15 +267,19 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = register_node(OurName, LSocket, ?DIST_VER_LOW, ?DIST_VER_LOW),
{NA, NB} = split(Node),
rpc:cast(Node, net_adm, ping, [OurName]),
receive after 1000 -> ok end,
- {port, PortNo, _} = erl_epmd:port_please(NA,NB),
+ {port, PortNo, EpmdSaysVersion} = erl_epmd:port_please(NA,NB),
{ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
- send_name(SocketA,OurName,5),
+ AssumedVersion = case TrustEpmd of
+ true -> EpmdSaysVersion;
+ false -> ?DIST_VER_LOW
+ end,
+ send_name(SocketA,OurName, OurVersion, AssumedVersion),
%% We are still not marked up on the other side, as our first message
%% is not sent.
SocketB = case gen_tcp:accept(LSocket) of
@@ -275,11 +292,13 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node ->
%% Now we are expected to close A
gen_tcp:close(SocketA),
%% But still Socket B will continue
- {normal,Node,5} = recv_name(SocketB), % See 1)
+ {Node,GotNameMsg,GotFlags} = recv_name(SocketB),
+ true = (GotFlags band ?DFLAG_HANDSHAKE_23) =/= 0,
send_status(SocketB, ok_simultaneous),
MyChallengeB = gen_challenge(),
- send_challenge(SocketB, OurName, MyChallengeB,5),
- HisChallengeB = recv_challenge_reply(SocketB, MyChallengeB, Cookie),
+ send_challenge(SocketB, OurName, MyChallengeB, OurVersion, GotFlags),
+ recv_complement(SocketB, GotNameMsg, OurVersion),
+ {ok,HisChallengeB} = recv_challenge_reply(SocketB, MyChallengeB, Cookie),
DigestB = gen_digest(HisChallengeB,Cookie),
send_challenge_ack(SocketB, DigestB),
inet:setopts(SocketB, [{active, false},
@@ -293,7 +312,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node ->
gen_tcp:close(EpmdSocket),
ok;
-simultaneous_md5(Node, OurName, Cookie) when OurName > Node ->
+simultaneous_md5(Node, OurName, OurVersion, TrustEpmd, Cookie) when OurName > Node ->
pong = net_adm:ping(Node),
LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of
{ok, Socket} ->
@@ -301,11 +320,12 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = register_node(OurName, LSocket,
+ ?DIST_VER_LOW, ?DIST_VER_LOW),
{NA, NB} = split(Node),
rpc:cast(Node, net_adm, ping, [OurName]),
receive after 1000 -> ok end,
- {port, PortNo, _} = erl_epmd:port_please(NA,NB),
+ {port, PortNo, EpmdSaysVersion} = erl_epmd:port_please(NA,NB),
{ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
@@ -315,16 +335,22 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node ->
Else2 ->
exit(Else2)
end,
- send_name(SocketA,OurName,5),
+ AssumedVersion = case TrustEpmd of
+ true -> EpmdSaysVersion;
+ false -> ?DIST_VER_LOW
+ end,
+ SentNameMsg = send_name(SocketA,OurName, OurVersion, AssumedVersion),
ok_simultaneous = recv_status(SocketA),
%% Socket B should die during this
case catch begin
- {normal,Node,5} = recv_name(SocketB), % See 1)
+ {Node,GotNameMsg,GotFlagsB} = recv_name(SocketB),
+ true = (GotFlagsB band ?DFLAG_HANDSHAKE_23) =/= 0,
send_status(SocketB, ok_simultaneous),
MyChallengeB = gen_challenge(),
send_challenge(SocketB, OurName, MyChallengeB,
- 5),
- HisChallengeB = recv_challenge_reply(
+ OurVersion, GotFlagsB),
+ recv_complement(SocketB, GotNameMsg, OurVersion),
+ {ok,HisChallengeB} = recv_challenge_reply(
SocketB,
MyChallengeB,
Cookie),
@@ -346,9 +372,10 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node ->
end,
gen_tcp:close(SocketB),
%% But still Socket A will continue
- {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1)
+ {Node,ChallengeMsg,HisChallengeA} = recv_challenge(SocketA,OurVersion),
OurChallengeA = gen_challenge(),
OurDigestA = gen_digest(HisChallengeA, Cookie),
+ send_complement(SocketA, SentNameMsg, ChallengeMsg, OurVersion),
send_challenge_reply(SocketA, OurChallengeA, OurDigestA),
ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie),
@@ -368,13 +395,16 @@ missing_compulsory_dflags(Config) when is_list(Config) ->
{ok, Node} = start_node(Name1,""),
{NA,NB} = split(Node),
{port,PortNo,_} = erl_epmd:port_please(NA,NB),
- {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
- [{active,false},
- {packet,2}]),
- BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)),
- send_name(SocketA,BadNode,5,0),
- not_allowed = recv_status(SocketA),
- gen_tcp:close(SocketA),
+ [begin
+ {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
+ [{active,false},
+ {packet,2}]),
+ BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)),
+ send_name(SocketA,BadNode, Version, Version, 0),
+ not_allowed = recv_status(SocketA),
+ gen_tcp:close(SocketA)
+ end
+ || Version <- lists:seq(?DIST_VER_LOW, ?DIST_VER_HIGH)],
stop_node(Node),
ok.
@@ -486,46 +516,91 @@ recv_status(Socket) ->
exit(Bad)
end.
-send_challenge(Socket, Node, Challenge, Version) ->
- send_challenge(Socket, Node, Challenge, Version, ?COMPULSORY_DFLAGS).
-send_challenge(Socket, Node, Challenge, Version, Flags) ->
- {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket),
- ?to_port(Socket, [$n,?int16(Version),?int32(Flags),
- ?int32(Challenge), atom_to_list(Node)]).
+send_challenge(Socket, Node, Challenge, Version, GotFlags) ->
+ send_challenge(Socket, Node, Challenge, Version, GotFlags, ?COMPULSORY_DFLAGS).
-recv_challenge(Socket) ->
- case gen_tcp:recv(Socket, 0) of
- {ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
+send_challenge(Socket, Node, Challenge, ?DIST_VER_LOW, _GotFlags, Flags) ->
+ {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket),
+ ?to_port(Socket, [$n,<<?DIST_VER_LOW:16>>,<<Flags:32>>,
+ <<Challenge:32>>, atom_to_list(Node)]);
+send_challenge(Socket, Node, Challenge, ?DIST_VER_HIGH, GotFlags, Flags) ->
+ true = (GotFlags band ?DFLAG_HANDSHAKE_23) =/= 0,
+ {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket),
+ NodeName = atom_to_list(Node),
+ Nlen = length(NodeName),
+ Creation = erts_internal:get_creation(),
+ ?to_port(Socket, [$N, <<(Flags bor ?DFLAG_HANDSHAKE_23):64>>,
+ <<Challenge:32>>, <<Creation:32>>,
+ <<Nlen:16>>, NodeName
+ ]).
+
+recv_challenge(Socket, OurVersion) ->
+ {ok, Msg} = gen_tcp:recv(Socket, 0),
+ %%io:format("recv_challenge Msg=~p\n", [Msg]),
+ case {OurVersion, Msg} of
+ {?DIST_VER_LOW,
+ [$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
Flags = ?u32(Fl1,Fl2,Fl3,Fl4),
- Type = case Flags band ?DFLAG_PUBLISHED of
- 0 ->
- hidden;
- _ ->
- normal
- end,
+ true = (Flags band ?COMPULSORY_DFLAGS) =:= ?COMPULSORY_DFLAGS,
Node =list_to_atom(Ns),
- Version = ?u16(V1,V0),
+ ?DIST_VER_LOW = ?u16(V1,V0),
+ Challenge = ?u32(CA3,CA2,CA1,CA0),
+ {Node,$n,Challenge};
+
+ {?DIST_VER_HIGH,
+ [$N, F7,F6,F5,F4,F3,F2,F1,F0, CA3,CA2,CA1,CA0,
+ Cr3,Cr2,Cr1,Cr0, NL1,NL0 | Ns]} ->
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ true = (Flags band ?COMPULSORY_DFLAGS) =:= ?COMPULSORY_DFLAGS,
+ <<Creation:32>> = <<Cr3,Cr2,Cr1,Cr0>>,
+ true = (Creation =/= 0),
+ <<NameLen:16>> = <<NL1,NL0>>,
+ NameLen = length(Ns),
+ Node = list_to_atom(Ns),
Challenge = ?u32(CA3,CA2,CA1,CA0),
- {Type,Node,Version,Challenge};
+ {Node,$N,Challenge};
+
_ ->
?shutdown(no_node)
end.
+send_complement(Socket, SentNameMsg, ChallengeMsg, OurVersion) ->
+ case {SentNameMsg,ChallengeMsg} of
+ {$n,$N} ->
+ FlagsHigh = our_flags(?COMPULSORY_DFLAGS, OurVersion) bsr 32,
+ ?to_port(Socket, [$c,
+ <<FlagsHigh:32>>,
+ ?int32(erts_internal:get_creation())]);
+ {Same,Same} ->
+ ok
+ end.
+
+recv_complement(Socket, $n, OurVersion) when OurVersion > ?DIST_VER_LOW ->
+ case gen_tcp:recv(Socket, 0) of
+ {ok,[$c,Cr3,Cr2,Cr1,Cr0]} ->
+ Creation = ?u32(Cr3,Cr2,Cr1,Cr0),
+ true = (Creation =/= 0);
+ Err ->
+ {error,Err}
+ end;
+recv_complement(_, _ , _) ->
+ ok.
+
send_challenge_reply(Socket, Challenge, Digest) ->
?to_port(Socket, [$r,?int32(Challenge),Digest]).
recv_challenge_reply(Socket, ChallengeA, Cookie) ->
case gen_tcp:recv(Socket, 0) of
- {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 ->
+ {ok,[$r,CB3,CB2,CB1,CB0 | SumB]=Data} when length(SumB) == 16 ->
SumA = gen_digest(ChallengeA, Cookie),
ChallengeB = ?u32(CB3,CB2,CB1,CB0),
if SumB == SumA ->
- ChallengeB;
+ {ok,ChallengeB};
true ->
- ?shutdown(bad_challenge_reply)
+ {error,Data}
end;
- _ ->
- ?shutdown(no_node)
+ Err ->
+ {error,Err}
end.
send_challenge_ack(Socket, Digest) ->
@@ -539,20 +614,34 @@ recv_challenge_ack(Socket, ChallengeB, CookieA) ->
ok;
true ->
?shutdown(bad_challenge_ack)
- end;
- _ ->
- ?shutdown(bad_challenge_ack)
+ end
end.
-send_name(Socket, MyNode0, Version) ->
- send_name(Socket, MyNode0, Version, ?COMPULSORY_DFLAGS).
-send_name(Socket, MyNode0, Version, Flags) ->
+send_name(Socket, MyNode0, OurVersion, AssumedVersion) ->
+ send_name(Socket, MyNode0, OurVersion, AssumedVersion, ?COMPULSORY_DFLAGS).
+
+send_name(Socket, MyNode0, OurVersion, AssumedVersion, Flags) ->
MyNode = atom_to_list(MyNode0),
- ok = ?to_port(Socket, [<<$n,Version:16,Flags:32>>|MyNode]).
+ if (OurVersion =:= ?DIST_VER_LOW) or
+ (AssumedVersion =:= ?DIST_VER_LOW) ->
+ OurFlags = our_flags(Flags,OurVersion),
+ ok = ?to_port(Socket, [<<$n,OurVersion:16,OurFlags:32>>|MyNode]),
+ $n;
+
+ (OurVersion > ?DIST_VER_LOW) and
+ (AssumedVersion > ?DIST_VER_LOW) ->
+ Creation = erts_internal:get_creation(),
+ NameLen = length(MyNode),
+ ok = ?to_port(Socket, [<<$N, (Flags bor ?DFLAG_HANDSHAKE_23):64,
+ Creation:32,NameLen:16>>|MyNode]),
+ $N
+ end.
+
+our_flags(Flags, ?DIST_VER_LOW) ->
+ Flags;
+our_flags(Flags, OurVersion) when OurVersion > ?DIST_VER_LOW ->
+ Flags bor ?DFLAG_HANDSHAKE_23.
-%%
-%% recv_name is common for both old and new handshake.
-%%
recv_name(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok,Data} ->
@@ -561,19 +650,18 @@ recv_name(Socket) ->
?shutdown({no_node,Res})
end.
-get_name([$m,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {normal, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$h,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {hidden, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) ->
- Type = case ?u32(Flag1, Flag2, Flag3, Flag4) band ?DFLAG_PUBLISHED of
- 0 ->
- hidden;
- _ ->
- normal
- end,
- {Type, list_to_atom(OtherNode),
- ?u16(VersionA,VersionB)};
+get_name([$n, V1,V0, F3,F2,F1,F0 | OtherNode]) ->
+ <<Version:16>> = <<V1,V0>>,
+ 5 = Version,
+ <<Flags:32>> = <<F3,F2,F1,F0>>,
+ {list_to_atom(OtherNode), $n, Flags};
+get_name([$N, F7,F6,F5,F4,F3,F2,F1,F0,
+ _C3,_C2,_C1,_C0, NLen1,NLen2 | OtherNode]) ->
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ true = (Flags band ?DFLAG_HANDSHAKE_23) =/= 0,
+ <<NameLen:16>> = <<NLen1,NLen2>>,
+ NameLen = length(OtherNode),
+ {list_to_atom(OtherNode), $N, Flags};
get_name(Data) ->
?shutdown(Data).
@@ -620,6 +708,13 @@ wait_for_reg_reply(Socket, SoFar) ->
receive
{tcp, Socket, Data0} ->
case SoFar ++ Data0 of
+ [$v, Result, A, B, C, D] ->
+ case Result of
+ 0 ->
+ {alive, Socket, ?u32(A, B, C, D)};
+ _ ->
+ {error, duplicate_name}
+ end;
[$y, Result, A, B] ->
case Result of
0 ->
@@ -640,7 +735,7 @@ wait_for_reg_reply(Socket, SoFar) ->
end.
-register(NodeName, ListenSocket, VLow, VHigh) ->
+register_node(NodeName, ListenSocket, VLow, VHigh) ->
{ok,{_,TcpPort}} = inet:sockname(ListenSocket),
case do_register_node(NodeName, TcpPort, VLow, VHigh) of
{alive, Socket, _Creation} ->
diff --git a/lib/kernel/test/erpc_SUITE.erl b/lib/kernel/test/erpc_SUITE.erl
new file mode 100644
index 0000000000..3106bba879
--- /dev/null
+++ b/lib/kernel/test/erpc_SUITE.erl
@@ -0,0 +1,796 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erpc_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([call/1, call_reqtmo/1, call_against_old_node/1, cast/1,
+ send_request/1, send_request_receive_reqtmo/1,
+ send_request_wait_reqtmo/1,
+ send_request_check_reqtmo/1,
+ send_request_against_old_node/1,
+ multicall/1, multicall_reqtmo/1,
+ timeout_limit/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+-export([call_func1/1, call_func2/1, call_func4/4]).
+
+-export([f/0, f2/0]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,2}}].
+
+all() ->
+ [call, call_reqtmo, call_against_old_node, cast,
+ send_request, send_request_receive_reqtmo,
+ send_request_wait_reqtmo, send_request_check_reqtmo,
+ send_request_against_old_node,
+ multicall, multicall_reqtmo,
+ timeout_limit].
+
+groups() ->
+ [].
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ [{testcase, Func}|Config].
+
+end_per_testcase(_Func, _Config) ->
+ ok.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+call(Config) when is_list(Config) ->
+ call_test(Config).
+
+call_test(Config) ->
+ call_test(node(), 10000),
+ call_test(node(), infinity),
+ try
+ erpc:call(node(), erlang, node, [], 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ {ok, Node} = start_node(Config),
+ call_test(Node, 10000),
+ call_test(Node, infinity),
+ try
+ erpc:call(Node, erlang, node, [], 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, halt, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end.
+
+call_test(Node, Timeout) ->
+ io:format("call_test(~p, ~p)~n", [Node, Timeout]),
+ Node = erpc:call(Node, erlang, node, [], Timeout),
+ try
+ erpc:call(Node, erlang, error, [oops|invalid], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, error, [oops|invalid], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, error, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception, oops, [{erlang,error,[oops],_}]} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, exit, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{exception, oops} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, throw, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ throw:oops ->
+ ok
+ end,
+ case {node() == Node, Timeout == infinity} of
+ {true, true} ->
+ %% This would kill the test since local calls
+ %% without timeout is optimized to execute in
+ %% calling process itself...
+ ok;
+ _ ->
+ ExitSignal = fun () ->
+ exit(self(), oops),
+ receive after infinity -> ok end
+ end,
+ try
+ erpc:call(Node, erlang, apply, [ExitSignal, []], Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{signal, oops} ->
+ ok
+ end
+ end,
+ try
+ erpc:call(Node, ?MODULE, call_func1, [boom], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception,
+ {exception,
+ boom,
+ [{?MODULE, call_func3, A2, _},
+ {?MODULE, call_func2, 1, _}]},
+ [{erpc, call, A1, _},
+ {?MODULE, call_func1, 1, _}]}
+ when ((A1 == 5)
+ orelse (A1 == [Node, ?MODULE, call_func2, [boom]]))
+ andalso ((A2 == 1)
+ orelse (A2 == [boom])) ->
+ ok
+ end,
+ try
+ call_func4(Node, node(), 10, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:Error1 ->
+%%% io:format("Error1=~p~n", [Error1]),
+ check_call_func4_error(Error1, 10)
+ end,
+ try
+ call_func4(node(), Node, 5, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:Error2 ->
+%%% io:format("Error2=~p~n", [Error2]),
+ check_call_func4_error(Error2, 5)
+ end,
+ ok.
+
+check_call_func4_error({exception,
+ badarg,
+ [{?MODULE, call_func5, _, _},
+ {?MODULE, call_func4, _, _}]},
+ 0) ->
+ ok;
+check_call_func4_error({exception,
+ Exception,
+ [{erpc, call, _, _},
+ {?MODULE, call_func5, _, _},
+ {?MODULE, call_func4, _, _}]},
+ N) ->
+ check_call_func4_error(Exception, N-1).
+
+call_func1(X) ->
+ erpc:call(node(), ?MODULE, call_func2, [X]),
+ ok.
+
+call_func2(X) ->
+ call_func3(X),
+ ok.
+
+call_func3(X) ->
+ erlang:error(X, [X]).
+
+call_func4(A, B, N, T) ->
+ call_func5(A, B, N, T),
+ ok.
+
+call_func5(A, B, N, T) when N >= 0 ->
+ erpc:call(A, ?MODULE, call_func4, [B, A, N-1, T], T),
+ ok;
+call_func5(_A, _B, _N, _T) ->
+ erlang:error(badarg).
+
+call_against_old_node(Config) ->
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ try
+ erpc:call(Node22, erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ stop_node(Node22),
+ ok;
+ _ ->
+ {skipped, "No OTP 22 available"}
+ end.
+
+call_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ try
+ erpc:call(Node, erlang, send,
+ [self(), SendMe], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+reqtmo_test(Config, Test) ->
+ %% Tests that we time out in time also when the request itself
+ %% does not get through. A typical issue we have had
+ %% in the past, is that the timeout has not triggered until
+ %% the request has gotten through...
+
+ Timeout = 500,
+ WaitBlock = 100,
+ BlockTime = 1000,
+
+ {ok, Node} = start_node(Config),
+
+ erpc:call(Node, erts_debug, set_internal_state, [available_internal_state,
+ true]),
+
+ SendMe = make_ref(),
+
+ erpc:cast(Node, erts_debug, set_internal_state, [block, BlockTime]),
+ receive after WaitBlock -> ok end,
+
+ Start = erlang:monotonic_time(),
+
+ Test(Node, SendMe, Timeout),
+
+ Stop = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(Stop-Start, native, millisecond),
+ io:format("Actual time: ~p ms~n", [Time]),
+ true = Time >= Timeout,
+ true = Time =< Timeout + 200,
+
+ receive SendMe -> ok end,
+
+ receive UnexpectedMsg -> ct:fail({unexpected_message, UnexpectedMsg})
+ after 0 -> ok
+ end,
+
+ stop_node(Node),
+
+ {comment,
+ "Timeout = " ++ integer_to_list(Timeout)
+ ++ " Actual = " ++ integer_to_list(Time)}.
+
+cast(Config) when is_list(Config) ->
+ try
+ erpc:cast(node, erlang, send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), "erlang", send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), erlang, make_ref(), [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), erlang, send, [hej|hopp]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ Me = self(),
+ Ok1 = make_ref(),
+ ok = erpc:cast(node(), erlang, send, [Me, Ok1]),
+ receive
+ Ok1 -> ok
+ end,
+ {ok, Node} = start_node(Config),
+ Ok2 = make_ref(),
+ ok = erpc:cast(Node, erlang, send, [Me, Ok2]),
+ receive
+ Ok2 -> ok
+ end,
+ stop_node(Node),
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ ok = erpc:cast(Node, erlang, send, [Me, wont_reach_me]),
+ receive
+ Msg -> ct:fail({unexpected_message, Msg})
+ after
+ 2000 -> ok
+ end,
+ stop_node(Node22),
+ {comment, "Tested against OTP 22 as well"};
+ _ ->
+ {comment, "No tested against OTP 22"}
+ end.
+
+send_request(Config) when is_list(Config) ->
+ %% Note: First part of nodename sets response delay in seconds.
+ PA = filename:dirname(code:which(?MODULE)),
+ NodeArgs = [{args,"-pa "++ PA}],
+ {ok,Node1} = test_server:start_node('1_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node2} = test_server:start_node('10_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node3} = test_server:start_node('20_erpc_SUITE_call', slave, NodeArgs),
+ ReqId1 = erpc:send_request(Node1, ?MODULE, f, []),
+ ReqId2 = erpc:send_request(Node2, ?MODULE, f, []),
+ ReqId3 = erpc:send_request(Node3, ?MODULE, f, []),
+ ReqId4 = erpc:send_request(Node1, erlang, error, [bang]),
+ ReqId5 = erpc:send_request(Node1, ?MODULE, f, []),
+
+ try
+ erpc:receive_response(ReqId4, 10)
+ catch
+ error:{exception, bang, [{erlang,error,[bang],_}]} ->
+ ok
+ end,
+ try
+ erpc:receive_response(ReqId5, 10)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+
+ %% Test fast timeouts.
+ no_response = erpc:wait_response(ReqId2),
+ no_response = erpc:wait_response(ReqId2, 10),
+
+ %% Let Node1 finish its work before yielding.
+ ct:sleep({seconds,2}),
+ {hej,_,Node1} = erpc:receive_response(ReqId1),
+
+ %% Wait for the Node2 and Node3.
+ {response,{hej,_,Node2}} = erpc:wait_response(ReqId2, infinity),
+ {hej,_,Node3} = erpc:receive_response(ReqId3),
+
+ try
+ erpc:receive_response(ReqId5, 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ receive
+ Msg0 -> ct:fail(Msg0)
+ after 0 -> ok
+ end,
+
+ {ok, Node4} = start_node(Config),
+
+ ReqId6 = erpc:send_request(Node4, erlang, node, []),
+
+ no_response = erpc:check_response({response, Node1}, ReqId6),
+ no_response = erpc:check_response(ReqId6, ReqId6),
+ receive
+ Msg1 ->
+ {response, Node4} = erpc:check_response(Msg1, ReqId6)
+ end,
+
+ ReqId7 = erpc:send_request(Node4, erlang, halt, []),
+ try
+ erpc:receive_response(ReqId7),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} -> ok
+ end,
+
+ ReqId8 = erpc:send_request(Node4, erlang, node, []),
+ receive
+ Msg2 ->
+ try
+ erpc:check_response(Msg2, ReqId8),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end
+ end,
+
+ case start_22_node(Config) of
+ {ok, Node5} ->
+ ReqId9 = erpc:send_request(Node5, erlang, node, []),
+ try
+ erpc:receive_response(ReqId9),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} -> ok
+ end,
+
+ stop_node(Node5),
+ ok;
+ _ ->
+ {comment, "No test against OTP 22 node performed"}
+ end.
+
+send_request_receive_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ try
+ erpc:receive_response(RID, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_wait_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ no_response = erpc:wait_response(RID, 0),
+ no_response = erpc:wait_response(RID, Timeout),
+ %% Cleanup...
+ try
+ erpc:receive_response(RID, 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_check_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ receive Msg -> erpc:check_response(Msg, RID)
+ after Timeout -> ok
+ end,
+ %% Cleanup...
+ try
+ erpc:receive_response(RID, 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_against_old_node(Config) when is_list(Config) ->
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ RID1 = erpc:send_request(Node22, erlang, node, []),
+ RID2 = erpc:send_request(Node22, erlang, node, []),
+ RID3 = erpc:send_request(Node22, erlang, node, []),
+ try
+ erpc:receive_response(RID1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ try
+ erpc:wait_response(RID2, infinity),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ try
+ receive
+ Msg ->
+ erpc:check_response(Msg, RID3),
+ ct:fail(unexpected)
+ end
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ stop_node(Node22),
+ ok;
+ _ ->
+ {skipped, "No OTP 22 available"}
+ end.
+
+multicall(Config) ->
+ {ok, Node1} = start_node(Config),
+ {ok, Node2} = start_node(Config),
+ {Node3, Node3Res} = case start_22_node(Config) of
+ {ok, N3} ->
+ {N3, {error, {erpc, notsup}}};
+ _ ->
+ {ok, N3} = start_node(Config),
+ stop_node(N3),
+ {N3, {error, {erpc, noconnection}}}
+ end,
+ {ok, Node4} = start_node(Config),
+ {ok, Node5} = start_node(Config),
+ stop_node(Node2),
+
+ ThisNode = node(),
+ Nodes = [ThisNode, Node1, Node2, Node3, Node4, Node5],
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes, erlang, node, []),
+
+ [BlingError,
+ BlingError,
+ {error, {erpc, noconnection}},
+ Node3Res,
+ BlingError,
+ BlingError]
+ = erpc:multicall(Nodes, ?MODULE, call_func2, [bling]),
+
+ {error, {exception,
+ bling,
+ [{?MODULE, call_func3, A, _},
+ {?MODULE, call_func2, 1, _}]}} = BlingError,
+ true = (A == 1) orelse (A == [bling]),
+
+ [{error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}}]
+ = erpc:multicall(Nodes, erlang, error, [blong]),
+
+ SlowNode4 = fun () ->
+ case node() of
+ Node4 ->
+ receive after 1000 -> ok end,
+ slow;
+ ThisNode ->
+ throw(fast);
+ _ ->
+ fast
+ end
+ end,
+
+ Start1 = erlang:monotonic_time(),
+ [{throw, fast},
+ {ok, fast},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, timeout}},
+ {ok, fast}]
+ = erpc:multicall(Nodes, erlang, apply, [SlowNode4, []], 500),
+
+ End1 = erlang:monotonic_time(),
+
+ Time1 = erlang:convert_time_unit(End1-Start1, native, millisecond),
+ io:format("Time1 = ~p~n",[Time1]),
+ true = Time1 >= 500,
+ true = Time1 =< 1000,
+
+ SlowThisNode = fun () ->
+ case node() of
+ ThisNode ->
+ receive after 1000 -> ok end,
+ slow;
+ Node4 ->
+ throw(fast);
+ Node5 ->
+ exit(fast);
+ _ ->
+ fast
+ end
+ end,
+
+ Start2 = erlang:monotonic_time(),
+ [{error, {erpc, timeout}},
+ {ok, fast},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {throw, fast},
+ {exit, {exception, fast}}]
+ = erpc:multicall(Nodes, erlang, apply, [SlowThisNode, []], 500),
+
+ End2 = erlang:monotonic_time(),
+
+ Time2 = erlang:convert_time_unit(End2-Start2, native, millisecond),
+ io:format("Time2 = ~p~n",[Time2]),
+ true = Time2 >= 500,
+ true = Time2 =< 1000,
+
+ %% We should not get any stray messages due to timed out operations...
+ receive Msg -> ct:fail({unexpected, Msg})
+ after 1000 -> ok
+ end,
+
+ [{error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, noconnection}},
+ {error, {erpc, noconnection}}]
+ = erpc:multicall([Node2, Node3, Node4, Node5], erlang, halt, []),
+
+ stop_node(Node3),
+ case Node3Res of
+ {error, {erpc, notsup}} ->
+ {comment, "Tested against an OTP 22 node as well"};
+ _ ->
+ {comment, "No OTP 22 node available; i.e., only testing against current release"}
+ end.
+
+multicall_reqtmo(Config) when is_list(Config) ->
+ {ok, QuickNode1} = start_node(Config),
+ {ok, QuickNode2} = start_node(Config),
+ Fun = fun (Node, SendMe, Timeout) ->
+ Me = self(),
+ SlowSend = fun () ->
+ if node() == Node ->
+ Me ! SendMe,
+ done;
+ true ->
+ done
+ end
+ end,
+ [{ok, done},{error,{erpc,timeout}},{ok, done}]
+ = erpc:multicall([QuickNode1, Node, QuickNode2],
+ erlang, apply, [SlowSend, []],
+ Timeout)
+ end,
+ Res = reqtmo_test(Config, Fun),
+ stop_node(QuickNode1),
+ stop_node(QuickNode2),
+ Res.
+
+timeout_limit(Config) when is_list(Config) ->
+ Node = node(),
+ MaxTmo = (1 bsl 32) - 1,
+ erlang:send_after(100, self(), dummy_message),
+ try
+ receive
+ M ->
+ M
+ after MaxTmo + 1 ->
+ ok
+ end,
+ ct:fail("The ?MAX_INT_TIMEOUT define in erpc.erl needs "
+ "to be updated to reflect max timeout value "
+ "in a receive/after...")
+ catch
+ error:timeout_value ->
+ ok
+ end,
+ Node = erpc:call(Node, erlang, node, [], MaxTmo),
+ try
+ erpc:call(node(), erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ [{ok,Node}] = erpc:multicall([Node], erlang, node, [], MaxTmo),
+ try
+ erpc:multicall([Node], erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ReqId1 = erpc:send_request(Node, erlang, node, []),
+ Node = erpc:receive_response(ReqId1, MaxTmo),
+ ReqId2 = erpc:send_request(Node, erlang, node, []),
+ try
+ erpc:receive_response(ReqId2, MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ReqId3 = erpc:send_request(Node, erlang, node, []),
+ Node = erpc:receive_response(ReqId3, MaxTmo),
+ ReqId4 = erpc:send_request(Node, erlang, node, []),
+ try
+ erpc:receive_response(ReqId4, MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ok.
+
+
+%%%
+%%% Utility functions.
+%%%
+
+start_node(Config) ->
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
+
+start_22_node(Config) ->
+ Rel = "22_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ notsup;
+ true ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name,
+ peer,
+ [{args, "-pa " ++ Pa ++ " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}])
+ end.
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+flush(L) ->
+ receive
+ M ->
+ flush([M|L])
+ after 0 ->
+ L
+ end.
+
+t() ->
+ [N | _] = string:tokens(atom_to_list(node()), "_"),
+ 1000*list_to_integer(N).
+
+f() ->
+ timer:sleep(T=t()),
+ spawn(?MODULE, f2, []),
+ {hej,T,node()}.
+
+f2() ->
+ timer:sleep(500),
+ halt().
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 1c1b35abc1..f432eec708 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -58,6 +58,8 @@
-export([ file_info_basic_file/1, file_info_basic_directory/1,
file_info_bad/1, file_info_times/1, file_write_file_info/1,
file_wfi_helpers/1]).
+-export([ file_handle_info_basic_file/1, file_handle_info_basic_directory/1,
+ file_handle_info_times/1]).
-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
@@ -155,7 +157,10 @@ groups() ->
{pos, [], [pos1, pos2, pos3]},
{file_info, [],
[file_info_basic_file, file_info_basic_directory,
- file_info_bad, file_info_times, file_write_file_info,
+ file_info_bad, file_info_times,
+ file_handle_info_basic_file, file_handle_info_basic_directory,
+ file_handle_info_times,
+ file_write_file_info,
file_wfi_helpers]},
{consult, [], [consult1, path_consult]},
{eval, [], [eval1, path_eval]},
@@ -275,11 +280,11 @@ mini_server(Parent) ->
Parent ! {io_request,From,To,{put_chars,Data}},
From ! {io_reply, To, ok},
mini_server(Parent);
- {io_request,From,To,{get_chars,'',N}} ->
+ {io_request,From,To,{get_chars,_Encoding,'',N}} ->
Parent ! {io_request,From,To,{get_chars,'',N}},
From ! {io_reply, To, {ok, lists:duplicate(N,$a)}},
mini_server(Parent);
- {io_request,From,To,{get_line,''}} ->
+ {io_request,From,To,{get_line,_Encoding,''}} ->
Parent ! {io_request,From,To,{get_line,''}},
From ! {io_reply, To, {ok, "hej\n"}},
mini_server(Parent)
@@ -989,6 +994,14 @@ new_modes(Config) when is_list(Config) ->
ok
end,
+ % open directory
+ {ok, Fd9} = ?FILE_MODULE:open(NewDir, [directory]),
+ ok = ?FILE_MODULE:close(Fd9),
+
+ % open raw directory
+ {ok, Fd10} = ?FILE_MODULE:open(NewDir, [raw, directory]),
+ ok = ?FILE_MODULE:close(Fd10),
+
[] = flush(),
ok.
@@ -1238,6 +1251,9 @@ open_errors(Config) when is_list(Config) ->
{error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]),
{eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4},
+ Real = filename:join(DataDir, "realmen.html"),
+ {error, enotdir} = ?FILE_MODULE:open(Real, [directory]),
+
[] = flush(),
ok.
@@ -1408,7 +1424,8 @@ file_info_basic_directory(Config) when is_list(Config) ->
{win32, _} ->
test_directory("/", read_write),
test_directory("c:/", read_write),
- test_directory("c:\\", read_write);
+ test_directory("c:\\", read_write),
+ test_directory("\\\\localhost\\c$", read_write);
_ ->
test_directory("/", read)
end,
@@ -1540,6 +1557,180 @@ filter_atime(Atime, Config) ->
Atime
end.
+%% Test read_file_info on I/O devices.
+
+file_handle_info_basic_file(Config) when is_list(Config) ->
+ RootDir = proplists:get_value(priv_dir, Config),
+
+ %% Create a short file.
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_basic_test.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1, "foo bar"),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ %% Test that the file has the expected attributes.
+ %% The times are tricky, so we will save them to a separate test case.
+
+ {ok, Fd} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Fd),
+ ok = ?FILE_MODULE:close(Fd),
+
+ {ok, FdRaw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfoRaw} = ?FILE_MODULE:read_file_info(FdRaw),
+ ok = ?FILE_MODULE:close(FdRaw),
+
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo = FileInfoRaw,
+ io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
+ Size = 7,
+ Type = regular,
+ read_write = Access,
+ true = abs(time_dist(filter_atime(AccessTime, Config),
+ filter_atime(ModifyTime,
+ Config))) < 5,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+
+ [] = flush(),
+ ok.
+
+file_handle_info_basic_directory(Config) when is_list(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+
+ %% Test that the RootDir directory has the expected attributes.
+ test_directory_handle(RootDir, read_write),
+
+ %% Note that on Windows file systems,
+ %% "/" or "c:/" are *NOT* directories.
+ %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
+ %% directories.
+ case os:type() of
+ {win32, _} ->
+ test_directory_handle("/", read_write),
+ test_directory_handle("c:/", read_write),
+ test_directory_handle("c:\\", read_write),
+ test_directory_handle("\\\\localhost\\c$", read_write);
+ _ ->
+ test_directory_handle("/", read)
+ end,
+ ok.
+
+test_directory_handle(Name, ExpectedAccess) ->
+ {ok, DirFd} = file:open(Name, [read, directory]),
+ try
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd, [raw]),
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo,
+ io:format("Testing directory ~s", [Name]),
+ io:format("Directory size is ~p", [Size]),
+ io:format("Access ~p", [Access]),
+ io:format("Access time ~p; Modify time~p",
+ [AccessTime, ModifyTime]),
+ Type = directory,
+ Access = ExpectedAccess,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+ [] = flush(),
+ ok
+ after
+ file:close(DirFd)
+ end.
+
+%% Test that the file times behave as they should.
+
+file_handle_info_times(Config) when is_list(Config) ->
+ %% We have to try this twice, since if the test runs across the change
+ %% of a month the time diff calculations will fail. But it won't happen
+ %% if you run it twice in succession.
+ test_server:m_out_of_n(
+ 1,2,
+ fun() -> file_handle_info_int(Config) end),
+ ok.
+
+file_handle_info_int(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+ io:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_file_info.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1,"foo"),
+ {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Fd1),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ {ok,Fd1Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Fd1Raw),
+ ok = ?FILE_MODULE:close(Fd1Raw),
+
+ %% We assert that everything but the size is the same, on some OSs the
+ %% size may not have been flushed to disc and we do not want to do a
+ %% sync to force it.
+ FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size },
+
+ #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1,
+
+ Now = erlang:localtime(), %???
+ io:format("Now ~p",[Now]),
+ io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
+ true = abs(time_dist(filter_atime(Now, Config),
+ filter_atime(AccTime1,
+ Config))) < 8,
+ true = abs(time_dist(Now,ModTime1)) < 8,
+
+ %% Sleep until we can be sure the seconds value has changed.
+ %% Note: FAT-based filesystem (like on Windows 95) have
+ %% a resolution of 2 seconds.
+ timer:sleep(2200),
+
+ %% close the file, and watch the modify date change
+
+ {ok,Fd2} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Fd2),
+ ok = ?FILE_MODULE:close(Fd2),
+
+ {ok,Fd2Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo2Raw} = ?FILE_MODULE:read_file_info(Fd2Raw),
+ ok = ?FILE_MODULE:close(Fd2Raw),
+
+ #file_info{size=Size,type=regular,access=Access,
+ atime=AccTime2,mtime=ModTime2} = FileInfo2 = FileInfo2Raw,
+ io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
+ true = time_dist(ModTime1,ModTime2) >= 0,
+
+ %% this file is supposed to be binary, so it'd better keep it's size
+ Size = 3,
+ Access = read_write,
+
+ %% Do some directory checking
+
+ {ok,Fd3} = ?FILE_MODULE:open(RootDir, [read, directory]),
+ {ok,FileInfo3} = ?FILE_MODULE:read_file_info(Fd3),
+ ok = ?FILE_MODULE:close(Fd3),
+
+ {ok,Fd3Raw} = ?FILE_MODULE:open(RootDir, [read, directory, raw]),
+ {ok,FileInfo3Raw} = ?FILE_MODULE:read_file_info(Fd3Raw),
+ ok = ?FILE_MODULE:close(Fd3Raw),
+
+ #file_info{size=DSize,type=directory,access=DAccess,
+ atime=AccTime3,mtime=ModTime3} = FileInfo3 = FileInfo3Raw,
+ %% this dir was modified only a few secs ago
+ io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
+ true = abs(time_dist(Now,ModTime3)) < 5,
+ DAccess = read_write,
+ io:format("Dir size is ~p",[DSize]),
+
+ [] = flush(),
+ ok.
+
%% Test the write_file_info/2 function.
file_write_file_info(Config) when is_list(Config) ->
@@ -2042,24 +2233,33 @@ allocate_and_assert(Fd, Offset, Length) ->
allocate_file_size(Config) when is_list(Config) ->
case os:type() of
{unix, darwin} ->
- PrivDir = proplists:get_value(priv_dir, Config),
- Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"),
-
- {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]),
- ok = ?FILE_MODULE:allocate(Fd, 0, 1024),
- {ok, 1024} = ?FILE_MODULE:position(Fd, eof),
- ok = ?FILE_MODULE:close(Fd),
-
- [] = flush(),
- ok;
+ do_allocate_file_size(Config);
{unix, linux} ->
- {skip, "file:allocate/3 on Linux does not change file size"};
+ do_allocate_file_size(Config);
{win32, _} ->
{skip, "Windows does not support file:allocate/3"};
_ ->
{skip, "Support for allocate/3 is spotty in our test platform at the moment."}
end.
+do_allocate_file_size(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"),
+
+ {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]),
+ Result =
+ case ?FILE_MODULE:allocate(Fd, 0, 1024) of
+ ok ->
+ {ok, 1024} = ?FILE_MODULE:position(Fd, eof),
+ ok;
+ {error, enotsup} ->
+ {skip, "Filesystem does not support file:allocate/3"}
+ end,
+ ok = ?FILE_MODULE:close(Fd),
+
+ [] = flush(),
+ Result.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
delete(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 1be016444f..00c9dc5ed5 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -594,10 +594,13 @@ unused_ip() ->
io:format("we = ~p, unused_ip = ~p~n", [Hent, IP]),
IP.
-unused_ip(_, _, _, 255) -> error;
+unused_ip(255, 255, 255, 255) -> error;
+unused_ip(255, B, C, D) -> unused_ip(1, B + 1, C, D);
+unused_ip(A, 255, C, D) -> unused_ip(A, 1, C + 1, D);
+unused_ip(A, B, 255, D) -> unused_ip(A, B, 1, D + 1);
unused_ip(A, B, C, D) ->
case inet:gethostbyaddr({A, B, C, D}) of
- {ok, _} -> unused_ip(A, B, C, D+1);
+ {ok, _} -> unused_ip(A + 1, B, C, D);
{error, _} -> {ok, {A, B, C, D}}
end.
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 5bff9cc292..3c4654b44c 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -43,7 +43,7 @@
-export([global_load/3, lock_global/2, lock_global2/2]).
-export([]).
--export([mass_spawn/1]).
+-export([init_mass_spawn/1]).
-export([start_tracer/0, stop_tracer/0, get_trace/0]).
@@ -3887,7 +3887,7 @@ mass_death(Config) when is_list(Config) ->
io:format("Nodes: ~p~n", [Nodes]),
Ns = lists:seq(1, 40),
%% Start processes with globally registered names on the nodes
- {Pids,[]} = rpc:multicall(Nodes, ?MODULE, mass_spawn, [Ns]),
+ {Pids,[]} = rpc:multicall(Nodes, ?MODULE, init_mass_spawn, [Ns]),
io:format("Pids: ~p~n", [Pids]),
%% Wait...
ct:sleep(10000),
@@ -3924,6 +3924,11 @@ wait_mass_death(Nodes, OrigNames, Then, Config) ->
wait_mass_death(Nodes, OrigNames, Then, Config)
end.
+init_mass_spawn(N) ->
+ Pid = mass_spawn(N),
+ unlink(Pid),
+ Pid.
+
mass_spawn([]) ->
ok;
mass_spawn([N|T]) ->
@@ -3937,7 +3942,10 @@ mass_spawn([N|T]) ->
Parent ! self(),
loop()
end),
- receive Pid -> Pid end.
+ receive
+ Pid ->
+ Pid
+ end.
mass_names([], _) ->
[];
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 44ec7e7076..efe48e7e99 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -35,7 +35,8 @@
ipv4_to_ipv6/0, ipv4_to_ipv6/1,
host_and_addr/0, host_and_addr/1,
t_gethostnative/1,
- gethostnative_parallell/1, cname_loop/1, missing_hosts_reload/1,
+ gethostnative_parallell/1, cname_loop/1,
+ missing_hosts_reload/1, hosts_file_quirks/1,
gethostnative_soft_restart/0, gethostnative_soft_restart/1,
gethostnative_debug_level/0, gethostnative_debug_level/1,
lookup_bad_search_option/1,
@@ -43,6 +44,7 @@
getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1,
parse_strict_address/1, ipv4_mapped_ipv6_address/1,
simple_netns/1, simple_netns_open/1,
+ add_del_host/1, add_del_host_v6/1,
simple_bind_to_device/1, simple_bind_to_device_open/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
@@ -57,11 +59,13 @@ all() ->
[t_gethostbyaddr, t_gethostbyname, t_getaddr,
t_gethostbyaddr_v6, t_gethostbyname_v6, t_getaddr_v6,
ipv4_to_ipv6, host_and_addr, {group, parse},
- t_gethostnative, gethostnative_parallell, cname_loop, missing_hosts_reload,
+ t_gethostnative, gethostnative_parallell, cname_loop,
+ missing_hosts_reload, hosts_file_quirks,
gethostnative_debug_level, gethostnative_soft_restart,
lookup_bad_search_option,
getif, getif_ifr_name_overflow, getservbyname_overflow,
getifaddrs, parse_strict_address, simple_netns, simple_netns_open,
+ add_del_host, add_del_host_v6,
simple_bind_to_device, simple_bind_to_device_open].
groups() ->
@@ -698,6 +702,8 @@ t_gethostnative(Config) when is_list(Config) ->
{error,notfound} ->
ok;
{error,no_data} ->
+ ok;
+ {error,try_again} ->
ok
end.
@@ -868,6 +874,176 @@ missing_hosts_reload(Config) when is_list(Config) ->
% cleanup
true = test_server:stop_node(TestNode).
+
+%% The /etc/hosts file format and limitations is quite undocumented.
+%%
+%% Our implementation of the hosts file resolver tries to
+%% do the right thing. Here is an attempt to define "the right thing",
+%% and this test case tries to check most of these rules:
+%%
+%% * A hosts file consists of entries with one IP address,
+%% and a list of host names. The IP address is IPv4 or IPv6.
+%% The first host name is the primary host name
+%% and the others are aliases.
+%%
+%% * A lookup for an IP address should return one #hostent{} record
+%% with the one IP address from the query, and the host names
+%% from all entries with the same IP address concatenated.
+%% The first host name from the first hosts file entry
+%% with the requested IP address will be the primary host name
+%% and all others are aliases. All host names are returned
+%% as in the hosts file entries i.e character case is preserved.
+%%
+%% * A lookup for a host name is character case insensitive.
+%%
+%% * A lookup for a host name should return one #hostent{} record
+%% with the host name list from the first hosts file entry
+%% with an IP address of the requested address family
+%% that has a matching host name, as it is in the hosts file
+%% i.e character case is preserved. The IP addresses in the
+%% returned #hostent{} record should be the first from the
+%% same matching hosts file entry, followed by all others
+%% for which there is a matching host name and address family.
+%% There should be no duplicates among the returned IP addresses.
+%%
+%% * These rules are of the opinion that if duplicate host names
+%% with the same character casing occurs for the same IP
+%% address, it is a configuration error, so it is not tested for
+%% and there is no preferred behaviour.
+hosts_file_quirks(Config) when is_list(Config) ->
+ Records = [R1, R2, R3, R4, R5] =
+ [#hostent{
+ h_name = h_ex(Name),
+ h_aliases = [h_ex(Alias) || Alias <- Aliases],
+ h_addrtype = Fam,
+ h_length =
+ case Fam of
+ inet -> 4;
+ inet6 -> 16
+ end,
+ h_addr_list =
+ [case Fam of
+ inet -> inet_ex(N);
+ inet6 -> inet6_ex(N)
+ end]}
+ || {{Fam,N}, Name, Aliases} <-
+ [{{inet,1}, "a", ["B"]},
+ {{inet,2}, "D", []},
+ {{inet6,3}, "A", ["c"]},
+ {{inet,1}, "c", []},
+ {{inet,5}, "A", []}]],
+ true = R1#hostent.h_addr_list =:= R4#hostent.h_addr_list,
+ R14 =
+ R1#hostent{
+ h_aliases =
+ R1#hostent.h_aliases ++
+ [R4#hostent.h_name | R4#hostent.h_aliases]},
+ R145 =
+ R14#hostent{
+ h_addr_list =
+ R1#hostent.h_addr_list ++ R5#hostent.h_addr_list},
+ %%
+ RootDir = proplists:get_value(priv_dir,Config),
+ HostsFile = filename:join(RootDir, atom_to_list(?MODULE) ++ "-quirks.hosts"),
+ InetRc = filename:join(RootDir, "quirks.inetrc"),
+ ok = file:write_file(HostsFile, hostents_to_list(Records)),
+ ok = file:write_file(InetRc, "{hosts_file, \"" ++ HostsFile ++ "\"}.\n"),
+ %%
+ %% start a node
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok, TestNode} = test_server:start_node(?MODULE, slave,
+ [{args, "-pa " ++ Pa ++ " -kernel inetrc '\"" ++ InetRc ++ "\"'"}]),
+ %% ensure it has our RC
+ Rc = rpc:call(TestNode, inet_db, get_rc, []),
+ {hosts_file, HostsFile} = lists:keyfind(hosts_file, 1, Rc),
+ %%
+ %% check entries
+ io:format("Check hosts file contents~n", []),
+ V1 =
+ [{R14, inet_ex(1)},
+ {R2, inet_ex(2)},
+ {R3, inet6_ex(3)},
+ {R5, inet_ex(5)},
+ {R145, h_ex("a"), inet},
+ {R14, h_ex("b"), inet},
+ {R14, h_ex("c"), inet},
+ {R2, h_ex("d"), inet},
+ {R3, h_ex("a"), inet6},
+ {R3, h_ex("c"), inet6}
+ ],
+ hosts_file_quirks_verify(TestNode, V1),
+ %%
+ %% test add and del
+ ok =
+ rpc:call(
+ TestNode, inet_db, add_host,
+ [inet_ex(1), [h_ex("a"), h_ex("B")]]),
+ io:format("Check after add host~n", []),
+ hosts_file_quirks_verify(
+ TestNode,
+ [{R1, inet_ex(1)},
+ {R2, inet_ex(2)},
+ {R3, inet6_ex(3)},
+ {R5, inet_ex(5)},
+ {R1, h_ex("a"), inet},
+ {R1, h_ex("b"), inet},
+ {R14, h_ex("c"), inet},
+ {R2, h_ex("d"), inet},
+ {R3, h_ex("a"), inet6},
+ {R3, h_ex("c"), inet6}
+ ]),
+ ok = rpc:call(TestNode, inet_db, del_host, [inet_ex(1)]),
+ io:format("Check after del host~n", []),
+ hosts_file_quirks_verify(TestNode, V1),
+ %%
+ %% cleanup
+ true = test_server:stop_node(TestNode).
+
+hosts_file_quirks_verify(_TestNode, Vs) ->
+ hosts_file_quirks_verify(_TestNode, Vs, true).
+%%
+hosts_file_quirks_verify(_TestNode, [], Ok) ->
+ case Ok of
+ true -> ok;
+ false -> error(verify_failed)
+ end;
+hosts_file_quirks_verify(TestNode, [V | Vs], Ok) ->
+ case
+ case V of
+ {R, Addr} ->
+ {R, rpc:call(TestNode, inet_hosts, gethostbyaddr, [Addr])};
+ {R, Host, Fam} ->
+ {R, rpc:call(TestNode, inet_hosts, gethostbyname, [Host, Fam])}
+ end
+ of
+ {nxdomain, {error, nxdomain}} ->
+ hosts_file_quirks_verify(TestNode, Vs, Ok);
+ {_R_1, {error, nxdomain}} ->
+ io:format("Verify failed ~p: nxdomain~n", [V]),
+ hosts_file_quirks_verify(TestNode, Vs, false);
+ {R_1, {ok, R_1}} ->
+ hosts_file_quirks_verify(TestNode, Vs, Ok);
+ {_R_1, {ok, R_2}} ->
+ io:format("Verify failed ~p: ~p~n", [V, R_2]),
+ hosts_file_quirks_verify(TestNode, Vs, false)
+ end.
+
+%% Expand entry
+h_ex(H) -> H ++ ".example.com".
+inet_ex(N) -> {127,17,17,N}.
+inet6_ex(N) -> {0,0,0,0,17,17,17,N}.
+
+hostents_to_list([]) -> [];
+hostents_to_list([R | Rs]) ->
+ #hostent{
+ h_name = Name,
+ h_aliases = Aliases,
+ h_addr_list = [IP]} = R,
+ [inet:ntoa(IP), $\t,
+ lists:join($\s, [Name | Aliases]), $\r, $\n
+ | hostents_to_list(Rs)].
+
+
%% These must be run in the whole suite since they need
%% the host list and require inet_gethost_native to be started.
%%
@@ -1413,3 +1589,46 @@ jog_bind_to_device_opt(S) ->
ok = inet:setopts(S, [{bind_to_device,<<"lo">>}]),
{ok,[{bind_to_device,<<"lo">>}]} = inet:getopts(S, [bind_to_device]),
ok.
+
+add_del_host(_Config) ->
+ Name = "foo.com",
+ Alias = "bar.org",
+ Ip = {69,89,31,226},
+ HostEnt = #hostent{
+ h_name = Name,
+ h_aliases = [Alias],
+ h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [Ip]
+ },
+ {error, nxdomain} = inet_hosts:gethostbyname(Name, inet),
+ ok = inet_db:add_host(Ip, [Name, Alias]),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Name, inet),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Alias, inet),
+ ok = inet_db:del_host(Ip),
+ {error, nxdomain} = inet_hosts:gethostbyname(Name, inet),
+ {error, nxdomain} = inet_hosts:gethostbyname(Alias, inet),
+ ok = inet_db:add_host(Ip, [Name, Alias]),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Name, inet).
+
+add_del_host_v6(_Config) ->
+ Name = "foo.com",
+ Alias = "bar.org",
+ Ip = {32,1,219,8,10,11,18,240},
+ HostEnt = #hostent{
+ h_name = Name,
+ h_aliases = [Alias],
+ h_addrtype = inet6,
+ h_length = 16,
+ h_addr_list = [Ip]
+ },
+ {error, nxdomain} = inet_hosts:gethostbyname(Name, inet6),
+ ok = inet_db:add_host(Ip, [Name, Alias]),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Name, inet6),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Alias, inet6),
+ ok = inet_db:del_host(Ip),
+ {error, nxdomain} = inet_hosts:gethostbyname(Name, inet6),
+ {error, nxdomain} = inet_hosts:gethostbyname(Alias, inet6),
+ ok = inet_db:add_host(Ip, [Name, Alias]),
+ {ok, HostEnt} = inet_hosts:gethostbyname(Name, inet6).
+
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 9d35611d93..ceee387289 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -25,7 +25,7 @@
init_per_group/2,end_per_group/2]).
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
- many_restarts/0, many_restarts/1,
+ many_restarts/0, many_restarts/1, restart_with_mode/1,
get_plain_arguments/1,
reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
find_system_processes/0]).
@@ -48,7 +48,7 @@ suite() ->
all() ->
[get_arguments, get_argument, boot_var,
- many_restarts,
+ many_restarts, restart_with_mode,
get_plain_arguments, restart, stop_status, get_status, script_id,
{group, boot}].
@@ -348,6 +348,22 @@ wait_for(N,Node,EHPid) ->
wait_for(N-1,Node,EHPid)
end.
+restart_with_mode(Config) when is_list(Config) ->
+ %% We cannot use loose_node because it doesn't run in
+ %% embedded mode so we quickly start one that exits after restarting
+ {ok,[[Erl]]} = init:get_argument(progname),
+ ModPath = filename:dirname(code:which(?MODULE)),
+
+ Eval1 = "'Mode=code:get_mode(), io:fwrite(Mode), case Mode of interactive -> init:restart([{mode,embedded}]); embedded -> erlang:halt() end'",
+ Cmd1 = Erl ++ " -mode interactive -noshell -eval " ++ Eval1,
+ "interactiveembedded" = os:cmd(Cmd1),
+
+ Eval2 = "'Mode=code:get_mode(), io:fwrite(Mode), case Mode of embedded -> init:restart([{mode,interactive}]); interactive -> erlang:halt() end'",
+ Cmd2 = Erl ++ " -mode embedded -noshell -eval " ++ Eval2,
+ "embeddedinteractive" = os:cmd(Cmd2),
+
+ ok.
+
%% ------------------------------------------------
%% Slave executes erlang:halt() on master nodedown.
%% Therefore the slave process must be killed
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index 173e25c520..883abda067 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -22,7 +22,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
get_columns_and_rows/1, exit_initial/1, job_control_local/1,
- job_control_remote/1,
+ job_control_remote/1,stop_during_init/1,
job_control_remote_noshell/1,ctrl_keys/1,
get_columns_and_rows_escript/1]).
@@ -44,7 +44,7 @@ all() ->
[get_columns_and_rows_escript,get_columns_and_rows,
exit_initial, job_control_local,
job_control_remote, job_control_remote_noshell,
- ctrl_keys].
+ ctrl_keys, stop_during_init].
groups() ->
[].
@@ -205,6 +205,22 @@ exit_initial(Config) when is_list(Config) ->
{getline_re,"35"}],[])
end.
+stop_during_init(Config) when is_list(Config) ->
+ case get_progs() of
+ {error,_Reason} ->
+ {skip,"No runerl present"};
+ {RunErl,_ToErl,Erl} ->
+ case create_tempdir() of
+ {error, Reason2} ->
+ {skip, Reason2};
+ Tempdir ->
+ XArg = " -kernel shell_history true -s init stop",
+ start_runerl_command(RunErl, Tempdir, "\\\""++Erl++"\\\""++XArg),
+ {ok, Binary} = file:read_file(filename:join(Tempdir, "erlang.log.1")),
+ nomatch = binary:match(Binary, <<"*** ERROR: Shell process terminated! ***">>)
+ end
+ end.
+
%% Tests that local shell can be started by means of job control.
job_control_local(Config) when is_list(Config) ->
case proplists:get_value(default_shell,Config) of
@@ -656,10 +672,10 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) ->
end)++
" -setcookie "++atom_to_list(erlang:get_cookie())
end,
- spawn(fun() ->
- os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++
- Erl++XArg++"\"")
- end).
+ spawn(fun() -> start_runerl_command(RunErl, Tempdir, Erl++XArg) end).
+
+start_runerl_command(RunErl, Tempdir, Cmd) ->
+ os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++Cmd++"\"").
start_toerl_server(ToErl,Tempdir) ->
Pid = spawn(?MODULE,toerl_server,[self(),ToErl,Tempdir]),
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 3e5ed855b5..1a88c70963 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -23,6 +23,7 @@
-module(kernel_SUITE).
-include_lib("common_test/include/ct.hrl").
+-compile(r21).
%% Test server specific exports
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
@@ -106,7 +107,7 @@ appup_tests(App,{OkVsns0,NokVsns}) ->
create_test_vsns(App) ->
ThisMajor = erlang:system_info(otp_release),
FirstMajor = previous_major(ThisMajor),
- SecondMajor = previous_major(FirstMajor),
+ SecondMajor = previous_major(previous_major(FirstMajor)),
Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index f8f3d27778..f9dce3b61e 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -276,7 +276,7 @@ change_config(_Config) ->
logger:get_primary_config(),
3 = maps:size(PC1),
%% Check that internal 'handlers' field has not been changed
- MS = [{{{?HANDLER_KEY,'$1'},'_','_'},[],['$1']}],
+ MS = [{{{?HANDLER_KEY,'$1'},'_'},[],['$1']}],
HIds1 = lists:sort(ets:select(?LOGGER_TABLE,MS)), % dirty, internal data
HIds2 = lists:sort(logger:get_handler_ids()),
HIds1 = HIds2,
@@ -478,14 +478,15 @@ set_application_level(cleanup,_Config) ->
ok.
cache_module_level(_Config) ->
- ok = logger:unset_module_level(?MODULE),
- [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config?
+
+ %% This test does a lot of whitebox tests so be prepared for that
+ persistent_term:erase({logger_config,?MODULE}),
+
+ primary = persistent_term:get({logger_config,?MODULE}, primary),
?LOG_NOTICE(?map_rep),
- %% Caching is done asynchronously, so wait a bit for the update
- timer:sleep(100),
- [_] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config?
- ok = logger:unset_module_level(?MODULE),
- [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config?
+ 5 = persistent_term:get({logger_config,?MODULE}, primary),
+ logger:set_primary_config(level, info),
+ 6 = persistent_term:get({logger_config,?MODULE}, primary),
ok.
cache_module_level(cleanup,_Config) ->
@@ -569,6 +570,7 @@ filter_failed(cleanup,_Config) ->
ok.
handler_failed(_Config) ->
+ logger:set_primary_config(level,all),
register(callback_receiver,self()),
{error,{invalid_id,1}} = logger:add_handler(1,?MODULE,#{}),
{error,{invalid_module,"nomodule"}} = logger:add_handler(h1,"nomodule",#{}),
@@ -612,7 +614,7 @@ handler_failed(_Config) ->
logger:add_handler(h1,?MODULE,#{add_call=>KillHandler}),
check_no_log(),
- ok = logger:add_handler(h1,?MODULE,#{}),
+ ok = logger:add_handler(h1,?MODULE,#{tc_proc=>self()}),
{error,{attempting_syncronous_call_to_self,_}} =
logger:set_handler_config(h1,#{conf_call=>CallAddHandler}),
{error,{callback_crashed,_}} =
@@ -628,7 +630,8 @@ handler_failed(_Config) ->
logger:set_handler_config(h1,conf_call,KillHandler),
ok = logger:remove_handler(h1),
- [add,remove] = test_server:messages_get(),
+ [add,{#{level:=error},_},{#{level:=error},_},
+ {#{level:=error},_},{#{level:=error},_},remove] = test_server:messages_get(),
check_no_log(),
ok = logger:add_handler(h1,?MODULE,#{rem_call=>CallAddHandler}),
@@ -644,6 +647,7 @@ handler_failed(_Config) ->
handler_failed(cleanup,_Config) ->
logger:remove_handler(h1),
logger:remove_handler(h2),
+ logger:set_primary_config(level,info),
ok.
config_sanity_check(_Config) ->
@@ -1146,7 +1150,7 @@ kernel_config(Config) ->
ok.
-pretty_print(Config) ->
+pretty_print(_Config) ->
ok = logger:add_handler(?FUNCTION_NAME,logger_std_h,#{}),
ok = logger:set_module_level([module1,module2],debug),
@@ -1280,11 +1284,21 @@ test_api(Level) ->
ok = check_logged(Level,#{Level=>rep},#{my=>meta}),
logger:Level("~w: ~w",[Level,fa]),
ok = check_logged(Level,"~w: ~w",[Level,fa],#{}),
+ logger:Level('~w: ~w',[Level,fa]),
+ ok = check_logged(Level,'~w: ~w',[Level,fa],#{}),
+ logger:Level(<<"~w: ~w">>,[Level,fa]),
+ ok = check_logged(Level,<<"~w: ~w">>,[Level,fa],#{}),
logger:Level("~w: ~w ~w",[Level,fa,meta],#{my=>meta}),
ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}),
logger:Level(fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end,x,
#{my=>meta}),
ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}),
+ logger:Level(fun(x) -> {<<"~w: ~w ~w">>,[Level,fun_to_fa,meta]} end,x,
+ #{my=>meta}),
+ ok = check_logged(Level,<<"~w: ~w ~w">>,[Level,fun_to_fa,meta],#{my=>meta}),
+ logger:Level(fun(x) -> {'~w: ~w ~w',[Level,fun_to_fa,meta]} end,x,
+ #{my=>meta}),
+ ok = check_logged(Level,'~w: ~w ~w',[Level,fun_to_fa,meta],#{my=>meta}),
logger:Level(fun(x) -> #{Level=>fun_to_r,meta=>true} end,x,
#{my=>meta}),
ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}),
@@ -1310,6 +1324,12 @@ test_log_function(Level) ->
logger:log(Level,fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end,
x, #{my=>meta}),
ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}),
+ logger:log(Level,fun(x) -> {<<"~w: ~w ~w">>,[Level,fun_to_fa,meta]} end,
+ x, #{my=>meta}),
+ ok = check_logged(Level,<<"~w: ~w ~w">>,[Level,fun_to_fa,meta],#{my=>meta}),
+ logger:log(Level,fun(x) -> {'~w: ~w ~w',[Level,fun_to_fa,meta]} end,
+ x, #{my=>meta}),
+ ok = check_logged(Level,'~w: ~w ~w',[Level,fun_to_fa,meta],#{my=>meta}),
logger:log(Level,fun(x) -> #{Level=>fun_to_r,meta=>true} end,
x, #{my=>meta}),
ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}),
diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl
index 83e3e6c40a..5217f76cbc 100644
--- a/lib/kernel/test/logger_formatter_SUITE.erl
+++ b/lib/kernel/test/logger_formatter_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -568,6 +568,11 @@ format_mfa(_Config) ->
ct:log(String4),
"othermfa" = String4,
+ Meta5 = #{mfa=>{'m o d','a\x{281}b',[' ']}},
+ String5 = format(info,{"~p",[term]},Meta5,#{template=>Template}),
+ ct:log(String5),
+ "'m o d':'a\x{281}b'/1" = String5,
+
ok.
format_time(_Config) ->
diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl
index c3cab07d81..0e46ec3ee3 100644
--- a/lib/kernel/test/logger_legacy_SUITE.erl
+++ b/lib/kernel/test/logger_legacy_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -137,9 +137,9 @@ gen_event(_Config) ->
ok = gen_event:add_handler(Pid,?MODULE,gen_event),
Msg = fun() -> erlang:error({badmatch,b}) end,
Pid ! Msg,
- ?check({warning_msg,"** Undefined handle_info in ~tp"++_,[?MODULE,Msg]}),
+ ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}),
gen_event:notify(Pid,Msg),
- ?check({error,"** gen_event handler ~p crashed."++_,
+ ?check({error,"** gen_event handler ~tp crashed."++_,
[?MODULE,Pid,Msg,gen_event,{{badmatch,b},_}]}).
gen_fsm(_Config) ->
diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl
index 602a79c78b..ec0e5122e8 100644
--- a/lib/kernel/test/logger_std_h_SUITE.erl
+++ b/lib/kernel/test/logger_std_h_SUITE.erl
@@ -305,6 +305,8 @@ formatter_fail(Config) ->
Dir = ?config(priv_dir,Config),
Log = filename:join(Dir,?FUNCTION_NAME),
+ logger:set_primary_config(level,all),
+
%% no formatter
ok = logger:add_handler(?MODULE,
logger_std_h,
@@ -346,6 +348,7 @@ formatter_fail(Config) ->
ok.
formatter_fail(cleanup,_Config) ->
+ logger:set_primary_config(level,info),
logger:remove_handler(?MODULE).
config_fail(_Config) ->
@@ -2200,10 +2203,14 @@ check_tracer(T,TimeoutFun) ->
TimeoutFun()
end.
-escape([$+|Rest]) ->
- [$\\,$+|escape(Rest)];
-escape([H|T]) ->
- [H|escape(T)];
+escape([C|Rest]) ->
+ %% The characters that have to be escaped in a regex
+ case lists:member(C,"[-[\]{}()*+?.,\\^$|#\s]") of
+ true ->
+ [$\\,C|escape(Rest)];
+ false ->
+ [C|escape(Rest)]
+ end;
escape([]) ->
[].
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 710b9b115c..e952dec625 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -324,14 +324,18 @@ close_stdin(Config) ->
"-1" = os:cmd(Fds).
max_size_command(_Config) ->
+ WSL = case os:getenv("WSLENV") of
+ false -> "";
+ _ -> "wsl "
+ end,
- Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }),
+ Res20 = os:cmd(WSL ++ "cat /dev/zero", #{ max_size => 20 }),
20 = length(Res20),
- Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }),
+ Res0 = os:cmd(WSL ++ "cat /dev/zero", #{ max_size => 0 }),
0 = length(Res0),
- Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
+ Res32768 = os:cmd(WSL ++ "cat /dev/zero", #{ max_size => 32768 }),
32768 = length(Res32768),
ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })),
diff --git a/lib/kernel/test/pg_SUITE.erl b/lib/kernel/test/pg_SUITE.erl
new file mode 100644
index 0000000000..bdb7abe99d
--- /dev/null
+++ b/lib/kernel/test/pg_SUITE.erl
@@ -0,0 +1,619 @@
+%%
+%%
+%% Copyright WhatsApp Inc. and its affiliates. All rights reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%%-------------------------------------------------------------------
+%% @author Maxim Fedorov <maximfca@gmail.com>
+%% Process Groups smoke test.
+-module(pg_SUITE).
+-author("maximfca@gmail.com").
+
+%% Test server callbacks
+-export([
+ suite/0,
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ stop_proc/1
+]).
+
+%% Test cases exports
+-export([
+ pg/0, pg/1,
+ errors/0, errors/1,
+ leave_exit_race/0, leave_exit_race/1,
+ single/0, single/1,
+ two/1,
+ thundering_herd/0, thundering_herd/1,
+ initial/1,
+ netsplit/1,
+ trisplit/1,
+ foursplit/1,
+ exchange/1,
+ nolocal/1,
+ double/1,
+ scope_restart/1,
+ missing_scope_join/1,
+ disconnected_start/1,
+ forced_sync/0, forced_sync/1,
+ group_leave/1
+]).
+
+-export([
+ control/1,
+ controller/3
+]).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
+
+suite() ->
+ [{timetrap, {seconds, 10}}].
+
+init_per_suite(Config) ->
+ case erlang:is_alive() of
+ false ->
+ %% verify epmd running (otherwise next call fails)
+ (erl_epmd:names("localhost") =:= {error, address}) andalso ([] = os:cmd("epmd -daemon")),
+ %% start a random node name
+ NodeName = list_to_atom(lists:concat([atom_to_list(?MODULE), "_", os:getpid()])),
+ {ok, Pid} = net_kernel:start([NodeName, shortnames]),
+ [{distribution, Pid} | Config];
+ true ->
+ Config
+ end.
+
+end_per_suite(Config) ->
+ is_pid(proplists:get_value(distribution, Config)) andalso net_kernel:stop().
+
+init_per_testcase(TestCase, Config) ->
+ {ok, _Pid} = pg:start_link(TestCase),
+ Config.
+
+end_per_testcase(TestCase, _Config) ->
+ gen_server:stop(TestCase),
+ ok.
+
+all() ->
+ [{group, basic}, {group, cluster}, {group, performance}].
+
+groups() ->
+ [
+ {basic, [parallel], [errors, pg, leave_exit_race, single]},
+ {performance, [sequential], [thundering_herd]},
+ {cluster, [parallel], [two, initial, netsplit, trisplit, foursplit,
+ exchange, nolocal, double, scope_restart, missing_scope_join,
+ disconnected_start, forced_sync, group_leave]}
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+
+pg() ->
+ [{doc, "This test must be names pg, to stay inline with default scope"}].
+
+pg(_Config) ->
+ ?assertNotEqual(undefined, whereis(?FUNCTION_NAME)), %% ensure scope was started
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, self())),
+ ?assertEqual([self()], pg:get_local_members(?FUNCTION_NAME)),
+ ?assertEqual([?FUNCTION_NAME], pg:which_groups()),
+ ?assertEqual([?FUNCTION_NAME], pg:which_local_groups()),
+ ?assertEqual(ok, pg:leave(?FUNCTION_NAME, self())),
+ ?assertEqual([], pg:get_members(?FUNCTION_NAME)),
+ ?assertEqual([], pg:which_groups(?FUNCTION_NAME)),
+ ?assertEqual([], pg:which_local_groups(?FUNCTION_NAME)).
+
+errors() ->
+ [{doc, "Tests that errors are handled as expected, for example pg server crashes when it needs to"}].
+
+errors(_Config) ->
+ %% kill with 'info' and 'cast'
+ ?assertException(error, badarg, pg:handle_info(garbage, garbage)),
+ ?assertException(error, badarg, pg:handle_cast(garbage, garbage)),
+ %% kill with call
+ {ok, _Pid} = pg:start(second),
+ ?assertException(exit, {{badarg, _}, _}, gen_server:call(second, garbage, 100)).
+
+leave_exit_race() ->
+ [{doc, "Tests that pg correctly handles situation when leave and 'DOWN' messages are both in pg queue"}].
+
+leave_exit_race(Config) when is_list(Config) ->
+ process_flag(priority, high),
+ [
+ begin
+ Pid = spawn(fun () -> ok end),
+ pg:join(leave_exit_race, test, Pid),
+ pg:leave(leave_exit_race, test, Pid)
+ end
+ || _ <- lists:seq(1, 100)].
+
+single() ->
+ [{doc, "Tests single node groups"}, {timetrap, {seconds, 5}}].
+
+single(Config) when is_list(Config) ->
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, self())),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, [self(), self()])),
+ ?assertEqual([self(), self(), self()], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ?assertEqual([self(), self(), self()], pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ?assertEqual(not_joined, pg:leave(?FUNCTION_NAME, '$missing$', self())),
+ ?assertEqual(ok, pg:leave(?FUNCTION_NAME, ?FUNCTION_NAME, [self(), self()])),
+ ?assertEqual(ok, pg:leave(?FUNCTION_NAME, ?FUNCTION_NAME, self())),
+ ?assertEqual([], pg:which_groups(?FUNCTION_NAME)),
+ ?assertEqual([], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ?assertEqual([], pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ %% double
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, self())),
+ Pid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, Pid)),
+ Expected = lists:sort([Pid, self()]),
+ ?assertEqual(Expected, lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ ?assertEqual(Expected, lists:sort(pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+
+ stop_proc(Pid),
+ sync(?FUNCTION_NAME),
+ ?assertEqual([self()], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ?assertEqual(ok, pg:leave(?FUNCTION_NAME, ?FUNCTION_NAME, self())),
+ ok.
+
+two(Config) when is_list(Config) ->
+ {TwoPeer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ Pid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, Pid)),
+ ?assertEqual([Pid], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ %% first RPC must be serialised
+ sync({?FUNCTION_NAME, TwoPeer}),
+ ?assertEqual([Pid], rpc:call(TwoPeer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+ ?assertEqual([], rpc:call(TwoPeer, pg, get_local_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+ stop_proc(Pid),
+ %% again, must be serialised
+ sync(?FUNCTION_NAME),
+ ?assertEqual([], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ?assertEqual([], rpc:call(TwoPeer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+
+ Pid2 = erlang:spawn(TwoPeer, forever()),
+ Pid3 = erlang:spawn(TwoPeer, forever()),
+ ?assertEqual(ok, rpc:call(TwoPeer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, Pid2])),
+ ?assertEqual(ok, rpc:call(TwoPeer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, Pid3])),
+ %% serialise through the *other* node
+ sync({?FUNCTION_NAME, TwoPeer}),
+ ?assertEqual(lists:sort([Pid2, Pid3]),
+ lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ %% stop the peer
+ stop_node(TwoPeer, Socket),
+ %% hope that 'nodedown' comes before we route our request
+ sync(?FUNCTION_NAME),
+ ?assertEqual([], pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ ok.
+
+thundering_herd() ->
+ [{doc, "Thousands of overlay network nodes sending sync to us, and we time out!"}, {timetrap, {seconds, 5}}].
+
+thundering_herd(Config) when is_list(Config) ->
+ GroupCount = 10000,
+ SyncCount = 2000,
+ %% make up a large amount of groups
+ [pg:join(?FUNCTION_NAME, {group, Seq}, self()) || Seq <- lists:seq(1, GroupCount)],
+ %% initiate a few syncs - and those are really slow...
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ PeerPid = erlang:spawn(Peer, forever()),
+ PeerPg = rpc:call(Peer, erlang, whereis, [?FUNCTION_NAME], 1000),
+ %% WARNING: code below acts for white-box! %% WARNING
+ FakeSync = [{{group, 1}, [PeerPid, PeerPid]}],
+ [gen_server:cast(?FUNCTION_NAME, {sync, PeerPg, FakeSync}) || _ <- lists:seq(1, SyncCount)],
+ %% next call must not timetrap, otherwise test fails
+ pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, self()),
+ stop_node(Peer, Socket).
+
+initial(Config) when is_list(Config) ->
+ Pid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, Pid)),
+ ?assertEqual([Pid], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ %% first RPC must be serialised
+ sync({?FUNCTION_NAME, Peer}),
+ ?assertEqual([Pid], rpc:call(Peer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+
+ ?assertEqual([], rpc:call(Peer, pg, get_local_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+ stop_proc(Pid),
+ sync({?FUNCTION_NAME, Peer}),
+ ?assertEqual([], rpc:call(Peer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+ stop_node(Peer, Socket),
+ ok.
+
+netsplit(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ ?assertEqual(Peer, rpc(Socket, erlang, node, [])), %% just to test RPC
+ RemoteOldPid = erlang:spawn(Peer, forever()),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, '$invisible', RemoteOldPid])),
+ %% hohoho, partition!
+ net_kernel:disconnect(Peer),
+ ?assertEqual(Peer, rpc(Socket, erlang, node, [])), %% just to ensure RPC still works
+ RemotePid = rpc(Socket, erlang, spawn, [forever()]),
+ ?assertEqual([], rpc(Socket, erlang, nodes, [])),
+ ?assertEqual(ok, rpc(Socket, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])), %% join - in a partition!
+
+ ?assertEqual(ok, rpc(Socket, pg, leave, [?FUNCTION_NAME, '$invisible', RemoteOldPid])),
+ ?assertEqual(ok, rpc(Socket, pg, join, [?FUNCTION_NAME, '$visible', RemoteOldPid])),
+ ?assertEqual([RemoteOldPid], rpc(Socket, pg, get_local_members, [?FUNCTION_NAME, '$visible'])),
+ %% join locally too
+ LocalPid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, LocalPid)),
+
+ ?assertNot(lists:member(Peer, nodes())), %% should be no nodes in the cluster
+
+ pong = net_adm:ping(Peer),
+ %% now ensure sync happened
+ Pids = lists:sort([RemotePid, LocalPid]),
+ sync({?FUNCTION_NAME, Peer}),
+ ?assertEqual(Pids, lists:sort(rpc:call(Peer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME]))),
+ ?assertEqual([RemoteOldPid], pg:get_members(?FUNCTION_NAME, '$visible')),
+ stop_node(Peer, Socket),
+ ok.
+
+trisplit(Config) when is_list(Config) ->
+ {Peer, Socket1} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ _PeerPid1 = erlang:spawn(Peer, forever()),
+ PeerPid2 = erlang:spawn(Peer, forever()),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, three, PeerPid2])),
+ net_kernel:disconnect(Peer),
+ ?assertEqual(true, net_kernel:connect_node(Peer)),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, one, PeerPid2])),
+ %% now ensure sync happened
+ {Peer2, Socket2} = spawn_node(?FUNCTION_NAME, trisplit_second),
+ ?assertEqual(true, rpc:call(Peer2, net_kernel, connect_node, [Peer])),
+ ?assertEqual(lists:sort([node(), Peer]), lists:sort(rpc:call(Peer2, erlang, nodes, []))),
+ sync({?FUNCTION_NAME, Peer2}),
+ ?assertEqual([PeerPid2], rpc:call(Peer2, pg, get_members, [?FUNCTION_NAME, one])),
+ stop_node(Peer, Socket1),
+ stop_node(Peer2, Socket2),
+ ok.
+
+foursplit(Config) when is_list(Config) ->
+ Pid = erlang:spawn(forever()),
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, one, Pid)),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, two, Pid)),
+ PeerPid1 = spawn(Peer, forever()),
+ ?assertEqual(ok, pg:leave(?FUNCTION_NAME, one, Pid)),
+ ?assertEqual(not_joined, pg:leave(?FUNCTION_NAME, three, Pid)),
+ net_kernel:disconnect(Peer),
+ ?assertEqual(ok, rpc(Socket, ?MODULE, stop_proc, [PeerPid1])),
+ ?assertEqual(not_joined, pg:leave(?FUNCTION_NAME, three, Pid)),
+ ?assertEqual(true, net_kernel:connect_node(Peer)),
+ ?assertEqual([], pg:get_members(?FUNCTION_NAME, one)),
+ ?assertEqual([], rpc(Socket, pg, get_members, [?FUNCTION_NAME, one])),
+ stop_node(Peer, Socket),
+ ok.
+
+exchange(Config) when is_list(Config) ->
+ {Peer1, Socket1} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ {Peer2, Socket2} = spawn_node(?FUNCTION_NAME, exchange_second),
+ Pids10 = [rpc(Socket1, erlang, spawn, [forever()]) || _ <- lists:seq(1, 10)],
+ Pids2 = [rpc(Socket2, erlang, spawn, [forever()]) || _ <- lists:seq(1, 10)],
+ Pids11 = [rpc(Socket1, erlang, spawn, [forever()]) || _ <- lists:seq(1, 10)],
+ %% kill first 3 pids from node1
+ {PidsToKill, Pids1} = lists:split(3, Pids10),
+
+ ?assertEqual(ok, rpc(Socket1, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, Pids10])),
+ sync({?FUNCTION_NAME, Peer1}),
+ ?assertEqual(lists:sort(Pids10), lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ [rpc(Socket1, ?MODULE, stop_proc, [Pid]) || Pid <- PidsToKill],
+ sync(?FUNCTION_NAME),
+ sync({?FUNCTION_NAME, Peer1}),
+
+ Pids = lists:sort(Pids1 ++ Pids2 ++ Pids11),
+ ?assert(lists:all(fun erlang:is_pid/1, Pids)),
+
+ net_kernel:disconnect(Peer1),
+ net_kernel:disconnect(Peer2),
+
+ sync(?FUNCTION_NAME),
+ ?assertEqual([], lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+
+ [?assertEqual(ok, rpc(Socket2, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, Pid])) || Pid <- Pids2],
+ [?assertEqual(ok, rpc(Socket1, pg, join, [?FUNCTION_NAME, second, Pid])) || Pid <- Pids11],
+ ?assertEqual(ok, rpc(Socket1, pg, join, [?FUNCTION_NAME, third, Pids11])),
+ %% rejoin
+ ?assertEqual(true, net_kernel:connect_node(Peer1)),
+ ?assertEqual(true, net_kernel:connect_node(Peer2)),
+ %% need to sleep longer to ensure both nodes made the exchange
+ sync(?FUNCTION_NAME),
+ sync({?FUNCTION_NAME, Peer1}),
+ sync({?FUNCTION_NAME, Peer2}),
+ ?assertEqual(Pids, lists:sort(pg:get_members(?FUNCTION_NAME, second) ++ pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ ?assertEqual(lists:sort(Pids11), lists:sort(pg:get_members(?FUNCTION_NAME, third))),
+
+ {Left, Stay} = lists:split(3, Pids11),
+ ?assertEqual(ok, rpc(Socket1, pg, leave, [?FUNCTION_NAME, third, Left])),
+ sync({?FUNCTION_NAME, Peer1}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual(lists:sort(Stay), lists:sort(pg:get_members(?FUNCTION_NAME, third))),
+ ?assertEqual(not_joined, rpc(Socket1, pg, leave, [?FUNCTION_NAME, left, Stay])),
+ ?assertEqual(ok, rpc(Socket1, pg, leave, [?FUNCTION_NAME, third, Stay])),
+ sync({?FUNCTION_NAME, Peer1}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual([], lists:sort(pg:get_members(?FUNCTION_NAME, third))),
+ sync({?FUNCTION_NAME, Peer1}),
+ sync(?FUNCTION_NAME),
+
+ stop_node(Peer1, Socket1),
+ stop_node(Peer2, Socket2),
+ ok.
+
+nolocal(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ RemotePid = spawn(Peer, forever()),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])),
+ ?assertEqual([], pg:get_local_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ stop_node(Peer, Socket),
+ ok.
+
+double(Config) when is_list(Config) ->
+ Pid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, Pid)),
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, [Pid])),
+ ?assertEqual([Pid, Pid], pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ sync(?FUNCTION_NAME),
+ sync({?FUNCTION_NAME, Peer}),
+ ?assertEqual([Pid, Pid], rpc:call(Peer, pg, get_members, [?FUNCTION_NAME, ?FUNCTION_NAME])),
+ stop_node(Peer, Socket),
+ ok.
+
+scope_restart(Config) when is_list(Config) ->
+ Pid = erlang:spawn(forever()),
+ ?assertEqual(ok, pg:join(?FUNCTION_NAME, ?FUNCTION_NAME, [Pid, Pid])),
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ RemotePid = spawn(Peer, forever()),
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])),
+ sync({?FUNCTION_NAME, Peer}),
+ ?assertEqual(lists:sort([RemotePid, Pid, Pid]), lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ %% stop scope locally, and restart
+ gen_server:stop(?FUNCTION_NAME),
+ pg:start(?FUNCTION_NAME),
+ %% ensure remote pids joined, local are missing
+ sync(?FUNCTION_NAME),
+ sync({?FUNCTION_NAME, Peer}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual([RemotePid], pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME)),
+ stop_node(Peer, Socket),
+ ok.
+
+missing_scope_join(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ ?assertEqual(ok, rpc:call(Peer, gen_server, stop, [?FUNCTION_NAME])),
+ RemotePid = spawn(Peer, forever()),
+ ?assertMatch({badrpc, {'EXIT', {noproc, _}}}, rpc:call(Peer, pg, join, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])),
+ ?assertMatch({badrpc, {'EXIT', {noproc, _}}}, rpc:call(Peer, pg, leave, [?FUNCTION_NAME, ?FUNCTION_NAME, RemotePid])),
+ stop_node(Peer, Socket),
+ ok.
+
+disconnected_start(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ net_kernel:disconnect(Peer),
+ ?assertEqual(ok, rpc(Socket, gen_server, stop, [?FUNCTION_NAME])),
+ ?assertMatch({ok, _Pid}, rpc(Socket, pg, start,[?FUNCTION_NAME])),
+ ?assertEqual(ok, rpc(Socket, gen_server, stop, [?FUNCTION_NAME])),
+ RemotePid = rpc(Socket, erlang, spawn, [forever()]),
+ ?assert(is_pid(RemotePid)),
+ stop_node(Peer, Socket),
+ ok.
+
+forced_sync() ->
+ [{doc, "This test was added when lookup_element was erroneously used instead of lookup, crashing pg with badmatch, and it tests rare out-of-order sync operations"}].
+
+forced_sync(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ Pid = erlang:spawn(forever()),
+ RemotePid = spawn(Peer, forever()),
+ Expected = lists:sort([Pid, RemotePid]),
+ pg:join(?FUNCTION_NAME, one, Pid),
+
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, one, RemotePid])),
+ RemoteScopePid = rpc:call(Peer, erlang, whereis, [?FUNCTION_NAME]),
+ ?assert(is_pid(RemoteScopePid)),
+ %% hohoho, partition!
+ net_kernel:disconnect(Peer),
+ ?assertEqual(true, net_kernel:connect_node(Peer)),
+ %% now ensure sync happened
+ sync({?FUNCTION_NAME, Peer}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual(Expected, lists:sort(pg:get_members(?FUNCTION_NAME, one))),
+ %% WARNING: this code uses pg as white-box, exploiting internals,
+ %% only to simulate broken 'sync'
+ %% Fake Groups: one should disappear, one should be replaced, one stays
+ %% This tests handle_sync function.
+ FakeGroups = [{one, [RemotePid, RemotePid]}, {?FUNCTION_NAME, [RemotePid, RemotePid]}],
+ gen_server:cast(?FUNCTION_NAME, {sync, RemoteScopePid, FakeGroups}),
+ %% ensure it is broken well enough
+ sync(?FUNCTION_NAME),
+ ?assertEqual(lists:sort([RemotePid, RemotePid]), lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ ?assertEqual(lists:sort([RemotePid, RemotePid, Pid]), lists:sort(pg:get_members(?FUNCTION_NAME, one))),
+ %% simulate force-sync via 'discover' - ask peer to send sync to us
+ {?FUNCTION_NAME, Peer} ! {discover, whereis(?FUNCTION_NAME)},
+ sync({?FUNCTION_NAME, Peer}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual(Expected, lists:sort(pg:get_members(?FUNCTION_NAME, one))),
+ ?assertEqual([], lists:sort(pg:get_members(?FUNCTION_NAME, ?FUNCTION_NAME))),
+ %% and simulate extra sync
+ sync({?FUNCTION_NAME, Peer}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual(Expected, lists:sort(pg:get_members(?FUNCTION_NAME, one))),
+
+ stop_node(Peer, Socket),
+ ok.
+
+group_leave(Config) when is_list(Config) ->
+ {Peer, Socket} = spawn_node(?FUNCTION_NAME, ?FUNCTION_NAME),
+ RemotePid = erlang:spawn(Peer, forever()),
+ Total = lists:duplicate(16, RemotePid),
+ {Left, Remain} = lists:split(4, Total),
+ %% join 16 times!
+ ?assertEqual(ok, rpc:call(Peer, pg, join, [?FUNCTION_NAME, two, Total])),
+ ?assertEqual(ok, rpc:call(Peer, pg, leave, [?FUNCTION_NAME, two, Left])),
+
+ sync({?FUNCTION_NAME, Peer}),
+ sync(?FUNCTION_NAME),
+ ?assertEqual(Remain, pg:get_members(?FUNCTION_NAME, two)),
+ stop_node(Peer, Socket),
+ sync(?FUNCTION_NAME),
+ ?assertEqual([], pg:get_members(?FUNCTION_NAME, two)),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Test Helpers - start/stop additional Erlang nodes
+
+sync(GS) ->
+ _ = sys:log(GS, get).
+
+-define (LOCALHOST, {127, 0, 0, 1}).
+
+%% @doc Kills process Pid and waits for it to exit using monitor,
+%% and yields after (for 1 ms).
+-spec stop_proc(pid()) -> ok.
+stop_proc(Pid) ->
+ monitor(process, Pid),
+ erlang:exit(Pid, kill),
+ receive
+ {'DOWN', _MRef, process, Pid, _Info} ->
+ timer:sleep(1)
+ end.
+
+%% @doc Executes remote call on the node via TCP socket
+%% Used when dist connection is not available, or
+%% when it's undesirable to use one.
+-spec rpc(gen_tcp:socket(), module(), atom(), [term()]) -> term().
+rpc(Sock, M, F, A) ->
+ ok = gen_tcp:send(Sock, term_to_binary({call, M, F, A})),
+ inet:setopts(Sock, [{active, once}]),
+ receive
+ {tcp, Sock, Data} ->
+ case binary_to_term(Data) of
+ {ok, Val} ->
+ Val;
+ {error, Error} ->
+ {badrpc, Error}
+ end;
+ {tcp_closed, Sock} ->
+ error(closed)
+ end.
+
+%% @doc starts peer node on this host.
+%% Returns spawned node name, and a gen_tcp socket to talk to it using ?MODULE:rpc.
+-spec spawn_node(Scope :: atom(), Node :: atom()) -> {node(), gen_tcp:socket()}.
+spawn_node(Scope, Name) ->
+ Self = self(),
+ Controller = erlang:spawn(?MODULE, controller, [Name, Scope, Self]),
+ receive
+ {'$node_started', Node, Port} ->
+ {ok, Socket} = gen_tcp:connect(?LOCALHOST, Port, [{active, false}, {mode, binary}, {packet, 4}]),
+ Controller ! {socket, Socket},
+ {Node, Socket};
+ Other ->
+ error({start_node, Name, Other})
+ after 60000 ->
+ error({start_node, Name, timeout})
+ end.
+
+%% @private
+-spec controller(atom(), atom(), pid()) -> ok.
+controller(Name, Scope, Self) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Pa2 = filename:dirname(code:which(pg)),
+ Args = lists:concat(["-setcookie ", erlang:get_cookie(),
+ "-connect_all false -kernel dist_auto_connect never -noshell -pa ", Pa, " -pa ", Pa2]),
+ {ok, Node} = test_server:start_node(Name, peer, [{args, Args}]),
+ case rpc:call(Node, ?MODULE, control, [Scope], 5000) of
+ {badrpc, nodedown} ->
+ Self ! {badrpc, Node},
+ ok;
+ {Port, _PgPid} ->
+ Self ! {'$node_started', Node, Port},
+ controller_wait()
+ end.
+
+controller_wait() ->
+ Port =
+ receive
+ {socket, Port0} ->
+ Port0
+ end,
+ MRef = monitor(port, Port),
+ receive
+ {'DOWN', MRef, port, Port, _Info} ->
+ ok
+ end.
+
+%% @doc Stops the node previously started with spawn_node,
+%% and also closes the RPC socket.
+-spec stop_node(node(), gen_tcp:socket()) -> true.
+stop_node(Node, Socket) when Node =/= node() ->
+ true = test_server:stop_node(Node),
+ Socket =/= undefined andalso gen_tcp:close(Socket),
+ true.
+
+forever() ->
+ fun() -> receive after infinity -> ok end end.
+
+
+-spec control(Scope :: atom()) -> {Port :: integer(), pid()}.
+control(Scope) ->
+ Control = self(),
+ erlang:spawn(fun () -> server(Control, Scope) end),
+ receive
+ {port, Port, PgPid} ->
+ {Port, PgPid};
+ Other ->
+ error({error, Other})
+ end.
+
+server(Control, Scope) ->
+ try
+ {ok, Pid} = if Scope =:= undefined -> {ok, undefined}; true -> pg:start(Scope) end,
+ {ok, Listen} = gen_tcp:listen(0, [{mode, binary}, {packet, 4}, {ip, ?LOCALHOST}]),
+ {ok, Port} = inet:port(Listen),
+ Control ! {port, Port, Pid},
+ {ok, Sock} = gen_tcp:accept(Listen),
+ server_loop(Sock)
+ catch
+ Class:Reason:Stack ->
+ Control ! {error, {Class, Reason, Stack}}
+ end.
+
+server_loop(Sock) ->
+ inet:setopts(Sock, [{active, once}]),
+ receive
+ {tcp, Sock, Data} ->
+ {call, M, F, A} = binary_to_term(Data),
+ Ret =
+ try
+ erlang:apply(M, F, A) of
+ Res ->
+ {ok, Res}
+ catch
+ exit:Reason ->
+ {error, {'EXIT', Reason}};
+ error:Reason ->
+ {error, {'EXIT', Reason}}
+ end,
+ ok = gen_tcp:send(Sock, term_to_binary(Ret)),
+ server_loop(Sock);
+ {tcp_closed, Sock} ->
+ erlang:halt(1)
+ end.
diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl
index a89a7600a2..d05db46837 100644
--- a/lib/kernel/test/rpc_SUITE.erl
+++ b/lib/kernel/test/rpc_SUITE.erl
@@ -25,7 +25,13 @@
call/1, block_call/1, multicall/1, multicall_timeout/1,
multicall_dies/1, multicall_node_dies/1,
called_dies/1, called_node_dies/1,
- called_throws/1, call_benchmark/1, async_call/1]).
+ called_throws/1, call_benchmark/1, async_call/1,
+ call_against_old_node/1,
+ multicall_mix/1,
+ timeout_limit/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+-export([call_func1/1]).
-export([suicide/2, suicide/3, f/0, f2/0]).
@@ -39,11 +45,17 @@ all() ->
[off_heap, call, block_call, multicall, multicall_timeout,
multicall_dies, multicall_node_dies, called_dies,
called_node_dies, called_throws, call_benchmark,
- async_call].
+ async_call, call_against_old_node, multicall_mix, timeout_limit].
groups() ->
[].
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ [{testcase, Func}|Config].
+
+end_per_testcase(_Func, _Config) ->
+ ok.
+
init_per_suite(Config) ->
Config.
@@ -224,7 +236,6 @@ do_multicall_2_nodes_dies(Mod, Func, Args) ->
ok.
-
%% OTP-3766.
called_dies(Config) when is_list(Config) ->
PA = filename:dirname(code:which(?MODULE)),
@@ -254,20 +265,23 @@ called_dies(Config) when is_list(Config) ->
%%
TrapExit = process_flag(trap_exit, true),
%%
- rep(fun (Tag, Call, Args=[Node|_]) when Node == node() ->
+ rep(fun (Tag, call, Args=[Node|_]) when Node == node() ->
{Tag,timeout} =
- {Tag,apply(rpc, Call, Args)},
+ {Tag,apply(rpc, call, Args)},
{Tag,flush,[{'EXIT',_,normal}]} =
{Tag,flush,flush([])};
(Tag, Call, Args) ->
{Tag,timeout} =
{Tag,apply(rpc, Call, Args)}
end, N, ?MODULE, suicide, [link,normal]),
- rep(fun (Tag, Call, Args=[Node|_]) when Node == node() ->
+ rep(fun (Tag, call, Args=[Node|_]) when Node == node() ->
{Tag,timeout} =
- {Tag,apply(rpc, Call, Args)},
+ {Tag,apply(rpc, call, Args)},
{Tag,flush,[{'EXIT',_,abnormal}]} =
{Tag,flush,flush([])};
+ (Tag, Call, Args=[Node|_]) when Node == node() ->
+ {Tag,timeout} =
+ {Tag,apply(rpc, Call, Args)};
(Tag, block_call, Args) ->
{Tag,timeout} =
{Tag,apply(rpc, block_call, Args)};
@@ -275,20 +289,23 @@ called_dies(Config) when is_list(Config) ->
{Tag,{badrpc,{'EXIT',abnormal}}} =
{Tag,apply(rpc, Call, Args)}
end, N, ?MODULE, suicide, [link,abnormal]),
- rep(fun (Tag, Call, Args=[Node|_]) when Node == node() ->
+ rep(fun (Tag, call, Args=[Node|_]) when Node == node() ->
{Tag,timeout} =
- {Tag,apply(rpc, Call, Args)},
+ {Tag,apply(rpc, call, Args)},
{Tag,flush,[{'EXIT',_,normal}]} =
{Tag,flush,flush([])};
(Tag, Call, Args) ->
{Tag,timeout} =
{Tag,apply(rpc, Call, Args)}
end, N, ?MODULE, suicide, [exit,normal]),
- rep(fun (Tag, Call, Args=[Node|_]) when Node == node() ->
+ rep(fun (Tag, call, Args=[Node|_]) when Node == node() ->
{Tag,timeout} =
- {Tag,apply(rpc, Call, Args)},
+ {Tag,apply(rpc, call, Args)},
{Tag,flush,[{'EXIT',_,abnormal}]} =
{Tag,flush,flush([])};
+ (Tag, Call, Args=[Node|_]) when Node == node() ->
+ {Tag,timeout} =
+ {Tag,apply(rpc, Call, Args)};
(Tag, block_call, Args) ->
{Tag,timeout} =
{Tag,apply(rpc, block_call, Args)};
@@ -370,8 +387,10 @@ called_node_dies(Config) when is_list(Config) ->
PA, ?MODULE, suicide, [init,stop,[]]),
node_rep(
- fun (Call, Args=[_|_]) ->
- {badrpc,{'EXIT',{killed,_}}} = apply(rpc, Call, Args)
+ fun (block_call, Args=[_|_]) ->
+ {badrpc,{'EXIT',{killed,_}}} = apply(rpc, block_call, Args);
+ (call, Args=[_|_]) ->
+ {badrpc,nodedown} = apply(rpc, call, Args)
end, "rpc_SUITE_called_node_dies_3",
PA, ?MODULE, suicide, [erlang,exit,[rex,kill]]),
@@ -380,7 +399,7 @@ called_node_dies(Config) when is_list(Config) ->
%% Cannot block call rpc - will hang
ok;
(Call, Args=[_|_]) ->
- {badrpc,{'EXIT',{normal,_}}} = apply(rpc, Call, Args)
+ {badrpc,nodedown} = apply(rpc, Call, Args)
end, "rpc_SUITE_called_node_dies_4",
PA, ?MODULE, suicide, [rpc,stop,[]]),
@@ -474,10 +493,153 @@ async_call(Config) when is_list(Config) ->
ok.
+call_against_old_node(Config) ->
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ Node22 = rpc:call(Node22, erlang, node, []),
+ stop_node(Node22),
+ ok;
+ _ ->
+ {skipped, "No OTP 22 available"}
+ end.
+
+multicall_mix(Config) ->
+ {ok, Node1} = start_node(Config),
+ {ok, Node2} = start_node(Config),
+ {Node3, OldNodeTest} = case start_22_node(Config) of
+ {ok, N3} ->
+ {N3, true};
+ _ ->
+ {ok, N3} = start_node(Config),
+ {N3, false}
+ end,
+ {ok, Node4} = start_node(Config),
+ {ok, Node5} = start_node(Config),
+ stop_node(Node2),
+
+ ThisNode = node(),
+ Nodes = [ThisNode, Node1, Node2, Node3, Node4, Node5],
+
+ {[ThisNode,
+ Node1,
+ Node3,
+ Node4,
+ Node5],
+ [Node2]}
+ = rpc:multicall(Nodes, erlang, node, []),
+
+ {[BlingError,
+ BlingError,
+ {badrpc, {'EXIT', _}},
+ BlingError,
+ BlingError],
+ [Node2]}
+ = rpc:multicall(Nodes, ?MODULE, call_func1, [bling]),
+
+ {badrpc, {'EXIT',
+ {bling,
+ [{?MODULE, call_func2, A, _},
+ {?MODULE, call_func1, 1, _}]}}} = BlingError,
+ true = (A == 1) orelse (A == [bling]),
+
+ {[], Nodes}
+ = rpc:multicall(Nodes, erlang, processes, [], 0),
+
+ OtherNodes = Nodes -- [ThisNode],
+
+ {[], OtherNodes}
+ = rpc:multicall(OtherNodes, erlang, halt, []),
+
+ case OldNodeTest of
+ true -> {comment, "Test with OTP 22 node as well"};
+ false -> {comment, "Test without OTP 22"}
+ end.
+
+call_func1(X) ->
+ call_func2(X),
+ ok.
+
+call_func2(X) ->
+ erlang:error(X, [X]).
+
+timeout_limit(Config) when is_list(Config) ->
+ Node = node(),
+ MaxTmo = (1 bsl 32) - 1,
+ erlang:send_after(100, self(), dummy_message),
+ try
+ receive
+ M ->
+ M
+ after MaxTmo + 1 ->
+ ok
+ end,
+ ct:fail("The ?MAX_INT_TIMEOUT define in rpc.erl needs "
+ "to be updated to reflect max timeout value "
+ "in a receive/after...")
+ catch
+ error:timeout_value ->
+ ok
+ end,
+ Node = rpc:call(Node, erlang, node, [], MaxTmo),
+ try
+ {badrpc, _} = rpc:call(Node, erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:_ ->
+ ok
+ end,
+ Node = rpc:block_call(Node, erlang, node, [], MaxTmo),
+ try
+ {badrpc, _} = rpc:block_call(Node, erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:_ ->
+ ok
+ end,
+ {[Node],[]} = rpc:multicall([Node], erlang, node, [], MaxTmo),
+ try
+ rpc:multicall([Node], erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:_ ->
+ ok
+ end,
+ ok.
+
+
%%%
%%% Utility functions.
%%%
+start_node(Config) ->
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
+
+start_22_node(Config) ->
+ Rel = "22_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ notsup;
+ true ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name,
+ peer,
+ [{args, "-pa " ++ Pa ++ " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}])
+ end.
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
flush(L) ->
receive
M ->
diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl
index 52342560b1..d984a071f6 100644
--- a/lib/kernel/test/seq_trace_SUITE.erl
+++ b/lib/kernel/test/seq_trace_SUITE.erl
@@ -31,7 +31,8 @@
trace_exit/1, distributed_exit/1, call/1, port/1,
port_clean_token/1,
match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1,
- send_literal/1]).
+ send_literal/1,inherit_on_spawn/1,inherit_on_dist_spawn/1,
+ dist_spawn_error/1]).
%% internal exports
-export([simple_tracer/2, one_time_receiver/0, one_time_receiver/1,
@@ -56,7 +57,8 @@ all() ->
old_heap_token,
distributed_exit, call, port, match_set_seq_token,
port_clean_token,
- gc_seq_token, label_capability_mismatch].
+ gc_seq_token, label_capability_mismatch,
+ inherit_on_spawn, inherit_on_dist_spawn, dist_spawn_error].
groups() ->
[].
@@ -86,14 +88,27 @@ token_set_get(Config) when is_list(Config) ->
do_token_set_get(timestamp),
do_token_set_get(monotonic_timestamp),
do_token_set_get(strict_monotonic_timestamp).
-
+
+-define(SEQ_TRACE_SEND, 1). %(1 << 0)
+-define(SEQ_TRACE_RECEIVE, 2). %(1 << 1)
+-define(SEQ_TRACE_PRINT, 4). %(1 << 2)
+-define(SEQ_TRACE_NOW_TIMESTAMP, 8). %(1 << 3)
+-define(SEQ_TRACE_STRICT_MON_TIMESTAMP, 16). %(1 << 4)
+-define(SEQ_TRACE_MON_TIMESTAMP, 32). %(1 << 5)
+
do_token_set_get(TsType) ->
- io:format("Testing ~p~n", [TsType]),
+ BaseOpts = ?SEQ_TRACE_SEND bor
+ ?SEQ_TRACE_RECEIVE bor
+ ?SEQ_TRACE_PRINT,
Flags = case TsType of
- timestamp -> 15;
- strict_monotonic_timestamp -> 23;
- monotonic_timestamp -> 39
- end,
+ timestamp ->
+ BaseOpts bor ?SEQ_TRACE_NOW_TIMESTAMP;
+ strict_monotonic_timestamp ->
+ BaseOpts bor ?SEQ_TRACE_STRICT_MON_TIMESTAMP;
+ monotonic_timestamp ->
+ BaseOpts bor ?SEQ_TRACE_MON_TIMESTAMP
+ end,
+ ct:pal("Type ~p, flags = ~p~n", [TsType, Flags]),
Self = self(),
seq_trace:reset_trace(),
%% Test that initial seq_trace is disabled
@@ -208,9 +223,9 @@ do_send_literal(Msg) ->
seq_trace:reset_trace(),
start_tracer(),
Label = make_ref(),
+ Receiver = spawn_link(fun() -> receive ok -> ok end end),
seq_trace:set_token(label,Label),
set_token_flags([send, 'receive', no_timestamp]),
- Receiver = spawn_link(fun() -> receive ok -> ok end end),
[Receiver ! Msg || _ <- lists:seq(1, N)],
erlang:garbage_collect(Receiver),
[Receiver ! Msg || _ <- lists:seq(1, N)],
@@ -482,8 +497,6 @@ call(Config) when is_list(Config) ->
1 =
erlang:trace(Self, true,
[call, set_on_spawn, {tracer, TrB(pid)}]),
- Label = 17,
- seq_trace:set_token(label, Label), % Token enters here!!
RefB = make_ref(),
Pid2B = spawn_link(
fun() ->
@@ -497,6 +510,12 @@ call(Config) when is_list(Config) ->
RefB = call_tracee_1(RefB),
Pid2B ! {self(), msg, RefB}
end),
+
+ %% The token is set *AFTER* spawning to make sure we're testing that the
+ %% token follows on send and not that it inherits on spawn.
+ Label = 17,
+ seq_trace:set_token(label, Label),
+
Pid1B ! {Self, msg, RefB},
%% The message is passed Self -> Pid1B -> Pid2B -> Self, and the
%% seq_trace token follows invisibly. Traced functions are
@@ -517,6 +536,357 @@ call(Config) when is_list(Config) ->
seq_trace:reset_trace(),
ok.
+%% The token should follow spawn, just like it follows messages.
+inherit_on_spawn(Config) when is_list(Config) ->
+ lists:foreach(fun (Test) ->
+ inherit_on_spawn_test(Test)
+ end,
+ [spawn, spawn_link, spawn_monitor,
+ spawn_opt, spawn_request]),
+ ok.
+
+inherit_on_spawn_test(Spawn) ->
+ io:format("Testing ~p()~n", [Spawn]),
+
+ seq_trace:reset_trace(),
+ start_tracer(),
+
+ Ref = make_ref(),
+ seq_trace:set_token(label,Ref),
+ set_token_flags([send,'receive',strict_monotonic_timestamp]),
+
+ Self = self(),
+ GurkaMsg = {gurka,Ref},
+ SpawnFun = fun() -> Self ! GurkaMsg, receive after infinity -> ok end end,
+ {Other, Tag, KnownReqId, KnownSpawnReply}
+ = case Spawn of
+ spawn ->
+ {spawn(SpawnFun), spawn_reply, undefined, undefined};
+ spawn_link ->
+ {spawn_link(SpawnFun), spawn_reply, undefined, undefined};
+ spawn_monitor ->
+ {P, _} = spawn_monitor(SpawnFun),
+ {P, spawn_reply, undefined, undefined};
+ spawn_opt ->
+ {spawn_opt(SpawnFun, [link]), spawn_reply, undefined, undefined};
+ spawn_request ->
+ SReply = make_ref(),
+ RID = spawn_request(SpawnFun, [link, {reply_tag, SReply}]),
+ receive
+ {SReply, RID, ok, P} = M ->
+ {P, SReply, RID, M}
+ end
+ end,
+
+ receive {gurka,Ref} -> ok end,
+ seq_trace:reset_trace(),
+
+ Sequence = lists:keysort(3, stop_tracer(6)),
+ io:format("Sequence: ~p~n", [Sequence]),
+ [SSpawnRequest, RSpawnRequest, SSpawnReply, RSpawnReply,
+ SGurkaMsg, RGurkaMsg] = Sequence,
+
+ %% Spawn request...
+ {Ref,
+ {send,
+ {0,1},
+ Self,Other,
+ ReqMessage},
+ _} = SSpawnRequest,
+
+ spawn_request = element(1, ReqMessage),
+ ReqId = element(2, ReqMessage),
+ case KnownReqId of
+ undefined -> ok;
+ ReqId -> ok
+ end,
+
+ {Ref,
+ {'receive',
+ {0,1},
+ Self,Other,
+ ReqMessage},
+ _} = RSpawnRequest,
+
+ %% Spawn reply...
+ SpawnReply = {Tag,ReqId,ok,Other},
+ {Ref,
+ {send,
+ {1,2},
+ Other,Self,
+ SpawnReply},
+ _} = SSpawnReply,
+
+ case KnownSpawnReply of
+ undefined -> ok;
+ SpawnReply -> ok
+ end,
+
+ {Ref,
+ {'receive',
+ {1,2},
+ Other,Self,
+ SpawnReply},
+ _} = RSpawnReply,
+
+ %% Gurka message...
+ {Ref,
+ {send,
+ {1,3},
+ Other, Self,
+ GurkaMsg},
+ _} = SGurkaMsg,
+
+ {Ref,
+ {'receive',
+ {1,3},
+ Other, Self,
+ GurkaMsg},
+ _} = RGurkaMsg,
+
+ unlink(Other),
+ exit(Other, kill),
+
+ ok.
+
+inherit_on_dist_spawn(Config) when is_list(Config) ->
+ lists:foreach(fun (Test) ->
+ inherit_on_dist_spawn_test(Test)
+ end,
+ [spawn, spawn_link, spawn_monitor,
+ spawn_opt, spawn_request]),
+ ok.
+
+inherit_on_dist_spawn_test(Spawn) ->
+ io:format("Testing ~p()~n", [Spawn]),
+ Pa = "-pa "++filename:dirname(code:which(?MODULE)),
+ {ok, Node} = start_node(seq_trace_dist_spawn, Pa),
+ %% ensure module is loaded on remote node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+ io:format("Self=~p~n",[self()]),
+
+ seq_trace:reset_trace(),
+ start_tracer(),
+ rpc:call(Node, seq_trace, reset_trace, []),
+ start_tracer(Node),
+
+ Ref = make_ref(),
+ io:format("Ref=~p~n",[Ref]),
+
+ seq_trace:set_token(label,Ref),
+ set_token_flags([send,'receive',strict_monotonic_timestamp]),
+
+ Self = self(),
+
+ GurkaMsg = {gurka,Ref},
+ SpawnFun = fun() -> Self ! GurkaMsg, receive after infinity -> ok end end,
+ {Other, Tag, KnownReqId, KnownSpawnReply}
+ = case Spawn of
+ spawn ->
+ {spawn(Node, SpawnFun), spawn_reply, undefined, undefined};
+ spawn_link ->
+ {spawn_link(Node, SpawnFun), spawn_reply, undefined, undefined};
+ spawn_monitor ->
+ {P, _} = spawn_monitor(Node, SpawnFun),
+ {P, spawn_reply, undefined, undefined};
+ spawn_opt ->
+ {spawn_opt(Node, SpawnFun, [link]), spawn_reply, undefined, undefined};
+ spawn_request ->
+ SReply = make_ref(),
+ RID = spawn_request(Node, SpawnFun, [{reply_tag, SReply}, link]),
+ receive
+ {SReply, RID, ok, P} = M ->
+ {P, SReply, RID, M}
+ end
+ end,
+
+ receive GurkaMsg -> ok end,
+ seq_trace:reset_trace(),
+
+ Sequence = lists:keysort(3,stop_tracer(4)),
+ io:format("Sequence: ~p~n", [Sequence]),
+ [StSpawnRequest, StAList, StSpawnReply, StGurkaMsg] = Sequence,
+
+ %% Spawn request...
+ {Ref,
+ {send,
+ {0,1},
+ Self,Node,
+ ReqMessage},
+ _} = StSpawnRequest,
+
+ spawn_request = element(1, ReqMessage),
+ ReqId = element(2, ReqMessage),
+ case KnownReqId of
+ undefined -> ok;
+ ReqId -> ok
+ end,
+
+ {Ref,
+ {send,
+ {0,2},
+ Self,Node,
+ ArgList},
+ _} = StAList,
+
+ %% Spawn reply...
+ SpawnReply = {Tag,ReqId,ok,Other},
+ case KnownSpawnReply of
+ undefined -> ok;
+ SpawnReply -> ok
+ end,
+
+ {Ref,
+ {'receive',
+ {1,2},
+ Other,Self,
+ SpawnReply},
+ _} = StSpawnReply,
+
+ %% Gurka message...
+ {Ref,
+ {'receive',
+ {2,3},
+ Other, Self,
+ GurkaMsg},
+ _} = StGurkaMsg,
+
+ SequenceNode = lists:keysort(3,stop_tracer(Node, 4)),
+ io:format("SequenceNode: ~p~n", [SequenceNode]),
+ [StSpawnRequestNode, StSpawnReplyNode, StAListNode, StGurkaMsgNode] = SequenceNode,
+
+
+ %% Spawn request...
+ {Ref,
+ {'receive',
+ {0,1},
+ Self,Other,
+ ReqMessage},
+ _} = StSpawnRequestNode,
+
+ %% Spawn reply...
+ {Ref,
+ {send,
+ {1,2},
+ Other,Self,
+ {spawn_reply, ReqId, ok, Other}},
+ _} = StSpawnReplyNode,
+
+ {Ref,
+ {'receive',
+ {0,2},
+ Self,Other,
+ ArgList},
+ _} = StAListNode,
+
+ %% Gurka message...
+ {Ref,
+ {send,
+ {2,3},
+ Other, Self,
+ GurkaMsg},
+ _} = StGurkaMsgNode,
+
+ unlink(Other),
+
+ stop_node(Node),
+
+ ok.
+
+dist_spawn_error(Config) when is_list(Config) ->
+ Pa = "-pa "++filename:dirname(code:which(?MODULE)),
+ {ok, Node} = start_node(seq_trace_dist_spawn, Pa),
+ %% ensure module is loaded on remote node...
+ _ = rpc:call(Node, ?MODULE, module_info, []),
+
+ io:format("Self=~p~n",[self()]),
+
+ seq_trace:reset_trace(),
+ start_tracer(),
+ rpc:call(Node, seq_trace, reset_trace, []),
+ start_tracer(Node),
+
+ Ref = make_ref(),
+ io:format("Ref=~p~n",[Ref]),
+
+ seq_trace:set_token(label,Ref),
+ set_token_flags([send,'receive',strict_monotonic_timestamp]),
+
+ Self = self(),
+ SpawnReplyTag = make_ref(),
+ GurkaMsg = {gurka,Ref},
+ ReqId = spawn_request(Node,
+ fun () ->
+ Self ! GurkaMsg,
+ receive after infinity -> ok end
+ end,
+ [lunk, {reply_tag, SpawnReplyTag}, link]),
+
+ receive
+ {SpawnReplyTag, ReqId, ResType, Err} ->
+ error = ResType,
+ badopt = Err
+ end,
+
+ seq_trace:reset_trace(),
+
+ Sequence = lists:keysort(3,stop_tracer(3)),
+ io:format("Sequence: ~p~n", [Sequence]),
+ [StSpawnRequest, StAList, StSpawnReply] = Sequence,
+
+ %% Spawn request...
+ {Ref,
+ {send,
+ {0,1},
+ Self,Node,
+ ReqMessage},
+ _} = StSpawnRequest,
+
+ spawn_request = element(1, ReqMessage),
+ ReqId = element(2, ReqMessage),
+
+ {Ref,
+ {send,
+ {0,2},
+ Self,Node,
+ _ArgList},
+ _} = StAList,
+
+ %% Spawn reply...
+ ReplyMessage = {SpawnReplyTag,ReqId,error,badopt},
+ {Ref,
+ {'receive',
+ {1,2},
+ Node,Self,
+ ReplyMessage},
+ _} = StSpawnReply,
+
+ SequenceNode = lists:keysort(3,stop_tracer(Node, 2)),
+ io:format("SequenceNode: ~p~n", [SequenceNode]),
+ [StSpawnRequestNode, StSpawnReplyNode] = SequenceNode,
+
+ %% Spawn request...
+ {Ref,
+ {'receive',
+ {0,1},
+ Self,Node,
+ ReqMessage},
+ _} = StSpawnRequestNode,
+
+ %% Spawn reply...
+ {Ref,
+ {send,
+ {1,2},
+ Node,Self,
+ {spawn_reply, ReqId, error, badopt}},
+ _} = StSpawnReplyNode,
+
+ stop_node(Node),
+
+ ok.
+
+
%% Send trace messages to a port.
port(Config) when is_list(Config) ->
lists:foreach(fun (TsType) -> do_port(TsType, Config) end,
@@ -951,24 +1321,52 @@ simple_tracer(Data, DN) ->
end.
stop_tracer(N) when is_integer(N) ->
- case catch (seq_trace_SUITE_tracer ! {stop,N,self()}) of
- {'EXIT', _} ->
- {error, not_started};
- _ ->
- receive
- {tracerlog,Data} ->
- Data
- after 1000 ->
- {error,timeout}
- end
+ stop_tracer(node(), N).
+
+stop_tracer(Node, N) when is_integer(N) ->
+ case rpc:call(Node,erlang,whereis,[seq_trace_SUITE_tracer]) of
+ Pid when is_pid(Pid) ->
+ unlink(Pid),
+ Mon = erlang:monitor(process, Pid),
+ Pid ! {stop,N,self()},
+ receive
+ {'DOWN', Mon, process, Pid, noproc} ->
+ {error, not_started};
+ {'DOWN', Mon, process, Pid, Reason} ->
+ {error, Reason};
+ {tracerlog,Data} ->
+ erlang:demonitor(Mon, [flush]),
+ Data
+ after 5000 ->
+ erlang:demonitor(Mon, [flush]),
+ {error,timeout}
+ end;
+ _ ->
+ {error, not_started}
end.
start_tracer() ->
- stop_tracer(0),
- Pid = spawn(?MODULE,simple_tracer,[[], 0]),
- register(seq_trace_SUITE_tracer,Pid),
- seq_trace:set_system_tracer(Pid),
- Pid.
+ start_tracer(node()).
+
+start_tracer(Node) ->
+ Me = self(),
+ Ref = make_ref(),
+ Pid = spawn_link(Node,
+ fun () ->
+ Self = self(),
+ stop_tracer(0),
+ register(seq_trace_SUITE_tracer,Self),
+ seq_trace:set_system_tracer(Self),
+ Self = seq_trace:get_system_tracer(),
+ Me ! Ref,
+ simple_tracer([], 0)
+ end),
+ receive
+ Ref ->
+ unlink(Pid),
+ Pid
+ end.
+
set_token_flags([]) ->
ok;
@@ -1020,7 +1418,7 @@ check_ts(strict_monotonic_timestamp, Ts) ->
ok.
start_node(Name, Param) ->
- test_server:start_node(Name, slave, [{args, Param}]).
+ test_server:start_node(Name, peer, [{args, Param}]).
stop_node(Node) ->
test_server:stop_node(Node).
diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile
index 1099c2a634..624c4b0619 100644
--- a/lib/megaco/Makefile
+++ b/lib/megaco/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2019. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: reconf conf dconf econf configure setup info version \
+.PHONY: reconf conf dconf econf configure setup info_megaco version \
app_install dialyzer
reconf:
@@ -136,16 +136,15 @@ configure: configure.in
setup:
(cd src && $(MAKE) $@)
-info:
+info_megaco:
@echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
@echo "APP_DIR: $(APP_DIR)"
@echo "APP_TAR_FILE: $(APP_TAR_FILE)"
@echo "OTP_INSTALL_DIR: $(OTP_INSTALL_DIR)"
@echo "APP_INSTALL_DIR: $(APP_INSTALL_DIR)"
@echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
+
+info: info_megaco
version:
@echo "$(VSN)"
@@ -204,31 +203,6 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd "$(APP_RELEASE_DIR)"; gtar zcf "$(subst $(space),\ ,$@)" $(DIR_NAME))
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT): Makefile
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- ../../lib/kernel/ebin \
- ../../lib/stdlib/ebin \
- ../../lib/runtime_tools/ebin \
- ../../lib/syntax_tools/ebin \
- ../../lib/asn1/ebin \
- ../../lib/debugger/ebin \
- ../../lib/et/ebin \
- ../../lib/mnesia/ebin \
- ../../lib/crypto/ebin \
- ../../lib/compiler/ebin \
- ../../lib/wx/ebin \
- ../../lib/hipe/ebin \
- ../../erts/preloaded/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=asn1 runtime_tools et debugger
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 5e085b60b0..b8488b4a13 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -27,11 +27,6 @@ include ../../vsn.mk
VSN=$(MEGACO_VSN)
APPLICATION=megaco
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
# ----------------------------------------------------
# Target Specs
@@ -39,130 +34,23 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
include files.mk
-
# ----------------------------------------------------
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-INTERNAL_HTML_FILES = $(TECHNICAL_DESCR_FILES:%.xml=$(HTMLDIR)/%.html)
-
-HTML_APP_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_EXTRA_FILES = $(XML_EXTRA_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_PART_FILES = $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-HTML_FILES = $(HTML_APP_FILES) $(HTML_EXTRA_FILES) $(HTML_PART_FILES)
-
-INFO_FILE = ../../info
-
-HTML_REF3_FILES = $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_CHAPTER_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-EXTRA_FILES = \
- $(DEFAULT_GIF_FILES) \
- $(DEFAULT_HTML_FILES) \
- $(HTML_REF3_FILES) \
- $(HTML_CHAPTER_FILES)
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
STANDARD_DIR = ../standard
STANDARDS = $(STANDARD_DIR)/rfc3525.txt \
$(STANDARD_DIR)/rfc4234.txt \
$(STANDARD_DIR)/rfc4566.txt \
$(STANDARD_DIR)/implementors_guide_v10-13.pdf
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-$(HTMLDIR)/%.jpg: %.jpg
- $(INSTALL_DATA) $< $@
-
-$(HTMLDIR)/%.png: %.png
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-ldocs: local_docs
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: imgs $(HTML_REF_MAN_FILE)
-
-clean clean_docs: clean_html clean_man
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-clean_man:
- rm -f $(MAN3DIR)/*
-
-clean_html:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
-
-imgs: $(IMG_FILES:%=$(HTMLDIR)/%)
-
-man: $(MAN3_FILES)
-
-debug opt:
-
-info:
- @echo "->Makefile<-"
- @echo ""
- @echo "HTML_REF_MAN_FILE = $(HTML_REF_MAN_FILE)"
- @echo ""
- @echo "XML_APPLICATION_FILES = $(XML_APPLICATION_FILES)"
- @echo "XML_PART_FILES = $(XML_PART_FILES)"
- @echo "XML_REF3_FILES = $(XML_REF3_FILES)"
- @echo "XML_CHAPTER_FILES = $(XML_CHAPTER_FILES)"
- @echo ""
- @echo "IMG_FILES = $(IMG_FILES)"
- @echo ""
- @echo "MAN3_FILES = $(MAN3_FILES)"
- @echo ""
- @echo "HTML_FILES = $(HTML_FILES)"
- @echo "TOP_HTML_FILES = $(TOP_HTML_FILES)"
- @echo ""
- @echo "DEFAULT_HTML_FILES = $(DEFAULT_HTML_FILES)"
- @echo "DEFAULT_GIF_FILES = $(DEFAULT_GIF_FILES)"
- @echo ""
- @echo ""
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/standard"
- $(INSTALL_DATA) $(STANDARDS) "$(RELSYSDIR)/doc/standard"
+NO_CHUNKS = megaco_edist_compress.xml megaco_user.xml megaco_encoder.xml \
+ megaco_transport.xml megaco_tcp.xml megaco_udp.xml \
+ megaco_codec_meas.xml megaco_codec_mstone1.xml \
+ megaco_codec_mstone2.xml megaco_codec_transform.xml
-release_spec:
+include $(ERL_TOP)/make/doc.mk
$(HTMLDIR)/megaco_architecture.html: megaco_architecture.xml
$(HTMLDIR)/megaco_codec_meas.html: megaco_codec_meas.xml
diff --git a/lib/megaco/doc/src/definitions/term.defs.xml b/lib/megaco/doc/src/definitions/term.defs.xml
deleted file mode 100644
index 096720af84..0000000000
--- a/lib/megaco/doc/src/definitions/term.defs.xml
+++ /dev/null
@@ -1,1518 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE terms SYSTEM "terms.dtd">
-
-<terms>
- <term>
- <id>agent</id>
- <shortdef>agent</shortdef>
- <def>
-An entity that terminates a management protocol in the Network Element. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>API</id>
- <shortdef>API</shortdef>
- <def>
-Application Programming Interface. The interface towards an application. Usually this is a set of functions available, but can also be a set of messages sent to or from an application. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>application</id>
- <shortdef>application</shortdef>
- <def>
-A collection of resources which is required to offer a specific service. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>appmon</id>
- <shortdef>Application Monitor</shortdef>
- <def>
-A graphical node and application process tree viewer. See also appmon. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Appmon</id>
- <shortdef>Appmon</shortdef>
- <def>
-Application name for the Application Monitor within Erlang/OTP. A graphical node and process viewer. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>app callback</id>
- <shortdef>application callback module</shortdef>
- <def>
-A module which is called when the application is started, and when it has stopped. Every application has one application callback module. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>AC</id>
- <shortdef>application controller</shortdef>
- <def>
-A process which coordinates all operations on applications. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>app master</id>
- <shortdef>application master</shortdef>
- <def>
-The application master is a process that monitors the application. It is provided by the Erlang run-time system. Every application has an application master process. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>.app file</id>
- <shortdef>application resource file</shortdef>
- <def>
-Specifies the resources required by the application and how the application should be started. Every application has one application resource file, called AppName.app. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>arity</id>
- <shortdef>arity</shortdef>
- <def>
-Denotes the number of arguments to a function. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>ASN.1</id>
- <shortdef>ASN.1</shortdef>
- <def>
-Abstract Syntax Notation One - an ITU-T and ISO standard notation for describing data formats used in communication protocols. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ASN.1 Compiler</id>
- <shortdef>ASN.1 Compiler</shortdef>
- <def>
-The Erlang/OTP ASN.1 Compiler translates an ASN.1 module into a corresponding Erlang module with encode and decode functions. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>atom</id>
- <shortdef>atom</shortdef>
- <def>
-An atom is a constant. Atoms always starts with a lower case letter (a-z) and are terminated by a non-alphanumeric character - otherwise they must be quoted (enclosed in ' '). An atom is a data type in Erlang, used to enhance the legibility of programs. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>atomicity</id>
- <shortdef>atomicity</shortdef>
- <def>
-Atomicity refers to the "all or nothing" property. If a transaction succeeds (i.e. commits), then all its effects on the data is captured in the database. If the transaction does not succeed (i.e. aborts), then none of its effect on the data is captured in the database. In other words, the transaction processing algorithm guarantees that the database will not reflect a partitial effect of a transaction. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>attach</id>
- <shortdef>attach</shortdef>
- <def>
-The debugger may attach to a process. When attached, the debugger may show process details, such as message queues and variable bindings. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>behaviour</id>
- <shortdef>behaviour</shortdef>
- <def>
-A "pattern of design" which can be used to build applications and processes in an applications. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>BIF</id>
- <shortdef>BIF</shortdef>
- <def>
-Built-In Functions which perform operations that are impossible or inefficient to program in Erlang itself. Are defined in the module Erlang in the application kernel </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>binary</id>
- <shortdef>binary</shortdef>
- <def>
-A data type in Erlang which is used to store an area of untyped memory. Binaries are used for efficiently handling large quantities of untyped data. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>boolean</id>
- <shortdef>boolean</shortdef>
- <def>
-A common data type in programming and specification languages. The value can be either true or false. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>boot file</id>
- <shortdef>boot file</shortdef>
- <def>
-A binary file with extension .boot which is read during start of an Erlang node. See SASL User's Guide for more info. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>break point</id>
- <shortdef>break point</shortdef>
- <def>
-By setting a break point using the debugger, the user specifies a position in the source code of a module where execution is to be suspended and control transferred to the debugger. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>CAshort</id>
- <shortdef>CA</shortdef>
- <def>
-See Certification Authority. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CA certificate</id>
- <shortdef>CA certificate</shortdef>
- <def>
-A certificate containing a CA's public key. Network entities use this public key to verify certificates signed with the CA's private key. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>callback function</id>
- <shortdef>callback function</shortdef>
- <def>
-A callback function is a function exported from a callback module, that a generic behaviour calls to perform a specific task. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>callback module</id>
- <shortdef>callback module</shortdef>
- <def>
-A callback module is a module that implements the specific parts of a generic behaviour. The generic behaviour specifies which callback functions must be exported from the module. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>certificate</id>
- <shortdef>certificate</shortdef>
- <def>
-A file used for authenticating network entities under the SSL protocol. A certificate contains information about its owner (called the subject) and issuer, plus the owner's public key and a signature made by a CA. Network entities verify these signatures using CA certificates. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CAlong</id>
- <shortdef>Certification Authority (CA)</shortdef>
- <def>
-A trusted third party whose purpose is to sign certificates for network entities it has authenticated using secure means. Other network entities can check the signature to verify that a CA has authenticated the bearer of a certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CSRlong</id>
- <shortdef>Certificate Signing Request (CSR)</shortdef>
- <def>
-An unsigned certificate for submission to a Certification Authority, which signs it with its private key. Once the CSR is signed, it becomes a certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>child</id>
- <shortdef>child</shortdef>
- <def>
-A supervised process. See also permanent, transient, temporary child. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>cipher</id>
- <shortdef>cipher</shortdef>
- <def>
-A system of encryption. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>ClearCase</id>
- <shortdef>ClearCase</shortdef>
- <def>
-A configuration management system from Rational Software Corporation. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>client-server model</id>
- <shortdef>client-server model</shortdef>
- <def>
-A model where there is a server, which manages some resource, and a number of clients which send requests to the server to access the resource. The client-server model is one of the basic programming techniques for coordinating the activities of several parallel processes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>cover</id>
- <shortdef>Coverage Analyser</shortdef>
- <def>
-Module name for the coverage analyser tool, located in the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>CORBAlong</id>
- <shortdef>Common Object Request Broker Architecture (CORBA)</shortdef>
- <def>
-A specification of an architecture for a distributed object system </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>CORBA</id>
- <shortdef>Common Object Request Broker Architecture (CORBA)</shortdef>
- <def>
-A specification of an architecture for a distributed object system </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>compiler</id>
- <shortdef>compiler</shortdef>
- <def>
-A compiler is a translator. A common type of compilers are those who takes source code for a programming language and translates it into code that is executable on a specific platform. E.g. the Erlang compiler translates Erlang source code to an intermediary code that is executable by the Erlang Run Time System. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>consistency</id>
- <shortdef>consistency</shortdef>
- <def>
-Consistency refers to the requirement that, given a consistent initial database state, the state of the database after the successful execution of a transaction is also consistent; that is, a transaction transforms the database from a consistent state to another consistent state. Database consistency may be defined as a set of rules or constraints. If the execution of a transaction causes the consistency constraints to be violated, the transaction is not accepted (and thus aborted) by the system. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>cookie</id>
- <shortdef>cookie</shortdef>
- <def>
-A magic cookie is a secret atom assigned to each Erlang node. The Erlang nodes in a distributed system must know each others cookies in order to authorize each other for communication </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>CORBAshort</id>
- <shortdef>CORBA</shortdef>
- <def>
-See Common Object RequestBroker Architecture. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>Coverage Analyser</id>
- <shortdef>Coverage Analyser</shortdef>
- <def>
-A tool which provides a set of functions for coverage analysis of Erlang programs, i.e observing how many times each line or function are executed. See also cover. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>coverage analysis</id>
- <shortdef>coverage analysis</shortdef>
- <def>
-The task of determining which lines, or how many lines of code, has actually been executed. Useful for determining the completeness of test suites. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>cross reference tool</id>
- <shortdef>cross reference tool</shortdef>
- <def>
-A tool that can be used for finding dependencies between functions, modules, applications and releases. The Erlang/OTP cross reference tool is called xref and is part of the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>CSRshort</id>
- <shortdef>CSR</shortdef>
- <def>
-See Certificate Signing Request. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>data type</id>
- <shortdef>data type</shortdef>
- <def>
-The data types in Erlang are numbers, atoms, tuples, lists, pids, funs, records, ports, references and binaries. The values of Erlang data types can be stored in variables. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>DBMSlong</id>
- <shortdef>Database Management System (DBMS)</shortdef>
- <def>
-A database is a collection of data and a DBMS is a system which manages the database. Applications accesses the database through the database management system. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>DBMSshort</id>
- <shortdef>DBMS</shortdef>
- <def>
-See Database Management System. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Debugger</id>
- <shortdef>Debugger</shortdef>
- <def>
-An Erlang/OTP tool which provides mechanisms which makes it possible to see what happens during the execution of code in specified modules, or when processes crash. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>dets</id>
- <shortdef>dets</shortdef>
- <def>
-A module within the stdlib application, which provides a term storage, and which is used as the underlying file storage mechanism by the Mnesia DBMS. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>dirty operations</id>
- <shortdef>dirty operations</shortdef>
- <def>
-Functions which manipulate data in a DBMS, without using transactions. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>distributed application</id>
- <shortdef>distributed application</shortdef>
- <def>
-An application which runs on one of several nodes. May be restarted on another node. (See local application.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>DNSshort</id>
- <shortdef>DNS</shortdef>
- <def>
-See Domain Name System. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>Docbuilder</id>
- <shortdef>Docbuilder</shortdef>
- <def>
-The documentation system used in Erlang/OTP. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>DNSlong</id>
- <shortdef>Domain Name System (DNS)</shortdef>
- <def>
-DNS is a service that map names to internet addresses </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>DTD</id>
- <shortdef>DTD</shortdef>
- <def>
-Document Type Definition as defined in SGML. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>durability</id>
- <shortdef>durability</shortdef>
- <def>
-If a transaction succeeds, then its effect on the data is persistently captured, and will survive subsequent system failures resulting in loss of data in volatile memory. Durability is usually enforced by first writing modified data to some non-volatile memory (usually disc), before a transaction is allowed to commit. If there is a system failure, the state of the non-volatile memory must be recovered to reflect the effect of all and only committed transactions. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Emacs</id>
- <shortdef>Emacs</shortdef>
- <def>
-A widely used text editor which allows customization of its behaviour. An Erlang mode for Emacs is included in the Erlang deliverables. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Emacs for Erlang</id>
- <shortdef>Emacs for Erlang</shortdef>
- <def>
-A tool which provides a major mode for editing Erlang source files in Emacs. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>encaps</id>
- <shortdef>encapsulation</shortdef>
- <def>
-Data can be encapsulated into another data element. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>eprof</id>
- <shortdef>eprof</shortdef>
- <def>
-A module in the tools application. See Profiler. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>erl</id>
- <shortdef>erl</shortdef>
- <def>
-The command which starts an Erlang run-time system. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>erl_interface</id>
- <shortdef>erl_interface library</shortdef>
- <def>
-A thread safe library with C-functions which makes it possible to write a C-program which appears as one of the nodes in a system of distributed Erlang nodes. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang</id>
- <shortdef>Erlang</shortdef>
- <def>
-Erlang is a functional programming language intended for designing large industrial soft real time systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang emulator</id>
- <shortdef>Erlang emulator</shortdef>
- <def>
-Another word for Erlang Virtual Machine. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ERTSlong</id>
- <shortdef>Erlang Run Time System</shortdef>
- <def>
-A fundamental part of Erlang/OTP which contains the Erlang Virtual Machine, the kernel and stdlib applications. The Erlang Run Time System is a mandatory part which all other Erlang applications are dependent upon. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang VM</id>
- <shortdef>Erlang Virtual Machine</shortdef>
- <def>
-The virtual machine, which makes Erlang/OTP work together with a specific OS/HW platform. The Erlang Virtual Machine is available on several different platforms. The Erlang Virtual Machine is the glue which makes it possible to run an Erlang application on any platform without change. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ERTSshort</id>
- <shortdef>ERTS</shortdef>
- <def>
-See Erlang Run Time System. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ETS</id>
- <shortdef>ETS</shortdef>
- <def>
-Erlang Term Storage tables. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>EVAshort</id>
- <shortdef>EVA</shortdef>
- <def>
-See Event and Alarm handling application </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>EVAlong</id>
- <shortdef>Event and Alarm handling application (EVA)</shortdef>
- <def>
-An application that consists of Fault Management functionality, such as sending and logging of events and alarms. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>event handler</id>
- <shortdef>event handler</shortdef>
- <def>
-A module exporting functions which can process events sent to an event manager process. The event handler is a behaviour of type gen_event. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>event manager</id>
- <shortdef>event manager</shortdef>
- <def>
-A process to which events of a certain category is sent. gen_event handler can be installed in the event manager. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>exit signal</id>
- <shortdef>exit signal</shortdef>
- <def>
-A signal which is sent from a terminating process to the processes and ports it is linked to. An EXIT signal has the following format: {'EXIT', Exiting_Process_Id, Reason}. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>foo</id>
- <shortdef>foo</shortdef>
- <def>
-Algebraic place holder. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>FSM</id>
- <shortdef>FSM</shortdef>
- <def>
-Finite State Machine. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>fun</id>
- <shortdef>fun</shortdef>
- <def>
-A data type, introduced in Erlang 4.4, which represent functional objects. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>function</id>
- <shortdef>function</shortdef>
- <def>
-Erlang programs are written entirely in terms of modules with functions. A function can have arguments and does always return a result. A function can be exported which makes it available for calls from other modules. Non exported functions can only be called internally within the module. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Gateway</id>
- <shortdef>gateway</shortdef>
- <def>
-A server which acts as an intermediary for some other server. Unlike a proxy, a gateway receives requests as if it were the origin server for the requested resource; the requesting client may not be aware that it is communicating with a gateway. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>gen_event</id>
- <shortdef>gen_event</shortdef>
- <def>
-A behaviour used for programming event handling mechanisms, such as alarm handlers, error loggers, and plug-and-play handlers. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gen_fsm</id>
- <shortdef>gen_fsm</shortdef>
- <def>
-A behaviour used for programming finite state machines. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gen_server</id>
- <shortdef>gen_server</shortdef>
- <def>
-A behaviour used for programming client-server processes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gterm</id>
- <shortdef>Global Glossary Database</shortdef>
- <def>
-A glossary database used to list common acronymns and defintions etc. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>xref</id>
- <shortdef>xref</shortdef>
- <def>
-A cross reference tool that can be used for finding dependencies between functions, modules, applications and releases. Part of the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>GSlong</id>
- <shortdef>Graphics System</shortdef>
- <def>
-A library module which provides a graphics interface for Erlang. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>grid</id>
- <shortdef>grid</shortdef>
- <def>
-A multi-column object which is used to display tables. (Graphics System.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>GSshort</id>
- <shortdef>GS</shortdef>
- <def>
-See Graphics System. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>GS Contributions</id>
- <shortdef>GS Contributions</shortdef>
- <def>
-Unsupported user supplied tools which are included with the Erlang/OTP software release. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>GUI</id>
- <shortdef>GUI</shortdef>
- <def>
-Graphical User Interface </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Home Directory</id>
- <shortdef>Home Directory</shortdef>
- <def>
-The position of a user account in the file system. The Home Directory is automatically passed to the Erlang run-time system at startup. On Unix the contents of the environment variable "HOME" is passed. Om Win32 the concatenation of the environment variables "HOMEDRIVE" and "HOMEPATH" is passed, or if these variables are not set, the value returned by the Win32 API function "GetWindowsDirectory" is passed. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>host name</id>
- <shortdef>host name</shortdef>
- <def>
-The name of a machine on a network, e.g. erlang.ericsson.se. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>HTML</id>
- <shortdef>HTML</shortdef>
- <def>
-Hypertext Markup Language. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>HTTP</id>
- <shortdef>HTTP</shortdef>
- <def>
-Hypertext Transfer Protocol. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>HTTPS</id>
- <shortdef>HTTPS</shortdef>
- <def>
-The Hypertext Transport Protocol, Secure, the standard SSL communication mechanism of the World Wide Web. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>IDLshort</id>
- <shortdef>IDL</shortdef>
- <def>
-See Interface Description Language. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>IDLlong</id>
- <shortdef>Interface Description Language (IDL)</shortdef>
- <def>
-The interface specification language created by OMG. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>indexing</id>
- <shortdef>indexing</shortdef>
- <def>
-Fast lookup using an (usually enumerated) key. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>I1</id>
- <shortdef>INETS</shortdef>
- <def>
-The Internet Services application </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>initial call</id>
- <shortdef>initial call</shortdef>
- <def>
-The first call to an interpreted function (when using the Interpreter). </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>instrumentation function</id>
- <shortdef>instrumentation function</shortdef>
- <def>
-A function used to implement a Managed Object, i.e. give access to the real resources behind an MO. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>IDLlong</id>
- <shortdef>Interface Description Language (IDL)</shortdef>
- <def>
-The interface specification language created by OMG. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>interpreter</id>
- <shortdef>interpreter</shortdef>
- <def>
-An application which provides mechanisms which make it possible to see what happens during the execution of code in specified (interpreted) modules, or when processes crash. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>isolation</id>
- <shortdef>isolation</shortdef>
- <def>
-A transaction executes as if no other concurrent transactions are executing, and thus its execution results are equivalent to those obtained by executing database transactions serially. A system which maintains transaction isolation is also said to be enforcing serializability. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>kernel</id>
- <shortdef>kernel</shortdef>
- <def>
-An application which contains file servers, code servers and other code necessary for the Erlang run-time system. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>key</id>
- <shortdef>key</shortdef>
- <def>
-A file containing the value that must be fed into an algorithm in order to encrypt or decrypt a message. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>key pair</id>
- <shortdef>key pair</shortdef>
- <def>
-A set of two keys used in public key cryptography. One is the public key used to encrypt data, and the other is the private key necessary to decrypt the same data. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>list</id>
- <shortdef>list</shortdef>
- <def>
-Terms separated by commas and enclosed in square brackets [ ] are called lists. A list is a data type in Erlang, used for storing a variable number of terms. It is dynamically sized. The first element of the list is referred to as the head of the list, and the remainer of the list as the tail. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>list box</id>
- <shortdef>list box </shortdef>
- <def>
-A list of labels with optional scroll bars attached. (Graphics System.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>lc</id>
- <shortdef>list comprehension</shortdef>
- <def>
-A language construct in Erlang which are analogous to set comprehensions in Zermelo-Frankel set theory. Analogous to the 'setof' and 'findall' predicates in Prolog. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>local application</id>
- <shortdef>local application</shortdef>
- <def>
-An application which runs on one node and which are always started at the local node only. (See distributed application.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>manager</id>
- <shortdef>manager</shortdef>
- <def>
-An entity that terminates a management protocol in the Network Management Station. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Master Agent</id>
- <shortdef>Master Agent</shortdef>
- <def>
-The SNMP agent system consists of one Master Agent which terminates the SNMP protocol </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MIB</id>
- <shortdef>Management Information Base (MIB)</shortdef>
- <def>
-An abstract definition of the management information available through a management interface in a system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>matching</id>
- <shortdef>matching</shortdef>
- <def>
-See pattern matching. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>message queue</id>
- <shortdef>message queue</shortdef>
- <def>
-The queue of not yet received messages that are in the mailbox of a process. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Mnesia</id>
- <shortdef>Mnesia</shortdef>
- <def>
-Mnesia is a distributed Database Management System, appropriate for telecommunications applications and other applications with need of continuous operation and soft real-time properties. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>MIBshort</id>
- <shortdef>MIB</shortdef>
- <def>
-See Management Information Base. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MIME</id>
- <shortdef>MIME</shortdef>
- <def>
-Multi-purpose Internet Mail Extensions. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>MOlong</id>
- <shortdef>Managed Object (MO)</shortdef>
- <def>
-The abstract management information defined in a MIB. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MO</id>
- <shortdef>MO</shortdef>
- <def>
-Managed Object; The abstract management information defined in a MIB. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>MOshort</id>
- <shortdef>MO</shortdef>
- <def>
-See Managed Object. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>module</id>
- <shortdef>module</shortdef>
- <def>
-Module is the unit for compilation and for loading in Erlang. A Module contains a module declaration, export declarations and code representing the functions in the module. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>NCSA</id>
- <shortdef>NCSA</shortdef>
- <def>
-The National Center for Supercomputing Applications. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>NEshort</id>
- <shortdef>NE</shortdef>
- <def>
-See Network Element. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NElong</id>
- <shortdef>Network Element</shortdef>
- <def>
-In OTP, the Network Element is the entire distributed OTP system, meaning that the distributed OTP system is managed as one entity. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NE</id>
- <shortdef>NE</shortdef>
- <def>
-Network Element; In OTP, the Network Element is the entire distributed OTP system, meaning that the distributed OTP system is managed as one entity. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NMSlong</id>
- <shortdef>Network Management Station (NMS)</shortdef>
- <def>
-The place where the operator manages the network. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NMS</id>
- <shortdef>NMS</shortdef>
- <def>
-Network Management Station; The place where the operator manages the network. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>NMSshort</id>
- <shortdef>NMS</shortdef>
- <def>
-See Network Management Station. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>node</id>
- <shortdef>node</shortdef>
- <def>
-An executing Erlang run-time system which can communicate with other Erlang run-time systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>node name</id>
- <shortdef>node name</shortdef>
- <def>
-A node name is an atom constructed as the concatenation of a name supplied by the user, an "@" character, and the name of the host where the node is executing. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>notation</id>
- <shortdef>notation</shortdef>
- <def>
-How things are written. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>notification</id>
- <shortdef>notification</shortdef>
- <def>
-Information of an event. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>NROFF</id>
- <shortdef>NROFF</shortdef>
- <def>
-A text formatting language for line printer quality output devices that runs on the UNIX operating system. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>number</id>
- <shortdef>number</shortdef>
- <def>
-A data type in Erlang. Are subdivided into integers, for storing natural numbers, or floats, for storing real numbers. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>OMGlong</id>
- <shortdef>Object Managment Group (OMG)</shortdef>
- <def>
-A standardisation group for all specifications in the area of CORBA. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>OMGshort</id>
- <shortdef>OMG</shortdef>
- <def>
-Object Managment Group. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>OTP</id>
- <shortdef>OTP</shortdef>
- <def>
-Open Telecom Platform </def>
- <resp>mike</resp>
- </term>
- <term>
- <id>os_mon</id>
- <shortdef>os_mon</shortdef>
- <def>
-An application which monitors the behaviour of the underlying operating system </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>parser generator</id>
- <shortdef>parser generator</shortdef>
- <def>
-A tool for making compilers which takes a grammar description as input and generates a complete program (a parser) which recognizes input which complies with the grammar. YECC is a parser generator included in the Erlang/OTP. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>pass phrase</id>
- <shortdef>pass phrase</shortdef>
- <def>
-The word or phrase which authenticates the user who is authorized to use private key file. The pass phrase prevents unauthorized users from starting, restarting, or reconfiguring the server. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>pattern matching</id>
- <shortdef>pattern matching</shortdef>
- <def>
-A basic mechanism in Erlang for assigning values to variables and for controlling the flow of a program. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>permanent child</id>
- <shortdef>permanent child</shortdef>
- <def>
-A supervised process which always is restarted when it dies. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Pid</id>
- <shortdef>Pid</shortdef>
- <def>
-Process Identifier. A data type in Erlang for storing process references. The process identity of the process displayed in the line. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Pman</id>
- <shortdef>Pman</shortdef>
- <def>
-Module and application name for the Process Trace Tool. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>point</id>
- <shortdef>point</shortdef>
- <def>
-A unit used to indicate the size of a typeface. Equal to 1/72 inches. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>pointer</id>
- <shortdef>pointer</shortdef>
- <def>
-A pointer tells where data is stored. Memory pointers are not used in Erlang. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>port</id>
- <shortdef>port</shortdef>
- <def>
-A data type in Erlang. Ports provide the basic mechanism for communication with the external world. </def>
- <resp>peterl</resp>
- </term>
- <term>
- <id>port controller</id>
- <shortdef>port controller</shortdef>
- <def>
-An Erlang process which controls a port program. A port has exactly one port controller. </def>
- <resp>peterl</resp>
- </term>
- <term>
- <id>port program</id>
- <shortdef>port program</shortdef>
- <def>
-A program that runs as an external program in the operating system and which the Erlang run-time system can start and communicate with by means of the Erlang port mechanism. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>PostScript</id>
- <shortdef>PostScript</shortdef>
- <def>
-A language describing a fully laid-out page in terms of fonts, lines, grey scales, and so on, in a way that is interpretable by a printer. The language was developed by Adobe Systems. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>pretty-printed</id>
- <shortdef>pretty-printed</shortdef>
- <def>
-Nicely formatted code or data, e.g. C or Erlang, with indents and tabs etc. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>primitive</id>
- <shortdef>primitive</shortdef>
- <def>
-The basic elements in a programming language. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>private key</id>
- <shortdef>private key</shortdef>
- <def>
-The secret key in a pair, used to decrypt incoming messages and sign outgoing ones. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>process</id>
- <shortdef>process</shortdef>
- <def>
-A process is a self-contained separate unit of execution which exists concurrently with other processes in the system. The BIF "spawn/3" creates and starts the execution of a new process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>process dictionary</id>
- <shortdef>process dictionary</shortdef>
- <def>
-Each process has an associated dictionary which provides the process with simple destructive storage capabilities. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Process Manager</id>
- <shortdef>Process Manager</shortdef>
- <def>
-Obsolete name for the Process Trace Tool. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Process Trace Tool</id>
- <shortdef>Process Trace Tool</shortdef>
- <def>
-A tool which gives an overview of the processes in the Erlang run-time system. See also Pman. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Profiler</id>
- <shortdef>Profiler</shortdef>
- <def>
-Another name for eprof, a tool used to profile a system in order to find out how much time is spent in various segments of a program. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>program</id>
- <shortdef>program</shortdef>
- <def>
-Routines which can be executed by a computer. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>Proxy</id>
- <shortdef>proxy</shortdef>
- <def>
-An intermediary program which acts as both a server and a client for the purpose of making requests on behalf of other clients. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>public key</id>
- <shortdef>public key</shortdef>
- <def>
-The publicly available key in a key pair, used to encrypt messages bound for its owner and to decrypt signatures made by its owner. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>query</id>
- <shortdef>query</shortdef>
- <def>
-Queries are used for accessing the data in a Database Management System. The query specify a maybe complicated relation that should hold for all of the selected data. This could involve several tables as well as conditions like for instance less then and greater then. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>query language</id>
- <shortdef>query language</shortdef>
- <def>
-A language which is specially designed to express database queries. Examples of query languages are QLC and SQL. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>receive</id>
- <shortdef>receive</shortdef>
- <def>
-A primitive for message processing in Erlang, receives a message from a process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>record</id>
- <shortdef>record</shortdef>
- <def>
-A data structure intended for storing a fixed number of related Erlang terms, it is similar to a "struct" in C or a "record" in Pascal. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>recursion</id>
- <shortdef>recursion</shortdef>
- <def>
-A function is recursive if it calls itself until the result desired is attained. Recursion is the heart of functional programming. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>reference</id>
- <shortdef>reference</shortdef>
- <def>
-A data type in Erlang for storing system unique references. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>release handler</id>
- <shortdef>release handler</shortdef>
- <def>
-A SASL process which handles software upgrade. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>relup</id>
- <shortdef>release upgrade script</shortdef>
- <def>
-A script with instructions to the release handler of how the release should be installed in the system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>RPC</id>
- <shortdef>Remote Proceedure Call</shortdef>
- <def>
-A technique for evaluating a function transparently on a remote node. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>resource</id>
- <shortdef>resource</shortdef>
- <def>
-The actual resource to be managed. A resource is represented by a Managed Object. Each resource is mapped to one or several resources. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>resources</id>
- <shortdef>resources</shortdef>
- <def>
-The actual resources to be managed. A resource is represented by a Managed Object. Each resource is mapped to one or several resources. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>RFC</id>
- <shortdef>RFC</shortdef>
- <def>
-A "Request for Comments" used as a proposed standard by IETF. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>SASLshort</id>
- <shortdef>SASL</shortdef>
- <def>
-See System Architecture Support Libraries. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>schema</id>
- <shortdef>schema</shortdef>
- <def>
-The schema contains the definitions and whereabouts for all tables. In Mnesia it is realized as a special table named "schema". </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>schema functions</id>
- <shortdef>schema functions</shortdef>
- <def>
-The functions which are available for managing schemas. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>SDL</id>
- <shortdef>SDL</shortdef>
- <def>
-Specification and Description Language. A ITU-T standard specification language which is used to specify the behaviour of switching systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>send</id>
- <shortdef>send</shortdef>
- <def>
-A primitive for message processing in Erlang, sends a message to a process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>shell</id>
- <shortdef>shell</shortdef>
- <def>
-The shell is an interactive front-end to an Erlang node where Erlang expressions can be evaluated. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>shell prompt</id>
- <shortdef>shell prompt</shortdef>
- <def>
-The text or symbol shown on the screen when the shell is ready to receive commands. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>SNMPEAlong</id>
- <shortdef>Simple Network Management Protocol Extensible Agent (SNMPEA).</shortdef>
- <def>
-An Erlang/OTP application that includes a bilingual extensible SNMP agent. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>single assignment</id>
- <shortdef>single assignment</shortdef>
- <def>
-Means that once a variable has been assigned a value, the value can never be changed. Erlang is a single assignment language. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>single step</id>
- <shortdef>single step</shortdef>
- <def>
-Single stepping is a function provided by the debugger. By single stepping the developer may use the debugger to follow the execution of a process and see what actually happens at each function call. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>slave</id>
- <shortdef>slave</shortdef>
- <def>
-Not in control, can never take over by himself. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>SSLlong</id>
- <shortdef>Secure Sockets Layer (SSL)</shortdef>
- <def>
-A protocol created by Netscape Communications Corporation for authentication and encryption over TCP/IP networks, including Web. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>signature</id>
- <shortdef>signature</shortdef>
- <def>
-An encrypted text block that validates a certificate or other file. A Certification Authority (CA) creates a signature by generating a hash of the public key embedded in a certificate, then encrypting the hash with its own private key. Only the CA's public key can decrypt the signature, verifying that the CA has authenticated the network entity that owns the certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SNMPshort</id>
- <shortdef>SNMP</shortdef>
- <def>
-Simple Network Management Protocol. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>SNMPshort</id>
- <shortdef>SNMPEA</shortdef>
- <def>
-See Simple Network Management Protocol Extensible Agent. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>spawn</id>
- <shortdef>spawn</shortdef>
- <def>
-A primitive for multiprocessing in Erlang, that starts a parallel computation (called a process). The creation of a new process </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>SSLshort</id>
- <shortdef>SSL</shortdef>
- <def>
-See Secure Sockets Layer. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SSLeay</id>
- <shortdef>SSLeay</shortdef>
- <def>
-An SSL library developed by Eric Yong (eay@mincom.oz.au). </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SSLTOP</id>
- <shortdef>SSLTOP</shortdef>
- <def>
-The path to your SSL directory, a subdirectory of ServerRoot. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>start script</id>
- <shortdef>start script</shortdef>
- <def>
-A start script is a file with .script extension which is the source when a boot file is created. See SASL User's Guide for more info. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>stdlib</id>
- <shortdef>stdlib</shortdef>
- <def>
-An application within Erlang/OTP which contains modules for manipulating lists, strings, files, etc. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>sticky directory</id>
- <shortdef>sticky directory</shortdef>
- <def>
-A directory containing Erlang object code that is part of the runtime system. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>sticky lock</id>
- <shortdef>sticky lock</shortdef>
- <def>
-A lock which lingers at a node after the transaction which first acquired the lock has terminated. Once a process has obtained a sticky lock on a node, subsequent locks acquired by processes on the same node, can be set without need of involving remote nodes. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>string</id>
- <shortdef>string</shortdef>
- <def>
-The ASCII or ISO-8859-1 representation of the list of characters occurring within quotation marks in Erlang code. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>Subagent</id>
- <shortdef>Subagent</shortdef>
- <def>
-The SNMP agent system consists of one Master Agent (See Master Agent) and zero or more Subagents which can be used to distribute the SNMP agent system on several nodes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>supervision tree</id>
- <shortdef>supervision tree</shortdef>
- <def>
-A hierarcial tree of processes used to program fault tolerant systems. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>supervisor</id>
- <shortdef>supervisor</shortdef>
- <def>
-A behaviour to stucture fault tolerant computations, and program supervision trees with. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>sup_bridge</id>
- <shortdef>supervisor bridge</shortdef>
- <def>
-A behaviour used to connect a process, or subsystem, to a supervisor tree. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>SASLlong</id>
- <shortdef>System Architecture Support Libraries (SASL)</shortdef>
- <def>
-An Erlang/OTP application which contains services for error logging, release handling and report browsing. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>.config</id>
- <shortdef>system configuration file</shortdef>
- <def>
-A file which specifies configuration parameters for the applications in the system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>table lock</id>
- <shortdef>table lock</shortdef>
- <def>
-Table locks are locks which are set on whole tables. They may either be read locks or write locks. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Table Visualizer</id>
- <shortdef>Table Visualizer</shortdef>
- <def>
-A tool which enables the user to examine ETS and Mnesia tables. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>temporary child</id>
- <shortdef>temporary child</shortdef>
- <def>
-A supervised process which is never restarted when it dies. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>term</id>
- <shortdef>term</shortdef>
- <def>
-The super type of all Erlang types. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Toolbar</id>
- <shortdef>Toolbar</shortdef>
- <def>
-A tool that provides an simplistic interface to the other various Erlang/OTP tools </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>tools</id>
- <shortdef>tools</shortdef>
- <def>
-An application within Erlang/OTP which contains the tools which are not applications themselves. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>transaction</id>
- <shortdef>transaction</shortdef>
- <def>
-Transactions groups a set of database accesses into an atomic unit. All transactions has the ACID (atomicity, concistency, isolation and durability) properties. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>transient child</id>
- <shortdef>transient child</shortdef>
- <def>
-A supervised process which is restarted if it dies non-normally. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>trigger</id>
- <shortdef>trigger</shortdef>
- <def>
-The Interpreter. A break point that is reached by a process triggers if it is active, and the execution of the process is stopped. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>tty</id>
- <shortdef>tty</shortdef>
- <def>
-tty is a simple command line interface program where keystrokes are collected and interpreted. Originally meant teletypewriter equipment. Now it usually means the user console/terminal/shell window. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>tuple</id>
- <shortdef>tuple</shortdef>
- <def>
-A tuple is a data type in Erlang. Tuples are used as place holders for complex data structures. Tuples may contain anything of any size, and are written as sequences of terms separated by commas, and enclosed in curly brackets { }. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>variable</id>
- <shortdef>variable</shortdef>
- <def>
-An alias for a memory position, in which a value can be put. Erlang variables always start with an upper case letter. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>workers</id>
- <shortdef>workers</shortdef>
- <def>
-The lower nodes in a supervision tree. These are the processes that actually performs some real work, e.g. servers. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>YECC</id>
- <shortdef>YECC</shortdef>
- <def>
-A LALR-1 parser generator included in Erlang/OTP. It is written in Erlang and generates a parser as an Erlang module. </def>
- <resp>kenneth</resp>
- </term>
-</terms>
-
diff --git a/lib/megaco/doc/src/files.mk b/lib/megaco/doc/src/files.mk
index e40889c3fb..6b7eaab531 100644
--- a/lib/megaco/doc/src/files.mk
+++ b/lib/megaco/doc/src/files.mk
@@ -55,7 +55,7 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-IMG_FILES = \
+IMAGE_FILES = \
single_node_config.gif \
distr_node_config.gif \
megaco_sys_arch.gif \
diff --git a/lib/megaco/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml
index c7bcdfcd6f..f5daa4dfce 100644
--- a/lib/megaco/doc/src/megaco.xml
+++ b/lib/megaco/doc/src/megaco.xml
@@ -1969,23 +1969,6 @@ megaco_incr_timer() = #megaco_incr_timer{}
</func>
<func>
- <name since="">get_sdp_record_from_PropertGroup(Type, PG) -> [sdp()]</name>
- <fsummary>Get all sdp records of a certain type from a property group</fsummary>
- <type>
- <v>Type = v | c | m | o | a | b | t | r | z | k | s | i | u | e | p</v>
- <v>PG = sdp_property_group()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Retreive all the sdp records of type <c>Type</c> from the
- property group <c>PG</c>.</p>
-
- <marker id="versions1"></marker>
- <marker id="versions2"></marker>
- </desc>
- </func>
-
- <func>
<name since="">versions1() -> {ok, VersionInfo} | {error, Reason}</name>
<name since="">versions2() -> {ok, Info} | {error, Reason}</name>
<fsummary>Retreive various system and application info</fsummary>
diff --git a/lib/megaco/src/app/megaco.erl b/lib/megaco/src/app/megaco.erl
index a876ff02f8..2c27043782 100644
--- a/lib/megaco/src/app/megaco.erl
+++ b/lib/megaco/src/app/megaco.erl
@@ -101,8 +101,7 @@
formated_long_timestamp/0
]).
-%% This is for XREF
--deprecated([{format_versions, 1, eventually}]).
+-deprecated([{format_versions,1,"use megaco:print_version_info/0,1 instead"}]).
-export_type([
void/0
diff --git a/lib/megaco/test/megaco_mess_SUITE.erl b/lib/megaco/test/megaco_mess_SUITE.erl
index f3dfc053c6..8fb5c4a982 100644
--- a/lib/megaco/test/megaco_mess_SUITE.erl
+++ b/lib/megaco/test/megaco_mess_SUITE.erl
@@ -13474,8 +13474,8 @@ otp_8183_r1_mgc_reply_msg(Mid, TransId, CR, Cid) ->
{?MODULE, otp_8183_r1_mg_verify_handle_connect, []}).
-define(otp_8183_r1_mg_verify_service_change_rep_fun(),
{?MODULE, otp_8183_r1_mg_verify_service_change_rep, []}).
--define(otp_8183_r1_mg_verify_notify_rep_fun(Nr
- {?MODULE, otp_8183_r1_mg_verify_notify_rep, [Nr).
+-define(otp_8183_r1_mg_verify_notify_rep_fun(Nr),
+ {?MODULE, otp_8183_r1_mg_verify_notify_rep, [Nr]}).
-else.
-define(otp_8183_r1_mg_verify_handle_connect_fun(),
otp_8183_r1_mg_verify_handle_connect_fun()).
diff --git a/lib/mnesia/Makefile b/lib/mnesia/Makefile
index 810433c4d0..d0edd48af9 100644
--- a/lib/mnesia/Makefile
+++ b/lib/mnesia/Makefile
@@ -38,3 +38,4 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/mnesia/doc/misc/Makefile b/lib/mnesia/doc/misc/Makefile
index 29e2682967..0622a0b809 100644
--- a/lib/mnesia/doc/misc/Makefile
+++ b/lib/mnesia/doc/misc/Makefile
@@ -39,7 +39,7 @@ TEX_FILES= $(SGML_FILES:.sgml=.tex)
DVI_FILES= $(SGML_FILES:.sgml=.dvi)
PSFIG_FILES= $(FIG_FILES:.fig=.ps)
PS_FILES= $(SGML_FILES:.sgml=.ps)
-GIF_FILES= min_head.gif
+GIF_FILES=
ERL_FILES=
HRL_FILES=
DATA_FILES=
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
index d9647fc081..f14fd33c7a 100644
--- a/lib/mnesia/doc/src/Makefile
+++ b/lib/mnesia/doc/src/Makefile
@@ -30,11 +30,6 @@ VSN=$(MNESIA_VSN)
APPLICATION=mnesia
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -70,81 +65,9 @@ XML_FILES = \
XML_GEN_FILES = $(XML_CHAPTER_GEN_FILES:%=$(XMLDIR)/%)
-GIF_FILES = \
+IMAGE_FILES = \
company.gif
-XML_HTML_FILES = \
- notes_history.xml
-
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_HTML_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 +=
-DVIPS_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)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-debug opt:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
index b8d86adbf1..b646393ce3 100644
--- a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
@@ -750,7 +750,7 @@ mnesia:all_keys/1</seealso>.
that can overload <c>Mnesia</c> on other nodes.</item>
</list>
<p>By passing the same "fun" as argument to the function
- <seealso marker="mnesia#async_dirty/2">mnesia:async_dirty(Fun [, Args])</seealso>,
+ <seealso marker="mnesia#async_dirty/1">mnesia:async_dirty(Fun [, Args])</seealso>,
it is performed in dirty context. The function calls are mapped to
the corresponding dirty functions. This still involves logging,
replication, and subscriptions but no locking,
@@ -761,9 +761,9 @@ mnesia:all_keys/1</seealso>.
node but not the others. If the table resides locally, no waiting
occurs.</p>
<p>By passing the same "fun" as an argument to the function
- <seealso marker="mnesia#sync_dirty/2">mnesia:sync_dirty(Fun [, Args])</seealso>,
+ <seealso marker="mnesia#sync_dirty/1">mnesia:sync_dirty(Fun [, Args])</seealso>,
it is performed in almost the same context as the function
- <seealso marker="mnesia#async_dirty/2">mnesia:async_dirty/1,2</seealso>.
+ <seealso marker="mnesia#async_dirty/1">mnesia:async_dirty/1,2</seealso>.
The difference is that the operations are performed
synchronously. The caller waits for the updates to be
performed on all active replicas. Using <c>mnesia:sync_dirty/1,2</c>
@@ -788,7 +788,7 @@ mnesia:all_keys/1</seealso>.
recommended if all options have been weighed and the possible
outcomes are understood. By passing the earlier mentioned "fun"
to the function
- <seealso marker="mnesia#ets/2">mnesia:ets(Fun [, Args])</seealso>,
+ <seealso marker="mnesia#ets/1">mnesia:ets(Fun [, Args])</seealso>,
it is performed but in a raw
context. The operations are performed directly on the
local <c>ets</c> tables, assuming that the local storage type is
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index 11b0b8e987..d05693cf8d 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -453,7 +453,7 @@ mnesia:add_table_index(person, age)</code>
</desc>
</func>
<func>
- <name since="">async_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <name since="">async_dirty(Fun [, Args]) -> ResultOfFun | exit(Reason)</name>
<fsummary>Calls the <c>Fun</c> in a context that is not protected by a transaction.</fsummary>
<desc>
<marker id="async_dirty"></marker>
@@ -1259,7 +1259,7 @@ mnesia:create_table(person,
</desc>
</func>
<func>
- <name since="">ets(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <name since="">ets(Fun [, Args]) -> ResultOfFun | exit(Reason)</name>
<fsummary>Calls the <c>Fun</c> in a raw context that is not protected by a transaction.</fsummary>
<desc>
<marker id="ets"></marker>
@@ -2134,7 +2134,7 @@ mnesia:create_table(employee,
</desc>
</func>
<func>
- <name since="">sync_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <name since="">sync_dirty(Fun [, Args]) -> ResultOfFun | exit(Reason)</name>
<fsummary>Calls the <c>Fun</c> in a context that is not protected by a transaction.</fsummary>
<desc>
<marker id="sync_dirty"></marker>
@@ -2497,8 +2497,12 @@ mnesia:create_table(employee,
table.</p>
</item>
<item>
- <p><c>memory</c>. Returns the number of
- words allocated to the table on this node.</p>
+ <p><c>memory</c>. Returns for <c>ram_copies</c> and
+ <c>disc_copies</c> tables the number of words allocated in
+ memory to the table on this node.
+ For <c>disc_only_copies</c> tables the number of bytes stored
+ on disc is returned.
+ </p>
</item>
<item>
<p><c>ram_copies</c>. Returns the nodes where a
diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile
index 7d316df263..90e8780754 100644
--- a/lib/mnesia/src/Makefile
+++ b/lib/mnesia/src/Makefile
@@ -65,6 +65,7 @@ MODULES= \
mnesia_monitor \
mnesia_recover \
mnesia_registry \
+ mnesia_rpc \
mnesia_schema\
mnesia_snmp_hook \
mnesia_subscr \
diff --git a/lib/mnesia/src/mnesia.app.src b/lib/mnesia/src/mnesia.app.src
index c755b4d4b9..77bd1a7816 100644
--- a/lib/mnesia/src/mnesia.app.src
+++ b/lib/mnesia/src/mnesia.app.src
@@ -2,36 +2,37 @@
[{description, "MNESIA CXC 138 12"},
{vsn, "%VSN%"},
{modules, [
- mnesia,
+ mnesia,
mnesia_app,
mnesia_backend_type,
- mnesia_backup,
- mnesia_bup,
- mnesia_checkpoint,
+ mnesia_backup,
+ mnesia_bup,
+ mnesia_checkpoint,
mnesia_checkpoint_sup,
mnesia_controller,
- mnesia_dumper,
- mnesia_event,
+ mnesia_dumper,
+ mnesia_event,
mnesia_ext_sup,
- mnesia_frag,
- mnesia_frag_hash,
- mnesia_index,
+ mnesia_frag,
+ mnesia_frag_hash,
+ mnesia_index,
mnesia_kernel_sup,
mnesia_late_loader,
- mnesia_lib,
- mnesia_loader,
- mnesia_locker,
- mnesia_log,
- mnesia_monitor,
+ mnesia_lib,
+ mnesia_loader,
+ mnesia_locker,
+ mnesia_log,
+ mnesia_monitor,
mnesia_recover,
mnesia_registry,
- mnesia_schema,
- mnesia_snmp_hook,
- mnesia_subscr,
- mnesia_sup,
+ mnesia_rpc,
+ mnesia_schema,
+ mnesia_snmp_hook,
+ mnesia_subscr,
+ mnesia_sup,
mnesia_sp,
mnesia_text,
- mnesia_tm
+ mnesia_tm
]},
{registered, [
mnesia_dumper_load_regulator,
@@ -39,12 +40,13 @@
mnesia_fallback,
mnesia_controller,
mnesia_kernel_sup,
- mnesia_late_loader,
- mnesia_locker,
+ mnesia_late_loader,
+ mnesia_locker,
mnesia_monitor,
mnesia_recover,
- mnesia_substr,
- mnesia_sup,
+ mnesia_rpc,
+ mnesia_substr,
+ mnesia_sup,
mnesia_tm
]},
{applications, [kernel, stdlib]},
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 02bc884e36..560ebca824 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -2057,8 +2057,14 @@ dirty_rpc(Tab, M, F, Args) ->
do_dirty_rpc(_Tab, nowhere, _, _, Args) ->
mnesia:abort({no_exists, Args});
+do_dirty_rpc(_Tab, Local, M, F, Args) when Local =:= node() ->
+ try apply(M,F,Args)
+ catch
+ throw:Res -> Res;
+ _:_ -> mnesia:abort({badarg, Args})
+ end;
do_dirty_rpc(Tab, Node, M, F, Args) ->
- case rpc:call(Node, M, F, Args) of
+ case mnesia_rpc:call(Node, M, F, Args) of
{badrpc, Reason} ->
timer:sleep(20), %% Do not be too eager, and can't use yield on SMP
%% Sync with mnesia_monitor
@@ -2166,7 +2172,7 @@ raw_table_info(Tab, Item) ->
disc_only_copies ->
info_reply(dets:info(Tab, Item), Tab, Item);
{ext, Alias, Mod} ->
- info_reply(catch Mod:info(Alias, Tab, Item), Tab, Item);
+ info_reply(Mod:info(Alias, Tab, Item), Tab, Item);
unknown ->
bad_info_reply(Tab, Item)
end
@@ -2731,7 +2737,7 @@ create_table(Arg) ->
create_table(Name, Arg) when is_list(Arg) ->
mnesia_schema:create_table([{name, Name}| Arg]);
create_table(Name, Arg) ->
- {aborted, badarg, Name, Arg}.
+ {aborted, {badarg, Name, Arg}}.
-spec delete_table(Tab::table()) -> t_result('ok').
delete_table(Tab) ->
diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl
index fe48a6fe3d..4ddfc17a06 100644
--- a/lib/mnesia/src/mnesia.hrl
+++ b/lib/mnesia/src/mnesia.hrl
@@ -44,6 +44,8 @@
-define(SAFE(OP), try (OP) catch error:_ -> ok end).
-define(CATCH(OP), try (OP) catch _:_Reason -> {'EXIT', _Reason} end).
+-define(CATCHU(OP), fun() -> try (OP) catch _:_Reason -> {'EXIT', _Reason} end end()).
+
-define(catch_val(Var), (try ?ets_lookup_element(mnesia_gvar, Var, 2)
catch error:_ -> {'EXIT', {badarg, []}} end)).
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index 49b3990086..0e96d5c73f 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -36,6 +36,8 @@
dumped_core = false, %% only dump fatal core once
args}).
+-include("mnesia.hrl").
+
%%%----------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------
@@ -131,14 +133,14 @@ handle_system_event({mnesia_down, Node}, State) ->
"must be restarted. Forcing shutdown "
"after mnesia_down from ~p...~n",
report_fatal(Msg, [Node], nocore, State#state.dumped_core),
- catch exit(whereis(mnesia_monitor), fatal),
+ ?SAFE(exit(whereis(mnesia_monitor), fatal)),
{ok, State};
{UserMod, UserFunc} ->
Msg = "Warning: A fallback is installed and Mnesia got mnesia_down "
"from ~p. ~n",
report_info(Msg, [Node]),
- case catch apply(UserMod, UserFunc, [Node]) of
- {'EXIT', {undef, _Reason}} ->
+ case ?CATCH(apply(UserMod, UserFunc, [Node])) of
+ {'EXIT', {undef, _R}} ->
%% Backward compatibility
apply(UserMod, UserFunc, []);
{'EXIT', Reason} ->
diff --git a/lib/mnesia/src/mnesia_kernel_sup.erl b/lib/mnesia/src/mnesia_kernel_sup.erl
index a761d5eed0..7f226d92c4 100644
--- a/lib/mnesia/src/mnesia_kernel_sup.erl
+++ b/lib/mnesia/src/mnesia_kernel_sup.erl
@@ -42,6 +42,7 @@ init([]) ->
worker_spec(mnesia_locker, timer:seconds(3), ProcLib),
worker_spec(mnesia_recover, timer:minutes(3), [gen_server]),
worker_spec(mnesia_tm, timer:seconds(30), ProcLib),
+ worker_spec(mnesia_rpc, timer:seconds(3), [gen_server]),
supervisor_spec(mnesia_checkpoint_sup),
worker_spec(mnesia_controller, timer:seconds(3), [gen_server]),
worker_spec(mnesia_late_loader, timer:seconds(3), ProcLib)
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index 6abc05fade..dd28686f78 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -675,41 +675,41 @@ mkcore(CrashInfo) ->
% dbg_out("Making a Mnesia core dump...~p~n", [CrashInfo]),
Nodes = [node() |nodes()],
%%TidLocks = (catch ets:tab2list(mnesia_tid_locks)),
- HeldLocks = (catch mnesia:system_info(held_locks)),
+ HeldLocks = ?CATCHU(mnesia:system_info(held_locks)),
Core = [
CrashInfo,
{time, {date(), time()}},
{self, proc_dbg_info(self())},
- {nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])},
- {applications, catch lists:sort(application:loaded_applications())},
- {flags, catch init:get_arguments()},
- {code_path, catch code:get_path()},
- {code_loaded, catch lists:sort(code:all_loaded())},
- {etsinfo, catch ets_info(ets:all())},
-
- {version, catch mnesia:system_info(version)},
- {schema, catch ets:tab2list(schema)},
- {gvar, catch ets:tab2list(mnesia_gvar)},
- {master_nodes, catch mnesia_recover:get_master_node_info()},
-
- {processes, catch procs()},
- {relatives, catch relatives()},
- {workers, catch workers(mnesia_controller:get_workers(2000))},
- {locking_procs, catch locking_procs(HeldLocks)},
+ {nodes, ?CATCHU(rpc:multicall(Nodes, ?MODULE, get_node_number, []))},
+ {applications, ?CATCHU(lists:sort(application:loaded_applications()))},
+ {flags, ?CATCHU(init:get_arguments())},
+ {code_path, ?CATCHU(code:get_path())},
+ {code_loaded, ?CATCHU(lists:sort(code:all_loaded()))},
+ {etsinfo, ?CATCHU(ets_info(ets:all()))},
+
+ {version, ?CATCHU(mnesia:system_info(version))},
+ {schema, ?CATCHU(ets:tab2list(schema))},
+ {gvar, ?CATCHU(ets:tab2list(mnesia_gvar))},
+ {master_nodes, ?CATCHU(mnesia_recover:get_master_node_info())},
+
+ {processes, ?CATCHU(procs())},
+ {relatives, ?CATCHU(relatives())},
+ {workers, ?CATCHU(workers(mnesia_controller:get_workers(2000)))},
+ {locking_procs, ?CATCHU(locking_procs(HeldLocks))},
{held_locks, HeldLocks},
- {lock_queue, catch mnesia:system_info(lock_queue)},
- {load_info, catch mnesia_controller:get_info(2000)},
- {trans_info, catch mnesia_tm:get_info(2000)},
+ {lock_queue, ?CATCHU(mnesia:system_info(lock_queue))},
+ {load_info, ?CATCHU(mnesia_controller:get_info(2000))},
+ {trans_info, ?CATCHU(mnesia_tm:get_info(2000))},
- {schema_file, catch file:read_file(tab2dat(schema))},
- {dir_info, catch dir_info()},
- {logfile, catch {ok, read_log_files()}}
+ {schema_file, ?CATCHU(file:read_file(tab2dat(schema)))},
+ {dir_info, ?CATCHU(dir_info())},
+ {logfile, ?CATCHU({ok, read_log_files()})}
],
term_to_binary(Core).
procs() ->
- Fun = fun(P) -> {P, (catch lists:zf(fun proc_info/1, process_info(P)))} end,
+ Fun = fun(P) -> {P, (?CATCH(lists:zf(fun proc_info/1, process_info(P))))} end,
lists:map(Fun, processes()).
proc_info({registered_name, Val}) -> {true, Val};
@@ -730,7 +730,7 @@ have_majority(_Tab, AllNodes, HaveNodes) ->
length(Present) > length(Missing).
read_log_files() ->
- [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()].
+ [{F, ?CATCH(file:read_file(F))} || F <- mnesia_log:log_files()].
dir_info() ->
{ok, Cwd} = file:get_cwd(),
@@ -739,7 +739,7 @@ dir_info() ->
{mnesia_dir, Dir, file:read_file_info(Dir)}] ++
case file:list_dir(Dir) of
{ok, Files} ->
- [{mnesia_file, F, catch file:read_file_info(dir(F))} || F <- Files];
+ [{mnesia_file, F, ?CATCH(file:read_file_info(dir(F)))} || F <- Files];
Other ->
[Other]
end.
@@ -854,7 +854,7 @@ vcore(Bin) when is_binary(Bin) ->
Core = binary_to_term(Bin),
Fun = fun({Item, Info}) ->
show("***** ~tp *****~n", [Item]),
- case catch vcore_elem({Item, Info}) of
+ case ?CATCHU(vcore_elem({Item, Info})) of
{'EXIT', Reason} ->
show("{'EXIT', ~tp}~n", [Reason]);
_ -> ok
@@ -1446,7 +1446,7 @@ eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) ->
ok
end
end
- catch error ->
+ catch _:_ ->
ok
end.
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index 2cdae0c906..c34a83bb6c 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -654,8 +654,8 @@ down(Tab, Storage) ->
mnesia_lib:dets_sync_close(Tab),
file:delete(TmpFile);
{ext, Alias, Mod} ->
- catch Mod:close_table(Alias, Tab),
- catch Mod:delete_table(Alias, Tab)
+ ?CATCHU(Mod:close_table(Alias, Tab)),
+ ?CATCHU(Mod:delete_table(Alias, Tab))
end,
mnesia_checkpoint:tm_del_copy(Tab, node()),
mnesia_controller:sync_del_table_copy_whereabouts(Tab, node()),
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index 4e50b46da8..5ce1439298 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -83,9 +83,9 @@
going_down = [], tm_started = false, early_connects = [],
connecting, mq = [], remote_node_status = []}).
--define(current_protocol_version, {8,4}).
+-define(current_protocol_version, {8,5}).
--define(previous_protocol_version, {8,3}).
+-define(previous_protocol_version, {8,4}).
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE,
@@ -196,7 +196,7 @@ protocol_version() ->
%% A sorted list of acceptable protocols the
%% preferred protocols are first in the list
acceptable_protocol_versions() ->
- [protocol_version(), ?previous_protocol_version].
+ [protocol_version(), ?previous_protocol_version, {8,3}].
needs_protocol_conversion(Node) ->
case {?catch_val({protocol, Node}), protocol_version()} of
@@ -409,7 +409,7 @@ handle_call({unsafe_close_log, Name}, _From, State) ->
{reply, ok, State};
handle_call({unsafe_create_external, Tab, Alias, Mod, Cs}, _From, State) ->
- case catch Mod:create_table(Alias, Tab, mnesia_schema:cs2list(Cs)) of
+ case ?CATCH(Mod:create_table(Alias, Tab, mnesia_schema:cs2list(Cs))) of
{'EXIT', ExitReason} ->
{reply, {error, ExitReason}, State};
Reply ->
diff --git a/lib/mnesia/src/mnesia_rpc.erl b/lib/mnesia/src/mnesia_rpc.erl
new file mode 100644
index 0000000000..bbeacce9db
--- /dev/null
+++ b/lib/mnesia/src/mnesia_rpc.erl
@@ -0,0 +1,86 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Don't use the system rpc server since it may overload other
+%% applications when using a lot of dirty read operations.
+
+-module(mnesia_rpc).
+-behaviour(gen_server).
+
+-export([start/0,
+ call/4
+ ]).
+
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+ ]).
+
+-include("mnesia.hrl").
+
+start() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [self()],
+ [{timeout, infinity} %%, {debug, [trace]}
+ ]).
+
+call(Node, M, F, Args) ->
+ case ?catch_val({protocol, Node}) of
+ {Ver, _} when Ver < {8,5} ->
+ rpc:call(Node, M, F, Args);
+ _ ->
+ try gen_server:call({?MODULE, Node}, {apply, M, F, Args}, infinity)
+ catch
+ _:Reason -> {badrpc, {'EXIT', Reason}}
+ end
+ end.
+
+init([_Parent]) ->
+ {ok, #{}}.
+
+handle_call({apply, Mod, Fun, Args}, _From, State) ->
+ %% rpc is just for ets:lookups so no need to spawn requests
+ Result = try apply(Mod, Fun, Args)
+ catch throw:Res -> Res;
+ _:Reason -> {badrpc, {'EXIT', Reason}}
+ end,
+ {reply, Result, State};
+handle_call(Msg, _From, State) ->
+ error("~p got unexpected call: ~tp~n", [?MODULE, Msg]),
+ {reply, badop, State}.
+
+handle_cast(Msg, State) ->
+ mnesia_lib:error("~p got unexpected cast: ~tp~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+handle_info(Msg, State) ->
+ mnesia_lib:error("~p got unexpected info: ~tp~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index d0f5d0e07b..4a65f3996a 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -1271,7 +1271,7 @@ verify_nodes(Cs) ->
lists:foreach(AtomCheck, Nodes).
verify(Expected, Fun, Error) when is_function(Fun) ->
- do_verify(Expected, catch Fun(), Error);
+ do_verify(Expected, ?CATCH(Fun()), Error);
verify(Expected, Actual, Error) ->
do_verify(Expected, Actual, Error).
diff --git a/lib/mnesia/src/mnesia_sp.erl b/lib/mnesia/src/mnesia_sp.erl
index d65c659690..0c56107c41 100644
--- a/lib/mnesia/src/mnesia_sp.erl
+++ b/lib/mnesia/src/mnesia_sp.erl
@@ -30,12 +30,15 @@
init_proc(Who, Mod, Fun, Args) ->
mnesia_lib:verbose("~p starting: ~p~n", [Who, self()]),
- case catch apply(Mod, Fun, Args) of
- {'EXIT', Reason} ->
- mnesia_monitor:terminate_proc(Who, Reason, Args),
+ try
+ apply(Mod, Fun, Args)
+ catch
+ exit:Reason when Reason =:= shutdown; Reason =:= kill; Reason =:= normal ->
+ mnesia_monitor:terminate_proc(Who, Reason, Args),
exit(Reason);
- Other ->
- Other
+ _:Reason:ST ->
+ mnesia_monitor:terminate_proc(Who, {Reason,ST}, Args),
+ exit(Reason)
end.
diff --git a/lib/mnesia/src/mnesia_sup.erl b/lib/mnesia/src/mnesia_sup.erl
index 3e5792900b..fd5156d2a4 100644
--- a/lib/mnesia/src/mnesia_sup.erl
+++ b/lib/mnesia/src/mnesia_sup.erl
@@ -88,7 +88,7 @@ add_event_handler() ->
kill() ->
Mnesia = [mnesia_fallback | mnesia:ms()],
- Kill = fun(Name) -> catch exit(whereis(Name), kill) end,
+ Kill = fun(Name) -> try exit(whereis(Name), kill) catch _:_ -> ok end end,
lists:foreach(Kill, Mnesia),
lists:foreach(fun ensure_dead/1, Mnesia),
timer:sleep(10),
diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl
index cc21621ff4..ee31fdfd27 100644
--- a/lib/mnesia/src/mnesia_text.erl
+++ b/lib/mnesia/src/mnesia_text.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -170,7 +170,7 @@ read_terms(Stream, File, Line, L) ->
end.
read_term_from_stream(Stream, File, Line) ->
- R = io:request(Stream, {get_until,'',erl_scan,tokens,[Line]}),
+ R = io:request(Stream, {get_until,latin1,'',erl_scan,tokens,[Line]}),
case R of
{ok,Toks,EndLine} ->
case erl_parse:parse_term(Toks) of
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 18520423ba..41e7ddce87 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -2082,9 +2082,9 @@ ask_commit(_Protocol, _Tid, [], _DiscNs, _RamNs, WaitFor, Local) ->
{WaitFor, Local}.
convert_old(sync_asym_trans, Node) ->
- case mnesia_monitor:needs_protocol_conversion(Node) of
- true -> asym_trans;
- false -> sync_asym_trans
+ case ?catch_val({protocol, Node}) of
+ {{8,3}, _} -> asym_trans;
+ _ -> sync_asym_trans
end;
convert_old(Protocol, _) ->
Protocol.
diff --git a/lib/observer/Makefile b/lib/observer/Makefile
index 8483922f76..ffd73051b9 100644
--- a/lib/observer/Makefile
+++ b/lib/observer/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=runtime_tools et wx
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/observer/doc/src/Makefile b/lib/observer/doc/src/Makefile
index e843772f0b..459aeeea0b 100644
--- a/lib/observer/doc/src/Makefile
+++ b/lib/observer/doc/src/Makefile
@@ -27,16 +27,11 @@ VSN=$(OBSERVER_VSN)
APPLICATION=observer
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
XML_REF1_FILES = \
- cdv.xml
+ cdv_cmd.xml
XML_REF3_FILES = \
crashdump.xml \
observer.xml \
@@ -57,91 +52,16 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF1_FILES) $(XML_REF3_FILES) \
$(XML_APPLICATION_FILES) $(XML_REF6_FILES)
-ONLY_HTML_FILE =
-
-GIF_FILES = \
+IMAGE_FILES = \
et_processes.gif \
et_modsprocs.gif
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
+NO_CHUNKS = crashdump.xml
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE) $(ONLY_HTML_FILE:%=$(HTMLDIR)/%)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-$(HTMLDIR)/$(ONLY_HTML_FILE):
- $(INSTALL_DATA) $(ONLY_HTML_FILE) $@
-
-man: $(MAN1_FILES) $(MAN3_FILES) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
-
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/observer/doc/src/cdv.xml b/lib/observer/doc/src/cdv_cmd.xml
index df1032780a..32f8f392cd 100644
--- a/lib/observer/doc/src/cdv.xml
+++ b/lib/observer/doc/src/cdv_cmd.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
diff --git a/lib/observer/doc/src/ref_man.xml b/lib/observer/doc/src/ref_man.xml
index 73e7e0053a..8ba920ecfb 100644
--- a/lib/observer/doc/src/ref_man.xml
+++ b/lib/observer/doc/src/ref_man.xml
@@ -36,6 +36,6 @@
<xi:include href="ttb.xml"/>
<xi:include href="etop.xml"/>
<xi:include href="crashdump.xml"/>
- <xi:include href="cdv.xml"/>
+ <xi:include href="cdv_cmd.xml"/>
</application>
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index fee95e0b21..ae5cc10b6e 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -277,8 +277,21 @@ ttb:p(all, call).</input></pre>
</func>
<func>
- <name since="">tp, tpl, ctp, ctpl, ctpg</name>
- <name since="OTP 19.0">tpe, ctpe</name>
+ <name since="">tp(Module [, Function [, Arity]], MatchSpec)</name>
+ <name since="">tp({Module, Function , Arity}, MatchSpec)</name>
+ <name since="">tpl(Module [, Function [, Arity]], MatchSpec)</name>
+ <name since="">tpl({Module, Function , Arity}, MatchSpec)</name>
+ <name since="">ctp()</name>
+ <name since="">ctp(Module [, Function [, Arity]])</name>
+ <name since="">ctp({Module, Function, Arity})</name>
+ <name since="">ctpl()</name>
+ <name since="">ctpl(Module [, Function [, Arity]])</name>
+ <name since="">ctpl({Module, Function, Arity})</name>
+ <name since="">ctpg()</name>
+ <name since="">ctpg(Module [, Function [, Arity]])</name>
+ <name since="">ctpg({Module, Function, Arity})</name>
+ <name since="OTP 19.0">tpe(Event,MatchSpec)</name>
+ <name since="OTP 19.0">ctpe(Event)</name>
<fsummary>Set and clear trace patterns.</fsummary>
<desc>
<p>These functions are to be used with trace
diff --git a/lib/observer/doc/src/ttb_ug.xml b/lib/observer/doc/src/ttb_ug.xml
index 34591ae8de..bc44b60bf4 100644
--- a/lib/observer/doc/src/ttb_ug.xml
+++ b/lib/observer/doc/src/ttb_ug.xml
@@ -88,8 +88,8 @@
<item><p>If you want to trace function calls (that is, if you have
trace flag <c>call</c> set on any process), you must
also set trace patterns on the required function(s) with
- <seealso marker="ttb#/0"><c>ttb:tp/2,3,4</c></seealso> or
- <seealso marker="ttb#/0"><c>ttb:tpl/2,3,4</c></seealso>.
+ <seealso marker="ttb#tp/2"><c>ttb:tp/2,3,4</c></seealso> or
+ <seealso marker="ttb#tpl/2"><c>ttb:tpl/2,3,4</c></seealso>.
A function is only traced
if it has a trace pattern. The trace pattern specifies how to trace the
function by using match specifications. Match specifications are
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index d48b846ad2..55de6de0c6 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -66,7 +66,7 @@
{registered, []},
{applications, [kernel, stdlib]},
{env, []},
- {runtime_dependencies, ["wx-1.2","stdlib-3.5","runtime_tools-1.8.14",
- "kernel-3.0","et-1.5","erts-7.0"]}]}.
+ {runtime_dependencies, ["wx-1.2","stdlib-@OTP-15251@","runtime_tools-1.8.14",
+ "kernel-@OTP-15251@","et-1.5","erts-@OTP-15251@"]}]}.
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index da47a30fb1..1b144e05dc 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -261,20 +261,31 @@ sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) ->
sum_alloc_instances([],BS,CS,TotalBS,TotalCS) ->
{BS,CS,TotalBS,TotalCS,true}.
-sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
- Rest],OldBS,OldCS,TotalBS,TotalCS) ->
- sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS);
-sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
+sum_alloc_one_instance([{_,[{blocks,TypedBlocks},{carriers_size,CS,_,_}]}|
Rest],OldBS,OldCS,TotalBS,TotalCS) ->
+ %% OTP 23 and later.
+ BS = sum_alloc_block_list(TypedBlocks, 0),
sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
-sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}|
+sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
Rest],OldBS,OldCS,TotalBS,TotalCS) ->
+ %% OTP 22 and earlier.
sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) ->
sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS);
sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) ->
{BS,CS,TotalBS,TotalCS}.
+sum_alloc_block_list([{_Type, [{size, Current, _, _}]} | Rest], Acc) ->
+ %% We ignore the type since we're returning a summary of all blocks in the
+ %% carriers employed by a certain instance.
+ sum_alloc_block_list(Rest, Current + Acc);
+sum_alloc_block_list([{_Type, [{size, Current}]} | Rest], Acc) ->
+ sum_alloc_block_list(Rest, Current + Acc);
+sum_alloc_block_list([_ | Rest], Acc) ->
+ sum_alloc_block_list(Rest, Acc);
+sum_alloc_block_list([], Acc) ->
+ Acc.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
create_mem_info(Parent) ->
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index 10d88c994a..5c77330e49 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -152,6 +152,7 @@ dump_maps() ->
Pid = spawn_link(F),
receive
{Pid,done} ->
+ unlink(Pid),
{ok,Pid}
end.
@@ -198,6 +199,7 @@ dump_persistent_terms() ->
Pid = spawn_link(F),
receive
{Pid,done} ->
+ unlink(Pid),
{ok,Pid}
end.
diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl
index 33133dd78d..5d1a5d8dc2 100644
--- a/lib/observer/test/ttb_SUITE.erl
+++ b/lib/observer/test/ttb_SUITE.erl
@@ -658,11 +658,29 @@ seq_trace(Config) when is_list(Config) ->
?line ok = ttb:format(
[filename:join(Privdir,atom_to_list(Node)++"-seq_trace")]),
?line [{trace_ts,StartProc,call,{?MODULE,seq,[]},{_,_,_}},
- {seq_trace,0,{send,{0,1},StartProc,P1Proc,{Start,P2}}},
- {seq_trace,0,{send,{1,2},P1Proc,P2Proc,{P1,Start}}},
- {seq_trace,0,{send,{2,3},P2Proc,StartProc,{P2,P1}}},
+ {seq_trace,0,{send,{First, Seq0},StartProc,P1Proc,SpawnRequest1}},
+ {seq_trace,0,{send,{Seq0, Seq1},P1Proc,StartProc,SpawnReply1}},
+ {seq_trace,0,{send,{Seq2, Seq3},StartProc,P2Proc,SpawnRequest2}},
+ {seq_trace,0,{send,{Seq3, Seq4},P2Proc,StartProc,SpawnReply2}},
+ {seq_trace,0,{send,{Seq5, Seq6},StartProc,P1Proc,{Start,P2}}},
+ {seq_trace,0,{send,{Seq6, Seq7},P1Proc,P2Proc,{P1,Start}}},
+ {seq_trace,0,{send,{Seq7, Last},P2Proc,StartProc,{P2,P1}}},
end_of_trace] = flush(),
-
+ spawn_request = element(1, SpawnRequest1),
+ SReq1 = element(2, SpawnRequest1),
+ spawn_reply = element(1, SpawnReply1),
+ SReq1 = element(2, SpawnReply1),
+ spawn_request = element(1, SpawnRequest2),
+ SReq2 = element(2, SpawnRequest2),
+ spawn_reply = element(1, SpawnReply2),
+ SReq2 = element(2, SpawnReply2),
+ true = First < Seq0,
+ true = Seq0 < Seq1,
+ true = Seq1 < Seq2,
+ true = Seq2 < Seq3,
+ true = Seq4 < Seq5,
+ true = Seq6 < Seq7,
+ true = Seq7 < Last,
%% Additional test for metatrace
case StartProc of
{Start,_,_} -> ok;
diff --git a/lib/odbc/Makefile b/lib/odbc/Makefile
index f7816c25fc..ee39992a84 100644
--- a/lib/odbc/Makefile
+++ b/lib/odbc/Makefile
@@ -69,12 +69,6 @@ do_configure: configure
configure: configure.in
autoconf
-.PHONY: info
-
-info:
- @echo "ODBC_VSN: $(ODBC_VSN)"
-
-
# ----------------------------------------------------
# Application (source) release targets
# ----------------------------------------------------
@@ -114,3 +108,5 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in
index 294d832797..3c16e7e294 100644
--- a/lib/odbc/c_src/Makefile.in
+++ b/lib/odbc/c_src/Makefile.in
@@ -54,14 +54,14 @@ RELSYSDIR = $(RELEASE_PATH)/lib/odbc-$(VSN)
EI_ROOT = $(ERL_TOP)/lib/erl_interface
EI_INCLUDE = -I$(EI_ROOT)/include -I$(EI_ROOT)/include/$(TARGET)
ifeq ($(findstring win32,$(TARGET)),win32)
-EI_LIB = -lerl_interface_md -lei_md
+EI_LIB = -lei_md
ENTRY_OBJ=$(ERL_TOP)/erts/obj/$(TARGET)/port_entry.o
PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
WIN32_TARGET = $(WIN_BIN_DIR)/odbcserver.exe
EXE_TARGET = $(WIN32_TARGET)
else
-EI_LIB = -lerl_interface -lei
+EI_LIB = -lei
UNIX_TARGET = $(BIN_DIR)/odbcserver
EXE_TARGET = $(UNIX_TARGET)
endif
diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile
index a6311ceede..b66341f9eb 100644
--- a/lib/odbc/doc/src/Makefile
+++ b/lib/odbc/doc/src/Makefile
@@ -30,11 +30,6 @@ VSN=$(ODBC_VSN)
APPLICATION=odbc
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -57,75 +52,8 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES = \
+IMAGE_FILES = \
odbc_app_arc.gif
# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_HTML_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 +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-$(HTMLDIR)/%.gif: %.gif # Copy them to ../html
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
-
-debug opt:
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/os_mon/Makefile b/lib/os_mon/Makefile
index 40ce94e0c7..f45065a79d 100644
--- a/lib/os_mon/Makefile
+++ b/lib/os_mon/Makefile
@@ -35,3 +35,4 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile
index 8e9a4c333c..d16f2b4831 100644
--- a/lib/os_mon/doc/src/Makefile
+++ b/lib/os_mon/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(OS_MON_VSN)
APPLICATION=os_mon
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -49,7 +44,7 @@ XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
-GIF_FILES =
+IMAGE_FILES =
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
@@ -57,66 +52,4 @@ XML_FILES = \
# ----------------------------------------------------
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# 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) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile
index 923a31f290..98c5ced068 100644
--- a/lib/os_mon/src/Makefile
+++ b/lib/os_mon/src/Makefile
@@ -34,7 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/os_mon-$(VSN)
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES= disksup memsup cpu_sup os_mon os_sup os_mon_sysinfo nteventlog
+MODULES= disksup memsup cpu_sup os_mon os_mon_mib os_sup os_mon_sysinfo nteventlog
INCLUDE=../include
CSRC=../c_src
diff --git a/lib/os_mon/src/os_mon.app.src b/lib/os_mon/src/os_mon.app.src
index 6c9b0d7576..894f63e227 100644
--- a/lib/os_mon/src/os_mon.app.src
+++ b/lib/os_mon/src/os_mon.app.src
@@ -21,7 +21,7 @@
{application, os_mon,
[{description, "CPO CXC 138 46"},
{vsn, "%VSN%"},
- {modules, [os_mon, os_sup,
+ {modules, [os_mon, os_mon_mib, os_sup,
disksup, memsup, cpu_sup, os_mon_sysinfo, nteventlog]},
{registered, [os_mon_sup, os_mon_sysinfo, disksup, memsup, cpu_sup,
os_sup_server]},
diff --git a/lib/os_mon/src/os_mon_mib.erl b/lib/os_mon/src/os_mon_mib.erl
new file mode 100644
index 0000000000..8a171b6071
--- /dev/null
+++ b/lib/os_mon/src/os_mon_mib.erl
@@ -0,0 +1,27 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This module was removed entirely in OTP 22.0, it's only retained for its
+%% removal warning attribute.
+%%
+
+-module(os_mon_mib).
+-removed([{'_','_', "this module was removed in OTP 22.0"}]).
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index ba28f31f26..7a8065c591 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -155,46 +155,72 @@ tiny_diff(A, B) ->
-define(SPIN_TIME, 1000).
+spinner(Parent) ->
+ receive
+ stop -> Parent ! stopped
+ after 0 -> spinner(Parent)
+ end.
+
%% Test utilization values
util_values(Config) when is_list(Config) ->
-
+ NrOfProcessors =
+ case erlang:system_info(logical_processors_available) of
+ unknown -> 2;
+ X -> X
+ end,
Tester = self(),
Ref = make_ref(),
- Loop = fun (L) -> L(L) end,
Spinner = fun () ->
- Looper = spawn_link(fun () -> Loop(Loop) end),
+ Spinner = self(),
+ NrOfProcesses = NrOfProcessors,
+ Loopers =
+ [spawn_link(fun () -> spinner(Spinner) end)
+ || _ <- lists:seq(1,NrOfProcesses)],
receive after ?SPIN_TIME -> ok end,
- unlink(Looper),
- exit(Looper, kill),
+ [begin
+ Looper ! stop,
+ receive stopped -> ok end
+ end
+ || Looper <- Loopers],
Tester ! Ref
end,
-
+ Spin = fun () ->
+ spawn_link(Spinner),
+ receive Ref -> ok end
+ end,
cpu_sup:util(),
-
- spawn_link(Spinner),
- receive Ref -> ok end,
- HighUtil1 = cpu_sup:util(),
-
receive after ?SPIN_TIME -> ok end,
- LowUtil1 = cpu_sup:util(),
+ LowUtil0 = cpu_sup:util(),
+ case LowUtil0 of
+ U when U > ((100.0 / NrOfProcessors) * 0.33) ->
+ %% We cannot run this test if the system is doing other
+ %% work at the same time as the result will be unreliable
+ {skip, io_lib:format("CPU utilization was too high (~f%)", [LowUtil0])};
+ _ ->
+ cpu_sup:util(),
+ Spin(),
+ HighUtil1 = cpu_sup:util(),
- spawn_link(Spinner),
- receive Ref -> ok end,
- HighUtil2 = cpu_sup:util(),
+ receive after ?SPIN_TIME -> ok end,
+ LowUtil1 = cpu_sup:util(),
- receive after ?SPIN_TIME -> ok end,
- LowUtil2 = cpu_sup:util(),
+ Spin(),
+ HighUtil2 = cpu_sup:util(),
- Utils = [{high1,HighUtil1}, {low1,LowUtil1},
- {high2,HighUtil2}, {low2,LowUtil2}],
- io:format("Utils: ~p~n", [Utils]),
+ receive after ?SPIN_TIME -> ok end,
+ LowUtil2 = cpu_sup:util(),
- false = LowUtil1 > HighUtil1,
- false = LowUtil1 > HighUtil2,
- false = LowUtil2 > HighUtil1,
- false = LowUtil2 > HighUtil2,
+ Utils = [{high1,HighUtil1}, {low1,LowUtil1},
+ {high2,HighUtil2}, {low2,LowUtil2}],
+ io:format("Utils: ~p~n", [Utils]),
- ok.
+ false = LowUtil1 > HighUtil1,
+ false = LowUtil1 > HighUtil2,
+ false = LowUtil2 > HighUtil1,
+ false = LowUtil2 > HighUtil2,
+
+ ok
+ end.
% Outdated
diff --git a/lib/parsetools/Makefile b/lib/parsetools/Makefile
index e9de5c43cb..2ddb06feb1 100644
--- a/lib/parsetools/Makefile
+++ b/lib/parsetools/Makefile
@@ -37,3 +37,4 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile
index 2e8b232902..a4d7d4381b 100644
--- a/lib/parsetools/doc/src/Makefile
+++ b/lib/parsetools/doc/src/Makefile
@@ -29,11 +29,6 @@ VSN=$(PARSETOOLS_VSN)
APPLICATION=parsetools
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -48,72 +43,10 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES =
+IMAGE_FILES =
XML_HTML_FILES = \
notes_history.xml
# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_HTML_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-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 +=
-DVIPS_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)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/parsetools/doc/src/leex.xml b/lib/parsetools/doc/src/leex.xml
index 3b82f60201..59a6c87586 100644
--- a/lib/parsetools/doc/src/leex.xml
+++ b/lib/parsetools/doc/src/leex.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2009</year><year>2017</year>
+ <year>2009</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -146,8 +146,8 @@ Token = tuple()</code>
<funcs>
<func>
- <name since="">string(String) -> StringRet</name>
- <name since="">string(String, StartLine) -> StringRet</name>
+ <name since="">Module:string(String) -> StringRet</name>
+ <name since="">Module:string(String, StartLine) -> StringRet</name>
<fsummary>Generated by Leex</fsummary>
<type>
<v>String = string()</v>
@@ -164,9 +164,9 @@ Token = tuple()</code>
</func>
<func>
- <name since="">token(Cont, Chars) -> {more,Cont1} | {done,TokenRet,RestChars}
+ <name since="">Module:token(Cont, Chars) -> {more,Cont1} | {done,TokenRet,RestChars}
</name>
- <name since="">token(Cont, Chars, StartLine) -> {more,Cont1}
+ <name since="">Module:token(Cont, Chars, StartLine) -> {more,Cont1}
| {done,TokenRet,RestChars}
</name>
<fsummary>Generated by Leex</fsummary>
@@ -193,15 +193,15 @@ Token = tuple()</code>
but used through the i/o system where it can typically be
called in an application by:</p>
<code>
-io:request(InFile, {get_until,Prompt,Module,token,[Line]})
+io:request(InFile, {get_until,unicode,Prompt,Module,token,[Line]})
-> TokenRet</code>
</desc>
</func>
<func>
- <name since="">tokens(Cont, Chars) -> {more,Cont1} | {done,TokensRet,RestChars}
+ <name since="">Module:tokens(Cont, Chars) -> {more,Cont1} | {done,TokensRet,RestChars}
</name>
- <name since="">tokens(Cont, Chars, StartLine) ->
+ <name since="">Module:tokens(Cont, Chars, StartLine) ->
{more,Cont1} | {done,TokensRet,RestChars}
</name>
<fsummary>Generated by Leex</fsummary>
@@ -240,7 +240,7 @@ io:request(InFile, {get_until,Prompt,Module,token,[Line]})
but used through the i/o system where it can typically be
called in an application by:</p>
<code>
-io:request(InFile, {get_until,Prompt,Module,tokens,[Line]})
+io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Line]})
-> TokensRet</code>
</desc>
</func>
diff --git a/lib/parsetools/doc/src/ref_man.xml b/lib/parsetools/doc/src/ref_man.xml
index a06221250f..a2c82eb711 100644
--- a/lib/parsetools/doc/src/ref_man.xml
+++ b/lib/parsetools/doc/src/ref_man.xml
@@ -31,9 +31,9 @@
</header>
<description>
<p>The <em>Parsetools</em> application contains utilities for
- parsing and scanning. Yecc is an <term id="LALR-1"></term>parser
- generator for Erlang, similar to yacc. Yecc takes a <term
- id="BNF"></term>grammar definition as input, and produces Erlang
+ parsing and scanning. Yecc is an LALR-1 parser
+ generator for Erlang, similar to yacc. Yecc takes a BNF grammar
+ definition as input, and produces Erlang
code for a parser as output. Leex is a regular expression based
lexical analyzer generator for Erlang, similar to lex or flex.</p>
</description>
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index 3343a4282b..f44b1da861 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1345,7 +1345,7 @@ make_rule_pointer_info(StC, RpRhs, RuleIndex) ->
rp_info([], _SymbolTab, _LcTab, _RuleIndex) ->
[];
rp_info([Category | Followers], SymbolTab, LcTab, RuleIndex) ->
- case dict:find(Category, RuleIndex) of
+ case maps:find(Category, RuleIndex) of
error -> % terminal
[];
{ok, ExpandingRules} when Followers =:= [] ->
@@ -1364,7 +1364,7 @@ rp_info([Category | Followers], SymbolTab, LcTab, RuleIndex) ->
make_lookahead([], _, _, LA) ->
{empty, LA};
make_lookahead([Symbol | Symbols], SymbolTab, LcTab, LA) ->
- case dict:find(Symbol, LcTab) of
+ case maps:find(Symbol, LcTab) of
{ok, LeftCorner} -> % nonterminal
case empty_member(LeftCorner) of
true ->
@@ -1377,7 +1377,7 @@ make_lookahead([Symbol | Symbols], SymbolTab, LcTab, LA) ->
set_add(Symbol, LA)
end.
-%% -> dict-of({Nonterminal, [Terminal]}).
+%% -> map-of({Nonterminal, [Terminal]}).
%% The algorithm FIRST/1 from the Dragon Book.
%% Left corner table, all terminals (including '$empty') that can
%% begin strings generated by Nonterminal.
@@ -1386,15 +1386,15 @@ make_left_corner_table(#yecc{rules_list = RulesList} = St) ->
Rules = map(fun(#rule{symbols = [Lhs | Rhs]}) ->
{Lhs,{Lhs, Rhs}}
end, RulesList),
- LeftHandTab = dict:from_list(family(Rules)),
+ LeftHandTab = maps:from_list(family(Rules)),
X0 = [{S,H} || {H,{H,Rhs}} <- Rules,
S <- Rhs,
not is_terminal(SymbolTab, S)],
XL = family_with_domain(X0, St#yecc.nonterminals),
- X = dict:from_list(XL),
- Xref = fun(NT) -> dict:fetch(NT, X) end,
+ X = maps:from_list(XL),
+ Xref = fun(NT) -> maps:get(NT, X) end,
E = set_empty(),
- LC0 = dict:from_list([{H, E} || {H,_} <- XL]),
+ LC0 = maps:from_list([{H, E} || {H,_} <- XL]),
%% Handle H -> a S, where a is a terminal ('$empty' inclusive).
{Q, LC1} =
foldl(fun({H,{H,[S | _]}}, {Q0, LC}) ->
@@ -1413,7 +1413,7 @@ left_corners(Q0, LC0, LeftHandTab, SymbolTab, Xref) ->
[] ->
LC0;
Q1 ->
- Rs = flatmap(fun(NT) -> dict:fetch(NT, LeftHandTab) end, Q1),
+ Rs = flatmap(fun(NT) -> maps:get(NT, LeftHandTab) end, Q1),
{LC, Q} = left_corners2(Rs, LC0, [], SymbolTab, Xref),
left_corners(Q, LC, LeftHandTab, SymbolTab, Xref)
end.
@@ -1422,7 +1422,7 @@ left_corners2([], LC, Q, _SymbolTab, _Xref) ->
{LC, Q};
left_corners2([{Head,Rhs} | Rs], LC, Q0, SymbolTab, Xref) ->
Ts = left_corner_rhs(Rhs, Head, LC, set_empty(), SymbolTab),
- First0 = dict:fetch(Head, LC),
+ First0 = maps:get(Head, LC),
case set_is_subset(Ts, First0) of
true ->
left_corners2(Rs, LC, Q0, SymbolTab, Xref);
@@ -1432,14 +1432,14 @@ left_corners2([{Head,Rhs} | Rs], LC, Q0, SymbolTab, Xref) ->
end.
upd_first(NT, Ts, LC) ->
- dict:update(NT, fun(First) -> set_union(First, Ts) end, LC).
+ maps:update_with(NT, fun(First) -> set_union(First, Ts) end, LC).
left_corner_rhs([S | Ss], Head, LC, Ts, SymbolTab) ->
case ets:lookup(SymbolTab, S) of
[{_,Num}=SymbolAndNum] when Num >= 0 ->
set_add_terminal(SymbolAndNum, Ts);
[_NonTerminalSymbol] ->
- First = dict:fetch(S, LC),
+ First = maps:get(S, LC),
case empty_member(First) of
true ->
NTs = set_union(empty_delete(First), Ts),
@@ -1466,7 +1466,7 @@ make_rule_index(#yecc{nonterminals = Nonterminals,
Symbol2Rule = [{Foo,R} || #rule{symbols = Symbols}=R <- RulesListNoCodes,
Foo <- Symbols],
Pointer2Rule = [{I, R} || {{_Foo,R},I} <- count(1, Symbol2Rule)],
- {dict:from_list(IndexedTab), dict:from_list(Pointer2Rule)}.
+ {maps:from_list(IndexedTab), maps:from_list(Pointer2Rule)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Computing parse action table from list of states and goto table:
@@ -2309,14 +2309,14 @@ function_name(St, Name, Suf) ->
rule(RulePointer, St) ->
#rule{n = N, anno = Anno, symbols = Symbols} =
- dict:fetch(RulePointer, St#yecc.rule_pointer2rule),
+ maps:get(RulePointer, St#yecc.rule_pointer2rule),
{Symbols, Anno, N}.
get_rule(RuleNmbr, St) ->
- dict:fetch(RuleNmbr, St#yecc.rule_pointer2rule).
+ maps:get(RuleNmbr, St#yecc.rule_pointer2rule).
tokens(RuleNmbr, St) ->
- Rule = dict:fetch(RuleNmbr, St#yecc.rule_pointer2rule),
+ Rule = maps:get(RuleNmbr, St#yecc.rule_pointer2rule),
Rule#rule.tokens.
goto(From, Symbol, St) ->
diff --git a/lib/public_key/Makefile b/lib/public_key/Makefile
index 7a5c1c1443..ed8901a8af 100644
--- a/lib/public_key/Makefile
+++ b/lib/public_key/Makefile
@@ -38,3 +38,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=asn crypto
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile
index c8647750af..6a694806bc 100644
--- a/lib/public_key/doc/src/Makefile
+++ b/lib/public_key/doc/src/Makefile
@@ -30,10 +30,6 @@ VSN=$(PUBLIC_KEY_VSN)
APPLICATION=public_key
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -52,107 +48,8 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
$(XML_REF6_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES =
-
-# ----------------------------------------------------
-
-TOP_HTML_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_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-SPECS_FLAGS = -I../../include -I../../src -I../../..
-
-# ----------------------------------------------------
-# 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)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECS_FILES)
- rm -f errs core *~
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
-release_spec:
-info:
- @echo "GIF_FILES:\n$(GIF_FILES)"
- @echo ""
- @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
- @echo ""
- @echo "HTML_FILES:\n$(HTML_FILES)"
- @echo ""
- @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
- @echo ""
- @echo "DEFAULT_GIF_FILES:\n$(DEFAULT_GIF_FILES)"
- @echo ""
- @echo "DEFAULT_HTML_FILES:\n$(DEFAULT_HTML_FILES)"
- @echo ""
- @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
- @echo ""
- @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
- @echo ""
- @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
- @echo ""
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl
index 6003bf21d0..75c17025ab 100644
--- a/lib/public_key/src/pubkey_pbe.erl
+++ b/lib/public_key/src/pubkey_pbe.erl
@@ -39,23 +39,22 @@
%%--------------------------------------------------------------------
encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, block_size(des_cbc)));
+ crypto:crypto_one_time(des_cbc, Key, IV, pbe_pad(Data, block_size(des_cbc)), true);
encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
- crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data, block_size(des_3ede)));
+ crypto:crypto_one_time(des_ede3_cbc, Key, IV, pbe_pad(Data, block_size(des_ede3_cbc)), true);
encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, block_size(rc2_cbc)));
+ crypto:crypto_one_time(rc2_cbc, Key, IV, pbe_pad(Data, block_size(rc2_cbc)), true);
encode(Data, Password, "AES-128-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(aes_128_cbc, Key, IV, pbe_pad(Data, block_size(aes_128_cbc)));
+ crypto:crypto_one_time(aes_128_cbc, Key, IV, pbe_pad(Data, block_size(aes_128_cbc)), true);
encode(Data, Password, "AES-192-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(aes_192_cbc, Key, IV, pbe_pad(Data, block_size(aes_192_cbc)));
+ crypto:crypto_one_time(aes_192_cbc, Key, IV, pbe_pad(Data, block_size(aes_192_cbc)), true);
encode(Data, Password, "AES-256-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(aes_256_cbc, Key, IV, pbe_pad(Data, block_size(aes_256_cbc))).
+ crypto:crypto_one_time(aes_256_cbc, Key, IV, pbe_pad(Data, block_size(aes_256_cbc)), true).
%%--------------------------------------------------------------------
-spec decode(binary(), string(), string(), term()) -> binary().
@@ -64,23 +63,22 @@ encode(Data, Password, "AES-256-CBC"= Cipher, KeyDevParams) ->
%%--------------------------------------------------------------------
decode(Data, Password,"DES-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_decrypt(des_cbc, Key, IV, Data);
+ crypto:crypto_one_time(des_cbc, Key, IV, Data, false);
decode(Data, Password,"DES-EDE3-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
- crypto:block_decrypt(des3_cbc, [Key1, Key2, Key3], IV, Data);
+ crypto:crypto_one_time(des_ede3_cbc, Key, IV, Data, false);
decode(Data, Password,"RC2-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_decrypt(rc2_cbc, Key, IV, Data);
+ crypto:crypto_one_time(rc2_cbc, Key, IV, Data, false);
decode(Data, Password,"AES-128-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_decrypt(aes_128_cbc, Key, IV, Data);
+ crypto:crypto_one_time(aes_128_cbc, Key, IV, Data, false);
decode(Data, Password,"AES-192-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_decrypt(aes_192_cbc, Key, IV, Data);
+ crypto:crypto_one_time(aes_192_cbc, Key, IV, Data, false);
decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_decrypt(aes_256_cbc, Key, IV, Data).
+ crypto:crypto_one_time(aes_256_cbc, Key, IV, Data, false).
%%--------------------------------------------------------------------
-spec pbdkdf1(iodata(), iodata(), integer(), atom()) -> binary().
@@ -247,17 +245,21 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc,
%% ?'id-hmacWithSHA1, but we need some kind of ASN1-fix for this.
pseudo_random_function(#'PBKDF2-params_prf'{algorithm =
{_,_, _,'id-hmacWithSHA1'}}) ->
- {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')};
+ {fun hmac4/4, sha, pseudo_output_length(?'id-hmacWithSHA1')};
pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1' = Algo}) ->
- {fun crypto:hmac/4, sha, pseudo_output_length(Algo)};
+ {fun hmac4/4, sha, pseudo_output_length(Algo)};
pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA224'= Algo}) ->
- {fun crypto:hmac/4, sha224, pseudo_output_length(Algo)};
+ {fun hmac4/4, sha224, pseudo_output_length(Algo)};
pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256' = Algo}) ->
- {fun crypto:hmac/4, sha256, pseudo_output_length(Algo)};
+ {fun hmac4/4, sha256, pseudo_output_length(Algo)};
pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA384' = Algo}) ->
- {fun crypto:hmac/4, sha384, pseudo_output_length(Algo)};
+ {fun hmac4/4, sha384, pseudo_output_length(Algo)};
pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA512' = Algo}) ->
- {fun crypto:hmac/4, sha512, pseudo_output_length(Algo)}.
+ {fun hmac4/4, sha512, pseudo_output_length(Algo)}.
+
+hmac4(SubType, Key, Data, MacLength) ->
+ crypto:macN(hmac, SubType, Key, Data, MacLength).
+
pseudo_output_length(?'id-hmacWithSHA1') ->
20; %%160/8
@@ -295,7 +297,7 @@ derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC");
block_size(Cipher) when Cipher == rc2_cbc;
Cipher == des_cbc;
- Cipher == des_3ede ->
+ Cipher == des_ede3_cbc ->
8;
block_size(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_192_cbc;
diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl
index 61db282dfa..75e067532c 100644
--- a/lib/public_key/test/pbe_SUITE.erl
+++ b/lib/public_key/test/pbe_SUITE.erl
@@ -114,7 +114,7 @@ pbdkdf2(Config) when is_list(Config) ->
<<16#0c, 16#60, 16#c8, 16#0f, 16#96, 16#1f, 16#0e, 16#71,
16#f3, 16#a9, 16#b5, 16#24, 16#af, 16#60, 16#12, 16#06,
- 16#2f, 16#e0, 16#37, 16#a6>> = pubkey_pbe:pbdkdf2("password", "salt", 1, 20, fun crypto:hmac/4, sha, 20),
+ 16#2f, 16#e0, 16#37, 16#a6>> = pubkey_pbe:pbdkdf2("password", "salt", 1, 20, fun hmac4/4, sha, 20),
%% Input:
%% P = "password" (8 octets)
@@ -130,7 +130,7 @@ pbdkdf2(Config) when is_list(Config) ->
<<16#ea, 16#6c, 16#01, 16#4d, 16#c7, 16#2d, 16#6f, 16#8c,
16#cd, 16#1e, 16#d9, 16#2a, 16#ce, 16#1d, 16#41, 16#f0,
16#d8, 16#de, 16#89, 16#57>> =
- pubkey_pbe:pbdkdf2("password", "salt", 2, 20, fun crypto:hmac/4, sha, 20),
+ pubkey_pbe:pbdkdf2("password", "salt", 2, 20, fun hmac4/4, sha, 20),
%% Input:
%% P = "password" (8 octets)
@@ -145,7 +145,7 @@ pbdkdf2(Config) when is_list(Config) ->
<<16#4b, 16#00, 16#79, 16#01, 16#b7, 16#65, 16#48, 16#9a,
16#be, 16#ad, 16#49, 16#d9, 16#26, 16#f7, 16#21, 16#d0,
- 16#65, 16#a4, 16#29, 16#c1>> = pubkey_pbe:pbdkdf2("password", "salt", 4096, 20, fun crypto:hmac/4, sha, 20),
+ 16#65, 16#a4, 16#29, 16#c1>> = pubkey_pbe:pbdkdf2("password", "salt", 4096, 20, fun hmac4/4, sha, 20),
%% Input:
%% P = "password" (8 octets)
@@ -161,7 +161,7 @@ pbdkdf2(Config) when is_list(Config) ->
<<16#ee, 16#fe, 16#3d, 16#61, 16#cd, 16#4d, 16#a4, 16#e4,
16#e9, 16#94, 16#5b, 16#3d, 16#6b, 16#a2, 16#15, 16#8c,
- 16#26, 16#34, 16#e9, 16#84>> = pubkey_pbe:pbdkdf2("password", "salt", 16777216, 20, fun crypto:hmac/4, sha, 20),
+ 16#26, 16#34, 16#e9, 16#84>> = pubkey_pbe:pbdkdf2("password", "salt", 16777216, 20, fun hmac4/4, sha, 20),
%% Input:
%% P = "passwordPASSWORDpassword" (24 octets)
@@ -180,7 +180,7 @@ pbdkdf2(Config) when is_list(Config) ->
16#8b, 16#29, 16#1a, 16#96, 16#4c, 16#f2, 16#f0, 16#70,
16#38>>
= pubkey_pbe:pbdkdf2("passwordPASSWORDpassword",
- "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, fun crypto:hmac/4, sha, 20),
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, 25, fun hmac4/4, sha, 20),
%% Input:
%% P = "pass\0word" (9 octets)
@@ -195,7 +195,7 @@ pbdkdf2(Config) when is_list(Config) ->
<<16#56, 16#fa, 16#6a, 16#a7, 16#55, 16#48, 16#09, 16#9d,
16#cc, 16#37, 16#d7, 16#f0, 16#34, 16#25, 16#e0, 16#c3>>
= pubkey_pbe:pbdkdf2("pass\0word",
- "sa\0lt", 4096, 16, fun crypto:hmac/4, sha, 20).
+ "sa\0lt", 4096, 16, fun hmac4/4, sha, 20).
pbes1() ->
[{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES1"}].
@@ -250,3 +250,6 @@ decode_encode_key_file(File, Password, Cipher, Config) ->
strip_ending_newlines(Bin) ->
string:strip(binary_to_list(Bin), right, 10).
+
+hmac4(SubType, Key, Data, MacLength) ->
+ crypto:macN(hmac, SubType, Key, Data, MacLength).
diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl
index 487b3dbe3f..9ddd642d45 100644
--- a/lib/public_key/test/pkits_SUITE.erl
+++ b/lib/public_key/test/pkits_SUITE.erl
@@ -691,8 +691,7 @@ run({Chap, Test, Result}, TA) ->
{ok, _OK} when Result =/= ok ->
?error(" ~p ~p~n Expected ~p got ~p ~n", [Chap, Test, Result, ok]),
fail
- catch Type:Reason ->
- Stack = erlang:get_stacktrace(),
+ catch Type:Reason:Stack ->
io:format("Crash ~p:~p in ~p~n",[Type,Reason,Stack]),
io:format(" ~p ~p Expected ~p ~n", [Chap, Test, Result]),
exit(crash)
diff --git a/lib/reltool/Makefile b/lib/reltool/Makefile
index 4b6aad07b3..0a39d7daa8 100644
--- a/lib/reltool/Makefile
+++ b/lib/reltool/Makefile
@@ -36,3 +36,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=wx sasl
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile
index dce8059616..76cf8b82dd 100644
--- a/lib/reltool/doc/src/Makefile
+++ b/lib/reltool/doc/src/Makefile
@@ -28,84 +28,9 @@ VSN=$(RELTOOL_VSN)
APPLICATION=reltool
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
include files.mk
-# ----------------------------------------------------
-
-XML_FILES = \
- $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-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:images $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES)
-
-images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- for file in $(XML_FILES); do \
- if [ -f $$file\src ]; then \
- rm -f $$file; \
- fi \
- done
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/reltool/doc/src/files.mk b/lib/reltool/doc/src/files.mk
index efd7d8f09c..46288d0f50 100644
--- a/lib/reltool/doc/src/files.mk
+++ b/lib/reltool/doc/src/files.mk
@@ -37,3 +37,6 @@ BOOK_FILES = book.xml
IMAGE_FILES =
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
diff --git a/lib/runtime_tools/Makefile b/lib/runtime_tools/Makefile
index eec1ff379b..4b0f1633ab 100644
--- a/lib/runtime_tools/Makefile
+++ b/lib/runtime_tools/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=mnesia
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml
index 89cbc805d8..e6fa8d484c 100644
--- a/lib/runtime_tools/doc/src/LTTng.xml
+++ b/lib/runtime_tools/doc/src/LTTng.xml
@@ -313,14 +313,6 @@ $ make </code>
<p>All tracepoints are in the domain of <c>org_erlang_otp</c></p>
<p>All Erlang types are the string equivalent in LTTng.</p>
- <p><em>scheduler_poll</em></p>
- <list type="bulleted">
- <item><c>scheduler : integer</c> :: Scheduler ID. Ex. <c>1</c></item>
- <item><c>runnable : integer</c> :: Runnable. Ex. <c>1</c></item>
- </list>
- <p>Example:</p>
- <code type="none">scheduler_poll: { cpu_id = 4 }, { scheduler = 1, runnable = 1 }</code>
-
<p><em>driver_init</em></p>
<list type="bulleted">
<item><c>driver : string</c> :: Driver name. Ex. <c>"tcp_inet"</c></item>
@@ -450,23 +442,6 @@ $ make </code>
<p>Example:</p>
<code type="none">driver_control: { cpu_id = 3 }, { pid = "&lt;0.32767.8191&gt;", port = "#Port&lt;0.0&gt;", driver = "forker", command = 83, bytes = 32 }</code>
- <p><em>aio_pool_get</em></p>
- <list type="bulleted">
- <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
- <item><c>length : integer</c> :: Async queue length. Ex. <c>0</c></item>
- </list>
- <p>Example:</p>
- <code type="none">aio_pool_get: { cpu_id = 4 }, { port = "#Port&lt;0.3614&gt;", length = 0 }</code>
-
- <p><em>aio_pool_put</em></p>
- <list type="bulleted">
- <item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
- <item><c>length : integer</c> :: Async queue length. Ex. <c>-1</c></item>
- </list>
- <p>Async queue length is not defined for <c>put</c> operations.</p>
- <p>Example:</p>
- <code type="none">aio_pool_put: { cpu_id = 3 }, { port = "#Port&lt;0.3614&gt;", length = -1 }</code>
-
<p><em>carrier_create</em></p>
<list type="bulleted">
<item><c>type : string</c> :: Carrier type. Ex. <c>"ets_alloc"</c></item>
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index 2399ed51e0..53c3cc9d26 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -33,11 +33,6 @@ VSN=$(RUNTIME_TOOLS_VSN)
APPLICATION=runtime_tools
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -64,83 +59,17 @@ XML_FILES = \
XML_GEN_FILES = $(GENERATED_XML_FILES:%=$(XMLDIR)/%)
-GIF_FILES =
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+IMAGE_FILES =
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-SPECS_ESRC = ../../src
-
-SPECS_FLAGS = -I../../include -I../../../kernel/src
-
-# ----------------------------------------------------
# Targets
# ----------------------------------------------------
$(XMLDIR)/%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
-$(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) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
-
-release_spec:
+
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index e15fc3efe6..6589e514ed 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -295,7 +295,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
specifications of how many processes and ports that matched (in the
case of a pure pid() exactly 1). The specification of
matched processes is <c>{matched, Node, N}</c>. If the
- remote processor call,<c>rpc</c>, to a remote node fails,
+ remote processor call, <c>rpc</c>, to a remote node fails,
the <c>rpc</c> error message is delivered as a fourth
argument and the number of matched processes are 0. Note
that the result {ok, List} may contain a list where
diff --git a/lib/runtime_tools/examples/function-calls.d b/lib/runtime_tools/examples/function-calls.d
index f8ca388228..a51ff51253 100644
--- a/lib/runtime_tools/examples/function-calls.d
+++ b/lib/runtime_tools/examples/function-calls.d
@@ -19,39 +19,85 @@
* %CopyrightEnd%
*/
+/**
+ * Triggered on local function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ * @param arg2 depth
+ */
erlang*:::local-function-entry
{
printf("pid %s enter (local) %s depth %d\n",
copyinstr(arg0), copyinstr(arg1), arg2);
}
+/**
+ * Triggered on global function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ * @param arg2 depth
+ */
erlang*:::global-function-entry
{
printf("pid %s enter (global) %s depth %d\n",
copyinstr(arg0), copyinstr(arg1), arg2);
}
+/**
+ * Triggered upon function return, either global or
+ * local
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ * @param arg2 depth
+ */
erlang*:::function-return
{
printf("pid %s return %s depth %d\n",
copyinstr(arg0), copyinstr(arg1), arg2);
}
+/**
+ * Triggered on built-in function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ */
erlang*:::bif-entry
{
printf("pid %s BIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1));
}
+/**
+ * Triggered on built-in function return
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ */
erlang*:::bif-return
{
printf("pid %s BIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1));
}
+/**
+ * Triggered on native function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ */
erlang*:::nif-entry
{
printf("pid %s NIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1));
}
+/**
+ * Triggered upon native function return
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ */
erlang*:::nif-return
{
printf("pid %s NIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1));
diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap
index 6bb173b3ec..8f748ce0d1 100644
--- a/lib/runtime_tools/examples/function-calls.systemtap
+++ b/lib/runtime_tools/examples/function-calls.systemtap
@@ -29,39 +29,85 @@
* to your environment.
*/
+/**
+ * Triggered on local function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ * @param arg2 depth
+ */
probe process("beam.smp").mark("local-function-entry")
{
printf("pid %s enter (local) %s depth %d\n",
user_string($arg1), user_string($arg2), $arg3);
}
+/**
+ * Triggered on global function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ * @param arg2 depth
+ */
probe process("beam.smp").mark("global-function-entry")
{
printf("pid %s enter (global) %s depth %d\n",
user_string($arg1), user_string($arg2), $arg3);
}
+/**
+ * Triggered upon function return, either global or
+ * local
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ * @param arg2 depth
+ */
probe process("beam.smp").mark("function-return")
{
printf("pid %s return %s depth %d\n",
user_string($arg1), user_string($arg2), $arg3);
}
+/**
+ * Triggered on built-in function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ */
probe process("beam.smp").mark("bif-entry")
{
printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2));
}
+/**
+ * Triggered on built-in function return
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ */
probe process("beam.smp").mark("bif-return")
{
printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2));
}
+/**
+ * Triggered on native function entry
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the function
+ */
probe process("beam.smp").mark("nif-entry")
{
printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2));
}
+/**
+ * Triggered upon native function return
+ *
+ * @param arg0 pid
+ * @param arg1 MFA of the returned from function
+ */
probe process("beam.smp").mark("nif-return")
{
printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2));
diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl
index 845efaf9ef..d8133ee14f 100644
--- a/lib/runtime_tools/src/erts_alloc_config.erl
+++ b/lib/runtime_tools/src/erts_alloc_config.erl
@@ -356,14 +356,23 @@ save_scenario(AlcList) ->
process_flag(priority, OP),
Res.
-save_ai2(Alc, AI) ->
- Alc1 = chk_sbct(Alc, AI),
- case ai_value(mbcs, blocks_size, AI) of
- {blocks_size, MinBS, _, MaxBS} ->
- set_alloc_util(chk_mbcs_blocks_size(Alc1, MinBS, MaxBS), true);
- _ ->
- set_alloc_util(Alc, false)
- end.
+save_ai2(#alloc{name=Name}=Alc0, AI) ->
+ Alc1 = chk_sbct(Alc0, AI),
+
+ {Alc, IsAUtil} =
+ case ai_value(mbcs, blocks, AI) of
+ {blocks, Bs} ->
+ case ai_value(Name, size, Bs) of
+ {size, MinBS, _, MaxBS} ->
+ {chk_mbcs_blocks_size(Alc1, MinBS, MaxBS), true};
+ _ ->
+ {Alc1, false}
+ end;
+ _ ->
+ {Alc1, false}
+ end,
+
+ set_alloc_util(Alc, IsAUtil).
save_ai(Alc, [{instance, 0, AI}]) ->
save_ai2(Alc, AI);
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 3a24986381..e38757b939 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -433,7 +433,9 @@ ttb_meta_tracer(MetaFile,PI,Parent,SessionData) ->
ReturnMS = [{'_',[],[{return_trace}]}],
erlang:trace_pattern({erlang,spawn,3},ReturnMS,[meta]),
erlang:trace_pattern({erlang,spawn_link,3},ReturnMS,[meta]),
- erlang:trace_pattern({erlang,spawn_opt,1},ReturnMS,[meta]),
+ erlang:trace_pattern({erlang,spawn_opt,4},ReturnMS,[meta]),
+ erlang:trace_pattern({erts_internal,spawn_init,1},[],[meta]),
+ erlang:trace_pattern({erts_internal,dist_spawn_init,1},[],[meta]),
erlang:trace_pattern({erlang,register,2},[],[meta]),
erlang:trace_pattern({global,register_name,2},[],[meta]),
ok;
@@ -459,7 +461,7 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) ->
{trace_ts,_,call,{global,register_name,[Name,Pid]},_} ->
ok = ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile),
ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
- {trace_ts,CallingPid,call,{erlang,spawn_opt,[{M,F,Args,_}]},_} ->
+ {trace_ts,CallingPid,call,{erlang,spawn_opt,[M,F,Args,_]},_} ->
MFA = {M,F,length(Args)},
NewAcc = dict:update(CallingPid,
fun(Old) -> [MFA|Old] end, [MFA],
@@ -497,6 +499,16 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) ->
Acc),
ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
+ {trace_ts,CallingPid,call,{erts_internal,spawn_init,[{M,F,Args}]},_} ->
+ %% Local spawn_request()...
+ ok = ttb_store_meta({pid,{CallingPid,{M,F,length(Args)}}},MetaFile),
+ ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
+
+ {trace_ts,CallingPid,call,{erts_internal, dist_spawn_init, [MFnoA]},_} ->
+ %% Distributed spawn_request()...
+ ok = ttb_store_meta({pid,{CallingPid,MFnoA}},MetaFile),
+ ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
+
{metadata,Data} when is_list(Data) ->
ok = ttb_store_meta(Data,MetaFile),
ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
@@ -530,7 +542,9 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) ->
try_stop_overload_check(State),
erlang:trace_pattern({erlang,spawn,3},false,[meta]),
erlang:trace_pattern({erlang,spawn_link,3},false,[meta]),
- erlang:trace_pattern({erlang,spawn_opt,1},false,[meta]),
+ erlang:trace_pattern({erlang,spawn_opt,4},false,[meta]),
+ erlang:trace_pattern({erts_internal,spawn_init,1},false,[meta]),
+ erlang:trace_pattern({erts_internal,dist_spawn_init,1},false,[meta]),
erlang:trace_pattern({erlang,register,2},false,[meta]),
erlang:trace_pattern({global,register_name,2},false,[meta]);
stop ->
@@ -752,6 +766,7 @@ sys_tables() ->
mnesia_gvar, mnesia_stats,
% mnesia_transient_decision,
pg2_table,
+ pg,
queue,
schema,
shell_records,
@@ -763,7 +778,7 @@ sys_tables() ->
sys_processes() ->
[auth, code_server, global_name_server, inet_db,
- mnesia_recover, net_kernel, timer_server, wxe_master].
+ mnesia_recover, net_kernel, pg, timer_server, wxe_master].
mnesia_tables() ->
[ir_AliasDef, ir_ArrayDef, ir_AttributeDef, ir_ConstantDef,
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index b026048b94..b55d50d040 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -29,5 +29,5 @@
{applications, [kernel, stdlib]},
{env, []},
{mod, {runtime_tools, []}},
- {runtime_dependencies, ["stdlib-3.0","mnesia-4.12","kernel-5.0",
- "erts-8.0"]}]}.
+ {runtime_dependencies, ["stdlib-@OTP-15251@","mnesia-4.12","kernel-@OTP-15251@",
+ "erts-@OTP-15251:OTP-16327@"]}]}.
diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile
index 065eb45fbb..06af80fd35 100644
--- a/lib/sasl/Makefile
+++ b/lib/sasl/Makefile
@@ -36,3 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=tools
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/sasl/doc/src/Makefile b/lib/sasl/doc/src/Makefile
index 8e1e8b502c..684fd2b5e4 100644
--- a/lib/sasl/doc/src/Makefile
+++ b/lib/sasl/doc/src/Makefile
@@ -27,11 +27,6 @@ VSN=$(SASL_VSN)
APPLICATION=sasl
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -51,80 +46,13 @@ XML_CHAPTER_FILES = sasl_intro.xml \
BOOK_FILES = book.xml
-GIF_FILES =
+IMAGE_FILES =
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES) \
$(XML_REF6_FILES) $(XML_APPLICATION_FILES)
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-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) $(MAN4_FILES) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN4DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl
index dd97aeff08..f5489e7900 100644
--- a/lib/sasl/src/systools_lib.erl
+++ b/lib/sasl/src/systools_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -63,8 +63,8 @@ read_term(File) ->
end.
read_term_from_stream(Stream, File) ->
- _ = epp:set_encoding(Stream),
- R = io:request(Stream, {get_until,'',erl_scan,tokens,[1]}),
+ Encoding = epp:set_encoding(Stream),
+ R = io:request(Stream, {get_until,Encoding,'',erl_scan,tokens,[1]}),
case R of
{ok,Toks,_EndLine} ->
case erl_parse:parse_term(Toks) of
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/src/m.erl b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/src/m.erl
index 418102bebb..e0ff480703 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/src/m.erl
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/src/m.erl
@@ -8,4 +8,5 @@ start(NProcs) ->
receive stop -> Cs end
end) ||
_ <- lists:seq(1,NProcs)],
+ [unlink(Pid) || Pid <- Pids],
{Modules,Pids}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/src/m.erl b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/src/m.erl
index 418102bebb..e0ff480703 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/src/m.erl
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/src/m.erl
@@ -8,4 +8,5 @@ start(NProcs) ->
receive stop -> Cs end
end) ||
_ <- lists:seq(1,NProcs)],
+ [unlink(Pid) || Pid <- Pids],
{Modules,Pids}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/src/m.erl b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/src/m.erl
index 2edc1e6be4..8ec3629e78 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/src/m.erl
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/src/m.erl
@@ -8,4 +8,5 @@ start(NProcs) ->
receive stop -> Cs end
end) ||
_ <- lists:seq(1,NProcs)],
+ [unlink(Pid) || Pid <- Pids],
{Modules,Pids}.
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index fc80e37210..beb129c1a6 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -32,6 +32,8 @@
log_file/1,
utc_log/1]).
+-compile(r21).
+
all() ->
[log_mf_h_env, log_file, app_test, appup_test, utc_log].
@@ -104,7 +106,7 @@ appup_tests(App,{OkVsns0,NokVsns}) ->
create_test_vsns(App) ->
ThisMajor = erlang:system_info(otp_release),
FirstMajor = previous_major(ThisMajor),
- SecondMajor = previous_major(FirstMajor),
+ SecondMajor = previous_major(previous_major(FirstMajor)),
Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
diff --git a/lib/sasl/test/sasl_report_SUITE.erl b/lib/sasl/test/sasl_report_SUITE.erl
index e639b55cee..bc984754cc 100644
--- a/lib/sasl/test/sasl_report_SUITE.erl
+++ b/lib/sasl/test/sasl_report_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
-export([gen_server_crash/1, gen_server_crash_unicode/1]).
+-export([gen_server_crash_chars_limit/1,
+ gen_server_crash_chars_limit_unicode/1]).
-export([legacy_gen_server_crash/1, legacy_gen_server_crash_unicode/1]).
-export([crash_me/0,start_link/0,init/1,handle_cast/2,terminate/2]).
@@ -32,6 +34,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[gen_server_crash,
gen_server_crash_unicode,
+ gen_server_crash_chars_limit,
+ gen_server_crash_chars_limit_unicode,
legacy_gen_server_crash,
legacy_gen_server_crash_unicode].
@@ -51,18 +55,31 @@ end_per_group(_GroupName, Config) ->
Config.
gen_server_crash(Config) ->
- gen_server_crash(Config, latin1).
+ gen_server_crash(Config, latin1, depth).
gen_server_crash_unicode(Config) ->
- gen_server_crash(Config, unicode).
+ gen_server_crash(Config, unicode, depth).
+
+gen_server_crash_chars_limit(Config) ->
+ gen_server_crash(Config, latin1, chars_limit).
+
+gen_server_crash_chars_limit_unicode(Config) ->
+ gen_server_crash(Config, unicode, chars_limit).
legacy_gen_server_crash(Config) ->
- legacy_gen_server_crash(Config,latin1).
+ legacy_gen_server_crash(Config, latin1).
legacy_gen_server_crash_unicode(Config) ->
- legacy_gen_server_crash(Config,unicode).
+ legacy_gen_server_crash(Config, unicode).
+
+gen_server_crash(Config, Encoding, depth) ->
+ FormatterOpts = #{depth=>30},
+ gen_server_crash(Config, Encoding, FormatterOpts, 70000, 150000);
+gen_server_crash(Config, Encoding, chars_limit) ->
+ FormatterOpts = #{chars_limit=>50000, single_line=>true},
+ gen_server_crash(Config, Encoding, FormatterOpts, 50000, 100000).
-gen_server_crash(Config, Encoding) ->
+gen_server_crash(Config, Encoding, FormatterOpts, Min, Max) ->
TC = list_to_atom(lists:concat([?FUNCTION_NAME,"_",Encoding])),
PrivDir = filename:join(?config(priv_dir,Config),?MODULE),
ConfigFileName = filename:join(PrivDir,TC),
@@ -85,9 +102,9 @@ gen_server_crash(Config, Encoding) ->
" -boot start_sasl -kernel start_timer true "
"-config ",ConfigFileName]}]),
- %% Set depth
- ok = rpc:call(Node,logger,update_formatter_config,[default,depth,30]),
- ok = rpc:call(Node,logger,update_formatter_config,[sasl,depth,30]),
+ %% Set depth or chars_limit.
+ ok = rpc:call(Node,logger,update_formatter_config,[default,FormatterOpts]),
+ ok = rpc:call(Node,logger,update_formatter_config,[sasl,FormatterOpts]),
%% Make sure remote node logs it's own logs, and current node does
%% not log them.
@@ -98,7 +115,7 @@ gen_server_crash(Config, Encoding) ->
(_,_) -> ignore
end,[]}),
ct:log("Local node Logger config:~n~p",
- [rpc:call(Node,logger,get_config,[])]),
+ [logger:get_config()]),
ct:log("Remote node Logger config:~n~p",
[rpc:call(Node,logger,get_config,[])]),
ct:log("Remote node error_logger handlers: ~p",
@@ -112,8 +129,8 @@ gen_server_crash(Config, Encoding) ->
test_server:stop_node(Node),
ok = logger:remove_primary_filter(no_remote),
- check_file(KernelLog, utf8, 70000, 150000),
- check_file(SaslLog, Encoding, 70000, 150000),
+ check_file(KernelLog, utf8, Min, Max),
+ check_file(SaslLog, Encoding, Min, Max),
ok = file:delete(KernelLog),
ok = file:delete(SaslLog),
@@ -169,6 +186,7 @@ check_file(File, Encoding, Min, Max) ->
_ -> io:format("~ts\n", [Bin])
end,
Sz = byte_size(Bin),
+ %% Sz = string:length(string:replace(Bin, " ", "", all)),
io:format("Size: ~p (allowed range is ~p..~p)\n",
[Sz,Min,Max]),
if
diff --git a/lib/snmp/Makefile b/lib/snmp/Makefile
index 1a5bed50a5..52debf1670 100644
--- a/lib/snmp/Makefile
+++ b/lib/snmp/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2019. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -71,24 +71,6 @@ do_configure: configure
configure: configure.in
autoconf
-.PHONY: info gclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "SNMP_VSN: $(SNMP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-
-gclean:
- git clean -fXd
-
-
# ----------------------------------------------------
# Application (source) release targets
# ----------------------------------------------------
@@ -130,30 +112,6 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT): Makefile
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- ../../lib/kernel/ebin \
- ../../lib/stdlib/ebin \
- ../../lib/compiler/ebin \
- ../../lib/hipe/ebin \
- ../../lib/runtime_tools/ebin \
- ../../lib/crypto/ebin \
- ../../lib/mnesia/ebin \
- ../../erts/preloaded/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=runtime_tools crypto mnesia
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index e7ab491eef..cfc41156cf 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -29,11 +29,6 @@ VSN = $(SNMP_VSN)
APPLICATION=snmp
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
@@ -58,89 +53,21 @@ XML_ERRS = $(XML_FILES:%.xml=%.latex.xmls_errs) \
XML_OUTPUT = $(XML_FILES:%.xml=%.latex.xmls_output) \
$(XML_FILES:%.xml=%.html.xmls_output)
-INFO_FILE = ../../info
-
-#HTML_REF1_FILES = $(XML_REF1_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_REF3_FILES = $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_REF6_FILES = $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_CHAP_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-EXTRA_FILES = \
- $(DEFAULT_HTML_FILES) \
- $(HTML_REF1_FILES) \
- $(HTML_REF3_FILES) \
- $(HTML_REF6_FILES) \
- $(HTML_CHAP_FILES)
-
-
-MAN7DIR = $(DOCDIR)/man7
-
-MAN1_FILES = $(MAN1DIR)/snmpc.1
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-MAN7_FILES = $(MIB_FILES:$(MIBSDIR)/%.mib=$(MAN7DIR)/%.7)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-GIF_TARGETS = $(GIF_FILES:%=$(HTMLDIR)/%)
-
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+NO_CHUNKS = snmpa_discovery_handler.xml \
+ snmpa_error_report.xml \
+ snmpa_mib_data.xml \
+ snmpa_mib_storage.xml \
+ snmpa_network_interface.xml \
+ snmpa_network_interface_filter.xml \
+ snmpa_notification_delivery_info_receiver.xml \
+ snmpa_notification_filter.xml \
+ snmpm_network_interface.xml \
+ snmpm_network_interface_filter.xml \
+ snmpm_user.xml
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif # Copy them to ../html
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-ldocs: local_docs
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs: clean_html clean_man clean_pdf
- rm -f errs core *~
-
-man: man1 man3 man6 man7
-
-man1: $(MAN1_FILES)
-
-man3: $(MAN3_FILES)
-
-man6: $(MAN6_FILES)
-
-man7: $(MAN7_FILES)
-
-gifs: $(GIF_TARGETS)
-
-debug opt:
-
-clean_pdf:
- @echo "cleaning pdf:"
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
-
-clean_man:
- @echo "cleaning man:"
- rm -f $(MAN1DIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(MAN7DIR)/*
-
-clean_html:
- @echo "cleaning html:"
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
$(MAN7DIR)/%.7: $(MIBSDIR)/%.mib
@echo "processing $*"
@@ -150,40 +77,15 @@ $(MAN7DIR)/%.7: $(MIBSDIR)/%.mib
@echo ".fi" >> $@
@echo "" >> $@
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-
$(MAN1DIR)/snmpc.1: snmpc_cmd.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man7"
- $(INSTALL_DATA) $(MAN7DIR)/* "$(RELEASE_PATH)/man/man7"
-
-release_spec:
+# ----------------------------------------------------
+
+include $(ERL_TOP)/make/doc.mk
info: info_xml info_man info_html
- @echo "MAN1DIR: $(MAN1DIR)"
- @echo "MAN3DIR: $(MAN3DIR)"
- @echo "MAN6DIR: $(MAN6DIR)"
- @echo "MAN7DIR: $(MAN7DIR)"
info_man:
@echo "man files:"
diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk
index f364cb6fa5..ea4143d121 100644
--- a/lib/snmp/doc/src/files.mk
+++ b/lib/snmp/doc/src/files.mk
@@ -113,7 +113,7 @@ XML_FILES = $(BOOK_FILES) \
$(XML_REF6_FILES) \
$(XML_APPLICATION_FILES)
-GIF_FILES = \
+IMAGE_FILES = \
getnext1.gif \
getnext2.gif \
getnext3.gif \
@@ -125,19 +125,7 @@ GIF_FILES = \
snmp-um-1-image-3.gif \
MIB_mechanism.gif
-PS_FILES = getnext1.ps \
- getnext2.ps \
- getnext3.ps \
- getnext4.ps \
- snmp_agent_netif.ps \
- snmp-um-1-image-1.ps \
- snmp-um-1-image-2.ps \
- snmp-um-1-image-3.ps \
- snmp-um-1-image-8.ps \
- MIB_mechanism.ps
-
-
-MIB_FILES = \
+MIB_REF7_FILES = \
$(MIBSDIR)/RFC1213-MIB.mib \
$(MIBSDIR)/STANDARD-MIB.mib \
$(MIBSDIR)/SNMPv2-TM.mib \
diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml
index 54a7eafe76..9d29787ba2 100644
--- a/lib/snmp/doc/src/snmp_app.xml
+++ b/lib/snmp/doc/src/snmp_app.xml
@@ -52,7 +52,7 @@
<!--
-There are achors (marker id) in this file, but do **NOT** link to anything
+There are anchors (marker id) in this file, but do **NOT** link to anything
in this file!! Because of the title is in capital letters, the generated
html file will also be in capital letters.
This should not be a problem since almost everything here is duplicated
diff --git a/lib/snmp/doc/src/snmp_pdus.xml b/lib/snmp/doc/src/snmp_pdus.xml
index 4b00dcb7f0..0377a1c050 100644
--- a/lib/snmp/doc/src/snmp_pdus.xml
+++ b/lib/snmp/doc/src/snmp_pdus.xml
@@ -125,7 +125,6 @@
<p>Decodes a list of bytes into an SNMP UsmSecurityParameters</p>
</desc>
</func>
-
<func>
<name since="">enc_message(Message) -> [byte()]</name>
<fsummary>Encode an SNMP Message</fsummary>
diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl
index ff7be54283..9e428466fa 100644
--- a/lib/snmp/src/agent/snmpa.erl
+++ b/lib/snmp/src/agent/snmpa.erl
@@ -123,7 +123,7 @@
mib_storage_options/0
]).
--deprecated([{old_info_format, 1, next_major_release}]).
+-deprecated([{old_info_format, 1, "use \"new\" format instead"}]).
-include("snmpa_atl.hrl").
diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl
index 1e6a93deff..b6cfe34047 100644
--- a/lib/snmp/src/app/snmp.erl
+++ b/lib/snmp/src/app/snmp.erl
@@ -129,53 +129,53 @@
%% This is for XREF
--deprecated([{c, 1, eventually},
- {c, 2, eventually},
- {compile, 3, eventually},
- {is_consistent, 1, eventually},
- {mib_to_hrl, 1, eventually},
-
- {change_log_size, 1, eventually},
- {log_to_txt, 2, eventually},
- {log_to_txt, 3, eventually},
- {log_to_txt, 4, eventually},
-
- {current_request_id, 0, eventually},
- {current_community, 0, eventually},
- {current_address, 0, eventually},
- {current_context, 0, eventually},
- {current_net_if_data, 0, eventually},
-
- {get_symbolic_store_db, 0, eventually},
- {name_to_oid, 1, eventually},
- {name_to_oid, 2, eventually},
- {oid_to_name, 1, eventually},
- {oid_to_name, 2, eventually},
- {int_to_enum, 2, eventually},
- {int_to_enum, 3, eventually},
- {enum_to_int, 2, eventually},
- {enum_to_int, 3, eventually},
-
- {get, 2, eventually},
- {info, 1, eventually},
- {load_mibs, 2, eventually},
- {unload_mibs, 2, eventually},
- {dump_mibs, 0, eventually},
- {dump_mibs, 1, eventually},
-
- {register_subagent, 3, eventually},
- {unregister_subagent, 2, eventually},
-
- {send_notification, 3, eventually},
- {send_notification, 4, eventually},
- {send_notification, 5, eventually},
- {send_notification, 6, eventually},
- {send_trap, 3, eventually},
- {send_trap, 4, eventually},
-
- {add_agent_caps, 2, eventually},
- {del_agent_caps, 1, eventually},
- {get_agent_caps, 0, eventually}]).
+-deprecated([{c, 1, "use snmpa:c/1 instead"},
+ {c, 2, "use snmpa:c/2 instead"},
+ {compile, 3, "use snmpa:compile/3 instead"},
+ {is_consistent, 1, "use snmpa:is_consistent/1 instead"},
+ {mib_to_hrl, 1, "use snmpa:mib_to_hrl/1 instead"},
+
+ {change_log_size, 1, "use snmpa:change_log_size/1 instead"},
+ {log_to_txt, 2, "use snmpa:log_to_txt/2 instead"},
+ {log_to_txt, 3, "use snmpa:log_to_txt/3 instead"},
+ {log_to_txt, 4, "use snmpa:log_to_txt/4 instead"},
+
+ {current_request_id, 0, "use snmpa:current_request_id/0 instead"},
+ {current_community, 0, "use snmpa:current_community/0 instead"},
+ {current_address, 0, "use snmpa:current_address/0 instead"},
+ {current_context, 0, "use snmpa:current_context/0 instead"},
+ {current_net_if_data, 0, "use snmpa:current_net_if_data/0 instead"},
+
+ {get_symbolic_store_db, 0, "use snmpa:get_symbolic_store_db/0 instead"},
+ {name_to_oid, 1, "use snmpa:name_to_oid/1 instead"},
+ {name_to_oid, 2, "use snmpa:name_to_oid/2 instead"},
+ {oid_to_name, 1, "use snmpa:oid_to_name/1 instead"},
+ {oid_to_name, 2, "use snmpa:oid_to_name/2 instead"},
+ {int_to_enum, 2, "use snmpa:int_to_enum/2 instead"},
+ {int_to_enum, 3, "use snmpa:int_to_enum/3 instead"},
+ {enum_to_int, 2, "use snmpa:enum_to_int/2 instead"},
+ {enum_to_int, 3, "use snmpa:enum_to_int/3 instead"},
+
+ {get, 2, "use snmpa:get/2 instead"},
+ {info, 1, "use snmpa:info/1 instead"},
+ {load_mibs, 2, "use snmpa:load_mibs/2 instead"},
+ {unload_mibs, 2, "use snmpa:unload_mibs/2 instead"},
+ {dump_mibs, 0, "use snmpa:dump_mibs/0 instead"},
+ {dump_mibs, 1, "use snmpa:dump_mibs/1 instead"},
+
+ {register_subagent, 3, "use snmpa:register_subagent/3 instead"},
+ {unregister_subagent, 2, "use snmpa:unregister_subagent/2 instead"},
+
+ {send_notification, 3, "use snmpa:send_notification/3 instead"},
+ {send_notification, 4, "use snmpa:send_notification/4 instead"},
+ {send_notification, 5, "use snmpa:send_notification/5 instead"},
+ {send_notification, 6, "use snmpa:send_notification/6 instead"},
+ {send_trap, 3, "use snmpa:send_trap/3 instead"},
+ {send_trap, 4, "use snmpa:send_trap/4 instead"},
+
+ {add_agent_caps, 2, "use snmpa:add_agent_caps/2 instead"},
+ {del_agent_caps, 1, "use snmpa:del_agent_caps/1 instead"},
+ {get_agent_caps, 0, "use snmpa:get_agent_caps/0 instead"}]).
-define(APPLICATION, snmp).
diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl
index 312074f2e7..d0fee418e1 100644
--- a/lib/snmp/src/compile/snmpc_misc.erl
+++ b/lib/snmp/src/compile/snmpc_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -156,7 +156,8 @@ loop(Fd, Res, Func, StartLine, File) ->
%% io:read modified to give us line numbers.
%%-----------------------------------------------------------------
read(Io, Prompt, StartLine) ->
- case io:request(Io, {get_until, Prompt, erl_scan, tokens, [StartLine]}) of
+ Enc = latin1,
+ case io:request(Io, {get_until, Enc, Prompt, erl_scan, tokens, [StartLine]}) of
{ok, Toks, EndLine} ->
case erl_parse:parse_term(Toks) of
{ok, Term} ->
diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl
index 20b7af0373..bec9d4d9d9 100644
--- a/lib/snmp/src/misc/snmp_conf.erl
+++ b/lib/snmp/src/misc/snmp_conf.erl
@@ -265,7 +265,8 @@ open_file(File) ->
end.
do_read(Io, Prompt, StartLine) ->
- case io:request(Io, {get_until,Prompt,erl_scan,tokens,[StartLine]}) of
+ Enc = latin1,
+ case io:request(Io, {get_until,Enc,Prompt,erl_scan,tokens,[StartLine]}) of
{ok, Toks, EndLine} ->
case erl_parse:parse_term(Toks) of
{ok, Term} ->
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
index 3104f2a096..5aab9a74e0 100644
--- a/lib/snmp/src/misc/snmp_config.erl
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -2737,7 +2737,8 @@ read_lines(Fd, Acc, StartLine) ->
end.
read_and_parse_term(Fd, StartLine) ->
- case io:request(Fd, {get_until, "", erl_scan, tokens, [StartLine]}) of
+ Enc = latin1,
+ case io:request(Fd, {get_until, Enc, "", erl_scan, tokens, [StartLine]}) of
{ok, Tokens, EndLine} ->
case erl_parse:parse_term(Tokens) of
{ok, Term} ->
diff --git a/lib/snmp/src/misc/snmp_usm.erl b/lib/snmp/src/misc/snmp_usm.erl
index bae6bdec72..39172c9c1a 100644
--- a/lib/snmp/src/misc/snmp_usm.erl
+++ b/lib/snmp/src/misc/snmp_usm.erl
@@ -45,7 +45,12 @@
-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
--define(BLOCK_CIPHER_AES, aes_cfb128).
+-define(BLOCK_CIPHER_AES(Key), case size(iolist_to_binary(Key)) of
+ 128 -> aes_128_cfb128;
+ 192 -> aes_192_cfb128;
+ 256 -> aes_256_cfb128
+ end).
+
-define(BLOCK_CIPHER_DES, des_cbc).
@@ -157,7 +162,7 @@ md5_auth_out(AuthKey, Message, UsmSecParams) ->
Packet = snmp_pdus:enc_message_only(Message2),
%% 6.3.1.2-4 is done by the crypto function
%% 6.3.1.4
- MAC = binary_to_list(crypto:hmac(md5, AuthKey, Packet, 12)),
+ MAC = binary_to_list(crypto:macN(hmac, md5, AuthKey, Packet, 12)),
%% ?vtrace("md5_auth_out -> crypto (md5) encoded"
%% "~n MAC: ~w", [MAC]),
%% 6.3.1.5
@@ -171,7 +176,7 @@ md5_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) == 12 ->
%% 6.3.2.3
Packet2 = patch_packet(binary_to_list(Packet)),
%% 6.3.2.5
- MAC = binary_to_list(crypto:hmac(md5, AuthKey, Packet2, 12)),
+ MAC = binary_to_list(crypto:macN(hmac, md5, AuthKey, Packet2, 12)),
%% 6.3.2.6
%% ?vtrace("md5_auth_in -> crypto (md5) encoded"
%% "~n MAC: ~w", [MAC]),
@@ -190,7 +195,7 @@ sha_auth_out(AuthKey, Message, UsmSecParams) ->
Packet = snmp_pdus:enc_message_only(Message2),
%% 7.3.1.2-4 is done by the crypto function
%% 7.3.1.4
- MAC = binary_to_list(crypto:hmac(sha, AuthKey, Packet, 12)),
+ MAC = binary_to_list(crypto:macN(hmac, sha, AuthKey, Packet, 12)),
%% 7.3.1.5
set_msg_auth_params(Message, UsmSecParams, MAC).
@@ -198,7 +203,7 @@ sha_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) =:= 12 ->
%% 7.3.2.3
Packet2 = patch_packet(binary_to_list(Packet)),
%% 7.3.2.5
- MAC = binary_to_list(crypto:hmac(sha, AuthKey, Packet2, 12)),
+ MAC = binary_to_list(crypto:macN(hmac, sha, AuthKey, Packet2, 12)),
%% 7.3.2.6
MAC == AuthParams;
sha_auth_in(_AuthKey, _AuthParams, _Packet) ->
@@ -216,8 +221,8 @@ des_encrypt(PrivKey, Data, SaltFun) ->
IV = list_to_binary(snmp_misc:str_xor(PreIV, Salt)),
TailLen = (8 - (length(Data) rem 8)) rem 8,
Tail = mk_tail(TailLen),
- EncData = crypto:block_encrypt(?BLOCK_CIPHER_DES,
- DesKey, IV, [Data,Tail]),
+ EncData = crypto:crypto_one_time(?BLOCK_CIPHER_DES,
+ DesKey, IV, [Data,Tail], true),
{ok, binary_to_list(EncData), Salt}.
des_decrypt(PrivKey, MsgPrivParams, EncData)
@@ -231,8 +236,8 @@ des_decrypt(PrivKey, MsgPrivParams, EncData)
Salt = MsgPrivParams,
IV = list_to_binary(snmp_misc:str_xor(PreIV, Salt)),
%% Whatabout errors here??? E.g. not a mulitple of 8!
- Data = binary_to_list(crypto:block_decrypt(?BLOCK_CIPHER_DES,
- DesKey, IV, EncData)),
+ Data = binary_to_list(crypto:crypto_one_time(?BLOCK_CIPHER_DES,
+ DesKey, IV, EncData, false)),
Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
{ok, Data2};
des_decrypt(PrivKey, BadMsgPrivParams, EncData) ->
@@ -248,8 +253,8 @@ aes_encrypt(PrivKey, Data, SaltFun, EngineBoots, EngineTime) ->
AesKey = PrivKey,
Salt = SaltFun(),
IV = list_to_binary([?i32(EngineBoots), ?i32(EngineTime) | Salt]),
- EncData = crypto:block_encrypt(?BLOCK_CIPHER_AES,
- AesKey, IV, Data),
+ EncData = crypto:crypto_one_time(?BLOCK_CIPHER_AES(AesKey),
+ AesKey, IV, Data, true),
{ok, binary_to_list(EncData), Salt}.
aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime)
@@ -258,8 +263,8 @@ aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime)
Salt = MsgPrivParams,
IV = list_to_binary([?i32(EngineBoots), ?i32(EngineTime) | Salt]),
%% Whatabout errors here??? E.g. not a mulitple of 8!
- Data = binary_to_list(crypto:block_decrypt(?BLOCK_CIPHER_AES,
- AesKey, IV, EncData)),
+ Data = binary_to_list(crypto:crypto_one_time(?BLOCK_CIPHER_AES(AesKey),
+ AesKey, IV, EncData, false)),
Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
{ok, Data2}.
diff --git a/lib/ssh/Makefile b/lib/ssh/Makefile
index dedc7ac3a6..da0e3e6cd1 100644
--- a/lib/ssh/Makefile
+++ b/lib/ssh/Makefile
@@ -17,6 +17,7 @@
#
# %CopyrightEnd%
#
+#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -37,4 +38,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto runtime_tools public_key
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index 4e32dd9976..4e6af79a1a 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -30,11 +30,6 @@ VSN=$(SSH_VSN)
APPLICATION=ssh
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -67,87 +62,9 @@ XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6
IMAGE_FILES = SSH_protocols.png
-# ----------------------------------------------------
-
-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_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-#SPECS_FLAGS = -I../../include -I../../../kernel/include
-SPECS_FLAGS = -I../../../public_key/include -I../../../public_key/src -I../../..
+NO_CHUNKS = ssh_client_key_api.xml ssh_server_key_api.xml ssh_server_channel.xml
# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.png: %.png
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
-
-pdf: $(TOP_PDF_FILE)
-
-html: images $(HTML_REF_MAN_FILE)
-
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECS_FILES)
- rm -f errs core *~
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-
-debug opt:
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
-
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 862f79ac56..913b162fe9 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -91,7 +91,7 @@
<section>
<title>Keys and files</title>
<p>A number of objects must be present for the SSH application to work.
- Thoose objects are per default stored in files.
+ Those objects are per default stored in files.
The default names, paths and file formats are the same as for
<url href="http://www.openssh.com">OpenSSH</url>. Keys could be generated with the <c>ssh-keygen</c>
program from OpenSSH. See the
@@ -746,6 +746,25 @@
</desc>
</datatype>
+ <datatype>
+ <name name="tcpip_tunnel_in_daemon_option"/>
+ <desc>
+ <p>Enables (<c>true</c>) or disables (<c>false</c>) the possibility to tunnel a TCP/IP connection in to a
+ <seealso marker="ssh:ssh#daemon-2">server</seealso>.
+ Disabled per default.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="tcpip_tunnel_out_daemon_option"/>
+ <desc>
+ <p>Enables (<c>true</c>) or disables (<c>false</c>) the possibility to tunnel a TCP/IP connection out of a
+ <seealso marker="ssh:ssh#daemon-2">server</seealso>.
+ Disabled per default.
+ </p>
+ </desc>
+ </datatype>
<!--................................................................-->
<datatype_title>Options common to clients and daemons</datatype_title>
@@ -1124,9 +1143,9 @@
</datatype>
<datatype>
- <name>opaque_client_options</name>
- <name>opaque_daemon_options</name>
- <name>opaque_common_options</name>
+ <name>opaque_client_options()</name>
+ <name>opaque_daemon_options()</name>
+ <name>opaque_common_options()</name>
<desc>
<marker id="type-opaque_client_options"/>
<marker id="type-opaque_daemon_options"/>
@@ -1376,6 +1395,49 @@
</desc>
</func>
+ <func>
+ <name name="tcpip_tunnel_from_server" arity="5" since=""/>
+ <name name="tcpip_tunnel_from_server" arity="6" since=""/>
+ <fsummary>TCP/IP tunneling from a server to a client ("tcpip-forward")</fsummary>
+ <desc>
+ <p>Asks the remote server of <c>ConnectionRef</c> to listen to <c>ListenHost:ListenPort</c>.
+ When someone connects that address, the connection is forwarded in an encrypted channel from
+ the server to the client. The client (that is, at the node that calls this function) then
+ connects to <c>ConnectToHost:ConnectToPort</c>.
+ </p>
+ <p>The returned <c>TrueListenPort</c> is the port that is listened to. It is the same as
+ <c>ListenPort</c>, except when <c>ListenPort = 0</c>. In that case a free port is selected
+ by the underlying OS.
+ </p>
+ <p>Note that in case of an Erlang/OTP SSH server (daemon) as peer, that server must have been
+ started with the option
+ <seealso marker="#type-tcpip_tunnel_out_daemon_option">tcpip_tunnel_out</seealso>
+ to allow the connection.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="tcpip_tunnel_to_server" arity="5" since=""/>
+ <name name="tcpip_tunnel_to_server" arity="6" since=""/>
+ <fsummary>TCP/IP tunneling from a client to a server ("direct-tcpip")</fsummary>
+ <desc>
+ <p>Tells the local client to listen to <c>ListenHost:ListenPort</c>. When someone
+ connects to that address, the connection is forwarded in an encrypted channel to the peer server
+ of <c>ConnectionRef</c>. That server then connects to <c>ConnectToHost:ConnectToPort</c>.
+ </p>
+ <p>The returned <c>TrueListenPort</c> is the port that is listened to. It is the same as
+ <c>ListenPort</c>, except when <c>ListenPort = 0</c>. In that case a free port is selected
+ by the underlying OS.
+ </p>
+ <p>Note that in case of an Erlang/OTP SSH server (daemon) as peer, that server must have been
+ started with the option
+ <seealso marker="#type-tcpip_tunnel_in_daemon_option">tcpip_tunnel_in</seealso>
+ to allow the connection.
+ </p>
+ </desc>
+ </func>
+
</funcs>
</erlref>
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index f5c520f2f0..68d7fd13e7 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -61,6 +61,7 @@ MODULES= \
ssh_app \
ssh_auth\
ssh_bits \
+ ssh_channel_sup \
ssh_cli \
ssh_connection \
ssh_connection_handler \
@@ -71,7 +72,6 @@ MODULES= \
ssh_message \
ssh_no_io \
ssh_options \
- ssh_server_channel_sup \
ssh_sftp \
ssh_sftpd \
ssh_sftpd_file\
@@ -79,6 +79,10 @@ MODULES= \
ssh_subsystem_sup \
ssh_sup \
ssh_system_sup \
+ ssh_tcpip_forward_srv \
+ ssh_tcpip_forward_client \
+ ssh_tcpip_forward_acceptor_sup \
+ ssh_tcpip_forward_acceptor \
ssh_transport \
ssh_xfer \
sshc_sup \
@@ -180,7 +184,7 @@ $(EBIN)/ssh_connection_handler.$(EMULATOR): ssh_connection_handler.erl ssh.hrl \
$(EBIN)/ssh_shell.$(EMULATOR): ssh_shell.erl ssh_connect.hrl
$(EBIN)/ssh_system_sup.$(EMULATOR): ssh_system_sup.erl ssh.hrl
$(EBIN)/ssh_subsystem_sup.$(EMULATOR): ssh_subsystem_sup.erl
-$(EBIN)/ssh_server_channel_sup.$(EMULATOR): ssh_server_channel_sup.erl
+$(EBIN)/ssh_channel_sup.$(EMULATOR): ssh_channel_sup.erl ssh.hrl
$(EBIN)/ssh_acceptor_sup.$(EMULATOR): ssh_acceptor_sup.erl ssh.hrl
$(EBIN)/ssh_acceptor.$(EMULATOR): ssh_acceptor.erl ssh.hrl
$(EBIN)/ssh_app.$(EMULATOR): ssh_app.erl
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 21e3604400..fda507727a 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -11,6 +11,7 @@
ssh_auth,
ssh_message,
ssh_bits,
+ ssh_channel_sup,
ssh_cli,
ssh_client_channel,
ssh_client_key_api,
@@ -28,7 +29,6 @@
ssh_info,
ssh_no_io,
ssh_server_channel,
- ssh_server_channel_sup,
ssh_server_key_api,
ssh_sftp,
ssh_sftpd,
@@ -36,6 +36,10 @@
ssh_sftpd_file_api,
ssh_subsystem_sup,
ssh_sup,
+ ssh_tcpip_forward_client,
+ ssh_tcpip_forward_srv,
+ ssh_tcpip_forward_acceptor_sup,
+ ssh_tcpip_forward_acceptor,
ssh_system_sup,
ssh_transport,
ssh_xfer]},
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 355b40eea8..2673f30774 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -40,7 +40,9 @@
chk_algos_opts/1,
stop_listener/1, stop_listener/2, stop_listener/3,
stop_daemon/1, stop_daemon/2, stop_daemon/3,
- shell/1, shell/2, shell/3
+ shell/1, shell/2, shell/3,
+ tcpip_tunnel_from_server/5, tcpip_tunnel_from_server/6,
+ tcpip_tunnel_to_server/5, tcpip_tunnel_to_server/6
]).
%%% "Deprecated" types export:
@@ -129,15 +131,13 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_port(Socket),
{error, Error} ->
{error, Error};
Options ->
- case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
- ok ->
- {ok, {Host,_Port}} = inet:peername(Socket),
- Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
- ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout);
- {error,SockError} ->
- {error,SockError}
- end
- end;
+ case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
+ ok ->
+ connect_socket(Socket, Options, NegotiationTimeout);
+ {error,SockError} ->
+ {error,SockError}
+ end
+ end;
connect(Host, Port, Options) when is_integer(Port),
Port>0,
@@ -151,9 +151,9 @@ connect(Host, Port, Options) when is_integer(Port),
Options :: client_options(),
NegotiationTimeout :: timeout().
-connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
- Port>0,
- is_list(UserOptions) ->
+connect(Host0, Port, UserOptions, NegotiationTimeout) when is_integer(Port),
+ Port>0,
+ is_list(UserOptions) ->
case ssh_options:handle_options(client, UserOptions) of
{error, _Reason} = Error ->
Error;
@@ -164,8 +164,7 @@ connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
Host = mangle_connect_address(Host0, SocketOpts),
try Transport:connect(Host, Port, SocketOpts, ConnectionTimeout) of
{ok, Socket} ->
- Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
- ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
+ connect_socket(Socket, Options, NegotiationTimeout);
{error, Reason} ->
{error, Reason}
catch
@@ -176,6 +175,22 @@ connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
end
end.
+
+connect_socket(Socket, Options0, NegotiationTimeout) ->
+ {ok, {Host,Port}} = inet:sockname(Socket),
+ Profile = ?GET_OPT(profile, Options0),
+ {ok, SystemSup} = sshc_sup:start_child(Host, Port, Profile, Options0),
+ {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, client, Host, Port, Profile, Options0),
+ ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
+ Opts = ?PUT_INTERNAL_OPT([{user_pid,self()},
+ {host,Host},
+ {supervisors, [{system_sup, SystemSup},
+ {subsystem_sup, SubSysSup},
+ {connection_sup, ConnectionSup}]}
+ ], Options0),
+ ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout).
+
+
%%--------------------------------------------------------------------
-spec close(ConnectionRef) -> ok | {error,term()} when
ConnectionRef :: connection_ref() .
@@ -452,7 +467,7 @@ stop_listener(Address, Port, Profile) ->
-spec stop_daemon(DaemonRef::daemon_ref()) -> ok.
stop_daemon(SysSup) ->
- ssh_system_sup:stop_system(SysSup).
+ ssh_system_sup:stop_system(server, SysSup).
-spec stop_daemon(inet:ip_address(), inet:port_number()) -> ok.
@@ -465,11 +480,11 @@ stop_daemon(Address, Port) ->
stop_daemon(any, Port, Profile) ->
map_ip(fun(IP) ->
- ssh_system_sup:stop_system(IP, Port, Profile)
+ ssh_system_sup:stop_system(server, IP, Port, Profile)
end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]);
stop_daemon(Address, Port, Profile) ->
map_ip(fun(IP) ->
- ssh_system_sup:stop_system(IP, Port, Profile)
+ ssh_system_sup:stop_system(server, IP, Port, Profile)
end, {address,Address}).
%%--------------------------------------------------------------------
@@ -581,6 +596,113 @@ get_sock_opts(ConnectionRef, SocketGetOptions) ->
ssh_connection_handler:get_sock_opts(ConnectionRef, SocketGetOptions).
%%--------------------------------------------------------------------
+%% Ask local client to listen to ListenHost:ListenPort. When someone
+%% connects that address, connect to ConnectToHost:ConnectToPort from
+%% the server.
+%%--------------------------------------------------------------------
+-spec tcpip_tunnel_to_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort
+ ) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort) ->
+ tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort, infinity).
+
+
+-spec tcpip_tunnel_to_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort,
+ Timeout) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ Timeout :: timeout(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost0, ConnectToPort, Timeout) ->
+ SockOpts = [],
+ try
+ list_to_binary(
+ case mangle_connect_address(ConnectToHost0,SockOpts) of
+ IP when is_tuple(IP) -> inet_parse:ntoa(IP);
+ _ when is_list(ConnectToHost0) -> ConnectToHost0
+ end)
+ of
+ ConnectToHost ->
+ ssh_connection_handler:handle_direct_tcpip(ConnectionHandler,
+ mangle_tunnel_address(ListenHost), ListenPort,
+ ConnectToHost, ConnectToPort,
+ Timeout)
+ catch
+ _:_ ->
+ {error, bad_connect_to_address}
+ end.
+
+%%--------------------------------------------------------------------
+%% Ask remote server to listen to ListenHost:ListenPort. When someone
+%% connects that address, connect to ConnectToHost:ConnectToPort from
+%% the client.
+%%--------------------------------------------------------------------
+-spec tcpip_tunnel_from_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort
+ ) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_from_server(ConnectionRef, ListenHost, ListenPort, ConnectToHost, ConnectToPort) ->
+ tcpip_tunnel_from_server(ConnectionRef, ListenHost, ListenPort, ConnectToHost, ConnectToPort, infinity).
+
+-spec tcpip_tunnel_from_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort,
+ Timeout) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ Timeout :: timeout(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_from_server(ConnectionRef, ListenHost0, ListenPort, ConnectToHost0, ConnectToPort, Timeout) ->
+ SockOpts = [],
+ ListenHost = mangle_tunnel_address(ListenHost0),
+ ConnectToHost = mangle_connect_address(ConnectToHost0, SockOpts),
+ case ssh_connection_handler:global_request(ConnectionRef, "tcpip-forward", true,
+ {ListenHost,ListenPort,ConnectToHost,ConnectToPort},
+ Timeout) of
+ {success,<<>>} ->
+ {ok, ListenPort};
+ {success,<<TruePort:32/unsigned-integer>>} when ListenPort==0 ->
+ {ok, TruePort};
+ {success,_} = Res ->
+ {error, {bad_result,Res}};
+ {failure,<<>>} ->
+ {error,not_accepted};
+ {failure,Error} ->
+ {error,Error};
+ Other ->
+ Other
+ end.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%% The handle_daemon_args/2 function basically only sets the ip-option in Opts
@@ -697,3 +819,16 @@ mangle_connect_address1(A, _) ->
{ok, {0,0,0,0,0,0,0,0}} -> loopback(true);
_ -> A
end.
+
+%%%----------------------------------------------------------------
+mangle_tunnel_address(any) -> <<"">>;
+mangle_tunnel_address(loopback) -> <<"localhost">>;
+mangle_tunnel_address({0,0,0,0}) -> <<"">>;
+mangle_tunnel_address({0,0,0,0,0,0,0,0}) -> <<"">>;
+mangle_tunnel_address(IP) when is_tuple(IP) -> list_to_binary(inet_parse:ntoa(IP));
+mangle_tunnel_address(A) when is_atom(A) -> mangle_tunnel_address(atom_to_list(A));
+mangle_tunnel_address(X) when is_list(X) -> case catch inet:parse_address(X) of
+ {ok, {0,0,0,0}} -> <<"">>;
+ {ok, {0,0,0,0,0,0,0,0}} -> <<"">>;
+ _ -> list_to_binary(X)
+ end.
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index a9d81f7252..e754b9ebc6 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -51,6 +51,7 @@
-define(STRING(X), ?UINT32((size(X))), (X)/binary).
-define(DEC_BIN(X,Len), ?UINT32(Len), X:Len/binary ).
+-define(DEC_INT(I,Len), ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
-define(DEC_MPINT(I,Len), ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
%% building macros
@@ -308,6 +309,8 @@
| shell_daemon_option()
| exec_daemon_option()
| ssh_cli_daemon_option()
+ | tcpip_tunnel_out_daemon_option()
+ | tcpip_tunnel_in_daemon_option()
| authentication_daemon_options()
| diffie_hellman_group_exchange_daemon_option()
| negotiation_timeout_daemon_option()
@@ -338,6 +341,9 @@
-type ssh_cli_daemon_option() :: {ssh_cli, mod_args() | no_cli }.
+-type tcpip_tunnel_out_daemon_option() :: {tcpip_tunnel_out, boolean()} .
+-type tcpip_tunnel_in_daemon_option() :: {tcpip_tunnel_in, boolean()} .
+
-type send_ext_info_daemon_option() :: {send_ext_info, boolean()} .
-type authentication_daemon_options() ::
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index b9813b6b5c..bb3bec9c7c 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -143,9 +143,9 @@ get_public_key(SigAlg, #ssh{opts = Opts}) ->
{ok, PrivKey} ->
try
%% Check the key - the KeyCb may be a buggy plugin
- true = ssh_transport:valid_key_sha_alg(PrivKey, KeyAlg),
+ true = ssh_transport:valid_key_sha_alg(private, PrivKey, KeyAlg),
Key = ssh_transport:extract_public_key(PrivKey),
- public_key:ssh_encode(Key, ssh2_pubkey)
+ ssh_message:ssh2_pubkey_encode(Key)
of
PubKeyBlob -> {ok,{PrivKey,PubKeyBlob}}
catch
@@ -495,7 +495,7 @@ get_password_option(Opts, User) ->
pre_verify_sig(User, KeyBlob, Opts) ->
try
- Key = public_key:ssh_decode(KeyBlob, ssh2_pubkey), % or exception
+ Key = ssh_message:ssh2_pubkey_decode(KeyBlob), % or exception
ssh_transport:call_KeyCb(is_auth_key, [Key, User], Opts)
catch
_:_ ->
@@ -505,7 +505,7 @@ pre_verify_sig(User, KeyBlob, Opts) ->
verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, #ssh{opts = Opts} = Ssh) ->
try
Alg = binary_to_list(AlgBin),
- Key = public_key:ssh_decode(KeyBlob, ssh2_pubkey), % or exception
+ Key = ssh_message:ssh2_pubkey_decode(KeyBlob), % or exception
true = ssh_transport:call_KeyCb(is_auth_key, [Key, User], Opts),
PlainText = build_sig_data(SessionId, User, Service, KeyBlob, Alg),
<<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
new file mode 100644
index 0000000000..4b36c8a5a0
--- /dev/null
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -0,0 +1,90 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Ssh channel supervisor.
+%%----------------------------------------------------------------------
+-module(ssh_channel_sup).
+
+-behaviour(supervisor).
+-include("ssh.hrl").
+
+-export([start_link/1, start_child/8]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% Internal API
+%%%=========================================================================
+start_link(Args) ->
+ supervisor:start_link(?MODULE, [Args]).
+
+
+start_child(client, ChannelSup, ConnRef, Callback, Id, Args, Exec, _Opts) when is_pid(ConnRef) ->
+ start_the_child(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+start_child(server, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts) when is_pid(ConnRef) ->
+ case max_num_channels_not_exceeded(ChannelSup, Opts) of
+ true ->
+ start_the_child(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+ false ->
+ {error, max_num_channels_exceeded}
+ end.
+
+
+start_the_child(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) ->
+ ChildSpec =
+ #{id => make_ref(),
+ start => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]},
+ restart => temporary,
+ type => worker,
+ modules => [ChanMod]
+ },
+ case supervisor:start_child(ChannelSup, ChildSpec) of
+ {ok, Pid} ->
+ {ok, Pid};
+ {ok, Pid, _Info} ->
+ {ok,Pid};
+ {error, {Error,_Info}} ->
+ {error, Error};
+ {error, Error} ->
+ {error, Error}
+ end.
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init(_Args) ->
+ RestartStrategy = one_for_one,
+ MaxR = 10,
+ MaxT = 3600,
+ Children = [],
+ {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
+
+%%%=========================================================================
+%%% Internal functions
+%%%=========================================================================
+max_num_channels_not_exceeded(ChannelSup, Opts) ->
+ MaxNumChannels = ?GET_OPT(max_channels, Opts),
+ NumChannels = length([x || {_,_,worker,[ssh_server_channel]} <-
+ supervisor:which_children(ChannelSup)]),
+ %% Note that NumChannels is BEFORE starting a new one
+ NumChannels < MaxNumChannels.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 380faeb11e..0d5ffaa509 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -45,6 +45,8 @@
handle_msg/3,
handle_stop/1,
+ open_channel/4,
+
channel_adjust_window_msg/2,
channel_close_msg/1,
channel_open_failure_msg/4,
@@ -57,6 +59,7 @@
channel_request_msg/4,
channel_success_msg/1,
+ request_global_msg/3,
request_failure_msg/0,
request_success_msg/1,
@@ -202,10 +205,22 @@ session_channel(ConnectionHandler, Timeout) ->
Result :: {ok, ssh:channel_id()} | {error, reason()} .
session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) ->
- case ssh_connection_handler:open_channel(ConnectionHandler, "session", <<>>,
- InitialWindowSize,
- MaxPacketSize, Timeout) of
- {open, Channel} ->
+ open_channel(ConnectionHandler, "session", <<>>,
+ InitialWindowSize,
+ MaxPacketSize,
+ Timeout).
+
+%%--------------------------------------------------------------------
+%% Description: Opens a channel for the given type.
+%% --------------------------------------------------------------------
+open_channel(ConnectionHandler, Type, ChanData, Timeout) ->
+ open_channel(ConnectionHandler, Type, ChanData, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
+
+open_channel(ConnectionHandler, Type, ChanData, InitialWindowSize, MaxPacketSize, Timeout) ->
+ case ssh_connection_handler:open_channel(ConnectionHandler, Type, ChanData,
+ InitialWindowSize, MaxPacketSize,
+ Timeout) of
+ {open, Channel} ->
{ok, Channel};
Error ->
Error
@@ -376,6 +391,7 @@ ptty_alloc(ConnectionHandler, Channel, Options0, TimeOut) ->
proplists:get_value(pixel_height, TermData, PixHeight),
proplists:get_value(pty_opts, TermData, []), TimeOut
).
+
%%--------------------------------------------------------------------
%% Not yet officialy supported! The following functions are part of the
%% initial contributed ssh application. They are untested. Do we want them?
@@ -566,6 +582,124 @@ handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
{[{connection_reply, FailMsg}], Connection0}
end;
+handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
+ sender_channel = RemoteId,
+ initial_window_size = WindowSize,
+ maximum_packet_size = PacketSize,
+ data = <<?DEC_BIN(ConnectedHost,_L1), ?UINT32(ConnectedPort),
+ ?DEC_BIN(_OriginHost,_L2), ?UINT32(_OriginPort)
+ >>
+ },
+ #connection{channel_cache = Cache,
+ channel_id_seed = ChId,
+ options = Options,
+ sub_system_supervisor = SubSysSup
+ } = C,
+ client) ->
+ {ReplyMsg, NextChId} =
+ case ssh_connection_handler:retrieve(C, {tcpip_forward,ConnectedHost,ConnectedPort}) of
+ {ok, {ConnectToHost,ConnectToPort}} ->
+ case gen_tcp:connect(ConnectToHost, ConnectToPort, [{active,false}, binary]) of
+ {ok,Sock} ->
+ {ok,Pid} = ssh_subsystem_sup:start_channel(client, SubSysSup, self(),
+ ssh_tcpip_forward_client, ChId,
+ [Sock], undefined, Options),
+ ssh_client_channel:cache_update(Cache,
+ #channel{type = "forwarded-tcpip",
+ sys = "none",
+ local_id = ChId,
+ remote_id = RemoteId,
+ user = Pid,
+ recv_window_size = ?DEFAULT_WINDOW_SIZE,
+ recv_packet_size = ?DEFAULT_PACKET_SIZE,
+ send_window_size = WindowSize,
+ send_packet_size = PacketSize,
+ send_buf = queue:new()
+ }),
+ gen_tcp:controlling_process(Sock, Pid),
+ inet:setopts(Sock, [{active,once}]),
+ {channel_open_confirmation_msg(RemoteId, ChId,
+ ?DEFAULT_WINDOW_SIZE,
+ ?DEFAULT_PACKET_SIZE),
+ ChId + 1};
+
+ {error,Error} ->
+ {channel_open_failure_msg(RemoteId,
+ ?SSH_OPEN_CONNECT_FAILED,
+ io_lib:format("Forwarded connection refused: ~p",[Error]),
+ "en"),
+ ChId}
+ end;
+
+ undefined ->
+ {channel_open_failure_msg(RemoteId,
+ ?SSH_OPEN_CONNECT_FAILED,
+ io_lib:format("No forwarding ordered",[]),
+ "en"),
+ ChId}
+ end,
+ {[{connection_reply, ReplyMsg}], C#connection{channel_id_seed = NextChId}};
+
+handle_msg(#ssh_msg_channel_open{channel_type = "direct-tcpip",
+ sender_channel = RemoteId,
+ initial_window_size = WindowSize,
+ maximum_packet_size = PacketSize,
+ data = <<?DEC_BIN(HostToConnect,_L1), ?UINT32(PortToConnect),
+ ?DEC_BIN(_OriginatorIPaddress,_L2), ?UINT32(_OrignatorPort)
+ >>
+ },
+ #connection{channel_cache = Cache,
+ channel_id_seed = ChId,
+ options = Options,
+ sub_system_supervisor = SubSysSup
+ } = C,
+ server) ->
+ {ReplyMsg, NextChId} =
+ case ?GET_OPT(tcpip_tunnel_in, Options) of
+ %% May add more to the option, like allowed ip/port pairs to connect to
+ false ->
+ {channel_open_failure_msg(RemoteId,
+ ?SSH_OPEN_CONNECT_FAILED,
+ "Forwarding disabled", "en"),
+ ChId};
+
+ true ->
+ case gen_tcp:connect(binary_to_list(HostToConnect), PortToConnect,
+ [{active,false}, binary]) of
+ {ok,Sock} ->
+ {ok,Pid} = ssh_subsystem_sup:start_channel(server, SubSysSup, self(),
+ ssh_tcpip_forward_srv, ChId,
+ [Sock], undefined, Options),
+ ssh_client_channel:cache_update(Cache,
+ #channel{type = "direct-tcpip",
+ sys = "none",
+ local_id = ChId,
+ remote_id = RemoteId,
+ user = Pid,
+ recv_window_size = ?DEFAULT_WINDOW_SIZE,
+ recv_packet_size = ?DEFAULT_PACKET_SIZE,
+ send_window_size = WindowSize,
+ send_packet_size = PacketSize,
+ send_buf = queue:new()
+ }),
+ gen_tcp:controlling_process(Sock, Pid),
+ inet:setopts(Sock, [{active,once}]),
+
+ {channel_open_confirmation_msg(RemoteId, ChId,
+ ?DEFAULT_WINDOW_SIZE,
+ ?DEFAULT_PACKET_SIZE),
+ ChId + 1};
+
+ {error,Error} ->
+ {channel_open_failure_msg(RemoteId,
+ ?SSH_OPEN_CONNECT_FAILED,
+ io_lib:format("Forwarded connection refused: ~p",[Error]),
+ "en"),
+ ChId}
+ end
+ end,
+ {[{connection_reply, ReplyMsg}], C#connection{channel_id_seed = NextChId}};
+
handle_msg(#ssh_msg_channel_open{channel_type = "session",
sender_channel = RemoteId},
Connection,
@@ -646,19 +780,14 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
#channel{remote_id=RemoteId} = Channel =
ssh_client_channel:cache_lookup(Cache, ChannelId),
Reply =
- try
- start_subsystem(SsName, Connection, Channel,
- {subsystem, ChannelId, WantReply, binary_to_list(SsName)})
- of
+ case start_subsystem(SsName, Connection, Channel,
+ {subsystem, ChannelId, WantReply, binary_to_list(SsName)}) of
{ok, Pid} ->
erlang:monitor(process, Pid),
ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
channel_success_msg(RemoteId);
{error,_Error} ->
channel_failure_msg(RemoteId)
- catch
- _:_ ->
- channel_failure_msg(RemoteId)
end,
{[{connection_reply,Reply}], Connection};
@@ -743,9 +872,45 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
{[], Connection}
end;
+handle_msg(#ssh_msg_global_request{name = <<"tcpip-forward">>,
+ want_reply = WantReply,
+ data = <<?DEC_BIN(ListenAddrStr,_Len),?UINT32(ListenPort)>>},
+ #connection{options = Opts} = Connection, server) ->
+ case ?GET_OPT(tcpip_tunnel_out, Opts) of
+ false ->
+ %% This daemon instance has not enabled tcpip_forwarding
+ {[{connection_reply, request_failure_msg()}], Connection};
+
+ true ->
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ SubSysSup = proplists:get_value(subsystem_sup, Sups),
+ FwdSup = ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+ ConnPid = self(),
+ case ssh_tcpip_forward_acceptor:supervised_start(FwdSup,
+ {ListenAddrStr, ListenPort},
+ undefined,
+ "forwarded-tcpip", ssh_tcpip_forward_srv,
+ ConnPid) of
+ {ok,ListenPort} when WantReply==true ->
+ {[{connection_reply, request_success_msg(<<>>)}], Connection};
+
+ {ok,LPort} when WantReply==true ->
+ {[{connection_reply, request_success_msg(<<?UINT32(LPort)>>)}], Connection};
+
+ {error,_} when WantReply==true ->
+ {[{connection_reply, request_failure_msg()}], Connection};
+
+ _ when WantReply==true ->
+ {[{connection_reply, request_failure_msg()}], Connection};
+
+ _ ->
+ {[], Connection}
+ end
+ end;
+
handle_msg(#ssh_msg_global_request{name = _Type,
want_reply = WantReply,
- data = _Data}, Connection, _) ->
+ data = _Data}, Connection, _Role) ->
if WantReply == true ->
FailMsg = request_failure_msg(),
{[{connection_reply, FailMsg}], Connection};
@@ -758,11 +923,22 @@ handle_msg(#ssh_msg_request_failure{},
{[{channel_request_reply, From, {failure, <<>>}}],
Connection#connection{requests = Rest}};
+handle_msg(#ssh_msg_request_failure{},
+ #connection{requests = [{_, From,_} | Rest]} = Connection, _) ->
+ {[{channel_request_reply, From, {failure, <<>>}}],
+ Connection#connection{requests = Rest}};
+
handle_msg(#ssh_msg_request_success{data = Data},
#connection{requests = [{_, From} | Rest]} = Connection, _) ->
{[{channel_request_reply, From, {success, Data}}],
Connection#connection{requests = Rest}};
+handle_msg(#ssh_msg_request_success{data = Data},
+ #connection{requests = [{_, From, Fun} | Rest]} = Connection0, _) ->
+ Connection = Fun({success,Data}, Connection0),
+ {[{channel_request_reply, From, {success, Data}}],
+ Connection#connection{requests = Rest}};
+
handle_msg(#ssh_msg_disconnect{code = Code,
description = Description},
Connection, _) ->
@@ -847,8 +1023,13 @@ channel_success_msg(ChannelId) ->
%%%----------------------------------------------------------------
%%% request_*_msg(...)
-%%% Returns a #ssh_msg_....{} for request responses.
+%%% Returns a #ssh_msg_....{}
%%%
+request_global_msg(Name, WantReply, Data) ->
+ #ssh_msg_global_request{name = Name,
+ want_reply = WantReply,
+ data = Data}.
+
request_failure_msg() ->
#ssh_msg_request_failure{}.
@@ -919,7 +1100,7 @@ start_cli(#connection{options = Options,
no_cli ->
{error, cli_disabled};
{CbModule, Args} ->
- start_channel(CbModule, ChannelId, Args, SubSysSup, Exec, Options)
+ ssh_subsystem_sup:start_channel(server, SubSysSup, self(), CbModule, ChannelId, Args, Exec, Options)
end.
@@ -929,37 +1110,15 @@ start_subsystem(BinName, #connection{options = Options,
Name = binary_to_list(BinName),
case check_subsystem(Name, Options) of
{Callback, Opts} when is_atom(Callback), Callback =/= none ->
- start_channel(Callback, ChannelId, Opts, SubSysSup, Options);
- {Other, _} when Other =/= none ->
+ ssh_subsystem_sup:start_channel(server, SubSysSup, self(), Callback, ChannelId, Opts, undefined, Options);
+ {none, _} ->
+ {error, bad_subsystem};
+ {_, _} ->
{error, legacy_option_not_supported}
end.
%%% Helpers for starting cli/subsystems
-start_channel(Cb, Id, Args, SubSysSup, Opts) ->
- start_channel(Cb, Id, Args, SubSysSup, undefined, Opts).
-
-start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
- ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
- case max_num_channels_not_exceeded(ChannelSup, Opts) of
- true ->
- case ssh_server_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec) of
- {error,{Error,_Info}} ->
- throw(Error);
- Others ->
- Others
- end;
- false ->
- throw(max_num_channels_exceeded)
- end.
-
-max_num_channels_not_exceeded(ChannelSup, Opts) ->
- MaxNumChannels = ?GET_OPT(max_channels, Opts),
- NumChannels = length([x || {_,_,worker,[ssh_server_channel]} <-
- supervisor:which_children(ChannelSup)]),
- %% Note that NumChannels is BEFORE starting a new one
- NumChannels < MaxNumChannels.
-
check_subsystem("sftp"= SsName, Options) ->
case ?GET_OPT(subsystems, Options) of
no_subsys -> % FIXME: Can 'no_subsys' ever be matched?
@@ -1298,13 +1457,13 @@ handle_cli_msg(C0, ChId, Reply0) ->
Ch0 = ssh_client_channel:cache_lookup(Cache, ChId),
case Ch0#channel.user of
undefined ->
- case (catch start_cli(C0, ChId)) of
+ case start_cli(C0, ChId) of
{ok, Pid} ->
erlang:monitor(process, Pid),
Ch = Ch0#channel{user = Pid},
ssh_client_channel:cache_update(Cache, Ch),
reply_msg(Ch, C0, Reply0);
- _Other ->
+ {error, _Error} ->
Reply = {connection_reply, channel_failure_msg(Ch0#channel.remote_id)},
{[Reply], C0}
end;
@@ -1315,6 +1474,10 @@ handle_cli_msg(C0, ChId, Reply0) ->
%%%----------------------------------------------------------------
%%%
+%%% TCP/IP forwarding
+
+%%%----------------------------------------------------------------
+%%%
%%% Request response handling on return to the calling ssh_connection_handler
%%% state machine.
%%%
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index e8c0d88e59..d34537950e 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -48,10 +48,15 @@
-export([start_connection/4,
available_hkey_algorithms/2,
open_channel/6,
+ start_channel/5,
+ handle_direct_tcpip/6,
request/6, request/7,
reply_request/3,
+ global_request/5,
send/5,
send_eof/2,
+ store/3,
+ retrieve/2,
info/1, info/2,
connection_info/2,
channel_info/3,
@@ -128,35 +133,29 @@ stop(ConnectionHandler)->
timeout()
) -> {ok, connection_ref()} | {error, term()}.
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-start_connection(client = Role, Socket, Options, Timeout) ->
+start_connection(Role, Socket, Options, Timeout) ->
try
- {ok, Pid} = sshc_sup:start_child([Role, Socket, Options]),
- ok = socket_control(Socket, Pid, Options),
- handshake(Pid, erlang:monitor(process,Pid), Timeout)
- catch
- exit:{noproc, _} ->
- {error, ssh_not_started};
- _:Error ->
- {error, Error}
- end;
-
-start_connection(server = Role, Socket, Options, Timeout) ->
- try
- case ?GET_OPT(parallel_login, Options) of
- true ->
- HandshakerPid =
- spawn_link(fun() ->
- receive
- {do_handshake, Pid} ->
- handshake(Pid, erlang:monitor(process,Pid), Timeout)
- end
- end),
- ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
- HandshakerPid ! {do_handshake, ChildPid};
- false ->
- ChildPid = start_the_connection_child(self(), Role, Socket, Options),
- handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
- end
+ case Role of
+ client ->
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout);
+ server ->
+ case ?GET_OPT(parallel_login, Options) of
+ true ->
+ HandshakerPid =
+ spawn_link(fun() ->
+ receive
+ {do_handshake, Pid} ->
+ handshake(Pid, erlang:monitor(process,Pid), Timeout)
+ end
+ end),
+ ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
+ HandshakerPid ! {do_handshake, ChildPid};
+ false ->
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
+ end
+ end
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -178,6 +177,8 @@ disconnect(Code, DetailedText, Module, Line) ->
[{next_event, internal, {send_disconnect, Code, DetailedText, Module, Line}}]}).
%%--------------------------------------------------------------------
+%%% Open a channel in the connection to the peer, that is, do the ssh
+%%% signalling with the peer.
-spec open_channel(connection_ref(),
string(),
iodata(),
@@ -197,6 +198,22 @@ open_channel(ConnectionHandler,
Timeout}).
%%--------------------------------------------------------------------
+%%% Start a channel handling process in the superviser tree
+-spec start_channel(connection_ref(), atom(), channel_id(), list(), term()) ->
+ {ok, pid()} | {error, term()}.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
+ {ok, {SubSysSup,Role,Opts}} = call(ConnectionHandler, get_misc),
+ ssh_subsystem_sup:start_channel(Role, SubSysSup,
+ ConnectionHandler, CallbackModule, ChannelId,
+ Args, Exec, Opts).
+
+%%--------------------------------------------------------------------
+handle_direct_tcpip(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout) ->
+ call(ConnectionHandler, {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout}).
+
+%%--------------------------------------------------------------------
-spec request(connection_ref(),
pid(),
channel_id(),
@@ -235,6 +252,12 @@ reply_request(ConnectionHandler, Status, ChannelId) ->
cast(ConnectionHandler, {reply_request, Status, ChannelId}).
%%--------------------------------------------------------------------
+global_request(ConnectionHandler, Type, true, Data, Timeout) ->
+ call(ConnectionHandler, {global_request, Type, Data, Timeout});
+global_request(ConnectionHandler, Type, false, Data, _) ->
+ cast(ConnectionHandler, {global_request, Type, Data}).
+
+%%--------------------------------------------------------------------
-spec send(connection_ref(),
channel_id(),
non_neg_integer(),
@@ -324,6 +347,21 @@ close(ConnectionHandler, ChannelId) ->
%%--------------------------------------------------------------------
+store(ConnectionHandler, Key, Value) ->
+ cast(ConnectionHandler, {store,Key,Value}).
+
+retrieve(#connection{options=Opts}, Key) ->
+ try ?GET_INTERNAL_OPT(Key, Opts) of
+ Value ->
+ {ok,Value}
+ catch
+ error:{badkey,Key} ->
+ undefined
+ end;
+retrieve(ConnectionHandler, Key) ->
+ call(ConnectionHandler, {retrieve,Key}).
+
+%%--------------------------------------------------------------------
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
set_sock_opts(ConnectionRef, SocketOptions) ->
try lists:foldr(fun({Name,_Val}, Acc) ->
@@ -408,13 +446,26 @@ alg(ConnectionHandler) ->
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
init_connection_handler(Role, Socket, Opts) ->
case init([Role, Socket, Opts]) of
- {ok, StartState, D} ->
+ {ok, StartState, D} when Role == server ->
process_flag(trap_exit, true),
gen_statem:enter_loop(?MODULE,
[], %%[{debug,[trace,log,statistics,debug]} ], %% []
StartState,
D);
+ {ok, StartState, D0=#data{connection_state=C}} when Role == client ->
+ process_flag(trap_exit, true),
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ D = D0#data{connection_state =
+ C#connection{system_supervisor = proplists:get_value(system_sup, Sups),
+ sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
+ connection_supervisor = proplists:get_value(connection_sup, Sups)
+ }},
+ gen_statem:enter_loop(?MODULE,
+ [], %%[{debug,[trace,log,statistics,debug]} ], %% []
+ StartState,
+ D);
+
{stop, Error} ->
D = try
%% Only servers have supervisorts defined in Opts
@@ -1183,6 +1234,10 @@ handle_event(cast, {unknown,Data}, StateName, D) when ?CONNECTED(StateName) ->
Msg = #ssh_msg_unimplemented{sequence = Data},
{keep_state, send_msg(Msg,D)};
+handle_event(cast, {global_request, Type, Data}, StateName, D) when ?CONNECTED(StateName) ->
+ {keep_state, send_msg(ssh_connection:request_global_msg(Type,false,Data), D)};
+
+
%%% Previously handle_sync_event began here
handle_event({call,From}, get_print_info, StateName, D) ->
Reply =
@@ -1274,6 +1329,34 @@ handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName,
{keep_state, D, cond_set_idle_timer(D)}
end;
+handle_event({call,From}, {global_request, "tcpip-forward" = Type,
+ {ListenHost,ListenPort,ConnectToHost,ConnectToPort},
+ Timeout}, StateName, D0) when ?CONNECTED(StateName) ->
+ Id = make_ref(),
+ Data = <<?STRING(ListenHost), ?Euint32(ListenPort)>>,
+ Fun = fun({success, <<Port:32/unsigned-integer>>}, C) ->
+ Key = {tcpip_forward,ListenHost,Port},
+ Value = {ConnectToHost,ConnectToPort},
+ C#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C#connection.options)};
+ ({success, <<>>}, C) ->
+ Key = {tcpip_forward,ListenHost,ListenPort},
+ Value = {ConnectToHost,ConnectToPort},
+ C#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C#connection.options)};
+ (_, C) ->
+ C
+ end,
+ D = send_msg(ssh_connection:request_global_msg(Type, true, Data),
+ add_request(Fun, Id, From, D0)),
+ start_channel_request_timer(Id, From, Timeout),
+ {keep_state, D, cond_set_idle_timer(D)};
+
+handle_event({call,From}, {global_request, Type, Data, Timeout}, StateName, D0) when ?CONNECTED(StateName) ->
+ Id = make_ref(),
+ D = send_msg(ssh_connection:request_global_msg(Type, true, Data),
+ add_request(true, Id, From, D0)),
+ start_channel_request_timer(Id, From, Timeout),
+ {keep_state, D, cond_set_idle_timer(D)};
+
handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
{Repls,D} = send_replies(ssh_connection:channel_data(ChannelId, Type, Data, D0#data.connection_state, From),
@@ -1291,6 +1374,13 @@ handle_event({call,From}, {eof, ChannelId}, StateName, D0)
{keep_state, D0, [{reply,From,{error,closed}}]}
end;
+handle_event({call,From}, get_misc, StateName,
+ #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) ->
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ SubSysSup = proplists:get_value(subsystem_sup, Sups),
+ Reply = {ok, {SubSysSup, role(StateName), Opts}},
+ {keep_state, D, [{reply,From,Reply}]};
+
handle_event({call,From},
{open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
StateName,
@@ -1347,6 +1437,17 @@ handle_event({call,From}, {close, ChannelId}, StateName, D0)
{keep_state_and_data, [{reply,From,ok}]}
end;
+handle_event(cast, {store,Key,Value}, _StateName, #data{connection_state=C0} = D) ->
+ C = C0#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C0#connection.options)},
+ {keep_state, D#data{connection_state = C}};
+
+handle_event({call,From}, {retrieve,Key}, _StateName, #data{connection_state=C}) ->
+ case retrieve(C, Key) of
+ {ok,Value} ->
+ {keep_state_and_data, [{reply,From,{ok,Value}}]};
+ _ ->
+ {keep_state_and_data, [{reply,From,undefined}]}
+ end;
%%===== Reception of encrypted bytes, decryption and framing
handle_event(info, {Proto, Sock, Info}, {hello,_}, #data{socket = Sock,
@@ -1515,6 +1616,32 @@ handle_event(info, {'EXIT', _Sup, Reason}, StateName, _) ->
handle_event(info, check_cache, _, D) ->
{keep_state, D, cond_set_idle_timer(D)};
+handle_event(info, {fwd_connect_received, Sock, ChId, ChanCB}, StateName, #data{connection_state = Connection}) ->
+ #connection{options = Options,
+ channel_cache = Cache,
+ sub_system_supervisor = SubSysSup} = Connection,
+ Channel = ssh_client_channel:cache_lookup(Cache, ChId),
+ {ok,Pid} = ssh_subsystem_sup:start_channel(role(StateName), SubSysSup, self(), ChanCB, ChId, [Sock], undefined, Options),
+ ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
+ gen_tcp:controlling_process(Sock, Pid),
+ inet:setopts(Sock, [{active,once}]),
+ keep_state_and_data;
+
+handle_event({call,From},
+ {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, _Timeout},
+ _StateName,
+ #data{connection_state = #connection{sub_system_supervisor=SubSysSup}}) ->
+ case ssh_tcpip_forward_acceptor:supervised_start(ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+ {ListenHost, ListenPort},
+ {ConnectToHost, ConnectToPort},
+ "direct-tcpip", ssh_tcpip_forward_client,
+ self()) of
+ {ok,LPort} ->
+ {keep_state_and_data, [{reply,From,{ok,LPort}}]};
+ {error,Error} ->
+ {keep_state_and_data, [{reply,From,{error,Error}}]}
+ end;
+
handle_event(info, UnexpectedMessage, StateName, D = #data{ssh_params = Ssh}) ->
case unexpected_fun(UnexpectedMessage, D) of
report ->
@@ -1705,17 +1832,36 @@ start_the_connection_child(UserPid, Role, Socket, Options0) ->
Sups = ?GET_INTERNAL_OPT(supervisors, Options0),
ConnectionSup = proplists:get_value(connection_sup, Sups),
Options = ?PUT_INTERNAL_OPT({user_pid,UserPid}, Options0),
- {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Options]),
- ok = socket_control(Socket, Pid, Options),
+ InitArgs = [Role, Socket, Options],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, InitArgs),
+ ok = socket_control(Socket, Pid, Options), % transfer the Socket ownership in a controlled way.
Pid.
%%--------------------------------------------------------------------
%% Stopping
-stop_subsystem(#data{connection_state =
+stop_subsystem(#data{ssh_params =
+ #ssh{role = Role},
+ connection_state =
#connection{system_supervisor = SysSup,
- sub_system_supervisor = SubSysSup}}) when is_pid(SubSysSup) ->
- ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
+ sub_system_supervisor = SubSysSup}
+ }) when is_pid(SysSup) andalso is_pid(SubSysSup) ->
+ process_flag(trap_exit, false),
+ C = self(),
+ spawn(fun() ->
+ Mref = erlang:monitor(process, C),
+ receive
+ {'DOWN', Mref, process, C, _Info} -> ok
+ after
+ 10000 -> ok
+ end,
+ case Role of
+ client ->
+ ssh_system_sup:stop_system(Role, SysSup);
+ _ ->
+ ssh_system_sup:stop_subsystem(SysSup, SubSysSup)
+ end
+ end);
stop_subsystem(_) ->
ok.
@@ -1807,6 +1953,8 @@ call(FsmPid, Event, Timeout) ->
exit:{normal, _R} ->
{error, closed};
exit:{{shutdown, _R},_} ->
+ {error, closed};
+ exit:{shutdown, _R} ->
{error, closed}
end.
@@ -1941,6 +2089,11 @@ add_request(true, ChannelId, From, #data{connection_state =
#connection{requests = Requests0} =
Connection} = State) ->
Requests = [{ChannelId, From} | Requests0],
+ State#data{connection_state = Connection#connection{requests = Requests}};
+add_request(Fun, ChannelId, From, #data{connection_state =
+ #connection{requests = Requests0} =
+ Connection} = State) when is_function(Fun) ->
+ Requests = [{ChannelId, From, Fun} | Requests0],
State#data{connection_state = Connection#connection{requests = Requests}}.
new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} =
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 510269bbb1..a9034d2085 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -24,218 +24,140 @@
-module(ssh_file).
--behaviour(ssh_server_key_api).
--behaviour(ssh_client_key_api).
-
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
-include("ssh.hrl").
--export([host_key/2,
- user_key/2,
- is_host_key/4,
- add_host_key/3,
- is_auth_key/3]).
-
-
--export_type([system_dir_daemon_option/0,
- user_dir_common_option/0,
- user_dir_fun_common_option/0,
- pubkey_passphrase_client_options/0
- ]).
-
+%%%--------------------- server exports ---------------------------
+-behaviour(ssh_server_key_api).
+-export([host_key/2, is_auth_key/3]).
+-export_type([system_dir_daemon_option/0]).
-type system_dir_daemon_option() :: {system_dir, string()}.
--type user_dir_common_option() :: {user_dir, string()}.
--type user_dir_fun_common_option() :: {user_dir_fun, user2dir()}.
--type user2dir() :: fun((RemoteUserName::string()) -> UserDir :: string()) .
+%%%--------------------- client exports ---------------------------
+-behaviour(ssh_client_key_api).
+-export([is_host_key/4, user_key/2, add_host_key/3]).
+-export_type([pubkey_passphrase_client_options/0]).
-type pubkey_passphrase_client_options() :: {dsa_pass_phrase, string()}
| {rsa_pass_phrase, string()}
%% Not yet implemented: | {ed25519_pass_phrase, string()}
%% Not yet implemented: | {ed448_pass_phrase, string()}
| {ecdsa_pass_phrase, string()} .
+%%%--------------------- common exports ---------------------------
+-export_type([user_dir_common_option/0,
+ user_dir_fun_common_option/0
+ ]).
--define(PERM_700, 8#700).
--define(PERM_644, 8#644).
-
+-type user_dir_common_option() :: {user_dir, string()}.
+-type user_dir_fun_common_option() :: {user_dir_fun, user2dir()}.
+-type user2dir() :: fun((RemoteUserName::string()) -> UserDir :: string()) .
+%%%================================================================
+%%%
%%% API
+%%%
-%% Used by server
+%%%---------------- SERVER API ------------------------------------
host_key(Algorithm, Opts) ->
- File = file_name(system, file_base_name(Algorithm), Opts),
- %% We do not expect host keys to have pass phrases
- %% so probably we could hardcod Password = ignore, but
- %% we keep it as an undocumented option for now.
- Password = proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
- case decode(File, Password) of
- {ok,Key} ->
- check_key_type(Key, Algorithm);
- {error,DecodeError} ->
- {error,DecodeError}
- end.
-
-is_auth_key(Key, User,Opts) ->
- case lookup_user_key(Key, User, Opts) of
- {ok, Key} ->
- true;
- _ ->
- false
- end.
+ read_ssh_key_file(system, private, Algorithm, Opts).
+is_auth_key(Key, User ,Opts) ->
+ KeyType = erlang:atom_to_binary(ssh_transport:public_algo(Key), latin1),
+ Dir = ssh_dir({remoteuser,User}, Opts),
+ lookup_auth_keys(KeyType, Key, filename:join(Dir,"authorized_keys"))
+ orelse
+ lookup_auth_keys(KeyType, Key, filename:join(Dir,"authorized_keys2")).
-%% Used by client
-is_host_key(Key, PeerName, Algorithm, Opts) ->
- case lookup_host_key(Key, PeerName, Algorithm, Opts) of
- {ok, Key} ->
- true;
- _ ->
- false
- end.
-
+%%%---------------- CLIENT API ------------------------------------
user_key(Algorithm, Opts) ->
- File = file_name(user, identity_key_filename(Algorithm), Opts),
- Password = proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
- case decode(File, Password) of
- {ok, Key} ->
- check_key_type(Key, Algorithm);
- Error ->
- Error
- end.
+ read_ssh_key_file(user, private, Algorithm, Opts).
-
-%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-check_key_type(Key, Algorithm) ->
- case ssh_transport:valid_key_sha_alg(Key,Algorithm) of
- true -> {ok,Key};
- false -> {error,bad_keytype_in_file}
- end.
-
-file_base_name('ssh-rsa' ) -> "ssh_host_rsa_key";
-file_base_name('rsa-sha2-256' ) -> "ssh_host_rsa_key";
-file_base_name('rsa-sha2-384' ) -> "ssh_host_rsa_key";
-file_base_name('rsa-sha2-512' ) -> "ssh_host_rsa_key";
-file_base_name('ssh-dss' ) -> "ssh_host_dsa_key";
-file_base_name('ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
-file_base_name('ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
-file_base_name('ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key";
-file_base_name('ssh-ed25519' ) -> "ssh_host_ed25519_key";
-file_base_name('ssh-ed448' ) -> "ssh_host_ed448_key";
-file_base_name(_ ) -> "ssh_host_key".
-
-decode(File, Password) ->
- try {ok, decode_ssh_file(read_ssh_file(File), Password)}
- catch
- throw:Reason ->
- {error, Reason};
- error:Reason ->
- {error, Reason}
- end.
-
-read_ssh_file(File) ->
- {ok, Bin} = file:read_file(File),
- Bin.
-
-%% Public key
-decode_ssh_file(SshBin, public_key) ->
- public_key:ssh_decode(SshBin, public_key);
-
-%% Private Key
-decode_ssh_file(Pem, Password) ->
- case public_key:pem_decode(Pem) of
- [{_, _, not_encrypted} = Entry] ->
- public_key:pem_entry_decode(Entry);
- [Entry] when Password =/= ignore ->
- public_key:pem_entry_decode(Entry, Password);
- _ ->
- throw("No pass phrase provided for private key file")
- end.
-
-
-%% lookup_host_key
-%% return {ok, Key(s)} or {error, not_found}
-%%
-
-lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
- Host1 = replace_localhost(Host),
- do_lookup_host_key(KeyToMatch, Host1, Alg, Opts).
-
+is_host_key(Key, PeerName, Algorithm, Opts) ->
+ KeyType = erlang:atom_to_binary(Algorithm, latin1),
+ Hosts = binary:split(list_to_binary(replace_localhost(PeerName)),
+ <<",">>, [global]), % make a list of hosts
+ Dir = ssh_dir(user, Opts),
+ lookup_host_keys(Hosts, KeyType, Key, filename:join(Dir,"known_hosts")).
add_host_key(Host, Key, Opts) ->
Host1 = add_ip(replace_localhost(Host)),
KnownHosts = file_name(user, "known_hosts", Opts),
case file:open(KnownHosts, [write,append]) of
- {ok, Fd} ->
- ok = file:change_mode(KnownHosts, ?PERM_644),
- Res = add_key_fd(Fd, Host1, Key),
- file:close(Fd),
- Res;
- Error ->
- Error
- end.
-
-lookup_user_key(Key, User, Opts) ->
- SshDir = ssh_dir({remoteuser,User}, Opts),
- case lookup_user_key_f(Key, User, SshDir, "authorized_keys", Opts) of
- {ok, Key} ->
- {ok, Key};
- _ ->
- lookup_user_key_f(Key, User, SshDir, "authorized_keys2", Opts)
+ {ok, Fd} ->
+ ok = file:change_mode(KnownHosts, 8#644),
+ KeyType = erlang:atom_to_binary(ssh_transport:public_algo(Key), latin1),
+ EncKey = ssh_message:ssh2_pubkey_encode(Key),
+ SshBin =
+ iolist_to_binary([Host1, " ",
+ KeyType," ",base64:encode(iolist_to_binary(EncKey)),
+ "\n"]),
+ Res = file:write(Fd, SshBin),
+ file:close(Fd),
+ Res;
+ Error ->
+ Error
end.
+%%%================================================================
+%%%
+%%% Local functions
+%%%
-%%
-%% Utils
-%%
+%%%---------------- SERVER FUNCTIONS ------------------------------
-%% server use this to find individual keys for
-%% an individual user when user tries to login
-%% with publickey
-ssh_dir({remoteuser, User}, Opts) ->
- case proplists:get_value(user_dir_fun, Opts) of
- undefined ->
- case proplists:get_value(user_dir, Opts, false) of
- false ->
- default_user_dir();
- Dir ->
- Dir
- end;
- FUN ->
- FUN(User)
- end;
+lookup_auth_keys(KeyType, Key, File) ->
+ case file:read_file(File) of
+ {ok,Bin} ->
+ Lines = binary:split(Bin, <<"\n">>, [global,trim_all]),
+ find_key(KeyType, Key, Lines);
+ _ ->
+ false
+ end.
-%% client use this to find client ssh keys
-ssh_dir(user, Opts) ->
- case proplists:get_value(user_dir, Opts, false) of
- false -> default_user_dir();
- D -> D
+find_key(KeyType, Key, [Line|Lines]) ->
+ case find_key_in_line(KeyType, Key, binary:split(Line, <<" ">>, [global,trim_all])) of
+ true ->
+ true;
+ false ->
+ find_key(KeyType, Key, Lines)
end;
+find_key(_, _, _) ->
+ false.
-%% server use this to find server host keys
-ssh_dir(system, Opts) ->
- proplists:get_value(system_dir, Opts, "/etc/ssh").
-
-
-file_name(Type, Name, Opts) ->
- FN = filename:join(ssh_dir(Type, Opts), Name),
- FN.
+
+find_key_in_line(_KeyType, _Key, [<<"#",_/binary>> |_]) ->
+ false;
+find_key_in_line(KeyType, Key, [KeyType, Base64EncodedKey, _Comment]) ->
+ %% Right KeyType. Try to decode to see if it matches
+ Key == decode_key(Base64EncodedKey);
+find_key_in_line(KeyType, Key, [_Option | [_,_,_|_]=Rest]) ->
+ %% Dont care for options
+ find_key_in_line(KeyType, Key, Rest);
+find_key_in_line(_, _, _) ->
+ false.
+decode_key(Base64EncodedKey) ->
+ ssh_message:ssh2_pubkey_decode(
+ base64:mime_decode(Base64EncodedKey)).
+%%%---------------- CLIENT FUNCTIONS ------------------------------
+%%%--------------------------------
%% in: "host" out: "host,1.2.3.4.
add_ip(IP) when is_tuple(IP) ->
ssh_connection:encode_ip(IP);
-add_ip(Host) ->
+add_ip(Host) ->
case inet:getaddr(Host, inet) of
{ok, Addr} ->
case ssh_connection:encode_ip(Addr) of
false -> Host;
+ Host -> Host;
IPString -> Host ++ "," ++ IPString
end;
_ -> Host
- end.
+ end.
replace_localhost("localhost") ->
{ok, Hostname} = inet:gethostname(),
@@ -243,142 +165,203 @@ replace_localhost("localhost") ->
replace_localhost(Host) ->
Host.
-do_lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
- case file:open(file_name(user, "known_hosts", Opts), [read, binary]) of
- {ok, Fd} ->
- Res = lookup_host_key_fd(Fd, KeyToMatch, Host, Alg),
- file:close(Fd),
- Res;
- {error, enoent} ->
- {error, not_found};
- Error ->
- Error
- end.
-
-identity_key_filename('ssh-dss' ) -> "id_dsa";
-identity_key_filename('ssh-rsa' ) -> "id_rsa";
-identity_key_filename('rsa-sha2-256' ) -> "id_rsa";
-identity_key_filename('rsa-sha2-384' ) -> "id_rsa";
-identity_key_filename('rsa-sha2-512' ) -> "id_rsa";
-identity_key_filename('ssh-ed25519' ) -> "id_ed25519";
-identity_key_filename('ssh-ed448' ) -> "id_ed448";
-identity_key_filename('ecdsa-sha2-nistp256') -> "id_ecdsa";
-identity_key_filename('ecdsa-sha2-nistp384') -> "id_ecdsa";
-identity_key_filename('ecdsa-sha2-nistp521') -> "id_ecdsa".
-
-identity_pass_phrase("ssh-dss" ) -> dsa_pass_phrase;
-identity_pass_phrase("ssh-rsa" ) -> rsa_pass_phrase;
-identity_pass_phrase("rsa-sha2-256" ) -> rsa_pass_phrase;
-identity_pass_phrase("rsa-sha2-384" ) -> rsa_pass_phrase;
-identity_pass_phrase("rsa-sha2-512" ) -> rsa_pass_phrase;
-%% Not yet implemented: identity_pass_phrase("ssh-ed25519" ) -> ed25519_pass_phrase;
-%% Not yet implemented: identity_pass_phrase("ssh-ed448" ) -> ed448_pass_phrase;
-identity_pass_phrase("ecdsa-sha2-"++_) -> ecdsa_pass_phrase;
-identity_pass_phrase(P) when is_atom(P) ->
- identity_pass_phrase(atom_to_list(P));
-identity_pass_phrase(_) -> undefined.
-
-lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) ->
- case io:get_line(Fd, '') of
- eof ->
- {error, not_found};
- {error,Error} ->
- %% Rare... For example NFS errors
- {error,Error};
- Line ->
- case ssh_decode_line(Line, known_hosts) of
- [{Key, Attributes}] ->
- handle_host(Fd, KeyToMatch, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
- [] ->
- lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
- end
+%%%--------------------------------
+lookup_host_keys(Hosts, KeyType, Key, File) ->
+ case file:read_file(File) of
+ {ok,Bin} ->
+ Lines = binary:split(Bin, <<"\n">>, [global,trim_all]),
+ find_key(Hosts, KeyType, Key, Lines);
+ _ ->
+ false
end.
-ssh_decode_line(Line, Type) ->
- try
- public_key:ssh_decode(Line, Type)
- catch _:_ ->
- []
- end.
+find_key(Hosts, KeyType, Key, [Line|Lines]) ->
+ case find_key_in_line(Hosts, KeyType, Key, binary:split(Line, <<" ">>, [global,trim_all])) of
+ true ->
+ true;
+ false ->
+ find_key(Hosts, KeyType, Key, Lines)
+ end;
+find_key(_, _, _, _) ->
+ false.
-handle_host(Fd, KeyToMatch, Host, HostList, Key, KeyType) ->
- Host1 = host_name(Host),
- case lists:member(Host1, HostList) andalso key_match(Key, KeyType) of
- true when KeyToMatch == Key ->
- {ok,Key};
- _ ->
- lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType)
- end.
-host_name(Atom) when is_atom(Atom) ->
- atom_to_list(Atom);
-host_name(List) ->
- List.
-
-key_match(#'RSAPublicKey'{}, 'ssh-rsa') ->
- true;
-key_match({_, #'Dss-Parms'{}}, 'ssh-dss') ->
- true;
-key_match({#'ECPoint'{},{namedCurve,Curve}}, Alg) ->
- case atom_to_list(Alg) of
- "ecdsa-sha2-"++IdS ->
- Curve == public_key:ssh_curvename2oid(list_to_binary(IdS));
- _ ->
- false
- end;
-key_match({ed_pub,ed25519,_}, 'ssh-ed25519') ->
- true;
-key_match({ed_pub,ed448,_}, 'ssh-ed448') ->
- true;
-key_match(_, _) ->
+find_key_in_line(_Hosts, _KeyType, _Key, [<<"#",_/binary>> |_]) ->
+ false;
+find_key_in_line(Hosts, KeyType, Key, [Patterns, KeyType, Base64EncodedKey, _Comment]) ->
+ host_match(Hosts, Patterns) andalso
+ Key == decode_key(Base64EncodedKey);
+find_key_in_line(Hosts, KeyType, Key, [Patterns, KeyType, Base64EncodedKey]) ->
+ host_match(Hosts, Patterns) andalso
+ Key == decode_key(Base64EncodedKey);
+find_key_in_line(Hosts, KeyType, Key, [_Option | [_,_,_|_]=Rest]) ->
+ %% Dont care for options
+ find_key_in_line(Hosts, KeyType, Key, Rest);
+find_key_in_line(_, _, _, _) ->
false.
-add_key_fd(Fd, Host,Key) ->
- SshBin = public_key:ssh_encode([{Key, [{hostnames, [Host]}]}], known_hosts),
- file:write(Fd, SshBin).
-
-lookup_user_key_f(_, _User, [], _F, _Opts) ->
- {error, nouserdir};
-lookup_user_key_f(_, _User, nouserdir, _F, _Opts) ->
- {error, nouserdir};
-lookup_user_key_f(Key, _User, Dir, F, _Opts) ->
- FileName = filename:join(Dir, F),
- case file:open(FileName, [read, binary]) of
- {ok, Fd} ->
- Res = lookup_user_key_fd(Fd, Key),
- file:close(Fd),
- Res;
- {error, Reason} ->
- {error, {{openerr, Reason}, {file, FileName}}}
+
+host_match(Hosts, PatternsBin) ->
+ Patterns = binary:split(PatternsBin, <<",">>, [global]),
+ lists:any(fun(Pat) ->
+ lists:any(fun(Hst) ->
+ Pat == Hst
+ end, Hosts)
+ end, Patterns).
+
+%%%---------------- COMMON FUNCTIONS ------------------------------
+
+read_ssh_key_file(Role, PrivPub, Algorithm, Opts) ->
+ File = file_name(Role, file_base_name(Role,Algorithm), Opts),
+ Password = %% Pwd for Host Keys is an undocumented option and should not be used
+ proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
+
+ case file:read_file(File) of
+ {ok, Pem} ->
+ try
+ decode_ssh_file(PrivPub, Algorithm, Pem, Password)
+ catch
+ throw:Reason ->
+ {error, Reason};
+ error:Reason ->
+ {error, Reason}
+ end;
+
+ {error, Reason} ->
+ {error, Reason}
end.
-lookup_user_key_fd(Fd, Key) ->
- case io:get_line(Fd, '') of
- eof ->
- {error, not_found};
- {error,Error} ->
- %% Rare... For example NFS errors
- {error,Error};
- Line ->
- case ssh_decode_line(Line, auth_keys) of
- [{AuthKey, _}] ->
- case is_auth_key(Key, AuthKey) of
- true ->
- {ok, Key};
- false ->
- lookup_user_key_fd(Fd, Key)
- end;
- [] ->
- lookup_user_key_fd(Fd, Key)
- end
+
+decode_ssh_file(PrivPub, Algorithm, Pem, Password) ->
+ %% Private Key
+ try get_key_part(Pem) of
+ {'openssh-key-v1', Bin, _KeyValues} ->
+ %% Holds both public and private keys
+ KeyPairs = new_openssh_decode(Bin, Password),
+ ValidKeys =
+ [Key || {Pub,Priv} <- KeyPairs,
+ Key <- [Pub,Priv],
+ ssh_transport:valid_key_sha_alg(PrivPub, Key, Algorithm)],
+ %% Select one (for now, just pick the first found):
+ case ValidKeys of
+ [Key|_] -> {ok,Key};
+ [] -> {error,bad_keytype_in_file}
+ end;
+
+ {rfc4716, Bin, _KeyValues} ->
+ %% rfc4716 only defines public keys
+ Key = ssh_message:ssh2_pubkey_decode(Bin),
+ case ssh_transport:valid_key_sha_alg(PrivPub, Key, Algorithm) of
+ true -> {ok,Key};
+ false -> {error,bad_keytype_in_file}
+ end;
+
+ {Type, Bin, KeyValues} ->
+ Key =
+ case get_encrypt_hdrs(KeyValues) of
+ not_encrypted ->
+ public_key:pem_entry_decode({Type,Bin,not_encrypted});
+ [Cipher,Salt] when is_binary(Cipher),
+ is_binary(Salt),
+ Password =/= ignore ->
+ CryptInfo =
+ {binary_to_list(Cipher), unhex(binary_to_list(Salt))},
+ public_key:pem_entry_decode({Type,Bin,CryptInfo}, Password);
+ _X ->
+ throw("No pass phrase provided for private key file")
+ end,
+ case ssh_transport:valid_key_sha_alg(PrivPub, Key, Algorithm) of
+ true -> {ok,Key};
+ false -> {error,bad_keytype_in_file}
+ end
+ catch
+ _:_ -> error(bad_or_unsupported_key_format)
end.
-is_auth_key(Key, Key) ->
- true;
-is_auth_key(_,_) ->
- false.
+get_encrypt_hdrs(KVs) ->
+ lists:foldl(fun({<<"Proc-Type">>, <<"4,ENCRYPTED", _/binary>>}, _Acc) ->
+ {proc_type, <<"4,ENCRYPTED">>};
+ ({<<"DEK-Info">>, DEKinfo}, {proc_type,_}) ->
+ binary:split(DEKinfo, <<",">>);
+ (_, Acc) ->
+ Acc
+ end, not_encrypted, KVs).
+
+unhex(S) ->
+ %% I would like to do erlang:list_to_integer(S,16), but that does not fit
+ %% the public_key:pem_entry_decode API
+ list_to_binary(
+ lists:foldr(fun(D2, {D1,Acc}) ->
+ [erlang:list_to_integer([D2,D1], 16) | Acc]; % sic!
+ (D1, Acc) when is_list(Acc) ->
+ {D1,Acc}
+ end, [], S)).
+
+file_base_name(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa";
+file_base_name(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa";
+file_base_name(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa";
+file_base_name(user, 'rsa-sha2-256' ) -> "id_rsa";
+file_base_name(user, 'rsa-sha2-384' ) -> "id_rsa";
+file_base_name(user, 'rsa-sha2-512' ) -> "id_rsa";
+file_base_name(user, 'ssh-dss' ) -> "id_dsa";
+file_base_name(user, 'ssh-ed25519' ) -> "id_ed25519";
+file_base_name(user, 'ssh-ed448' ) -> "id_ed448";
+file_base_name(user, 'ssh-rsa' ) -> "id_rsa";
+file_base_name(system, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+file_base_name(system, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+file_base_name(system, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key";
+file_base_name(system, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+file_base_name(system, 'rsa-sha2-384' ) -> "ssh_host_rsa_key";
+file_base_name(system, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+file_base_name(system, 'ssh-dss' ) -> "ssh_host_dsa_key";
+file_base_name(system, 'ssh-ed25519' ) -> "ssh_host_ed25519_key";
+file_base_name(system, 'ssh-ed448' ) -> "ssh_host_ed448_key";
+file_base_name(system, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+file_base_name(system, _ ) -> "ssh_host_key".
+
+
+identity_pass_phrase('ssh-dss' ) -> dsa_pass_phrase;
+identity_pass_phrase('ssh-rsa' ) -> rsa_pass_phrase;
+identity_pass_phrase('rsa-sha2-256' ) -> rsa_pass_phrase;
+identity_pass_phrase('rsa-sha2-384' ) -> rsa_pass_phrase;
+identity_pass_phrase('rsa-sha2-512' ) -> rsa_pass_phrase;
+identity_pass_phrase('ecdsa-sha2-nistp256') -> ecdsa_pass_phrase;
+identity_pass_phrase('ecdsa-sha2-nistp384') -> ecdsa_pass_phrase;
+identity_pass_phrase('ecdsa-sha2-nistp521') -> ecdsa_pass_phrase;
+%% Not yet implemented: identity_pass_phrase('ssh-ed25519' ) -> ed25519_pass_phrase;
+%% Not yet implemented: identity_pass_phrase('ssh-ed448' ) -> ed448_pass_phrase;
+identity_pass_phrase(_) -> undefined.
+
+%%%----------------------------------------------------------------
+file_name(Type, Name, Opts) ->
+ filename:join(ssh_dir(Type, Opts), Name).
+
+
+%%%--------------------------------
+ssh_dir({remoteuser, User}, Opts) ->
+ %% server use this to find individual keys for an individual
+ %% user when user tries to login with publickey
+ case proplists:get_value(user_dir_fun, Opts) of
+ undefined ->
+ %% Try the local user instead
+ ssh_dir(user, Opts);
+ FUN ->
+ FUN(User)
+ end;
+ssh_dir(user, Opts) ->
+ %% client use this to find client ssh keys
+ case proplists:get_value(user_dir, Opts, false) of
+ false -> default_user_dir();
+ D -> D
+ end;
+
+ssh_dir(system, Opts) ->
+ %% server use this to find server host keys
+ proplists:get_value(system_dir, Opts, "/etc/ssh").
+
+%%%--------------------------------
default_user_dir() ->
try
default_user_dir(os:getenv("HOME"))
@@ -395,9 +378,138 @@ default_user_dir(Home) when is_list(Home) ->
{ok,Info} = file:read_file_info(UserDir),
#file_info{mode=Mode} = Info,
case (Mode band 8#777) of
- ?PERM_700 ->
+ 8#700 ->
ok;
_Other ->
- ok = file:change_mode(UserDir, ?PERM_700)
+ ok = file:change_mode(UserDir, 8#700)
end,
UserDir.
+
+%%%################################################################
+get_key_part(RawBin) when is_binary(RawBin) ->
+ case binary:split(
+ binary:replace(RawBin, <<"\\\n">>, <<"">>, [global]),
+ <<"\n">>, [global,trim_all])
+ of
+ [<<"---- BEGIN SSH2 PUBLIC KEY ----">> | Lines0] ->
+ %% RFC 4716 format
+ {KeyValues,Lines} = get_hdr_lines(Lines0, []),
+ ExpectedEndLine = <<"---- END SSH2 PUBLIC KEY ----">>,
+ {rfc4716, get_body(Lines,ExpectedEndLine), KeyValues};
+
+ [<<"-----BEGIN ", Rest/binary>> | Lines0] ->
+ %% PEM format
+ ExpectedEndLine = <<"-----END ",Rest/binary>>,
+ [MiddlePart, <<>>] = binary:split(Rest, <<" KEY-----">>),
+ {KeyValues,Lines} = get_hdr_lines(Lines0, []),
+ {asn1_type(MiddlePart), get_body(Lines,ExpectedEndLine), KeyValues}
+ end.
+
+
+get_hdr_lines(Lines, Acc) ->
+ Line1 = hd(Lines),
+ case binary:split(Line1, <<":">>) of
+ [Line1] ->
+ {lists:reverse(Acc), Lines};
+ [Key,Value] ->
+ get_hdr_lines(tl(Lines), [{trim(Key),trim(Value)}|Acc])
+ end.
+
+
+get_body(Lines, ExpectedEndLine) ->
+ {KeyPart, [ExpectedEndLine]} = lists:split(length(Lines)-1, Lines),
+ base64:mime_decode(iolist_to_binary(KeyPart)).
+
+trim(<<" ",B/binary>>) -> trim(B);
+trim(B) -> B.
+
+asn1_type(<<"RSA PRIVATE">>) -> 'RSAPrivateKey';
+asn1_type(<<"RSA PUBLIC">>) -> 'RSAPublicKey';
+asn1_type(<<"DSA PRIVATE">>) -> 'DSAPrivateKey';
+asn1_type(<<"EC PRIVATE">>) -> 'ECPrivateKey';
+asn1_type(<<"OPENSSH PRIVATE">>) -> 'openssh-key-v1';
+asn1_type(_) -> undefined.
+
+%%%================================================================
+%%% From https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
+%%%
+
+-define(NON_CRYPT_BLOCKSIZE, 8).
+
+new_openssh_decode(<<"openssh-key-v1",0,
+ ?DEC_BIN(CipherName, _L1),
+ ?DEC_BIN(KdfName, _L2),
+ ?DEC_BIN(KdfOptions, _L3),
+ ?UINT32(N), % number of keys
+ Rest/binary
+ >>, Pwd) ->
+ new_openssh_decode(Rest, N, Pwd, CipherName, KdfName, KdfOptions, N, []).
+
+
+new_openssh_decode(<<?DEC_BIN(BinKey,_L1), Rest/binary>>, I, Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAcc) when I>0 ->
+ PublicKey = ssh_message:ssh2_pubkey_decode(BinKey),
+ new_openssh_decode(Rest, I-1, Pwd, CipherName, KdfName, KdfOptions, N, [PublicKey|PubKeyAcc]);
+
+new_openssh_decode(<<?DEC_BIN(Encrypted,_L)>>,
+ 0, Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAccRev) ->
+ PubKeys = lists:reverse(PubKeyAccRev),
+ try
+ Plain = decrypt_new_openssh(Encrypted, KdfName, KdfOptions, CipherName, Pwd),
+ new_openssh_decode_priv_keys(Plain, N, N, [], [])
+ of
+ {PrivKeys, _Comments} ->
+ lists:map(fun({ {ed_pub,A,Pub}, {ed_pri,A,Pub,Pri0} }) ->
+ Pri = binary:part(Pri0, {0,size(Pri0)-size(Pub)}),
+ {{ed_pub,A,Pub}, {ed_pri,A,Pub,Pri}};
+ (Pair) ->
+ Pair
+ end, lists:zip(PubKeys, PrivKeys))
+ catch
+ error:{decryption, DecryptError} ->
+ error({decryption, DecryptError})
+ end.
+
+
+new_openssh_decode_priv_keys(Bin, I, N, KeyAcc, CmntAcc) when I>0 ->
+ {PrivKey, <<?DEC_BIN(Comment,_Lc),Rest/binary>>} = ssh_message:ssh2_privkey_decode2(Bin),
+ new_openssh_decode_priv_keys(Rest, I-1, N, [PrivKey|KeyAcc], [Comment|CmntAcc]);
+new_openssh_decode_priv_keys(_Padding, 0, _N, PrivKeyAccRev, CommentAccRev) ->
+ {lists:reverse(PrivKeyAccRev),
+ lists:reverse(CommentAccRev)}.
+
+
+decrypt_new_openssh(Encrypted, <<"none">>, <<>>, _CipherName, _Pwd) ->
+ check_valid_decryption(Encrypted, ?NON_CRYPT_BLOCKSIZE);
+decrypt_new_openssh(Encrypted, <<>>, <<>>, _CipherName, _Pwd) ->
+ check_valid_decryption(Encrypted, ?NON_CRYPT_BLOCKSIZE);
+decrypt_new_openssh(_Encrypted, <<"bcrypt">>, <<?DEC_BIN(_Salt,_L),?UINT32(_Rounds)>>, _CipherName, _Pwd) ->
+ error({decryption, {not_supported,bcrypt}});
+decrypt_new_openssh(_Encrypted, KdfName, _KdfOpts, _CipherName, _Pwd) ->
+ error({decryption, {not_supported,KdfName}}).
+
+
+check_valid_decryption(<<?UINT32(Checkint1),?UINT32(Checkint2),Plain/binary>>, BlockSize) when Checkint2==Checkint1 ->
+ case check_padding(Plain, BlockSize) of
+ true ->
+ Plain;
+ false ->
+ error({decryption,bad_padding})
+ end;
+check_valid_decryption(_, _) ->
+ error({decryption,bad_result}).
+
+
+check_padding(Bin, BlockSize) ->
+ N = binary:last(Bin),
+ if
+ N < BlockSize ->
+ %% Check that Bin is <<...,1,2,...,N>>
+ Padding = binary:part(Bin, {byte_size(Bin),-N}),
+ ExpectedPadding = list_to_binary(lists:seq(1,N)), % <<1,2,...,N>>
+ Padding == ExpectedPadding;
+ true ->
+ true
+ end.
+
+%%%================================================================
+%%%
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
index 79cd95e422..91365205aa 100644
--- a/lib/ssh/src/ssh_info.erl
+++ b/lib/ssh/src/ssh_info.erl
@@ -79,8 +79,8 @@ print_clients() ->
lists:map(fun print_client/1,
supervisor:which_children(sshc_sup))
catch
- C:E ->
- io_lib:format('***print_clients FAILED: ~p:~p~n',[C,E])
+ C:E:S ->
+ io_lib:format('***print_clients FAILED: ~p:~p,~n ~p~n',[C,E,S])
end.
print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
@@ -94,9 +94,9 @@ print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
io_lib:format(?INDENT?INDENT?INDENT"No channels~n",[])
end];
-print_client(Other) ->
- io_lib:format(" [[Other 1: ~p]]~n",[Other]).
-
+print_client({{client,ssh_system_sup,_,_,_},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
+ lists:map(fun print_system_sup/1,
+ supervisor:which_children(Pid)).
%%%================================================================
print_servers() ->
@@ -104,8 +104,8 @@ print_servers() ->
lists:map(fun print_server/1,
supervisor:which_children(sshd_sup))
catch
- C:E ->
- io_lib:format('***print_servers FAILED: ~p:~p~n',[C,E])
+ C:E:S ->
+ io_lib:format('***print_servers FAILED: ~p:~p,~n ~p~n',[C,E,S])
end.
@@ -140,22 +140,33 @@ print_system_sup({{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, Pid, superv
-print_channels({{server,ssh_server_channel_sup,_,_},Pid,supervisor,[ssh_server_channel_sup]}) when is_pid(Pid) ->
+print_channels({{Role,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
+ ChanBehaviour =
+ case Role of
+ server -> ssh_server_channel;
+ client -> ssh_client_channel
+ end,
Children = supervisor:which_children(Pid),
- ChannelPids = [P || {R,P,worker,[ssh_server_channel]} <- Children,
+ ChannelPids = [P || {R,P,worker,[Mod]} <- Children,
+ ChanBehaviour == Mod,
is_pid(P),
is_reference(R)],
case ChannelPids of
[] -> io_lib:format(?INDENT?INDENT"No channels~n",[]);
[Ch1Pid|_] ->
- {{ConnManager,_}, _Str} = ssh_server_channel:get_print_info(Ch1Pid),
+ {{ConnManager,_}, _Str} = ChanBehaviour:get_print_info(Ch1Pid),
{{_,Remote},_} = ssh_connection_handler:get_print_info(ConnManager),
[io_lib:format(?INDENT?INDENT"Remote: ~s ConnectionRef = ~p~n",[fmt_host_port(Remote),ConnManager]),
lists:map(fun print_ch/1, ChannelPids)
]
end;
-print_channels({{server,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) ->
- []. % The supervisor of the connections socket owning process
+print_channels({{_Role,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) ->
+ []; % The supervisor of the connections socket owning process
+
+print_channels({Ref,Pid,supervisor,[ssh_tcpip_forward_acceptor_sup]}) when is_pid(Pid),
+ is_reference(Ref) ->
+ []. % The supervisor of the forward_acceptor process
+
print_ch(Pid) ->
try
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 47cbec1513..81c2e00018 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -31,6 +31,9 @@
-include("ssh_transport.hrl").
-export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]).
+-export([ssh2_pubkey_decode/1,
+ ssh2_pubkey_encode/1,
+ ssh2_privkey_decode2/1]).
-behaviour(ssh_dbg).
-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
@@ -46,6 +49,11 @@ ucl(B) ->
-define(unicode_list(B), ucl(B)).
+%%%================================================================
+%%%
+%%% Encode/decode messages
+%%%
+
encode(#ssh_msg_global_request{
name = Name,
want_reply = Bool,
@@ -242,7 +250,7 @@ encode(#ssh_msg_kexdh_reply{
f = F,
h_sig = Signature
}) ->
- EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncKey = ssh2_pubkey_encode(Key),
EncSign = encode_signature(Key, SigAlg, Signature),
<<?Ebyte(?SSH_MSG_KEXDH_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
@@ -268,7 +276,7 @@ encode(#ssh_msg_kex_dh_gex_reply{
f = F,
h_sig = Signature
}) ->
- EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncKey = ssh2_pubkey_encode(Key),
EncSign = encode_signature(Key, SigAlg, Signature),
<<?Ebyte(?SSH_MSG_KEX_DH_GEX_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
@@ -276,7 +284,7 @@ encode(#ssh_msg_kex_ecdh_init{q_c = Q_c}) ->
<<?Ebyte(?SSH_MSG_KEX_ECDH_INIT), ?Ebinary(Q_c)>>;
encode(#ssh_msg_kex_ecdh_reply{public_host_key = {Key,SigAlg}, q_s = Q_s, h_sig = Sign}) ->
- EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncKey = ssh2_pubkey_encode(Key),
EncSign = encode_signature(Key, SigAlg, Sign),
<<?Ebyte(?SSH_MSG_KEX_ECDH_REPLY), ?Ebinary(EncKey), ?Ebinary(Q_s), ?Ebinary(EncSign)>>;
@@ -453,7 +461,7 @@ decode(<<"dh",?BYTE(?SSH_MSG_KEXDH_INIT), ?DEC_MPINT(E,__0)>>) ->
decode(<<"dh", ?BYTE(?SSH_MSG_KEXDH_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
#ssh_msg_kexdh_reply{
- public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
+ public_host_key = ssh2_pubkey_decode(Key),
f = F,
h_sig = decode_signature(Hashsign)
};
@@ -483,7 +491,7 @@ decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?DEC_MPINT(E,__0)>>) ->
decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
#ssh_msg_kex_dh_gex_reply{
- public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
+ public_host_key = ssh2_pubkey_decode(Key),
f = F,
h_sig = decode_signature(Hashsign)
};
@@ -496,7 +504,7 @@ decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_INIT), ?DEC_BIN(Q_c,__0)>>) ->
decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_REPLY),
?DEC_BIN(Key,__1), ?DEC_BIN(Q_s,__2), ?DEC_BIN(Sig,__3)>>) ->
#ssh_msg_kex_ecdh_reply{
- public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
+ public_host_key = ssh2_pubkey_decode(Key),
q_s = Q_s,
h_sig = decode_signature(Sig)
};
@@ -540,6 +548,132 @@ decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?DEC_BIN(Msg,__0), ?DEC_BIN(Lang,__
message = Msg,
language = Lang}.
+
+%%%================================================================
+%%%
+%%% Encode/decode ssh public/private keys
+%%%
+
+%%%-------- public key --------
+ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
+ <<?STRING(<<"ssh-rsa">>), ?Empint(E), ?Empint(N)>>;
+ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
+ <<?STRING(<<"ssh-dss">>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>;
+ssh2_pubkey_encode({#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
+ Curve = public_key:oid2ssh_curvename(OID),
+ KeyType = <<"ecdsa-sha2-", Curve/binary>>,
+ <<?STRING(KeyType), ?STRING(Curve), ?Estring(Q)>>;
+ssh2_pubkey_encode({ed_pub, ed25519, Key}) ->
+ <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>;
+ssh2_pubkey_encode({ed_pub, ed448, Key}) ->
+ <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>.
+
+%%%--------
+ssh2_pubkey_decode(KeyBlob) ->
+ {Key,_RestBlob} = ssh2_pubkey_decode2(KeyBlob),
+ Key.
+
+ssh2_pubkey_decode2(<<?UINT32(7), "ssh-rsa",
+ ?DEC_INT(E, _EL),
+ ?DEC_INT(N, _NL),
+ Rest/binary>>) ->
+ {#'RSAPublicKey'{modulus = N,
+ publicExponent = E
+ }, Rest};
+ssh2_pubkey_decode2(<<?UINT32(7), "ssh-dss",
+ ?DEC_INT(P, _PL),
+ ?DEC_INT(Q, _QL),
+ ?DEC_INT(G, _GL),
+ ?DEC_INT(Y, _YL),
+ Rest/binary>>) ->
+ {{Y, #'Dss-Parms'{p = P,
+ q = Q,
+ g = G}
+ }, Rest};
+ssh2_pubkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
+ Sz = TL-11,
+ <<_Curve:Sz/binary,
+ ?DEC_BIN(SshName, _IL),
+ ?DEC_BIN(Q, _QL),
+ Rest/binary>> = KeyRest,
+ OID = public_key:ssh_curvename2oid(SshName),
+ {{#'ECPoint'{point = Q}, {namedCurve,OID}
+ }, Rest};
+ssh2_pubkey_decode2(<<?UINT32(11), "ssh-ed25519",
+ ?DEC_BIN(Key, _L),
+ Rest/binary>>) ->
+ {{ed_pub, ed25519, Key},
+ Rest};
+ssh2_pubkey_decode2(<<?UINT32(9), "ssh-ed448",
+ ?DEC_BIN(Key, _L),
+ Rest/binary>>) ->
+ {{ed_pub, ed448, Key},
+ Rest}.
+
+%%%-------- private key --------
+
+%% dialyser... ssh2_privkey_decode(KeyBlob) ->
+%% dialyser... {Key,_RestBlob} = ssh2_privkey_decode2(KeyBlob),
+%% dialyser... Key.
+
+%% See sshkey_private_serialize_opt in sshkey.c
+ssh2_privkey_decode2(<<?UINT32(7), "ssh-rsa",
+ ?DEC_INT(N, _NL), % Yes, N and E is reversed relative pubkey format
+ ?DEC_INT(E, _EL), % --"--
+ ?DEC_INT(D, _DL),
+ ?DEC_INT(IQMP, _IQMPL),
+ ?DEC_INT(P, _PL),
+ ?DEC_INT(Q, _QL),
+ Rest/binary>>) ->
+ {#'RSAPrivateKey'{version = 'two-prime', % Found this in public_key:generate_key/1 ..
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ %exponent1, % D_mod_P_1
+ %exponent2, % D_mod_Q_1
+ coefficient = IQMP
+ }, Rest};
+ssh2_privkey_decode2(<<?UINT32(7), "ssh-dss",
+ ?DEC_INT(P, _PL),
+ ?DEC_INT(Q, _QL),
+ ?DEC_INT(G, _GL),
+ ?DEC_INT(Y, _YL), % Publ key
+ ?DEC_INT(X, _XL), % Priv key
+ Rest/binary>>) ->
+ {#'DSAPrivateKey'{version = 0,
+ p = P,
+ q = Q,
+ g = G,
+ y = Y,
+ x = X
+ }, Rest};
+ssh2_privkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
+ Sz = TL-11,
+ <<_Curve:Sz/binary,
+ ?DEC_BIN(CurveName, _SNN),
+ ?DEC_BIN(Q, _QL),
+ ?DEC_BIN(Priv, _PrivL),
+ Rest/binary>> = KeyRest,
+ OID = public_key:ssh_curvename2oid(CurveName),
+ {#'ECPrivateKey'{version = 1,
+ parameters = {namedCurve,OID},
+ privateKey = Priv,
+ publicKey = Q
+ }, Rest};
+ssh2_privkey_decode2(<<?UINT32(11), "ssh-ed25519",
+ ?DEC_BIN(Pub,_Lpub),
+ ?DEC_BIN(Priv,_Lpriv),
+ Rest/binary>>) ->
+ {{ed_pri, ed25519, Pub, Priv}, Rest};
+ssh2_privkey_decode2(<<?UINT32(9), "ssh-ed448",
+ ?DEC_BIN(Pub,_Lpub),
+ ?DEC_BIN(Priv,_Lpriv),
+ Rest/binary>>) ->
+ {{ed_pri, ed448, Pub, Priv}, Rest}.
+
+
%%%================================================================
%%%
%%% Helper functions
@@ -594,8 +728,8 @@ encode_signature(#'RSAPublicKey'{}, SigAlg, Signature) ->
encode_signature({_, #'Dss-Parms'{}}, _SigAlg, Signature) ->
<<?Ebinary(<<"ssh-dss">>), ?Ebinary(Signature)>>;
encode_signature({#'ECPoint'{}, {namedCurve,OID}}, _SigAlg, Signature) ->
- CurveName = public_key:oid2ssh_curvename(OID),
- <<?Ebinary(<<"ecdsa-sha2-",CurveName/binary>>), ?Ebinary(Signature)>>;
+ Curve = public_key:oid2ssh_curvename(OID),
+ <<?Ebinary(<<"ecdsa-sha2-",Curve/binary>>), ?Ebinary(Signature)>>;
encode_signature({ed_pub, ed25519,_}, _SigAlg, Signature) ->
<<?Ebinary(<<"ssh-ed25519">>), ?Ebinary(Signature)>>;
encode_signature({ed_pub, ed448,_}, _SigAlg, Signature) ->
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index e2a18edc0d..b165c77341 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -331,6 +331,18 @@ default(server) ->
class => user_option
},
+ tcpip_tunnel_out =>
+ #{default => false,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
+ tcpip_tunnel_in =>
+ #{default => false,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
system_dir =>
#{default => "/etc/ssh",
chk => fun(V) -> check_string(V) andalso check_dir(V) end,
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 11d48bb1e5..007f912e5f 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -165,8 +165,8 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
PacketSize = proplists:get_value(packet_size, ChanOpts, ?XFER_PACKET_SIZE),
case ssh_connection:session_channel(Cm, WindowSize, PacketSize, Timeout) of
{ok, ChannelId} ->
- case ssh_client_channel:start(Cm, ChannelId,
- ?MODULE, [Cm, ChannelId, SftpOpts]) of
+ case ssh_connection_handler:start_channel(Cm, ?MODULE, ChannelId,
+ [Cm,ChannelId,SftpOpts], undefined) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -175,9 +175,7 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
TimeOut
end;
{error, Reason} ->
- {error, format_channel_start_error(Reason)};
- ignore ->
- {error, ignore}
+ {error, format_channel_start_error(Reason)}
end;
Error ->
Error
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index b8dc905d4d..d478d939ff 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -62,14 +62,15 @@
%%====================================================================
-spec subsystem_spec(Options) -> Spec when
Options :: [ {cwd, string()} |
- {file_handler, CallbackModule::string()} |
+ {file_handler, CbMod | {CbMod, FileState}} |
{max_files, integer()} |
{root, string()} |
{sftpd_vsn, integer()}
],
Spec :: {Name, {CbMod,Options}},
Name :: string(),
- CbMod :: atom() .
+ CbMod :: atom(),
+ FileState :: term().
subsystem_spec(Options) ->
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index 5fc8f7e764..140b219b32 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -30,7 +30,9 @@
-export([start_link/5,
connection_supervisor/1,
- channel_supervisor/1
+ channel_supervisor/1,
+ tcpip_fwd_supervisor/1,
+ start_channel/8
]).
%% Supervisor callback
@@ -46,9 +48,17 @@ connection_supervisor(SupPid) ->
Children = supervisor:which_children(SupPid),
ssh_connection_sup(Children).
-channel_supervisor(SupPid) ->
+channel_supervisor(SupPid) when is_pid(SupPid) ->
Children = supervisor:which_children(SupPid),
- ssh_server_channel_sup(Children).
+ ssh_channel_sup(Children).
+
+tcpip_fwd_supervisor(SupPid) when is_pid(SupPid) ->
+ Children = supervisor:which_children(SupPid),
+ tcpip_fwd_sup(Children).
+
+start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
+ ChannelSup = channel_supervisor(SupPid),
+ ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
%%%=========================================================================
%%% Supervisor callback
@@ -64,11 +74,10 @@ init([Role, Address, Port, Profile, Options]) ->
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
-child_specs(client, _Address, _Port, _Profile, _Options) ->
- [];
-child_specs(server, Address, Port, Profile, Options) ->
- [ssh_channel_child_spec(server, Address, Port, Profile, Options),
- ssh_connection_child_spec(server, Address, Port, Profile, Options)].
+child_specs(Role, Address, Port, Profile, Options) ->
+ [ssh_channel_child_spec(Role, Address, Port, Profile, Options),
+ ssh_connection_child_spec(Role, Address, Port, Profile, Options),
+ ssh_tcpip_forward_acceptor_child_spec()].
ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_connection_sup, Address, Port),
@@ -78,12 +87,20 @@ ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
}.
ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
- #{id => id(Role, ssh_server_channel_sup, Address, Port),
- start => {ssh_server_channel_sup, start_link, [Options]},
+ #{id => id(Role, ssh_channel_sup, Address, Port),
+ start => {ssh_channel_sup, start_link, [Options]},
+ restart => temporary,
+ type => supervisor
+ }.
+
+ssh_tcpip_forward_acceptor_child_spec() ->
+ #{id => make_ref(),
+ start => {ssh_tcpip_forward_acceptor_sup, start_link, []},
restart => temporary,
type => supervisor
}.
+
id(Role, Sup, Address, Port) ->
{Role, Sup, Address, Port}.
@@ -92,10 +109,13 @@ ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) ->
ssh_connection_sup([_ | Rest]) ->
ssh_connection_sup(Rest).
-ssh_server_channel_sup([{_, Child, _, [ssh_server_channel_sup]} | _]) ->
+ssh_channel_sup([{_, Child, _, [ssh_channel_sup]} | _]) ->
Child;
-ssh_server_channel_sup([_ | Rest]) ->
- ssh_server_channel_sup(Rest).
-
+ssh_channel_sup([_ | Rest]) ->
+ ssh_channel_sup(Rest).
+tcpip_fwd_sup([{_, Child, _, [ssh_tcpip_forward_acceptor_sup]} | _]) ->
+ Child;
+tcpip_fwd_sup([_ | Rest]) ->
+ tcpip_fwd_sup(Rest).
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 80406a6c47..704c17c4e7 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -31,9 +31,9 @@
-include("ssh.hrl").
--export([start_link/4, stop_listener/1,
- stop_listener/3, stop_system/1,
- stop_system/3, system_supervisor/3,
+-export([start_link/5, stop_listener/1,
+ stop_listener/3, stop_system/2,
+ stop_system/4, system_supervisor/3,
subsystem_supervisor/1, channel_supervisor/1,
connection_supervisor/1,
acceptor_supervisor/1, start_subsystem/6,
@@ -50,14 +50,14 @@
%%%=========================================================================
%%% API
%%%=========================================================================
-start_link(Address, Port, Profile, Options) ->
+start_link(Role, Address, Port, Profile, Options) ->
Name = make_name(Address, Port, Profile),
- supervisor:start_link({local, Name}, ?MODULE, [Address, Port, Profile, Options]).
+ supervisor:start_link({local, Name}, ?MODULE, [Role, Address, Port, Profile, Options]).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init([Address, Port, Profile, Options]) ->
+init([server, Address, Port, Profile, Options]) ->
SupFlags = #{strategy => one_for_one,
intensity => 0,
period => 3600
@@ -73,6 +73,14 @@ init([Address, Port, Profile, Options]) ->
_ ->
[]
end,
+ {ok, {SupFlags,ChildSpecs}};
+
+init([client, _Address, _Port, _Profile, _Options]) ->
+ SupFlags = #{strategy => one_for_one,
+ intensity => 0,
+ period => 3600
+ },
+ ChildSpecs = [],
{ok, {SupFlags,ChildSpecs}}.
%%%=========================================================================
@@ -92,11 +100,10 @@ stop_listener(Address, Port, Profile) ->
system_supervisor(Address, Port, Profile)).
-stop_system(SysSup) ->
- catch sshd_sup:stop_child(SysSup),
- ok.
+stop_system(server, SysSup) -> catch sshd_sup:stop_child(SysSup), ok;
+stop_system(client, SysSup) -> catch sshc_sup:stop_child(SysSup), ok.
-stop_system(Address, Port, Profile) ->
+stop_system(server, Address, Port, Profile) ->
catch sshd_sup:stop_child(Address, Port, Profile),
ok.
diff --git a/lib/ssh/src/ssh_tcpip_forward_acceptor.erl b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
new file mode 100644
index 0000000000..6f2fda2bf9
--- /dev/null
+++ b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
@@ -0,0 +1,116 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The supervisor for tcpip-forwarding acceptor
+%%----------------------------------------------------------------------
+
+-module(ssh_tcpip_forward_acceptor).
+
+-export([supervised_start/6,
+ start_link/6]).
+
+-include("ssh.hrl").
+
+%%%----------------------------------------------------------------
+supervised_start(FwdSup, {ListenAddrStr, ListenPort}, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
+ case get_fwd_listen_opts(ListenAddrStr) of
+ {ok,Opts} ->
+ %% start listening on Addr:BoundPort
+ case gen_tcp:listen(ListenPort, [binary,
+ {reuseaddr,true},
+ {active,false} | Opts]) of
+ {ok,LSock} ->
+ {ok,{_, TrueListenPort}} = inet:sockname(LSock),
+ ssh_tcpip_forward_acceptor_sup:start_child(FwdSup,
+ LSock,
+ {ListenAddrStr,TrueListenPort},
+ ConnectToAddr,
+ ChanType,
+ ChanCB,
+ ConnPid),
+ {ok, TrueListenPort};
+
+ {error,Error} ->
+ {error,Error}
+ end;
+
+ {error,Error} ->
+ {error,Error}
+ end.
+
+
+%%%----------------------------------------------------------------
+start_link(LSock, {ListenAddrStr,ListenPort}, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
+ Pid = proc_lib:spawn_link(
+ fun() ->
+ acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid)
+ end),
+ {ok, Pid}.
+
+%%%================================================================
+%%%
+%%% Internal
+%%%
+acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
+ case gen_tcp:accept(LSock) of
+ {ok, Sock} ->
+ {ok, {RemHost,RemPort}} = inet:peername(Sock),
+ RemHostBin = list_to_binary(encode_ip(RemHost)),
+ Data =
+ case ConnectToAddr of
+ undefined ->
+ <<?STRING(ListenAddrStr), ?UINT32(ListenPort),
+ ?STRING(RemHostBin), ?UINT32(RemPort)>>;
+ {ConnectToHost, ConnectToPort} ->
+ <<?STRING(ConnectToHost), ?UINT32(ConnectToPort),
+ ?STRING(RemHostBin), ?UINT32(RemPort)>>
+ end,
+ case ssh_connection:open_channel(ConnPid, ChanType, Data, infinity) of
+ {ok,ChId} ->
+ gen_tcp:controlling_process(Sock, ConnPid),
+ ConnPid ! {fwd_connect_received, Sock, ChId, ChanCB};
+ _ ->
+ gen_tcp:close(Sock)
+ end,
+ acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid);
+
+ {error,closed} ->
+ ok
+ end.
+
+%%%----------------------------------------------------------------
+get_fwd_listen_opts(<<"">> ) -> {ok, []};
+get_fwd_listen_opts(<<"0.0.0.0">> ) -> {ok, [inet]};
+get_fwd_listen_opts(<<"::">> ) -> {ok, [inet6]};
+get_fwd_listen_opts(<<"localhost">>) -> {ok, [{ip,loopback}]};
+get_fwd_listen_opts(AddrStr) ->
+ case inet:getaddr(binary_to_list(AddrStr), inet) of
+ {ok, Addr} -> {ok, [{ip,Addr}]};
+ {error,Error} -> {error,Error}
+ end.
+
+%%%----------------------------------------------------------------
+encode_ip(Addr) when is_tuple(Addr) ->
+ case catch inet_parse:ntoa(Addr) of
+ {'EXIT',_} -> false;
+ A -> A
+ end.
diff --git a/lib/ssh/src/ssh_server_channel_sup.erl b/lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl
index ff74061bb3..522ce650ff 100644
--- a/lib/ssh/src/ssh_server_channel_sup.erl
+++ b/lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl
@@ -20,42 +20,42 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Ssh channel supervisor.
+%% Purpose: The supervisor for tcpip-forwarding acceptor
%%----------------------------------------------------------------------
--module(ssh_server_channel_sup).
+-module(ssh_tcpip_forward_acceptor_sup).
-behaviour(supervisor).
--export([start_link/1, start_child/5]).
+-include("ssh.hrl").
+
+-export([start_link/0, start_child/7]).
%% Supervisor callback
-export([init/1]).
%%%=========================================================================
-%%% Internal API
+%%% API
%%%=========================================================================
-start_link(Args) ->
- supervisor:start_link(?MODULE, [Args]).
-
-start_child(Sup, Callback, Id, Args, Exec) ->
- ChildSpec =
- #{id => make_ref(),
- start => {ssh_server_channel, start_link, [self(), Id, Callback, Args, Exec]},
- restart => temporary,
- type => worker,
- modules => [ssh_server_channel]
- },
- supervisor:start_child(Sup, ChildSpec).
+start_link() ->
+ supervisor:start_link(?MODULE, []).
+
+start_child(Sup, LSock, ListenAddr, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
+ Args = [LSock, ListenAddr, ConnectToAddr, ChanType, ChanCB, ConnPid],
+ supervisor:start_child(Sup, Args).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init(_Args) ->
- RestartStrategy = one_for_one,
- MaxR = 10,
- MaxT = 3600,
- Children = [],
- {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
+init([]) ->
+ SupFlags = #{strategy => simple_one_for_one,
+ intensity => 10,
+ period => 3600
+ },
+ ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
+ start => {ssh_tcpip_forward_acceptor, start_link, []}
+ }
+ ],
+ {ok, {SupFlags,ChildSpecs}}.
%%%=========================================================================
%%% Internal functions
diff --git a/lib/ssh/src/ssh_tcpip_forward_client.erl b/lib/ssh/src/ssh_tcpip_forward_client.erl
new file mode 100644
index 0000000000..490d283461
--- /dev/null
+++ b/lib/ssh/src/ssh_tcpip_forward_client.erl
@@ -0,0 +1,84 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ssh_tcpip_forward_client).
+
+-behaviour(ssh_client_channel).
+
+-record(state, {
+ id, cm,
+ fwd_socket
+ }).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_msg/2, handle_ssh_msg/2, terminate/2, code_change/3]).
+
+init([FwdSocket]) ->
+ {ok, #state{fwd_socket=FwdSocket}}.
+
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{id = ChannelId,
+ cm = ConnectionManager}};
+
+handle_msg({tcp,Sock,Data}, #state{fwd_socket = Sock,
+ cm = CM,
+ id = ChId} = State) ->
+ ssh_connection:send(CM, ChId, Data),
+ inet:setopts(Sock, [{active,once}]),
+ {ok, State};
+
+handle_msg({tcp_closed,Sock}, #state{fwd_socket = Sock,
+ cm = CM,
+ id = ChId} = State) ->
+ ssh_connection:send_eof(CM, ChId),
+ {stop, ChId, State#state{fwd_socket=undefined}}.
+
+
+
+handle_ssh_msg({ssh_cm, _CM, {data, _ChannelId, _Type, Data}}, #state{fwd_socket=Sock} = State) ->
+ gen_tcp:send(Sock, Data),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _CM, {eof, ChId}}, State) ->
+ {stop, ChId, State};
+
+handle_ssh_msg({ssh_cm, _CM, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _CM, {exit_signal, ChId, _, _Error, _}}, State) ->
+ {stop, ChId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChId, _Status}}, State) ->
+ {stop, ChId, State}.
+
+
+terminate(_Reason, #state{fwd_socket=Sock}) ->
+ gen_tcp:close(Sock),
+ ok.
+
+
+handle_call(Req, _, S) -> {reply, {unknown,Req}, S}.
+
+handle_cast(_, S) -> {noreply, S}.
+
+code_change(_, S, _) -> {ok, S}.
+
+
diff --git a/lib/ssh/src/ssh_tcpip_forward_srv.erl b/lib/ssh/src/ssh_tcpip_forward_srv.erl
new file mode 100644
index 0000000000..a29f70cf48
--- /dev/null
+++ b/lib/ssh/src/ssh_tcpip_forward_srv.erl
@@ -0,0 +1,75 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ssh_tcpip_forward_srv).
+
+-behaviour(ssh_server_channel).
+
+-record(state, {
+ id, cm,
+ fwd_socket
+ }).
+
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+init([FwdSocket]) ->
+ {ok, #state{fwd_socket=FwdSocket}}.
+
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{id = ChannelId,
+ cm = ConnectionManager}};
+
+handle_msg({tcp,Sock,Data}, #state{fwd_socket = Sock,
+ cm = CM,
+ id = ChId} = State) ->
+ ssh_connection:send(CM, ChId, Data),
+ inet:setopts(Sock, [{active,once}]),
+ {ok, State};
+
+handle_msg({tcp_closed,Sock}, #state{fwd_socket = Sock,
+ cm = CM,
+ id = ChId} = State) ->
+ ssh_connection:send_eof(CM, ChId),
+ {stop, ChId, State#state{fwd_socket=undefined}}.
+
+
+
+handle_ssh_msg({ssh_cm, _CM, {data, _ChannelId, _Type, Data}}, #state{fwd_socket=Sock} = State) ->
+ gen_tcp:send(Sock, Data),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _CM, {eof, ChId}}, State) ->
+ {stop, ChId, State};
+
+handle_ssh_msg({ssh_cm, _CM, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _CM, {exit_signal, ChId, _, _Error, _}}, State) ->
+ {stop, ChId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChId, _Status}}, State) ->
+ {stop, ChId, State}.
+
+
+terminate(_Reason, #state{fwd_socket=Sock}) ->
+ gen_tcp:close(Sock),
+ ok.
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 9a4ae5bbc6..e1a7ecf66f 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -50,10 +50,11 @@
parallell_gen_key/1,
extract_public_key/1,
ssh_packet/2, pack/2,
- valid_key_sha_alg/2,
+ valid_key_sha_alg/3,
sha/1, sign/3, verify/5,
get_host_key/2,
- call_KeyCb/3]).
+ call_KeyCb/3,
+ public_algo/1]).
-behaviour(ssh_dbg).
-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
@@ -786,7 +787,7 @@ get_host_key(SignAlg, Opts) ->
case call_KeyCb(host_key, [SignAlg], Opts) of
{ok, PrivHostKey} ->
%% Check the key - the KeyCb may be a buggy plugin
- case valid_key_sha_alg(PrivHostKey, SignAlg) of
+ case valid_key_sha_alg(private, PrivHostKey, SignAlg) of
true -> PrivHostKey;
false -> exit({error, bad_hostkey})
end;
@@ -804,7 +805,7 @@ extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
{Y, #'Dss-Parms'{p=P, q=Q, g=G}};
extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
- publicKey = Q}) ->
+ publicKey = Q}) when is_tuple(OID) ->
{#'ECPoint'{point=Q}, {namedCurve,OID}};
extract_public_key({ed_pri, Alg, Pub, _Priv}) ->
{ed_pub, Alg, Pub};
@@ -1480,7 +1481,7 @@ encrypt(#ssh{encrypt = 'chacha20-poly1305@openssh.com',
%% MAC tag
PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, true),
EncBytes = <<EncLen/binary,EncPayloadData/binary>>,
- Ctag = crypto:poly1305(PolyKey, EncBytes),
+ Ctag = crypto:mac(poly1305, PolyKey, EncBytes),
%% Result
{Ssh, {EncBytes,Ctag}};
@@ -1561,7 +1562,7 @@ decrypt(#ssh{decrypt = 'chacha20-poly1305@openssh.com',
%% The length is already decrypted and used to divide the input
%% Check the mac (important that it is timing-safe):
PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
- case crypto:equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
+ case crypto:equal_const_time(Ctag, crypto:mac(poly1305, PolyKey, <<AAD/binary,Ctext/binary>>)) of
true ->
%% MAC is ok, decode
IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
@@ -1722,17 +1723,17 @@ recv_mac_final(SSH) ->
mac(none, _ , _, _) ->
<<>>;
mac('hmac-sha1', Key, SeqNum, Data) ->
- crypto:hmac(sha, Key, [<<?UINT32(SeqNum)>>, Data]);
+ crypto:mac(hmac, sha, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-sha1-96', Key, SeqNum, Data) ->
- crypto:hmac(sha, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-sha1-96'));
+ crypto:macN(hmac, sha, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-sha1-96'));
mac('hmac-md5', Key, SeqNum, Data) ->
- crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data]);
+ crypto:mac(hmac, md5, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-md5-96', Key, SeqNum, Data) ->
- crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96'));
+ crypto:macN(hmac, md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96'));
mac('hmac-sha2-256', Key, SeqNum, Data) ->
- crypto:hmac(sha256, Key, [<<?UINT32(SeqNum)>>, Data]);
+ crypto:mac(hmac, sha256, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-sha2-512', Key, SeqNum, Data) ->
- crypto:hmac(sha512, Key, [<<?UINT32(SeqNum)>>, Data]).
+ crypto:mac(hmac, sha512, Key, [<<?UINT32(SeqNum)>>, Data]).
%%%----------------------------------------------------------------
@@ -1760,7 +1761,7 @@ kex_hash(SSH, Key, HashAlg, Args) ->
kex_plaintext(SSH, Key, Args) ->
- EncodedKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncodedKey = ssh_message:ssh2_pubkey_encode(Key),
<<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version),
?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit),
?Ebinary(EncodedKey),
@@ -1787,33 +1788,36 @@ kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) ->
%%%----------------------------------------------------------------
-valid_key_sha_alg(#{engine:=_, key_id:=_}, _Alg) -> true; % Engine key
+valid_key_sha_alg(_, #{engine:=_, key_id:=_}, _Alg) -> true; % Engine key
-valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-512') -> true;
-valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-384') -> true;
-valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-256') -> true;
-valid_key_sha_alg(#'RSAPublicKey'{}, 'ssh-rsa' ) -> true;
+valid_key_sha_alg(public, #'RSAPublicKey'{}, 'rsa-sha2-512') -> true;
+valid_key_sha_alg(public, #'RSAPublicKey'{}, 'rsa-sha2-384') -> true;
+valid_key_sha_alg(public, #'RSAPublicKey'{}, 'rsa-sha2-256') -> true;
+valid_key_sha_alg(public, #'RSAPublicKey'{}, 'ssh-rsa' ) -> true;
-valid_key_sha_alg(#'RSAPrivateKey'{}, 'rsa-sha2-512') -> true;
-valid_key_sha_alg(#'RSAPrivateKey'{}, 'rsa-sha2-384') -> true;
-valid_key_sha_alg(#'RSAPrivateKey'{}, 'rsa-sha2-256') -> true;
-valid_key_sha_alg(#'RSAPrivateKey'{}, 'ssh-rsa' ) -> true;
+valid_key_sha_alg(private, #'RSAPrivateKey'{}, 'rsa-sha2-512') -> true;
+valid_key_sha_alg(private, #'RSAPrivateKey'{}, 'rsa-sha2-384') -> true;
+valid_key_sha_alg(private, #'RSAPrivateKey'{}, 'rsa-sha2-256') -> true;
+valid_key_sha_alg(private, #'RSAPrivateKey'{}, 'ssh-rsa' ) -> true;
-valid_key_sha_alg({_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
-valid_key_sha_alg(#'DSAPrivateKey'{}, 'ssh-dss') -> true;
+valid_key_sha_alg(public, {_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
+valid_key_sha_alg(private, #'DSAPrivateKey'{}, 'ssh-dss') -> true;
-valid_key_sha_alg({ed_pub, ed25519,_}, 'ssh-ed25519') -> true;
-valid_key_sha_alg({ed_pri, ed25519,_,_},'ssh-ed25519') -> true;
-valid_key_sha_alg({ed_pub, ed448,_}, 'ssh-ed448') -> true;
-valid_key_sha_alg({ed_pri, ed448,_,_}, 'ssh-ed448') -> true;
+valid_key_sha_alg(public, {ed_pub, ed25519,_}, 'ssh-ed25519') -> true;
+valid_key_sha_alg(private, {ed_pri, ed25519,_,_},'ssh-ed25519') -> true;
+valid_key_sha_alg(public, {ed_pub, ed448,_}, 'ssh-ed448') -> true;
+valid_key_sha_alg(private, {ed_pri, ed448,_,_}, 'ssh-ed448') -> true;
-valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
-valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
-valid_key_sha_alg(_, _) -> false.
+valid_key_sha_alg(public, {#'ECPoint'{},{namedCurve,OID}}, Alg) when is_tuple(OID) ->
+ valid_key_sha_alg_ec(OID, Alg);
+valid_key_sha_alg(private, #'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) when is_tuple(OID) ->
+ valid_key_sha_alg_ec(OID, Alg);
+valid_key_sha_alg(_, _, _) -> false.
-valid_key_sha_alg_ec(OID, Alg) ->
- Curve = public_key:oid2ssh_curvename(OID),
- try Alg == list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve))
+valid_key_sha_alg_ec(OID, Alg) ->
+ try
+ Curve = public_key:oid2ssh_curvename(OID),
+ Alg == list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve))
catch
_:_ -> false
end.
@@ -1825,9 +1829,9 @@ public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa'; % FIXME: Not right with draft-cu
public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
public_algo({ed_pub, ed25519,_}) -> 'ssh-ed25519';
public_algo({ed_pub, ed448,_}) -> 'ssh-ed448';
-public_algo({#'ECPoint'{},{namedCurve,OID}}) ->
- Curve = public_key:oid2ssh_curvename(OID),
- try list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve))
+public_algo({#'ECPoint'{},{namedCurve,OID}}) when is_tuple(OID) ->
+ SshName = public_key:oid2ssh_curvename(OID),
+ try list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(SshName))
catch
_:_ -> undefined
end.
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index 2292beaf68..d89b59100f 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -117,13 +117,20 @@ rename(XF, ReqID, OldPath, NewPath, Flags) ->
true ->
(<<>>)
end,
- xf_request(XF, ?SSH_FXP_RENAME,
- [?uint32(ReqID),
- ?string_utf8(OldPath),
- ?string_utf8(NewPath),
- FlagBits]).
-
-
+ Ext = XF#ssh_xfer.ext,
+ ExtRename = "posix-rename@openssh.com",
+ case lists:member({ExtRename, "1"}, Ext) of
+ true ->
+ extended(XF, ReqID, ExtRename,
+ [?string_utf8(OldPath),
+ ?string_utf8(NewPath)]);
+ false ->
+ xf_request(XF, ?SSH_FXP_RENAME,
+ [?uint32(ReqID),
+ ?string_utf8(OldPath),
+ ?string_utf8(NewPath),
+ FlagBits])
+ end.
%% Create directory
mkdir(XF, ReqID, Path, Attrs) ->
@@ -222,7 +229,7 @@ extended(XF, ReqID, Request, Data) ->
xf_request(XF, ?SSH_FXP_EXTENDED,
[?uint32(ReqID),
?string(Request),
- ?binary(Data)]).
+ Data]).
%% Send xfer request to connection manager
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 869de244ac..0c9f8be5a9 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -27,7 +27,10 @@
-behaviour(supervisor).
--export([start_link/0, start_child/1]).
+-export([start_link/0,
+ start_child/4,
+ stop_child/1
+ ]).
%% Supervisor callback
-export([init/1]).
@@ -38,22 +41,45 @@
%%% API
%%%=========================================================================
start_link() ->
- supervisor:start_link({local,?SSHC_SUP}, ?MODULE, []).
+ supervisor:start_link({local,?MODULE}, ?MODULE, []).
-start_child(Args) ->
- supervisor:start_child(?MODULE, Args).
+start_child(Address, Port, Profile, Options) ->
+ %% Here we a new connction on a new Host/EFERMERAL Port/Profile
+ Spec = child_spec(Address, Port, Profile, Options),
+ supervisor:start_child(?MODULE, Spec).
+
+stop_child(ChildId) when is_tuple(ChildId) ->
+ supervisor:terminate_child(?SSHC_SUP, ChildId);
+stop_child(ChildPid) when is_pid(ChildPid)->
+ stop_child(system_name(ChildPid)).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
init(_) ->
- SupFlags = #{strategy => simple_one_for_one,
+ SupFlags = #{strategy => one_for_one,
intensity => 0,
period => 3600
},
- ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
- start => {ssh_connection_handler, start_link, []},
- restart => temporary % because there is no way to restart a crashed connection
- }
- ],
+ ChildSpecs = [],
{ok, {SupFlags,ChildSpecs}}.
+
+%%%=========================================================================
+%%% Internal functions
+%%%=========================================================================
+child_spec(Address, Port, Profile, Options) ->
+ #{id => id(Address, Port, Profile),
+ start => {ssh_system_sup, start_link, [client, Address, Port, Profile, Options]},
+ restart => temporary,
+ type => supervisor
+ }.
+
+id(Address, Port, Profile) ->
+ {client, ssh_system_sup, Address, Port, Profile}.
+
+system_name(SysSup) ->
+ case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHC_SUP)) of
+ {Name, SysSup, _, _} -> Name;
+ false -> undefind
+ end.
+
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index b5361abba5..b716b66ec7 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -89,7 +89,7 @@ init(_) ->
%%%=========================================================================
child_spec(Address, Port, Profile, Options) ->
#{id => id(Address, Port, Profile),
- start => {ssh_system_sup, start_link, [Address, Port, Profile, Options]},
+ start => {ssh_system_sup, start_link, [server, Address, Port, Profile, Options]},
restart => temporary,
type => supervisor
}.
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index e221e94075..ee8480dfdb 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -43,6 +43,7 @@ MODULES= \
ssh_engine_SUITE \
ssh_protocol_SUITE \
ssh_property_test_SUITE \
+ ssh_pubkey_SUITE \
ssh_sftp_SUITE \
ssh_sftpd_SUITE \
ssh_sftpd_erlclient_SUITE \
diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
index 6470fa5f3d..cfcc56d1a4 100644
--- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -186,7 +186,7 @@ gen_pubkey_string(Type) ->
ecdsa -> {#'ECPoint'{point=[1,2,3,4,5]},
{namedCurve,{1,2,840,10045,3,1,7}}} % 'secp256r1' nistp256
end,
- gen_string(public_key:ssh_encode(PubKey, ssh2_pubkey)).
+ gen_string(ssh_message:ssh2_pubkey_encode(PubKey)).
gen_signature_string(Type) ->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 2ed3a248d5..3a90b4d0fe 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -52,7 +52,8 @@ all() ->
groups() ->
[{all_tests, [?PARALLEL], [{group, ssh_renegotiate_SUITE},
- {group, ssh_basic_SUITE}
+ {group, ssh_basic_SUITE},
+ ssh_file_is_host_key4
]},
{ssh_basic_SUITE, [], [app_test,
appup_test,
@@ -900,13 +901,87 @@ known_hosts(Config) when is_list(Config) ->
{ok, _Channel} = ssh_connection:session_channel(ConnectionRef, infinity),
ok = ssh:close(ConnectionRef),
{ok, Binary} = file:read_file(KnownHosts),
+ ct:log("known_hosts:~n~p",[Binary]),
Lines = string:tokens(binary_to_list(Binary), "\n"),
[Line] = Lines,
[HostAndIp, Alg, _KeyData] = string:tokens(Line, " "),
- [StoredHost, _Ip] = string:tokens(HostAndIp, ","),
+ [StoredHost|_] = string:tokens(HostAndIp, ","),
true = ssh_test_lib:match_ip(StoredHost, Host),
"ssh-" ++ _ = Alg,
+ NLines = length(binary:split(Binary, <<"\n">>, [global,trim_all])),
+ ct:log("NLines = ~p~n~p", [NLines,Binary]),
+ if
+ NLines>1 -> ct:fail("wrong num lines", []);
+ NLines<1 -> ct:fail("wrong num lines", []);
+ true -> ok
+ end,
+
+ _ConnectionRef2 =
+ ssh_test_lib:connect(Host, Port, [{user_dir, PrivDir},
+ {user_interaction, false},
+ silently_accept_hosts]),
+ {ok, Binary2} = file:read_file(KnownHosts),
+ case Binary of
+ Binary2 -> ok;
+ _ -> ct:log("2nd differ~n~p", [Binary2]),
+ ct:fail("wrong num lines", [])
+ end,
+
+ Binary3 = <<"localhost,",Binary/binary>>,
+ ok = file:write_file(KnownHosts, Binary3),
+ _ConnectionRef3 =
+ ssh_test_lib:connect(Host, Port, [{user_dir, PrivDir},
+ {user_interaction, false},
+ silently_accept_hosts]),
+ ct:log("New known_hosts:~n~p",[Binary3]),
+ {ok, Binary4} = file:read_file(KnownHosts),
+ case Binary3 of
+ Binary4 -> ok;
+ _ -> ct:log("2nd differ~n~p", [Binary4]),
+ ct:fail("wrong num lines", [])
+ end,
+
+
ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+ssh_file_is_host_key4(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ Key1 = {ed_pub,ed25519,<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
+ 214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+ Key2 = {ed_pub,ed448,<<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
+ 161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
+ 36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
+ 190,175,232,37,97,128>>},
+
+ FileContents = <<"h11,h12,h13,h14 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIElI66JgZZo72XJ7wGBp+h3WTDw/pxXddvaomAIHrIl9\n",
+ "h21,[h22]:2345,h23 ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlf10SbWbRh/Sznh+xhatRqHaE0JIWnDh"
+ "+KDqddgOlneO3xJHabRscGG9Z4PfHlD2zR+hq+r+glYYA=\n"
+ >>,
+ ok = file:write_file(KnownHosts, FileContents),
+
+ true = ssh_file:is_host_key(Key1, "h11", 'ssh-ed25519', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key1, "h12", 'ssh-ed25519', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key1, "h13", 'ssh-ed25519', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key1, "h14", 'ssh-ed25519', [{user_dir,PrivDir}]),
+
+ true = ssh_file:is_host_key(Key1, "h11,noh1", 'ssh-ed25519', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key1, "noh1,h11", 'ssh-ed25519', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key1, "noh1,h12,noh2", 'ssh-ed25519', [{user_dir,PrivDir}]),
+
+ true = ssh_file:is_host_key(Key2, "h21", 'ssh-ed448', [{user_dir,PrivDir}]),
+ false = ssh_file:is_host_key(Key2, "h22", 'ssh-ed448', [{user_dir,PrivDir}]),
+ false = ssh_file:is_host_key(Key2, "[h22]",'ssh-ed448', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key2 , "[h22]:2345",'ssh-ed448', [{user_dir,PrivDir}]),
+ true = ssh_file:is_host_key(Key2, "h23", 'ssh-ed448', [{user_dir,PrivDir}]),
+
+ false = ssh_file:is_host_key(Key2, "h11", 'ssh-ed448', [{user_dir,PrivDir}]),
+ false = ssh_file:is_host_key(Key1, "h21", 'ssh-ed25519', [{user_dir,PrivDir}]),
+
+ ok.
+
%%--------------------------------------------------------------------
%%% Test that we can use keyes protected by pass phrases
diff --git a/lib/ssh/test/ssh_pubkey_SUITE.erl b/lib/ssh/test/ssh_pubkey_SUITE.erl
new file mode 100644
index 0000000000..160a78beb2
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE.erl
@@ -0,0 +1,269 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssh_pubkey_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("ssh_test_lib.hrl").
+
+%%%----------------------------------------------------------------
+%%% Common Test interface functions -------------------------------
+%%%----------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+ [{group, old_format},
+ {group, new_format}
+ ].
+
+
+-define(tests_old, [connect_rsa_to_rsa,
+ connect_rsa_to_dsa,
+ connect_rsa_to_ecdsa,
+ connect_dsa_to_rsa,
+ connect_dsa_to_dsa,
+ connect_dsa_to_ecdsa,
+ connect_ecdsa_to_rsa,
+ connect_ecdsa_to_dsa,
+ connect_ecdsa_to_ecdsa
+ ]).
+
+-define(tests_new, [connect_ecdsa_to_ed25519,
+ connect_rsa_to_ed25519,
+ connect_dsa_to_ed25519,
+ connect_ed25519_to_rsa,
+ connect_ed25519_to_dsa,
+ connect_ed25519_to_ecdsa,
+ connect_ed25519_to_ed25519
+ | ?tests_old]).
+
+groups() ->
+ [{new_format, [], ?tests_new},
+ {old_format, [], ?tests_old++[{group,passphrase}]},
+ {passphrase, [], ?tests_old}
+ ].
+
+%%%----------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ begin
+ ssh:start(),
+ [{client_opts,[]}
+ | Config]
+ end).
+
+end_per_suite(_onfig) ->
+ ssh:stop().
+
+%%%----------------------------------------------------------------
+init_per_group(new_format, Config) ->
+ Dir = filename:join(proplists:get_value(data_dir,Config), "new_format"),
+ [{fmt,new_format},
+ {key_src_dir,Dir} | Config];
+
+init_per_group(old_format, Config) ->
+ Dir = filename:join(proplists:get_value(data_dir,Config), "old_format"),
+ [{fmt,old_format},
+ {key_src_dir,Dir} | Config];
+
+init_per_group(passphrase, Config0) ->
+ case supported(hashs, md5) of
+ true ->
+ Dir = filename:join(proplists:get_value(data_dir,Config0), "old_format_passphrase"),
+ PassPhrases = [{K,"somepwd"} || K <- [dsa_pass_phrase,
+ rsa_pass_phrase,
+ ecdsa_pass_phrase]],
+ Config1 = extend_optsL(client_opts, PassPhrases, Config0),
+ replace_opt(key_src_dir, Dir, Config1);
+ false ->
+ {skip, "Unsupported hash"}
+ end;
+
+init_per_group(_, Config) ->
+ Config.
+
+
+extend_opts(OptName, Value, Config) ->
+ Opts = proplists:get_value(OptName, Config),
+ replace_opt(OptName, [Value|Opts], Config).
+
+extend_optsL(OptName, Values, Config) ->
+ Opts = proplists:get_value(OptName, Config),
+ replace_opt(OptName, Values ++ Opts, Config).
+
+replace_opt(OptName, Value, Config) ->
+ lists:keyreplace(OptName, 1, Config, {OptName,Value}).
+
+
+
+end_per_group(_, Config) ->
+ Config.
+
+%%%----------------------------------------------------------------
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%%----------------------------------------------------------------
+%%% Test Cases ----------------------------------------------------
+%%%----------------------------------------------------------------
+connect_rsa_to_rsa(Config0) ->
+ Config = setup_user_system_dir(rsa, rsa, Config0),
+ try_connect(Config).
+
+connect_rsa_to_dsa(Config0) ->
+ Config = setup_user_system_dir(rsa, dsa, Config0),
+ try_connect(Config).
+
+connect_rsa_to_ecdsa(Config0) ->
+ Config = setup_user_system_dir(rsa, ecdsa, Config0),
+ try_connect(Config).
+
+connect_rsa_to_ed25519(Config0) ->
+ Config = setup_user_system_dir(rsa, ed25519, Config0),
+ try_connect(Config).
+
+connect_dsa_to_rsa(Config0) ->
+ Config = setup_user_system_dir(dsa, rsa, Config0),
+ try_connect(Config).
+
+connect_dsa_to_dsa(Config0) ->
+ Config = setup_user_system_dir(dsa, dsa, Config0),
+ try_connect(Config).
+
+connect_dsa_to_ecdsa(Config0) ->
+ Config = setup_user_system_dir(dsa, ecdsa, Config0),
+ try_connect(Config).
+
+connect_dsa_to_ed25519(Config0) ->
+ Config = setup_user_system_dir(dsa, ed25519, Config0),
+ try_connect(Config).
+
+connect_ecdsa_to_rsa(Config0) ->
+ Config = setup_user_system_dir(ecdsa, rsa, Config0),
+ try_connect(Config).
+
+connect_ecdsa_to_dsa(Config0) ->
+ Config = setup_user_system_dir(ecdsa, dsa, Config0),
+ try_connect(Config).
+
+connect_ecdsa_to_ecdsa(Config0) ->
+ Config = setup_user_system_dir(ecdsa, ecdsa, Config0),
+ try_connect(Config).
+
+connect_ecdsa_to_ed25519(Config0) ->
+ Config = setup_user_system_dir(ecdsa, ed25519, Config0),
+ try_connect(Config).
+
+connect_ed25519_to_rsa(Config0) ->
+ Config = setup_user_system_dir(ed25519, rsa, Config0),
+ try_connect(Config).
+
+connect_ed25519_to_dsa(Config0) ->
+ Config = setup_user_system_dir(ed25519, dsa, Config0),
+ try_connect(Config).
+
+connect_ed25519_to_ecdsa(Config0) ->
+ Config = setup_user_system_dir(ed25519, ecdsa, Config0),
+ try_connect(Config).
+
+connect_ed25519_to_ed25519(Config0) ->
+ Config = setup_user_system_dir(ed25519, ed25519, Config0),
+ try_connect(Config).
+
+
+%%%----------------------------------------------------------------
+try_connect({skip,Reson}) ->
+ {skip,Reson};
+try_connect(Config) ->
+ SystemDir = proplists:get_value(system_dir, Config),
+ UserDir = proplists:get_value(user_dir, Config),
+ ClientOpts = proplists:get_value(client_opts, Config, []),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir}]),
+ C = ssh_test_lib:connect(Host, Port, [{user_dir, UserDir},
+ {silently_accept_hosts, true},
+ {user_interaction, false}
+ | ClientOpts]),
+ ssh:close(C),
+ ssh:stop_daemon(Pid).
+
+%%%----------------------------------------------------------------
+%%% Local ---------------------------------------------------------
+%%%----------------------------------------------------------------
+setup_user_system_dir(ClientAlg, ServerAlg, Config) ->
+ case supported(public_keys, ClientAlg) andalso supported(public_keys, ServerAlg) of
+ true ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ KeySrcDir = proplists:get_value(key_src_dir, Config),
+ Fmt = proplists:get_value(fmt, Config),
+
+ System = lists:concat(["system_", ClientAlg, "_", ServerAlg, "_", Fmt]),
+ SystemDir = filename:join(PrivDir, System),
+ file:make_dir(SystemDir),
+
+ User = lists:concat(["user_", ClientAlg, "_", ServerAlg, "_", Fmt]),
+ UserDir = filename:join(PrivDir, User),
+ file:make_dir(UserDir),
+
+ HostSrcFile = filename:join(KeySrcDir, file(host,ServerAlg)),
+ HostDstFile = filename:join(SystemDir, file(host,ServerAlg)),
+ {ok,_} = file:copy(HostSrcFile, HostDstFile),
+
+ UserSrcFile = filename:join(KeySrcDir, file(user,ClientAlg)),
+ UserDstFile = filename:join(UserDir, file(user,ClientAlg)),
+ {ok,_} = file:copy(UserSrcFile, UserDstFile),
+
+ UserPubSrcFile = filename:join(KeySrcDir, file(user,ClientAlg)++".pub"),
+ AuthorizedKeys = filename:join(UserDir, "authorized_keys"),
+ {ok,_} = file:copy(UserPubSrcFile, AuthorizedKeys),
+
+ [{system_dir,SystemDir},
+ {user_dir,UserDir} | Config];
+
+ false ->
+ {skip, unsupported_algorithm}
+ end.
+
+%%%----------------------------------------------------------------
+file(host, dsa) -> "ssh_host_dsa_key";
+file(host, ecdsa) -> "ssh_host_ecdsa_key";
+file(host, ed25519) -> "ssh_host_ed25519_key";
+file(host, rsa) -> "ssh_host_rsa_key";
+file(user, dsa) -> "id_dsa";
+file(user, ecdsa) -> "id_ecdsa";
+file(user, ed25519) -> "id_ed25519";
+file(user, rsa) -> "id_rsa".
+
+
+supported(public_keys, dsa) -> supported(public_keys, dss);
+supported(public_keys, ed448) -> supported(public_keys, eddsa);
+supported(public_keys, ed25519) -> supported(public_keys, eddsa);
+supported(Type, Alg) -> lists:member(Alg, crypto:supports(Type)).
+
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa
new file mode 100644
index 0000000000..5167322957
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa
@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH
+NzAAAAgQDsCchYjb27VZTDJuPwnYQQYZBCnQlVROAfDHa1dmAIQswOm/xFu5RyVpYCQPOY
+Y2OBdEibM7/FzaAJUs9gRLfbcCHA9jYy8zjag5KijtPJe9BxoDPAM8bSEFfLDklebS8NDZ
+tZvHsSz4UwagBQKNvoTPjljsf7fgjaQ9735S2jmwAAABUAiLuwAwdJr1qlGSmJSFqeM0Ao
+18UAAACAMA+NIBNjhzYr4nIhWv1x0TYZ8OldFIEh5iUDRf53ZxcdCloxtfNpZjYJbwEwLK
+UUW6xcPEp7/nCVOd50Yk4HgaDV5YvHwhS7g2yFXoK2gHKSa42BfWR4c8fPoMfapWSJ0aQU
+xGqebjTDeavwJq5umZCbk9/MfIcSctT9Pn88BncAAACBANMOLq9WhZs3LtechqTpFmXgzQ
+zjtoqFYbOd1ERDMXBffyS12aAlrJ1uUTKA1P/XVrIUMNuuXapWY6QwmqSOFVD58QQx9Z4l
+itZUu4H+lOSVPHpJrMq45hqmr4Mu35ENtxzNkfQI544/QLlfqkXJ3SME2tUUXAHvTHEnUY
+UvvteXAAAB8BiO51oYjudaAAAAB3NzaC1kc3MAAACBAOwJyFiNvbtVlMMm4/CdhBBhkEKd
+CVVE4B8MdrV2YAhCzA6b/EW7lHJWlgJA85hjY4F0SJszv8XNoAlSz2BEt9twIcD2NjLzON
+qDkqKO08l70HGgM8AzxtIQV8sOSV5tLw0Nm1m8exLPhTBqAFAo2+hM+OWOx/t+CNpD3vfl
+LaObAAAAFQCIu7ADB0mvWqUZKYlIWp4zQCjXxQAAAIAwD40gE2OHNiviciFa/XHRNhnw6V
+0UgSHmJQNF/ndnFx0KWjG182lmNglvATAspRRbrFw8Snv+cJU53nRiTgeBoNXli8fCFLuD
+bIVegraAcpJrjYF9ZHhzx8+gx9qlZInRpBTEap5uNMN5q/Amrm6ZkJuT38x8hxJy1P0+fz
+wGdwAAAIEA0w4ur1aFmzcu15yGpOkWZeDNDOO2ioVhs53UREMxcF9/JLXZoCWsnW5RMoDU
+/9dWshQw265dqlZjpDCapI4VUPnxBDH1niWK1lS7gf6U5JU8ekmsyrjmGqavgy7fkQ23HM
+2R9Ajnjj9AuV+qRcndIwTa1RRcAe9McSdRhS++15cAAAAUfRVtNnUPI+lhQgeu4d1i+H8x
+Y9UAAAATdWFiaG5pbEBlbHhhZGxqM3EzMgECAwQFBgc=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa.pub
new file mode 100644
index 0000000000..2f155b6ac9
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOwJyFiNvbtVlMMm4/CdhBBhkEKdCVVE4B8MdrV2YAhCzA6b/EW7lHJWlgJA85hjY4F0SJszv8XNoAlSz2BEt9twIcD2NjLzONqDkqKO08l70HGgM8AzxtIQV8sOSV5tLw0Nm1m8exLPhTBqAFAo2+hM+OWOx/t+CNpD3vflLaObAAAAFQCIu7ADB0mvWqUZKYlIWp4zQCjXxQAAAIAwD40gE2OHNiviciFa/XHRNhnw6V0UgSHmJQNF/ndnFx0KWjG182lmNglvATAspRRbrFw8Snv+cJU53nRiTgeBoNXli8fCFLuDbIVegraAcpJrjYF9ZHhzx8+gx9qlZInRpBTEap5uNMN5q/Amrm6ZkJuT38x8hxJy1P0+fzwGdwAAAIEA0w4ur1aFmzcu15yGpOkWZeDNDOO2ioVhs53UREMxcF9/JLXZoCWsnW5RMoDU/9dWshQw265dqlZjpDCapI4VUPnxBDH1niWK1lS7gf6U5JU8ekmsyrjmGqavgy7fkQ23HM2R9Ajnjj9AuV+qRcndIwTa1RRcAe9McSdRhS++15c= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa
new file mode 100644
index 0000000000..16f8d330e9
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa
@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
+1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQ6WBbp8A+ypG22sM4pqd+973IoBF9a
+TcWU237H8NQRf4BbAKlA+NFfi5XHYrRtpW7XCTkdTQn1jdayyq8RtYaGAAAAsHHJLVBxyS
+1QAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDpYFunwD7Kkbbaw
+zimp373vcigEX1pNxZTbfsfw1BF/gFsAqUD40V+LlcditG2lbtcJOR1NCfWN1rLKrxG1ho
+YAAAAhAIDK4KZm7jhx4e0gRH/DlLg8WYu+BUDNjmMAgcMLsDv9AAAAE3VhYmhuaWxAZWx4
+YWRsajNxMzIBAgME
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa.pub
new file mode 100644
index 0000000000..162ebd3fb0
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ecdsa.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDpYFunwD7Kkbbawzimp373vcigEX1pNxZTbfsfw1BF/gFsAqUD40V+LlcditG2lbtcJOR1NCfWN1rLKrxG1hoY= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519 b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519
new file mode 100644
index 0000000000..e2b83c0800
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBo2p2XftaIjxYLqNQeIcDrdejVxhTX+db3O5UFV5XdLwAAAJhZQvGOWULx
+jgAAAAtzc2gtZWQyNTUxOQAAACBo2p2XftaIjxYLqNQeIcDrdejVxhTX+db3O5UFV5XdLw
+AAAECiqNZLTp3o73H+B1VseAJKhEiGXf4otOH461y+sAwBCmjanZd+1oiPFguo1B4hwOt1
+6NXGFNf51vc7lQVXld0vAAAAE3VhYmhuaWxAZWx4YWRsajNxMzIBAg==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519.pub
new file mode 100644
index 0000000000..4d2f9f3677
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGjanZd+1oiPFguo1B4hwOt16NXGFNf51vc7lQVXld0v uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa
new file mode 100644
index 0000000000..243c17cfd2
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAQEA6helL/aMwpp5cHOCZ5bB8tkpgiXykiV0U61t6K4h7fdud8zOyI9e
+N/H9l787QkzOoLBJxzFvTOYcqsC1CMYKNiCvPEtFDZyfD6tZv03nEOc3VZhEO9+pKmnW3y
+/7xjRekEW3bqyEk3GkMhnDFfm3JILtZmdRrooXCkVGZaVxELm8N+cEtAE8TCbOIOo1mjsv
+x+R8bGktx057EZ8Ieg8tFWh/atFemHj75ZILjY795ji5YQHBHfDUBAwNNY5lCsue4COT6Z
+Udv03JgwnCVTu+XsXqH6yemjefU7nvWktudnaKGbg5OS/H2pDrm6ivjRtGdz0gkMiD9miw
+Q2n03cFThQAAA8h66iCmeuogpgAAAAdzc2gtcnNhAAABAQDqF6Uv9ozCmnlwc4JnlsHy2S
+mCJfKSJXRTrW3oriHt9253zM7Ij1438f2XvztCTM6gsEnHMW9M5hyqwLUIxgo2IK88S0UN
+nJ8Pq1m/TecQ5zdVmEQ736kqadbfL/vGNF6QRbdurISTcaQyGcMV+bckgu1mZ1GuihcKRU
+ZlpXEQubw35wS0ATxMJs4g6jWaOy/H5HxsaS3HTnsRnwh6Dy0VaH9q0V6YePvlkguNjv3m
+OLlhAcEd8NQEDA01jmUKy57gI5PplR2/TcmDCcJVO75exeofrJ6aN59Tue9aS252dooZuD
+k5L8fakOubqK+NG0Z3PSCQyIP2aLBDafTdwVOFAAAAAwEAAQAAAQA0vFbuUzCqtnodJyh9
+hazztJBxTXM0EVP/ddaI0JG8Nj2gp3b+H64uFEn44Y/MA9mYwZ4dTbmxLTXQEdG2xEaQox
+RXFO3dfycmNIfnXPltCWmh0sesZVqKv4U0im7B3BJhlhMYz6yeOr+uubcFQFhN1WD97NCt
+7VX7blfJlle+WGsH6IB728MSlo+pU7desTPaWIamsDTftUkzrIVvgbppmk79XX/zhObIi/
+8xR3cCxygq3LM31LwTOcEnRvMufJOv3lR0ybUE5INtJVYYpqZ2hFaNON+hOTxaAFZ/pJJ1
+zm5XTb9HWSaouLlalzkQONCTucp7qU7PnBnIB/nQ5A0BAAAAgDO683uK2WICRSr2J7E1Q3
+jQ+GqU0c4SslORQ4mwjMbJVMyQwd4U00ctex4XIoQdB6dn17jmvEJa7UiH0+TMm9cJefoL
+V8G4O4XThEDIBtF7Kp1oKV9roxVdJ/iS7psMop3g9X+/9L4nOUMxlI21j7YKf3C5KJwhD3
+RAqyE/YClnAAAAgQD6BacnxjxmcFJdCevQcfIhSCwzXp7fjYWK46U9fBF5k+H+GUicfaL6
+LGp0DDBhhpQ/lAzNO5IMn6pPAfl0ZSOsGie9OszSpMujxG1fSTDeUqyf0hL+t7yVrCRTEc
+cyl5t0JBSUyoHy0w1B3pXF2vjRtrmqFvTmTrWYJAHxbPePsQAAAIEA77B9KyiCwX8AKubN
+AA+nRRdmJfju7yM0xiHO6RfOgSnp9EAMKyoUdVFY1yddxgTQvpGSSigBogJHLB2ptGtmTE
+6DtIEtP0SDQGso/Q51AhTtk4ScYlPvZJiXVfRmyibVdxmQqa8+aJcqS0j1cReSmAu5zRQm
+zW0/752JsHk3qhUAAAATdWFiaG5pbEBlbHhhZGxqM3EzMg==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa.pub
new file mode 100644
index 0000000000..3042323a71
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqF6Uv9ozCmnlwc4JnlsHy2SmCJfKSJXRTrW3oriHt9253zM7Ij1438f2XvztCTM6gsEnHMW9M5hyqwLUIxgo2IK88S0UNnJ8Pq1m/TecQ5zdVmEQ736kqadbfL/vGNF6QRbdurISTcaQyGcMV+bckgu1mZ1GuihcKRUZlpXEQubw35wS0ATxMJs4g6jWaOy/H5HxsaS3HTnsRnwh6Dy0VaH9q0V6YePvlkguNjv3mOLlhAcEd8NQEDA01jmUKy57gI5PplR2/TcmDCcJVO75exeofrJ6aN59Tue9aS252dooZuDk5L8fakOubqK+NG0Z3PSCQyIP2aLBDafTdwVOF uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key
new file mode 100644
index 0000000000..071d761e46
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key
@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
+NzAAAAgQDQp0Dp9ne0IXxq93hFwqQi+iOTNqw0GeJlCQvzSYsnLgPzWCj/4o1R6czLdx4/
+6nNWPJVmbManC0gcBwdcJbVXW7nTbW3xnD2LmzyB9dwjka8CgfC/A87MkslY8YCt0ZHz8f
+/9L9eRqAv5udHlxgtaX1u81VDVPxAk4bOAAyz4AwAAABUA0pu1vOhk0mBbY9cz+uN+ELA2
+20MAAACAW1PJ1Rau3yNLwzldcoejYt4gOKmAoAUWL9F3fp+IKGhYf6Z6GX5OI/98BE2wWu
+3/Kk12K7ZtVyTj2B1JheNjzivsZryNhZwJlCCbFQalkU9C2tApALix2j/PYQy++Hefk8yK
+4qFwSG9DTC/TvMKPjaZA5w3TjIkWuC0tGoqvkkwAAACAIRynnqPLhvlyD0wg5F4v7iG+fr
+d1JfusrjLobdgBz+d60wyu+0IUuaJdhB1z0TjPdxmkwwrbOzmkAhcRbHduV8HTAY/l0Trp
+X8E4b7gzpJwcy/2T1lPx5pIwlcd3TwqwMBQPNOXV8FR2fZYhIARSZy0ccvePc+/XKFmKT6
+jjBCcAAAHwPSMhRT0jIUUAAAAHc3NoLWRzcwAAAIEA0KdA6fZ3tCF8avd4RcKkIvojkzas
+NBniZQkL80mLJy4D81go/+KNUenMy3ceP+pzVjyVZmzGpwtIHAcHXCW1V1u5021t8Zw9i5
+s8gfXcI5GvAoHwvwPOzJLJWPGArdGR8/H//S/XkagL+bnR5cYLWl9bvNVQ1T8QJOGzgAMs
++AMAAAAVANKbtbzoZNJgW2PXM/rjfhCwNttDAAAAgFtTydUWrt8jS8M5XXKHo2LeIDipgK
+AFFi/Rd36fiChoWH+mehl+TiP/fARNsFrt/ypNdiu2bVck49gdSYXjY84r7Ga8jYWcCZQg
+mxUGpZFPQtrQKQC4sdo/z2EMvvh3n5PMiuKhcEhvQ0wv07zCj42mQOcN04yJFrgtLRqKr5
+JMAAAAgCEcp56jy4b5cg9MIOReL+4hvn63dSX7rK4y6G3YAc/netMMrvtCFLmiXYQdc9E4
+z3cZpMMK2zs5pAIXEWx3blfB0wGP5dE66V/BOG+4M6ScHMv9k9ZT8eaSMJXHd08KsDAUDz
+Tl1fBUdn2WISAEUmctHHL3j3Pv1yhZik+o4wQnAAAAFQCWaj2vQN6mnxfVgP8YzlPakzQI
+WAAAABN1YWJobmlsQGVseGFkbGozcTMyAQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..97193b1c58
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBANCnQOn2d7QhfGr3eEXCpCL6I5M2rDQZ4mUJC/NJiycuA/NYKP/ijVHpzMt3Hj/qc1Y8lWZsxqcLSBwHB1wltVdbudNtbfGcPYubPIH13CORrwKB8L8DzsySyVjxgK3RkfPx//0v15GoC/m50eXGC1pfW7zVUNU/ECThs4ADLPgDAAAAFQDSm7W86GTSYFtj1zP6434QsDbbQwAAAIBbU8nVFq7fI0vDOV1yh6Ni3iA4qYCgBRYv0Xd+n4goaFh/pnoZfk4j/3wETbBa7f8qTXYrtm1XJOPYHUmF42POK+xmvI2FnAmUIJsVBqWRT0La0CkAuLHaP89hDL74d5+TzIrioXBIb0NML9O8wo+NpkDnDdOMiRa4LS0aiq+STAAAAIAhHKeeo8uG+XIPTCDkXi/uIb5+t3Ul+6yuMuht2AHP53rTDK77QhS5ol2EHXPROM93GaTDCts7OaQCFxFsd25XwdMBj+XROulfwThvuDOknBzL/ZPWU/HmkjCVx3dPCrAwFA805dXwVHZ9liEgBFJnLRxy949z79coWYpPqOMEJw== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key
new file mode 100644
index 0000000000..3d01041bed
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key
@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
+1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQT21rZj/MTJN9fK+jDZ6l0bYF1EndNL
+N+3a4cAYl8jRhRk18s8QbCN3GP+CsFWtor5fRhragFo2X7yPVAVU75FoAAAAsEntb7pJ7W
++6AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPbWtmP8xMk318r6
+MNnqXRtgXUSd00s37drhwBiXyNGFGTXyzxBsI3cY/4KwVa2ivl9GGtqAWjZfvI9UBVTvkW
+gAAAAhAJVvBMRkEfuS8/YZ9PayecITNQ5CfZ5I49z3Ay17cxbbAAAAE3VhYmhuaWxAZWx4
+YWRsajNxMzIBAgME
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key.pub
new file mode 100644
index 0000000000..b125971661
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ecdsa_key.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPbWtmP8xMk318r6MNnqXRtgXUSd00s37drhwBiXyNGFGTXyzxBsI3cY/4KwVa2ivl9GGtqAWjZfvI9UBVTvkWg= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key
new file mode 100644
index 0000000000..0ebf1dc8b7
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBKsbrzown8TYrOXlZYV/D+LICEyw23bwjdXRGfm4FEAwAAAJheMIX9XjCF
+/QAAAAtzc2gtZWQyNTUxOQAAACBKsbrzown8TYrOXlZYV/D+LICEyw23bwjdXRGfm4FEAw
+AAAEBxNZQd4RXl1DDYt+bm7WeSXlVmncu/bQ/ubdj56T2xV0qxuvOjCfxNis5eVlhX8P4s
+gITLDbdvCN1dEZ+bgUQDAAAAE3VhYmhuaWxAZWx4YWRsajNxMzIBAg==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key.pub
new file mode 100644
index 0000000000..0108498194
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_ed25519_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEqxuvOjCfxNis5eVlhX8P4sgITLDbdvCN1dEZ+bgUQD uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key
new file mode 100644
index 0000000000..6d51bf7672
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAQEAt+8ElruXAw+OXtyGBUMxAkSfsw/A7BWqIzbtRn6hD3YlJPFYBo4O
+0wNeW4g9z+k5u42Q4Sv3gZAPuVAVi/E4MEd3eg8ZRkkC674a5lUvEzIrDYPbkhzR5bLidG
+0TK4wdqL/HRGE9gpTIaOcIDkK2RI2PeRhjvjscUHTsa/Kx6dwBiKOK5vjIXA2MlsTgVY7N
+MSpjR/sH/TG6UZXxhlgmBGsrpVsVM/3kQomhVVoMn1yRG6Wz2Ed+b6ENYKNmuomeIoCZoU
++uE0RMqiE5qD32YVExt1B89CVAR3GbQKKTop+Pb1xkNMnYvCpDo8+68dZ7U7zJ1SZr00yd
+G0M4pFntDwAAA9At3ILHLdyCxwAAAAdzc2gtcnNhAAABAQC37wSWu5cDD45e3IYFQzECRJ
++zD8DsFaojNu1GfqEPdiUk8VgGjg7TA15biD3P6Tm7jZDhK/eBkA+5UBWL8TgwR3d6DxlG
+SQLrvhrmVS8TMisNg9uSHNHlsuJ0bRMrjB2ov8dEYT2ClMho5wgOQrZEjY95GGO+OxxQdO
+xr8rHp3AGIo4rm+MhcDYyWxOBVjs0xKmNH+wf9MbpRlfGGWCYEayulWxUz/eRCiaFVWgyf
+XJEbpbPYR35voQ1go2a6iZ4igJmhT64TREyqITmoPfZhUTG3UHz0JUBHcZtAopOin49vXG
+Q0ydi8KkOjz7rx1ntTvMnVJmvTTJ0bQzikWe0PAAAAAwEAAQAAAQBjIt2rTIJxMOJAeMV3
+cqaonUoiHdySon6oKkOrGjc++SO+DKKwLcMJsqgZ143RUNhAIWY0Jxlo6LfA3swuOB5bzz
+kzPY4W1uVPIJCpEsKjqwePakFfOE9daZQqwltxvjyCJpOFZI/doMl/2P37ibNpsY7h6uZf
+ssZpCwwehpmj/HknoAlrGTrG2SlzLTMvk6vwYgNoeHKCQfM2wfr9fFlbwNFJE7L44o0PfK
+8UX9u8mC7PR1Q1u2OopOsUXWu6f1Vc/nU5La6Z6W1voHxMfUMLhq7c/Jih7SfPVX2z6U22
+VKGXinhh1q5a71nv44BZPxTGw0TC/FrDntTWyDu3jkuhAAAAgQCNZx4JVV+yoFnbypRHh+
+n0hhYvPbtHEzmhK9WEyjQCWIhf7zMXau+00bhDaS+6nqiZOfXPecC9UbjH06KT8da6yqYK
+3SwsXA+RALbKe0uqWO1KKufge+FxZWX3j07D/8+pL1fE0996f9yjR06kM8b0afxQBMDxnG
+HzVnPJEmUpjAAAAIEA50BnLTKqHsQCiBHRYuzRAXomeW7EWO+lQSB7WUcx91QtuzspoXMK
+NyiaabzsPfTqmqRH9fcL1ORSTy4Jk+0tZjK20P+gGtGthHCxQjQnOhgyjvSzp0P7F55Cme
+LSr1lYtFtjrh4nLval05ddQ6cYmebzRzy0NveKUbeIgkR5zJEAAACBAMuePyCBm2RB6Hlw
+cYqUTJ+zduNFhu83YJ2vQg0ojJ5If9jHNrTHyiRG/k81AJS0fZs9aF6//fvq+85Bf3bGd6
+4PSWbSwjlxLNvbcGqfQ8gOk75lni2hr5e5SQBimGavJM7bjqr3+nqBvAiPrDROWQpALdbh
+hVWNOMdo8JBxcW+fAAAAE3VhYmhuaWxAZWx4YWRsajNxMzIBAgMEBQYH
+-----END OPENSSH PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..0b12e79f63
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/new_format/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC37wSWu5cDD45e3IYFQzECRJ+zD8DsFaojNu1GfqEPdiUk8VgGjg7TA15biD3P6Tm7jZDhK/eBkA+5UBWL8TgwR3d6DxlGSQLrvhrmVS8TMisNg9uSHNHlsuJ0bRMrjB2ov8dEYT2ClMho5wgOQrZEjY95GGO+OxxQdOxr8rHp3AGIo4rm+MhcDYyWxOBVjs0xKmNH+wf9MbpRlfGGWCYEayulWxUz/eRCiaFVWgyfXJEbpbPYR35voQ1go2a6iZ4igJmhT64TREyqITmoPfZhUTG3UHz0JUBHcZtAopOin49vXGQ0ydi8KkOjz7rx1ntTvMnVJmvTTJ0bQzikWe0P uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa
new file mode 100644
index 0000000000..f76cc234b0
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvQIBAAKBgQD5eG+xea43Nas9xKOEPqkMaBmTZuKo+nz9S2mo3Qkuj5OV6Mqc
+Jd6g9DUylc5XMtyYvq0AImgirc3ZTCOLyH3wXunNDkafUxwxrSO3w68mgdGbjJxZ
+euS/dRx6pYmeb/ykWCQFd6D/t1OGK0QJT7gn9ke6pXAL1ARVafP2Yri8rQIVAORv
+Zk6erYLd3aAvPNzVSLH6P6CRAoGBAImjtInOXhMXa+7ABuwrvdN78bJX5pSdlgUW
+W1Nx1obUb65njR78CqB+fynwxevHDBPNEVE0T5xfAg3zWBTGH/mwQ/ivA3t350hT
+usbRBbPs9kzzhfWdq7GKVKL76UmefYU1cwM58VdOFcuMbfUEujwIgw+KbJzI7c6y
+DH8ll+s7AoGBAK8z5gZp4k43uEFRsLwhZj4edgb3vmZvOvEzo3awP+Pr2Et4ReL1
+48YYfb422FafzePzCkvkRNMdZF2iiPQXK1r4JIVCfi2Zyci8fMzHmBR3bmvz5cjP
+9tVtKGR9z48dYe5R74Td+Vp//h6lCHyi06kSg8yoambE8sdAnp5DdgN6AhUAg8aR
+exXtcrukzyrWLA/4jZSnrFY=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa.pub
new file mode 100644
index 0000000000..20c42561b0
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPl4b7F5rjc1qz3Eo4Q+qQxoGZNm4qj6fP1LaajdCS6Pk5Xoypwl3qD0NTKVzlcy3Ji+rQAiaCKtzdlMI4vIffBe6c0ORp9THDGtI7fDryaB0ZuMnFl65L91HHqliZ5v/KRYJAV3oP+3U4YrRAlPuCf2R7qlcAvUBFVp8/ZiuLytAAAAFQDkb2ZOnq2C3d2gLzzc1Uix+j+gkQAAAIEAiaO0ic5eExdr7sAG7Cu903vxslfmlJ2WBRZbU3HWhtRvrmeNHvwKoH5/KfDF68cME80RUTRPnF8CDfNYFMYf+bBD+K8De3fnSFO6xtEFs+z2TPOF9Z2rsYpUovvpSZ59hTVzAznxV04Vy4xt9QS6PAiDD4psnMjtzrIMfyWX6zsAAACBAK8z5gZp4k43uEFRsLwhZj4edgb3vmZvOvEzo3awP+Pr2Et4ReL148YYfb422FafzePzCkvkRNMdZF2iiPQXK1r4JIVCfi2Zyci8fMzHmBR3bmvz5cjP9tVtKGR9z48dYe5R74Td+Vp//h6lCHyi06kSg8yoambE8sdAnp5DdgN6 uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa
new file mode 100644
index 0000000000..e0ed11843a
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEINRRCrEPoBUJJueQ6IDc7UxpvYi+gzBJkn3pv+yp08tvoAoGCCqGSM49
+AwEHoUQDQgAEKAnppzgUZKpYf6qLbLL1LmTLWPLI7NDjY/+oWE1NqrUrMOM4NXKG
+NCTNhLKWtICjfb8h0E3zbCh5PNubVw14WQ==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa.pub
new file mode 100644
index 0000000000..2793c68b70
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_ecdsa.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCgJ6ac4FGSqWH+qi2yy9S5ky1jyyOzQ42P/qFhNTaq1KzDjODVyhjQkzYSylrSAo32/IdBN82woeTzbm1cNeFk= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa
new file mode 100644
index 0000000000..30871b6a67
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAtnd1zCAPBkxEHFBQjpt9TOY7epRozetWWBp1wVQYxHMicpwe
+2NwuN+3A6Y5r20lhdaElVQ08DBEcJ184sSxv/+8PCu4umiSyqRyrUvdap60InZq1
+Fg50OJMfAGcWjeOp4UrH/nGdJyqdN2Y40eVsXg4pogy7ElG3Q5hROmbC9vpOTkrb
+3Krv7JFFXz3vhRkxiYCMNw8hK8TOye9o1JCyVzImZGGwdUA9OZN4Nizoj6ie90KC
+VRoI+2GoE0gNy4Mt7P+lOyDBzFeJbGl0ob9S5P3gnyCVUmlV0EY1fSijN8IIOj5l
+qsBpADt+sN3iYltZJmCn5wDP/iORn6P04jWU1wIDAQABAoIBAHlncHw5lGWXVvYT
+xhWshSkmQsrjdfwUqmWCbXkNkFEdXf0tvSSDE0lpKqL7fO3xnCPc7W7ymFJbDAVy
+SNExhO+fyr12DpHG+wykI6XXKH1KFuJuLjCXu2JtGQJ2lL4hjUV2MS0twOdvZh2X
+KRUW9gx6ld7ZY5rjvfD+poUaHHygnN6f0+PiyBpUZaL+ZTj/6CpHiCxdZtOCf0o6
+bU7TaPNcZ3vf3Qhk4jk140vEDLJQnPF8stBqPWa9HfmM7CNjWBdmhQXHtHw8CtF6
+aba9BRC/FMYx43itE9hkg6p3JrSqAN/gZ6RCXLog6mQYttJV+y8oLTXvblT2Y3c9
+YjnigGkCgYEA2Y4+sjsGw3l5HnQ9CNuUQvJZgloVd/NTw8/UXXkV1QudI6tzmMJn
+XAxCCtt2DVlPBqFJ74uwdWpY0nwFJslEp4sV6VPv2TpBmAOPDB6QtQ8SXhFWQ7vj
+BVh5kwl3LUDVI+NIACoaMyZuS6N97Fp4a/mKtgj+ucOkfr15+IF7L/UCgYEA1rXd
+ATBFQlLoPuv3VglU1LLgaLs/3qzoBH9DzwPPyEFdWYQUCuMHsL9eEfDZMsG/GeZb
+Fch/CR0R2Qt+ZlcxcRichgMw3ydzIBqCvLe444lBzxdLWFqS5gCnSh5iage2QRs0
++6QD9O16HJER6HmBwR6DtwpP3N4dHxLXJRjDrhsCgYEAl3/M/UTJkvpWc/SyRCbU
++kHWP0YIST2ziVqDIoydvXyW8y4EE87dN2Z53yGw9d7Jf252FFCMk1d5fypKVBY4
+rwvWOGPxVK6S2w8vYFswnkVenw8nqYd/sktIbjJbQbIyOwmdLDAlipUqnZW+rQbb
+cSWXiOh+qlIpjPDZrUpNxLkCgYAMPjiI7dC1NHcLx3bGECgnLMABGNROhTuBriQW
+tNfvSlLhXNeru0BgArmBemNYMpYMCwecmV8tDNxMrQwbF9O46DdcqOfrgZtd9EUK
+L8u6JcR7448nTZrcxKLFZjAkbaYl1kBSLQsQt03kPR1xTSRp96x5Dnx5Uq0EbZWZ
+Bu15iwKBgAlSiCUqCNir3fdd0wE2+MIczam3YshQmnS3/XEk+7Bmfmb7Rxdwk94l
+P/SaQYZ3buCKoBTz5OveBl4aEdiyvEqBkeJoUbzwFILyo0RNncqULcrYAPIJtbKK
+H0o0naCZUgUJGsX0/DdJsEE27KMljc1A1Fpd3qQ2qqVLFfDTrsuB
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa.pub
new file mode 100644
index 0000000000..f7b1180aad
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2d3XMIA8GTEQcUFCOm31M5jt6lGjN61ZYGnXBVBjEcyJynB7Y3C437cDpjmvbSWF1oSVVDTwMERwnXzixLG//7w8K7i6aJLKpHKtS91qnrQidmrUWDnQ4kx8AZxaN46nhSsf+cZ0nKp03ZjjR5WxeDimiDLsSUbdDmFE6ZsL2+k5OStvcqu/skUVfPe+FGTGJgIw3DyErxM7J72jUkLJXMiZkYbB1QD05k3g2LOiPqJ73QoJVGgj7YagTSA3Lgy3s/6U7IMHMV4lsaXShv1Lk/eCfIJVSaVXQRjV9KKM3wgg6PmWqwGkAO36w3eJiW1kmYKfnAM/+I5Gfo/TiNZTX uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key
new file mode 100644
index 0000000000..99da9723ef
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQDLRBMHSgExTaMVjXrZwxdgmkpZgjQXGy/IUqBNw9MTq1AmndGH
+6Pkj3LzPiNsZArTwg3k0strcx+VtbQcJ1TrH7nOABdO5vTGAd9arPIkhts0LgwHA
+HcAHf+9iNr26uG8jtIc7+o7IpJyTzy2buVmfosYhKcwVbPHIhQp8KjAWfQIVAPd+
+7YzKkxRyBPZZ4K+G3QI3YJiPAoGBAKGRI74EwgLwRWMeVOJun7oWh0uVQFdi51sY
+3e9JC4/0hx0D0JHO2+b3opGPw/wrVXmGWZdmgpslsPZbta/oVR0OgAK4V2tTgn8p
+ijOIcEJmvpogtaJj40laTV+mpwRfQZWsfJPYTibZqtPVvvtu+6eVB1dTlNYngOAN
+mDHh6wpzAoGATv2nRnv0l878aIchG99kKFq5hX1rfLOYqeDq5uKXHu+Zf9IDPEQB
+Dmo73RA4pXWlD8Kf9mV3p5CSxg3tdSTelCt7h0cVoq3LmSU5fjA8eVNI7KsFi9QB
+dBnxEdMIBW2S8x4FfuWzgBUrQBzLqLR0CEEPmr1N9Dw63OiTgh9drJcCFHmpYuRa
+y/ynGrBb/mzt9QAGhNls
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..485997cfb9
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAMtEEwdKATFNoxWNetnDF2CaSlmCNBcbL8hSoE3D0xOrUCad0Yfo+SPcvM+I2xkCtPCDeTSy2tzH5W1tBwnVOsfuc4AF07m9MYB31qs8iSG2zQuDAcAdwAd/72I2vbq4byO0hzv6jsiknJPPLZu5WZ+ixiEpzBVs8ciFCnwqMBZ9AAAAFQD3fu2MypMUcgT2WeCvht0CN2CYjwAAAIEAoZEjvgTCAvBFYx5U4m6fuhaHS5VAV2LnWxjd70kLj/SHHQPQkc7b5veikY/D/CtVeYZZl2aCmyWw9lu1r+hVHQ6AArhXa1OCfymKM4hwQma+miC1omPjSVpNX6anBF9Blax8k9hOJtmq09W++277p5UHV1OU1ieA4A2YMeHrCnMAAACATv2nRnv0l878aIchG99kKFq5hX1rfLOYqeDq5uKXHu+Zf9IDPEQBDmo73RA4pXWlD8Kf9mV3p5CSxg3tdSTelCt7h0cVoq3LmSU5fjA8eVNI7KsFi9QBdBnxEdMIBW2S8x4FfuWzgBUrQBzLqLR0CEEPmr1N9Dw63OiTgh9drJc= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key
new file mode 100644
index 0000000000..75ba71da56
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIJeZs+jukpumHYLii4OXg5k9dN7u1aNLZovxbqFoEfgToAoGCCqGSM49
+AwEHoUQDQgAEBg0zAjfzxl0ccv2wnJHZvLXETa6bopctXD1V/FWcPoBL5dh42mOj
+I6ZgtrUnnjbhdxJyeG3BjntqhP5rLMMpeA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key.pub
new file mode 100644
index 0000000000..26e7285240
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ecdsa_key.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAYNMwI388ZdHHL9sJyR2by1xE2um6KXLVw9VfxVnD6AS+XYeNpjoyOmYLa1J5424XcScnhtwY57aoT+ayzDKXg= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key
new file mode 100644
index 0000000000..6044fc7725
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEArrzkBUAMYvAso4hR79vmNbxNbLYt7QocukbiCWOq29HQvqbS
+zj/OSE1Qg6C/aTbghvdpvxbaFt3aqdWroF0PhVVoEsJY76bI7RsobIe9zI/Z3dkQ
+RmW3IyjHvpVBwy84fKZ05A8Bd10kca1GfrQXi7LkFZ5FRjxarFzMojGVesWcZLag
+ThPG0QAjSw2sG+oql4YoIeagSdf3tzOOF+04vcpgqCRugsscw17lI1Rwq0nM3thU
+BgSWDRbjzmkWo9i5Wpc9ZKS1z4ANET+I2hGW7PEA6XAXJKC6nIWdVIie0GN02C/m
+i45NyTPn52I/TJKFAtIoZ8fbrHEepX7V/Dt7DQIDAQABAoIBAA1yJY2t3wYh+x1e
+WQe/ARjzc3XBEwmhdJJ07+HPFI+ztn9lMOWEDWiM4nwue2wqN97K3Q1CQefujGvz
+MDC32IDnEIoZAGT4jY+JPnQTgexiyV4D3Pe9zfjbo3sr2xKc6JjW6jm+WduIhExn
+C/yl+QXb7ycmtafw7v1CatC0Rg9bUtE8nMHKYVPazn30wlHdPl6TyTtEXoZCKMg2
+OTxrva80x5JboUqLZXX41VqVmqqoakEO3NOGlhrIzIOB866py8d+f6wilN/rUcGe
+MJwB8aTrYPxLkCYl9PGeMDMLARvhjMm53f6UQFDpL2rY0XpnaqrZqS9KrbJoTQDW
+lMj3OiECgYEA4FL3fba4FYGzf7T/m2xRSC5qrpr0gNn1mZJ5oUWGiHBwjAHC7EVW
+apcjskac7WrznIBJiV2ozzxQOgIymuYO9LX95G6b3nrkWhVXqUiyCkpdMSE/YVfA
+iMc3Z0QNz+I/cbEPUKZ7osKPZm4BRpUNvJwj4Vvt5jXwTZlPmVmpNHUCgYEAx2lx
+q6HO+Grba0Azg7wobnnZ8uZZvdZ+QpxgGhH1Rx9862KM+uVYrJ7xEUlNTYfBtdpq
+JXQnGlpzjGPeziZjJxv7AgWbJA80aXtTH/oE9E0KMmGRzE2bQNR8kUZHU5S2e0u+
+x1DUyvKqyKSRByxUp8wj5QZGPOH4MPvCHVZF+TkCgYAg52qQERYtaWn36Ie5t4iw
+qsZROD93CwGAdkDLDBSwvLV1g+igmYcUeXjt9HeeR5rWMOcYdBmH1FP8PkhH+kjl
+UjCcqjDI0IPgRtMl7JjY85F53GOclq+SII6a4huYi5o8xfj2HoVyGVHJd4dOYBy0
+tr54lvBtXSoTZ9KKLuGn5QKBgBUy4XGkfvMrsO3C4ncTrpyn+YJ3+HxU7BE6vICo
+/hE0iLwhOumFLhsTvn7e8wfV8cLaWERpB6smiHgZOdtie1HyCIobfHWl5CV+hcS1
+eIdcFURr2OsGKQYIUMHE3dpFyexrjfl0X1q/12YDEKPZk5pO+lXjh937C75xVR53
+SHMJAoGAS57QNKLFaWZunoMNuvvNAj7Z0q1JrFuLEwfnkG28g5+ov3wIBinPtlrr
+3HaK6sny0hHLPoRP+fa6BVRaQhDzeeKDu6PqNEkNnocWPi79lxfk531EJQHOgQgX
+yt7Ruq0TlBYs5wGrmtYXLKAGvcfyx9EoFs2Km1iNKqu6b/dbQXc=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..8e62458395
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuvOQFQAxi8CyjiFHv2+Y1vE1sti3tChy6RuIJY6rb0dC+ptLOP85ITVCDoL9pNuCG92m/FtoW3dqp1augXQ+FVWgSwljvpsjtGyhsh73Mj9nd2RBGZbcjKMe+lUHDLzh8pnTkDwF3XSRxrUZ+tBeLsuQVnkVGPFqsXMyiMZV6xZxktqBOE8bRACNLDawb6iqXhigh5qBJ1/e3M44X7Ti9ymCoJG6CyxzDXuUjVHCrScze2FQGBJYNFuPOaRaj2Llalz1kpLXPgA0RP4jaEZbs8QDpcBckoLqchZ1UiJ7QY3TYL+aLjk3JM+fnYj9MkoUC0ihnx9uscR6lftX8O3sN uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa
new file mode 100644
index 0000000000..5420f40352
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,C5C1380D397337A7425EF5FE7B2A01B5
+
+/xZ9L6iKTvN6AbZu3Uper6DzMxLdhGMix9UB9pq3JXJAElZRfP5LkAYBX+3mYYXu
+VGYsMsHrPLtItgDQQ7DNhbo+IOoC3Id7gIssxitXaNbuDs91ahMANzYPeypV71+U
+fgNnznaTIL2yl6pr342IFksCw1lt9L6Z3nnslUO/4mwbESL7nIe2WDpxdAxIYQSw
+QetL8FoP0ohyMvNdrmooJ48oJylR3Uctir1Szb6Rg65LEuvasY0SbJhDaS+weN9c
++dRgPTrZmIhohEy5FT52JqsSaONxM0NVV8wmkw0uX642cYpPU1WkVDK5RwXExiR0
+5hORAu1lqdCGG8M1KNutn/8ASN+ger79Jccp9Ity9TP3I2RbSFJCR8byysIJoFdF
+Pb6ju+SRmtMBmrIMaAQdO10zAeBdVq78ALiK78Jw8D2ciA1zRnzvzBCoQhKw4dxU
+XmD4V2qBVUuYFKms6Z/LRnjvShooQgmCm4CtX7RehQFTlbrJJcrfEaJdrb84d9QK
+uY/lY/47tSsyC8YIK9ta9uyLgvxOmPvfKSA5lJpTPk0oEeRJEmtrMzDhfB4tXR7p
++d1F5kWJlTVdsYOpd1li28LpAu/YJrop85QP75TV/F8=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa.pub
new file mode 100644
index 0000000000..2ce1d597f4
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBALpx1AMh2SgvU5KTZLYlMMADqt46X/7KrXNiM/JbCG2vsUg+Pf1k24J5DThLH7Ex0o5ghmd4xrCi9hMrkcNe+RI1KU2B68mIrddGYmL9nLCwWf4loKmzVnTY+RCjBT7YRKOT4H9B1gF33kpRellmd+x7zVFSM1DbEdKjuPA9wseFAAAAFQD1R2qkuiiy67GoE6nErIaFH7cOIwAAAIEAqcswmM+/NrpXfaEF+mCUN1ylnO/YF2arqJp+CvAzSB9VhGnk7+hAX6vbQ+iQg6pDL8OOtky5fYqwlGdLJ4rTMiZuOOYx8grxgUxjMCmLCq3exH5VpXQnbcffZ+Zrqv6HtpqenLZgJxxcNG27oH/n4CLSoQOw0+AeJ8/NzIU8A68AAACBAJnnmAXlF9QW408bw9t0hNZDtacPDdiRENJaSVsb37+gTQvJPMfr6YpvDh2PO0ODg9Omwqddm/YpLAQAJ2tWzRMuxoI18ikcFWPveetaDZS/1eEPrTY3SMitmevrdCXGSpdEuvidbXNdW874MtmP5E4hW4vVe7wJkCEZ0hCL8AcN uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa
new file mode 100644
index 0000000000..3108bf688f
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa
@@ -0,0 +1,8 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,E2E108EACB898D6E55241106B2CDEC45
+
+hAmOR9aVIF2RNJet/vFVGgsx9Jh1/r02FJEPMWcdfSMgxJMfjKfCpKDqY72+Idzs
++WVk/Nr6s/TzNZwOH4WJV2ObZ1EG9yClHPPnAYsgoog88JhW5RRwEaa5X7oHFmVc
+zK1cixK5qc39SUiAGGn0PWcnx6qVVWd1KIWbqFzV5Hs=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa.pub
new file mode 100644
index 0000000000..8854f3be20
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_ecdsa.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGcNr/NDr9ueJUzkYyy/MdhK5CFFJKcTIYMod81dlhvhtynWgE7B/eW5hdKwuUm07FLu6lOw14s39VCQ/WVPox4= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa
new file mode 100644
index 0000000000..a74e47862c
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,B965F2149914DA8A54DD98275B94EC1C
+
+QRDCx1Taj7lBQRhLiPs8WLLsSSd8AKK/mzRUOwuf3E0VdrP20LLtcjn5xm9+zj91
+BQgPZshoB4eP2j2exJfTOOIkBdzjF3gYz/QyrSJ8fjdIA/MDzHcrv+HDpMes6gCP
+UTb4daKtBmae/fnRoUJtOJeyfQsJT4HqSRuGitvp+/47TdiD8N4GeBO/buMGEch0
+zw89/9w5bGrn7cPYo6mB5WWjxBcGM0ertCJ4x+uU+QRaohXnatgVpaydCYIzbhdS
+/thmlNEFizDtfSAoZQlGv30DXQWsvoYdDVZLtRPvYnNifTrcPBkfy/Etl9cgwmFT
++3aF8pQKppdGSeR5vaT5ardjyWEhoM1hVlucWzBjbhlWLeozjzMyT74lxw3hSiJW
+KJsoFHrFsfMB1G4cfI/gANbaFOc834w5GHxrz6PCHnWwDLulnhMTPDv1Pu6tg+8Z
++Dxu8pVIehnNUifj9OfPfY+ZNIbdroRavYr/YXB+16lyP1u4r1/rbIbBWTorebLw
+8Z6Yc1q4svnhiytWoyfzF3UQotCucZ13tHN3OaQ/u0SQnckYU1SdlGJqSvLW8N7S
+Yx5jW5mFbuOAQbvzs2CgeO/ooXq0KNVTYOkr3neN6PMk4WYcEsagmaqhMfIFVCBJ
+Fc4GFXbkSO4KKqRysGnnEt4sI9TJX9uIBVTtg38Q4rGisGMejD4f7eTjwqSMy4Xy
+ETF2+JQW5rJTnMstAqA1RvGxqpsZXNxx4+pLSeHTe0xIC+LuB92THXiD1a5xcQa3
+RS2s8CT4trOqKLLr22eRkV2XfEG+v1LEcp2uVyjBVM8CPKRP6w5KNnut19CA7AnQ
+WVVeNRGdVEtvId/kq2J91ZGTUWkmjgapTY+xhFNX67Sx0kwuosb2N1SvR6FidWIH
+d7xCHf/WO2aKuQgLv5fVvPqN4vPM/VMd8Vsx4y5IxUne0dkSYtk5iAeHwS+b3sti
+sMQAEauF8clt+jUXSGX4vSLIAc8JYSLxDRr2yy6q8dzdQ77pqqcFgg7Fa6tLKNQ6
+Op1eknVzPRDUDyaDr0mlE9pnSde1g++mEwEca4VORLljEQpf6vYDD/bDnyEfTXoA
+d1zt06X8OHAubtG8GcLilw9rMglWLthX+ymHqC1UWMOzgBpua6mffDb2I4qYWR29
+NPdEF1u0PKlzT7fqBE4tpiL4OY5lT/vcNDmf6cq59KKgSVYTzADTH/nQ6eLTkTgt
+pU4L60NtCIsn5r2xqwMI8R2f9gjqIwjn45BkqqJBq2vdTlyW6m0vdUdYnGHRkA0c
+fU1XOYUHUPi7Wa9MfcwyKYe55zMo7zspa0kIVOgkwfMVSnFr1eDM4jTNfaWFjIf0
+xEqnF6sOGO+KefL7nscJHfX7UipASXO3cWl26YvT5sJPCbwOZSNS3S+6+YCfimLn
+kNxWOUBDuHMWOsUDc26lh7YtBaImsLrM8eqdz8vRP2V9WH5RoHu0WT43HMZkWYZd
+FQeE5fNUPTAJb/PLOyh9LZQ4BibDE4ZbikEJfj1ut9+4C5aWt2NKmwSnDnJTCu3J
+qUeSlk0zIZXLbRsnK6TBQLVXc4cnZrVeb/Dfb2fropioh0f/WFusO5TKuP88XcSG
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa.pub
new file mode 100644
index 0000000000..469a6ceb7c
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDRAvJCrHYBhf5riHOyqBFfBCBZpKXf2zIc3iwbwY2xl/CHkEwZujBHe2KX1O1h8m7TpUNKMcdGNinsloZsDVKW2VEN7yT6oe3M2fyHLXDfd1KTyINF7LXqgtVq9+A8iLYdOBat7JBkY6c18pMvVU6yCWyr3m8+lnGskCW7swthANbwuW42PvyFdZf9/CkzNIVY6SsAF8wdXdf830wDIimZpN+ER8sLDGcu7dIaoypvs2KlCqwS6DE9kN7X2zXrxZA4thFsoJafgPDCaMOLRkCD2L8NBlRjXBE/U/sOb46oOfZTUy2wjo7pPwp9zekEO8l7OPZ9VgZ5bxXU+WTWox+z uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key
new file mode 100644
index 0000000000..99da9723ef
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQDLRBMHSgExTaMVjXrZwxdgmkpZgjQXGy/IUqBNw9MTq1AmndGH
+6Pkj3LzPiNsZArTwg3k0strcx+VtbQcJ1TrH7nOABdO5vTGAd9arPIkhts0LgwHA
+HcAHf+9iNr26uG8jtIc7+o7IpJyTzy2buVmfosYhKcwVbPHIhQp8KjAWfQIVAPd+
+7YzKkxRyBPZZ4K+G3QI3YJiPAoGBAKGRI74EwgLwRWMeVOJun7oWh0uVQFdi51sY
+3e9JC4/0hx0D0JHO2+b3opGPw/wrVXmGWZdmgpslsPZbta/oVR0OgAK4V2tTgn8p
+ijOIcEJmvpogtaJj40laTV+mpwRfQZWsfJPYTibZqtPVvvtu+6eVB1dTlNYngOAN
+mDHh6wpzAoGATv2nRnv0l878aIchG99kKFq5hX1rfLOYqeDq5uKXHu+Zf9IDPEQB
+Dmo73RA4pXWlD8Kf9mV3p5CSxg3tdSTelCt7h0cVoq3LmSU5fjA8eVNI7KsFi9QB
+dBnxEdMIBW2S8x4FfuWzgBUrQBzLqLR0CEEPmr1N9Dw63OiTgh9drJcCFHmpYuRa
+y/ynGrBb/mzt9QAGhNls
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..485997cfb9
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAMtEEwdKATFNoxWNetnDF2CaSlmCNBcbL8hSoE3D0xOrUCad0Yfo+SPcvM+I2xkCtPCDeTSy2tzH5W1tBwnVOsfuc4AF07m9MYB31qs8iSG2zQuDAcAdwAd/72I2vbq4byO0hzv6jsiknJPPLZu5WZ+ixiEpzBVs8ciFCnwqMBZ9AAAAFQD3fu2MypMUcgT2WeCvht0CN2CYjwAAAIEAoZEjvgTCAvBFYx5U4m6fuhaHS5VAV2LnWxjd70kLj/SHHQPQkc7b5veikY/D/CtVeYZZl2aCmyWw9lu1r+hVHQ6AArhXa1OCfymKM4hwQma+miC1omPjSVpNX6anBF9Blax8k9hOJtmq09W++277p5UHV1OU1ieA4A2YMeHrCnMAAACATv2nRnv0l878aIchG99kKFq5hX1rfLOYqeDq5uKXHu+Zf9IDPEQBDmo73RA4pXWlD8Kf9mV3p5CSxg3tdSTelCt7h0cVoq3LmSU5fjA8eVNI7KsFi9QBdBnxEdMIBW2S8x4FfuWzgBUrQBzLqLR0CEEPmr1N9Dw63OiTgh9drJc= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key
new file mode 100644
index 0000000000..75ba71da56
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIJeZs+jukpumHYLii4OXg5k9dN7u1aNLZovxbqFoEfgToAoGCCqGSM49
+AwEHoUQDQgAEBg0zAjfzxl0ccv2wnJHZvLXETa6bopctXD1V/FWcPoBL5dh42mOj
+I6ZgtrUnnjbhdxJyeG3BjntqhP5rLMMpeA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key.pub
new file mode 100644
index 0000000000..26e7285240
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ecdsa_key.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAYNMwI388ZdHHL9sJyR2by1xE2um6KXLVw9VfxVnD6AS+XYeNpjoyOmYLa1J5424XcScnhtwY57aoT+ayzDKXg= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key
new file mode 100644
index 0000000000..6044fc7725
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEArrzkBUAMYvAso4hR79vmNbxNbLYt7QocukbiCWOq29HQvqbS
+zj/OSE1Qg6C/aTbghvdpvxbaFt3aqdWroF0PhVVoEsJY76bI7RsobIe9zI/Z3dkQ
+RmW3IyjHvpVBwy84fKZ05A8Bd10kca1GfrQXi7LkFZ5FRjxarFzMojGVesWcZLag
+ThPG0QAjSw2sG+oql4YoIeagSdf3tzOOF+04vcpgqCRugsscw17lI1Rwq0nM3thU
+BgSWDRbjzmkWo9i5Wpc9ZKS1z4ANET+I2hGW7PEA6XAXJKC6nIWdVIie0GN02C/m
+i45NyTPn52I/TJKFAtIoZ8fbrHEepX7V/Dt7DQIDAQABAoIBAA1yJY2t3wYh+x1e
+WQe/ARjzc3XBEwmhdJJ07+HPFI+ztn9lMOWEDWiM4nwue2wqN97K3Q1CQefujGvz
+MDC32IDnEIoZAGT4jY+JPnQTgexiyV4D3Pe9zfjbo3sr2xKc6JjW6jm+WduIhExn
+C/yl+QXb7ycmtafw7v1CatC0Rg9bUtE8nMHKYVPazn30wlHdPl6TyTtEXoZCKMg2
+OTxrva80x5JboUqLZXX41VqVmqqoakEO3NOGlhrIzIOB866py8d+f6wilN/rUcGe
+MJwB8aTrYPxLkCYl9PGeMDMLARvhjMm53f6UQFDpL2rY0XpnaqrZqS9KrbJoTQDW
+lMj3OiECgYEA4FL3fba4FYGzf7T/m2xRSC5qrpr0gNn1mZJ5oUWGiHBwjAHC7EVW
+apcjskac7WrznIBJiV2ozzxQOgIymuYO9LX95G6b3nrkWhVXqUiyCkpdMSE/YVfA
+iMc3Z0QNz+I/cbEPUKZ7osKPZm4BRpUNvJwj4Vvt5jXwTZlPmVmpNHUCgYEAx2lx
+q6HO+Grba0Azg7wobnnZ8uZZvdZ+QpxgGhH1Rx9862KM+uVYrJ7xEUlNTYfBtdpq
+JXQnGlpzjGPeziZjJxv7AgWbJA80aXtTH/oE9E0KMmGRzE2bQNR8kUZHU5S2e0u+
+x1DUyvKqyKSRByxUp8wj5QZGPOH4MPvCHVZF+TkCgYAg52qQERYtaWn36Ie5t4iw
+qsZROD93CwGAdkDLDBSwvLV1g+igmYcUeXjt9HeeR5rWMOcYdBmH1FP8PkhH+kjl
+UjCcqjDI0IPgRtMl7JjY85F53GOclq+SII6a4huYi5o8xfj2HoVyGVHJd4dOYBy0
+tr54lvBtXSoTZ9KKLuGn5QKBgBUy4XGkfvMrsO3C4ncTrpyn+YJ3+HxU7BE6vICo
+/hE0iLwhOumFLhsTvn7e8wfV8cLaWERpB6smiHgZOdtie1HyCIobfHWl5CV+hcS1
+eIdcFURr2OsGKQYIUMHE3dpFyexrjfl0X1q/12YDEKPZk5pO+lXjh937C75xVR53
+SHMJAoGAS57QNKLFaWZunoMNuvvNAj7Z0q1JrFuLEwfnkG28g5+ov3wIBinPtlrr
+3HaK6sny0hHLPoRP+fa6BVRaQhDzeeKDu6PqNEkNnocWPi79lxfk531EJQHOgQgX
+yt7Ruq0TlBYs5wGrmtYXLKAGvcfyx9EoFs2Km1iNKqu6b/dbQXc=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..8e62458395
--- /dev/null
+++ b/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuvOQFQAxi8CyjiFHv2+Y1vE1sti3tChy6RuIJY6rb0dC+ptLOP85ITVCDoL9pNuCG92m/FtoW3dqp1augXQ+FVWgSwljvpsjtGyhsh73Mj9nd2RBGZbcjKMe+lUHDLzh8pnTkDwF3XSRxrUZ+tBeLsuQVnkVGPFqsXMyiMZV6xZxktqBOE8bRACNLDawb6iqXhigh5qBJ1/e3M44X7Ti9ymCoJG6CyxzDXuUjVHCrScze2FQGBJYNFuPOaRaj2Llalz1kpLXPgA0RP4jaEZbs8QDpcBckoLqchZ1UiJ7QY3TYL+aLjk3JM+fnYj9MkoUC0ihnx9uscR6lftX8O3sN uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 4ecec4e79d..a8436ab53b 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -86,7 +86,7 @@ groups() ->
directory_to_tar, binaries_to_tar, null_crypto_tar,
simple_crypto_tar_small, simple_crypto_tar_big,
read_tar, read_null_crypto_tar, read_crypto_tar,
- aes_cbc256_crypto_tar, aes_ctr_stream_crypto_tar
+ block_size_1_crypto_tar, block_size_16_crypto_tar
]},
{write_read_tests, [], [open_close_file, open_close_dir, read_file, read_dir,
@@ -1017,54 +1017,10 @@ read_crypto_tar(Config) ->
chk_tar(NameBins, Config, [{crypto,Cr}]).
%%--------------------------------------------------------------------
-aes_cbc256_crypto_tar(Config) ->
- ChPid2 = proplists:get_value(channel_pid2, Config),
- NameBins = lists:sort(
- [{"b1",<<"A binary">>},
- {"b2",list_to_binary(lists:duplicate(750000,"a"))},
- {"d1",fn("d1",Config)} % Dir
- ]),
- Key = <<"This is a 256 bit key. Boring...">>,
- Ivec0 = crypto:strong_rand_bytes(16),
- DataSize = 1024, % data_size rem 16 = 0 for aes_cbc
-
- Cinitw = fun() -> {ok, Ivec0, DataSize} end,
- Cinitr = fun() -> {ok, Ivec0, DataSize} end,
-
- Cenc = fun(PlainBin,Ivec) ->
- CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, PlainBin),
- {ok, CipherBin, crypto:next_iv(aes_cbc,CipherBin), DataSize}
- end,
- Cdec = fun(CipherBin,Ivec) ->
- PlainBin = crypto:block_decrypt(aes_cbc256, Key, Ivec, CipherBin),
- {ok, PlainBin, crypto:next_iv(aes_cbc,CipherBin), DataSize}
- end,
-
- Cendw = fun(PlainBin, _) when PlainBin == <<>> -> {ok, <<>>};
- (PlainBin, Ivec) ->
- CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec,
- pad(16,PlainBin)), %% Last chunk
- {ok, CipherBin}
- end,
+block_size_16_crypto_tar(Config) -> cipher_crypto_tar(aes_256_cbc, Config).
+block_size_1_crypto_tar(Config) -> cipher_crypto_tar(aes_256_ctr, Config).
- Cw = {Cinitw,Cenc,Cendw},
- TarFileName = proplists:get_value(tar_filename, Config),
- SftpTarFileName = w2l(Config, TarFileName),
-
- {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,Cw}]),
- [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins],
- ok = erl_tar:close(HandleWrite),
-
- Cr = {Cinitr,Cdec},
- chk_tar(NameBins, Config, [{crypto,Cr}]).
-
-
-pad(BlockSize, Bin) ->
- PadSize = (BlockSize - (size(Bin) rem BlockSize)) rem BlockSize,
- list_to_binary( lists:duplicate(PadSize,0) ).
-
-%%--------------------------------------------------------------------
-aes_ctr_stream_crypto_tar(Config) ->
+cipher_crypto_tar(Cipher, Config) ->
ChPid2 = proplists:get_value(channel_pid2, Config),
NameBins = lists:sort(
[{"b1",<<"A binary">>},
@@ -1074,22 +1030,25 @@ aes_ctr_stream_crypto_tar(Config) ->
Key = <<"This is a 256 bit key. Boring...">>,
Ivec0 = crypto:strong_rand_bytes(16),
- Cinitw = Cinitr = fun() -> {ok, crypto:stream_init(aes_ctr,Key,Ivec0)} end,
+ Cinitw = fun() -> {ok, crypto:crypto_init(Cipher,Key,Ivec0,[{encrypt,true},
+ {padding,zero}])} end,
+ Cinitr = fun() -> {ok, crypto:crypto_init(Cipher,Key,Ivec0,false)} end,
Cenc = fun(PlainBin,State) ->
- {NewState,CipherBin} = crypto:stream_encrypt(State, PlainBin),
- {ok, CipherBin, NewState}
+ CipherBin = crypto:crypto_update(State, PlainBin),
+ {ok, CipherBin, State}
end,
Cdec = fun(CipherBin,State) ->
- {NewState,PlainBin} = crypto:stream_decrypt(State, CipherBin),
- {ok, PlainBin, NewState}
+ PlainBin = crypto:crypto_update(State, CipherBin),
+ {ok, PlainBin, State}
end,
- Cendw = fun(PlainBin, _) when PlainBin == <<>> -> {ok, <<>>};
- (PlainBin, Ivec) ->
- CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec,
- pad(16,PlainBin)), %% Last chunk
- {ok, CipherBin}
+ Cendw = fun(PlainBin, State) ->
+ CipherBin1 = crypto:crypto_update(State, PlainBin),
+ Sz1 = size(CipherBin1),
+ CipherBin2 = crypto:crypto_final(State),
+ Sz2 = size(CipherBin2),
+ {ok, <<CipherBin1:Sz1/binary, CipherBin2:Sz2/binary>>}
end,
Cw = {Cinitw,Cenc,Cendw},
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 73bfc13eef..ba4f420b1f 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -115,18 +115,21 @@ sshc_subtree(Config) when is_list(Config) ->
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]),
- ?wait_match([{_, _,worker,[ssh_connection_handler]}],
- supervisor:which_children(sshc_sup)),
+ ?wait_match([{{client,ssh_system_sup, LocalIP, LocalPort, ?DEFAULT_PROFILE},
+ SysSup, supervisor,[ssh_system_sup]}],
+ supervisor:which_children(sshc_sup),
+ [SysSup, LocalIP, LocalPort]),
+ check_sshc_system_tree(SysSup, Pid1, LocalIP, LocalPort, Config),
{ok, Pid2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD}, {user_dir, UserDir}]),
- ?wait_match([{_,_,worker,[ssh_connection_handler]},
- {_,_,worker,[ssh_connection_handler]}],
+ ?wait_match([{_, _,supervisor,[ssh_system_sup]},
+ {_, _,supervisor,[ssh_system_sup]}],
supervisor:which_children(sshc_sup)),
ssh:close(Pid1),
- ?wait_match([{_,_,worker,[ssh_connection_handler]}],
+ ?wait_match([{_, _,supervisor,[ssh_system_sup]}],
supervisor:which_children(sshc_sup)),
ssh:close(Pid2),
?wait_match([], supervisor:which_children(sshc_sup)).
@@ -212,6 +215,7 @@ killed_acceptor_restarts(Config) ->
ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Make acceptor restart:
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [AccPid]),
exit(AccPid, kill),
?wait_match(undefined, process_info(AccPid)),
@@ -221,6 +225,8 @@ killed_acceptor_restarts(Config) ->
AccPid1,
500, 30),
+ ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []),
+
true = (AccPid1 =/= AccPid2),
%% Connect second client and check it is alive:
@@ -331,12 +337,14 @@ chk_empty_con_daemon(Daemon) ->
{{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
supervisor:which_children(Daemon),
[SubSysSup,AccSup]),
- ?wait_match([{{server,ssh_connection_sup, _,_},
+ ?wait_match([{_,_, supervisor,
+ [ssh_tcpip_forward_acceptor_sup]},
+ {{server,ssh_connection_sup, _,_},
ConnectionSup, supervisor,
[ssh_connection_sup]},
- {{server,ssh_server_channel_sup,_ ,_},
+ {{server,ssh_channel_sup,_ ,_},
ChannelSup,supervisor,
- [ssh_server_channel_sup]}],
+ [ssh_channel_sup]}],
supervisor:which_children(SubSysSup),
[ConnectionSup,ChannelSup]),
?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
@@ -362,12 +370,15 @@ check_sshd_system_tree(Daemon, Host, Port, Config) ->
supervisor:which_children(Daemon),
[SubSysSup,AccSup]),
- ?wait_match([{{server,ssh_connection_sup, _,_},
+ ?wait_match([{_,
+ _AcceptorSup, supervisor,
+ [ssh_tcpip_forward_acceptor_sup]},
+ {{server,ssh_connection_sup, _,_},
ConnectionSup, supervisor,
[ssh_connection_sup]},
- {{server,ssh_server_channel_sup,_ ,_},
+ {{server,ssh_channel_sup,_ ,_},
ChannelSup,supervisor,
- [ssh_server_channel_sup]}],
+ [ssh_channel_sup]}],
supervisor:which_children(SubSysSup),
[ConnectionSup,ChannelSup]),
@@ -379,12 +390,58 @@ check_sshd_system_tree(Daemon, Host, Port, Config) ->
?wait_match([], supervisor:which_children(ChannelSup)),
- ssh_sftp:start_channel(Client),
+ {ok,PidC} = ssh_sftp:start_channel(Client),
+
+ ?wait_match([{_, PidS,worker,[ssh_server_channel]}],
+ supervisor:which_children(ChannelSup),
+ [PidS]),
+ true = (PidS =/= PidC),
- ?wait_match([{_, _,worker,[ssh_server_channel]}],
- supervisor:which_children(ChannelSup)),
ssh:close(Client).
+
+check_sshc_system_tree(SysSup, Connection, LocalIP, LocalPort, _Config) ->
+ ?wait_match([{_,SubSysSup,supervisor,[ssh_subsystem_sup]}],
+ supervisor:which_children(SysSup),
+ [SubSysSup]),
+ ?wait_match([{_,FwdAccSup, supervisor,
+ [ssh_tcpip_forward_acceptor_sup]},
+ {{client,ssh_connection_sup, LocalIP, LocalPort},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{client,ssh_channel_sup, LocalIP, LocalPort},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup,FwdAccSup]),
+ ?wait_match([{_, Connection, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ ?wait_match([], supervisor:which_children(FwdAccSup)),
+
+ {ok,ChPid1} = ssh_sftp:start_channel(Connection),
+ ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup)),
+
+ {ok,ChPid2} = ssh_sftp:start_channel(Connection),
+ ?wait_match([{_,ChPidA,worker,[ssh_client_channel]},
+ {_,ChPidB,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup),
+ [ChPidA, ChPidB]),
+ true = (lists:sort([ChPidA, ChPidB]) == lists:sort([ChPid1, ChPid2])),
+
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid1]),
+ exit(ChPid1, kill),
+ ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup)),
+
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid2]),
+ exit(ChPid2, kill),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []).
+
+
+
acceptor_pid(DaemonPid) ->
Parent = self(),
Pid = spawn(fun() ->
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 83481e6c33..50f63c4096 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -432,7 +432,7 @@ setup_eddsa(Alg, DataDir, UserDir) ->
file:copy(filename:join(DataDir, HostPub), filename:join(System, HostPub)),
ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_eddsa_known_host(HostPub, DataDir, UserDir),
- setup_eddsa_auth_keys(IdPriv, DataDir, UserDir).
+ setup_eddsa_auth_keys(Alg, DataDir, UserDir).
clean_dsa(UserDir) ->
del_dirs(filename:join(UserDir, "system")),
@@ -572,9 +572,12 @@ setup_ecdsa_auth_keys(Size, Dir, UserDir) ->
PKey = #'ECPoint'{point = Q},
setup_auth_keys([{ {PKey,Param}, [{comment, "Test"}]}], UserDir).
-setup_eddsa_auth_keys(IdPriv, Dir, UserDir) ->
- {ok, Pem} = file:read_file(filename:join(Dir, IdPriv)),
- {ed_pri, Alg, Pub, _} = public_key:pem_entry_decode(hd(public_key:pem_decode(Pem))),
+setup_eddsa_auth_keys(Alg, Dir, UserDir) ->
+ SshAlg = case Alg of
+ ed25519 -> 'ssh-ed25519';
+ ed448 -> 'ssh-ed448'
+ end,
+ {ok, {ed_pri,Alg,Pub,_}} = ssh_file:user_key(SshAlg, [{user_dir,Dir}]),
setup_auth_keys([{{ed_pub,Alg,Pub}, [{comment, "Test"}]}], UserDir).
setup_auth_keys(Keys, Dir) ->
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 4f06bd3d65..dc03445b41 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -48,12 +48,20 @@ all() ->
end.
groups() ->
- [{erlang_client, [], [erlang_shell_client_openssh_server
+ [{erlang_client, [], [tunnel_in_erlclient_erlserver,
+ tunnel_out_erlclient_erlserver,
+ {group, tunnel_distro_server},
+ erlang_shell_client_openssh_server
]},
- {erlang_server, [], [erlang_server_openssh_client_renegotiate,
+ {tunnel_distro_server, [], [tunnel_in_erlclient_openssh_server,
+ tunnel_out_erlclient_openssh_server]},
+ {erlang_server, [], [{group, tunnel_distro_client},
+ erlang_server_openssh_client_renegotiate,
exec_with_io_in_sshc,
exec_direct_with_io_in_sshc
- ]}
+ ]},
+ {tunnel_distro_client, [], [tunnel_in_non_erlclient_erlserver,
+ tunnel_out_non_erlclient_erlserver]}
].
init_per_suite(Config) ->
@@ -78,6 +86,14 @@ init_per_group(erlang_server, Config) ->
ssh_test_lib:setup_dsa_known_host(DataDir, UserDir),
ssh_test_lib:setup_rsa_known_host(DataDir, UserDir),
Config;
+init_per_group(G, Config) when G==tunnel_distro_server ;
+ G==tunnel_distro_client ->
+ case no_forwarding() of
+ true ->
+ {skip, "port forwarding disabled in external ssh"};
+ false ->
+ Config
+ end;
init_per_group(erlang_client, Config) ->
CommonAlgs = ssh_test_lib:algo_intersection(
ssh:default_algorithms(),
@@ -247,8 +263,184 @@ erlang_server_openssh_client_renegotiate(Config) ->
end.
%%--------------------------------------------------------------------
+tunnel_out_non_erlclient_erlserver(Config) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_out, true},
+ {system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ {ToSock, _ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ ListenPort = 2345,
+
+ Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
+ [" -o UserKnownHostsFile=", KnownHosts,
+ " -o StrictHostKeyChecking=no",
+ " -R ",integer_to_list(ListenPort),":127.0.0.1:",integer_to_list(ToPort)]),
+ spawn(fun() ->
+ ct:log(["ssh command:\r\n ",Cmd],[]),
+ R = os:cmd(Cmd),
+ ct:log(["ssh returned:\r\n",R],[])
+ end),
+
+ ct:sleep(1000),
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
+tunnel_in_non_erlclient_erlserver(Config) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ UserDir = proplists:get_value(priv_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_in, true},
+ {system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ {ToSock, _ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ ListenPort = 2345,
+
+ Cmd =
+ ssh_test_lib:open_sshc_cmd(Host, Port,
+ [" -o UserKnownHostsFile=", KnownHosts,
+ " -o StrictHostKeyChecking=no",
+ " -L ",integer_to_list(ListenPort),":127.0.0.1:",integer_to_list(ToPort)]),
+ spawn(fun() ->
+ ct:log(["ssh command:\r\n ",Cmd],[]),
+ R = os:cmd(Cmd),
+ ct:log(["ssh returned:\r\n",R],[])
+ end),
+ ct:sleep(1000),
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
+tunnel_in_erlclient_erlserver(Config) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ UserDir = proplists:get_value(priv_dir, Config),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_in, true},
+ {system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"foo", "bar"}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user,"foo"},{password,"bar"},
+ {user_interaction, false}]),
+ {ToSock, ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ {ok,ListenPort} = ssh:tcpip_tunnel_to_server(C, ListenHost,0, ToHost,ToPort, 2000),
+
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
+tunnel_in_erlclient_openssh_server(_Config) ->
+ C = ssh_test_lib:connect(loopback, 22, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ToSock, ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ {ok,ListenPort} = ssh:tcpip_tunnel_to_server(C, ListenHost,0, ToHost,ToPort, 5000),
+
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
+tunnel_out_erlclient_erlserver(Config) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ UserDir = proplists:get_value(priv_dir, Config),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_out, true},
+ {system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"foo", "bar"}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user,"foo"},{password,"bar"},
+ {user_interaction, false}]),
+ {ToSock, ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ {ok,ListenPort} = ssh:tcpip_tunnel_from_server(C, ListenHost,0, ToHost,ToPort, 5000),
+
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
+tunnel_out_erlclient_openssh_server(_Config) ->
+ C = ssh_test_lib:connect(loopback, 22, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ToSock, ToHost, ToPort} = tunneling_listner(),
+
+ ListenHost = {127,0,0,1},
+ {ok,ListenPort} = ssh:tcpip_tunnel_from_server(C, ListenHost,0, ToHost,ToPort, 5000),
+
+ test_tunneling(ToSock, ListenHost, ListenPort).
+
+%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
+tunneling_listner() ->
+ {ok,LSock} = gen_tcp:listen(0, [{active,false}]),
+ {ok, {LHost,LPort}} = inet:sockname(LSock),
+ {LSock, LHost, LPort}.
+
+test_tunneling(ListenSocket, Host, Port) ->
+ {ok,Client1} = gen_tcp:connect(Host, Port, [{active,false}]),
+ {ok,Server1} = gen_tcp:accept(ListenSocket),
+ {ok,Client2} = gen_tcp:connect(Host, Port, [{active,false}]),
+ {ok,Server2} = gen_tcp:accept(ListenSocket),
+ send_rcv("Hi!", Client1, Server1),
+ send_rcv("Happy to see you!", Server1, Client1),
+ send_rcv("Hi, you to!", Client2, Server2),
+ send_rcv("Happy to see you also!", Server2, Client2),
+ close_and_check(Client1, Server1),
+ send_rcv("Still there?", Client2, Server2),
+ send_rcv("Yes!", Server2, Client2),
+ close_and_check(Server2, Client2).
+
+
+tcp_connect(Host, Port, Options) ->
+ tcp_connect(Host, Port, Options, 0).
+tcp_connect(Host, Port, Options, Timeout) ->
+ ct:log("Try connect to ~p:~p ~p Timeout=~p", [Host, Port, Options, Timeout]),
+ case gen_tcp:connect(Host, Port, Options, Timeout) of
+ {error,econnrefused} ->
+ timer:sleep( 2*max(Timeout,250)),
+ tcp_connect(Host, Port, Options, 2*max(Timeout,250));
+ {error,timeout} ->
+ timer:sleep( 2*max(Timeout,250)),
+ tcp_connect(Host, Port, Options, 2*max(Timeout,250));
+ {ok,S} ->
+ ct:log("connect to ~p:~p ~p Timeout=~p -> ~p", [Host, Port, Options, Timeout, S]),
+ {ok,S}
+ end.
+
+close_and_check(OneSide, OtherSide) ->
+ ok = gen_tcp:close(OneSide),
+ ok = chk_closed(OtherSide).
+
+
+chk_closed(Sock) ->
+ chk_closed(Sock, 0).
+chk_closed(Sock, Timeout) ->
+ case gen_tcp:recv(Sock, 0, Timeout) of
+ {error,closed} ->
+ ok;
+ {error,timeout} ->
+ chk_closed(Sock, 2*max(Timeout,250));
+ Other ->
+ Other
+ end.
+
+send_rcv(Txt, From, To) ->
+ ct:log("Send ~p from ~p to ~p", [Txt, From, To]),
+ ok = gen_tcp:send(From, Txt),
+ ct:log("Recv ~p on ~p", [Txt, To]),
+ {ok,Txt} = gen_tcp:recv(To, 0, 5000),
+ ok.
+
+%%--------------------------------------------------------------------
receive_data(Data, Conn) ->
receive
Info when is_binary(Info) ->
@@ -340,3 +532,41 @@ comment(AtomList) ->
ct:comment(
string:join(lists:map(fun erlang:atom_to_list/1, AtomList),
", ")).
+
+%%%----------------------------------------------------------------
+no_forwarding() ->
+ %%% Check if the ssh of the OS has tunneling enabled
+ Cmnd = "ssh -R 0:localhost:4567 localhost exit",
+ FailRegExp =
+ "Port forwarding is disabled"
+ "|remote port forwarding failed"
+ "|Bad.*specification"
+ "|Bad forwarding port",
+ {Result,TheText} =
+ try
+ Parent = self(),
+ Pid = spawn(fun() ->
+ Parent ! {self(), os:cmd(Cmnd)}
+ end),
+ receive
+ {Pid, Txt} ->
+ case re:run(Txt, FailRegExp) of
+ {match,_} -> {true,Txt};
+ _ -> {false,Txt}
+ end
+ after 10000 ->
+ ct:log("*** TIMEOUT ***",[]),
+ {true,""}
+ end
+ catch C:E:S ->
+ ct:log("Exception in no_forwarding():~n~p:~p~n~p~n", [C,E,S]),
+ {true, ""}
+ end,
+ ct:log("---- os:cmd(~p) returned:~n~s~n"
+ "~n"
+ "---- Checking with regexp~n"
+ "~p~n"
+ "~n"
+ "---- The function no_forwarding() returns ~p",
+ [Cmnd,TheText, FailRegExp, Result]),
+ Result.
diff --git a/lib/ssl/Makefile b/lib/ssl/Makefile
index bd43794a36..526560288f 100644
--- a/lib/ssl/Makefile
+++ b/lib/ssl/Makefile
@@ -38,4 +38,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto runtime_tools inets public_key
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 064131944c..cb9ff02aa0 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -30,11 +30,6 @@ VSN=$(SSL_VSN)
APPLICATION=ssl
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = refman.xml
@@ -55,86 +50,9 @@ BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
-GIF_FILES =
-
-PS_FILES =
-
-XML_FLAGS += -defs cite cite.defs -booksty otpA4
-
-# ----------------------------------------------------
-
-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_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-SPECS_FLAGS = -I../../../public_key/include -I../../../public_key/src -I../../..
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: html pdf man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECS_FILES)
- rm -f errs core *~
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
+NO_CHUNKS = ssl_crl_cache_api.xml ssl_session_cache_api.xml
# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 27b8a3f457..0126177c76 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -647,7 +647,7 @@ fun(Chain::[public_key:der_encoded()]) ->
<desc><p>TLS protocol versions supported by started clients and servers.
This option overrides the application environment option
<c>protocol_version</c> and <c>dtls_protocol_version</c>. If the environment option is not set, it defaults
- to all versions, except SSL-3.0, supported by the SSL application.
+ to all versions, supported by the SSL application.
See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p>
</desc>
</datatype>
@@ -736,7 +736,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<datatype>
<name name="beast_mitigation"/>
- <desc><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST
+ <desc><p>Affects TLS-1.0 connections only. Used to change the BEAST
mitigation strategy to interoperate with legacy software.
Defaults to <c>one_n_minus_one</c>.</p>
@@ -746,7 +746,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p><c>disabled</c> - Disable BEAST mitigation.</p>
- <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL-3.0 or TLS-1.0
+ <warning><p>Using <c>{beast_mitigation, disabled}</c> makes TLS-1.0
vulnerable to the BEAST attack.</p></warning>
</desc>
</datatype>
@@ -916,10 +916,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
<warning><p>Note this option is not needed in normal TLS usage and should not be used
to implement new clients. But legacy clients that retries connections in the following manner</p>
- <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1', 'sslv3']}, {fallback, true}]) </c></p>
- <p><c> ssl:connect(Host, Port, [...{versions, ['sslv3']}, {fallback, true}]) </c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1']}])</c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1']}, {fallback, true}])</c></p>
+ <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1']}, {fallback, true}]) </c></p>
<p>may use it to avoid undesired TLS version downgrade. Note that TLS_FALLBACK_SCSV must also
be supported by the server for the prevention to work.
@@ -1633,8 +1632,6 @@ fun(srp, Username :: string(), UserState :: term()) ->
extra key material. It either takes user-generated values for
<c>Secret</c> and <c>Seed</c> or atoms directing it to use a specific
value from the session security parameters.</p>
- <p>Can only be used with TLS/DTLS connections; <c>{error, undefined}</c>
- is returned for SSLv3 connections.</p>
</desc>
</func>
@@ -1749,7 +1746,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name since="OTP R14B" name="start" arity="0" />
- <name since="OTP R14B">start(Type) -> ok | {error, Reason}</name>
+ <name since="OTP R14B" name="start" arity="1" />
<fsummary>Starts the SSL application.</fsummary>
<desc>
<p>Starts the SSL application. Default type
@@ -1859,7 +1856,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<tag><c>available</c></tag>
<item>All SSL/TLS versions supported by the SSL application.
- TLS 1.2 requires sufficient support from the Crypto
+ TLS 1.2 and TLS 1.3 requires sufficient support from the Crypto
application.</item>
<tag><c>available_dtls</c></tag>
diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml
index 553ee79a39..05c571a632 100644
--- a/lib/ssl/doc/src/standards_compliance.xml
+++ b/lib/ssl/doc/src/standards_compliance.xml
@@ -91,6 +91,7 @@
<section>
<title>SSL 3.0</title>
+ <p>For security reasons SSL-3.0 is no longer supported at all. (OTP 23) </p>
<p>For security reasons SSL-3.0 is no longer supported by default, but can be configured. (OTP 19)</p>
</section>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index e961f05b37..6a8de38a1e 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -83,7 +83,6 @@ MODULES= \
ssl_session_cache \
ssl_srp_primes \
ssl_sup \
- ssl_v3 \
tls_bloom_filter \
tls_connection \
tls_connection_sup \
@@ -142,6 +141,13 @@ DEPDIR=$(ERL_TOP)/lib/ssl/src/deps
DEP_FILE=$(DEPDIR)/ssl.d
$(shell mkdir -p $(dir $(DEP_FILE)) >/dev/null)
+ifeq ($(TARGET), win32)
+ # Native path without C: ignore driveletter case
+ ERL_TOP_NATIVE = $(shell w32_path.sh -m $(ERL_TOP) | sed "s@[a-zA-Z]:@:@")
+else
+ ERL_TOP_NATIVE = $(ERL_TOP)
+endif
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -151,7 +157,6 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
-pz $(ERL_TOP)/lib/public_key/ebin \
$(EXTRA_ERLC_FLAGS)
-
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -159,8 +164,9 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
$(DEP_FILE): $(ERL_FILES)
+ @echo SED $(TARGET) $(ERL_TOP_NATIVE)
$(gen_verbose)erlc -M $(ERL_FILES) \
- | sed "s@$(ERL_TOP)@../../..@g" \
+ | sed "s@[a-zA-Z]\?$(ERL_TOP_NATIVE)@../../..@g" \
| sed "s/\.$(EMULATOR)/\.$$\(EMULATOR\)/" \
| sed 's@^dtls_@$$(EBIN)/dtls_@' \
| sed 's@^inet_@$$(EBIN)/inet_@' \
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index cad31b23ee..6649c4c341 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -120,7 +120,7 @@ cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor},
CookieData = [address_to_bin(Address, Port),
<<?BYTE(Major), ?BYTE(Minor)>>,
Random, SessionId, CipherSuites, CompressionMethods],
- crypto:hmac(sha, Key, CookieData).
+ crypto:mac(hmac, sha, Key, CookieData).
%%--------------------------------------------------------------------
-spec hello_verify_request(binary(), ssl_record:ssl_version()) -> #hello_verify_request{}.
%%
diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl
index 11ad659ff5..13960a19d8 100644
--- a/lib/ssl/src/dtls_packet_demux.erl
+++ b/lib/ssl/src/dtls_packet_demux.erl
@@ -101,9 +101,9 @@ getstat(PacketSocket, Opts) ->
%%% gen_server callbacks
%%%===================================================================
-init([Port, {TransportModule, _,_,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) ->
+init([Port0, {TransportModule, _,_,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) ->
try
- {ok, Socket} = TransportModule:open(Port, InetOptions),
+ {ok, Socket} = TransportModule:open(Port0, InetOptions),
InternalActiveN = case application:get_env(ssl, internal_active_n) of
{ok, N} when is_integer(N) ->
N;
@@ -111,6 +111,14 @@ init([Port, {TransportModule, _,_,_,_} = TransportInfo, EmOpts, InetOptions, DTL
?INTERNAL_ACTIVE_N
end,
+ Port = case Port0 of
+ 0 ->
+ {ok, P} = inet:port(Socket),
+ P;
+ _ ->
+ Port0
+ end,
+
{ok, #state{active_n = InternalActiveN,
port = Port,
first = true,
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 8d9b92361b..bc51dd63fe 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -759,7 +759,7 @@ nodelay() ->
get_ssl_options(Type) ->
try ets:lookup(ssl_dist_opts, Type) of
[{Type, Opts}] ->
- [{erl_dist, true} | Opts];
+ [{erl_dist, true}, {versions, ['tlsv1.2']} | Opts];
_ ->
get_ssl_dist_arguments(Type)
catch
@@ -770,9 +770,9 @@ get_ssl_options(Type) ->
get_ssl_dist_arguments(Type) ->
case init:get_argument(ssl_dist_opt) of
{ok, Args} ->
- [{erl_dist, true} | ssl_options(Type, lists:append(Args))];
+ [{erl_dist, true}, {versions, ['tlsv1.2']} | ssl_options(Type, lists:append(Args))];
_ ->
- [{erl_dist, true}]
+ [{erl_dist, true}, {versions, ['tlsv1.2']}]
end.
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index c45e6bcf9a..e35da9206e 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -11,7 +11,6 @@
tls_record_1_3,
tls_socket,
tls_v1,
- ssl_v3,
tls_connection_sup,
tls_sender,
tls_server_sup,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index ee1664a82f..d842eafa62 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -100,9 +100,12 @@
suite_to_openssl_str/1,
str_to_suite/1]).
--deprecated({ssl_accept, 1, eventually}).
--deprecated({ssl_accept, 2, eventually}).
--deprecated({ssl_accept, 3, eventually}).
+-deprecated({ssl_accept, '_', "use ssl_handshake/1,2,3 instead"}).
+
+-removed([{negotiated_next_protocol,1,
+ "use ssl:negotiated_protocol/1 instead"}]).
+-removed([{connection_info,1,
+ "use ssl:connection_information/[1,2] instead"}]).
-export_type([socket/0,
sslsocket/0,
@@ -153,7 +156,7 @@
-type protocol_version() :: tls_version() | dtls_version(). % exported
-type tls_version() :: 'tlsv1.2' | 'tlsv1.3' | tls_legacy_version().
-type dtls_version() :: 'dtlsv1.2' | dtls_legacy_version().
--type tls_legacy_version() :: tlsv1 | 'tlsv1.1' | sslv3.
+-type tls_legacy_version() :: tlsv1 | 'tlsv1.1' .
-type dtls_legacy_version() :: 'dtlsv1'.
-type verify_type() :: verify_none | verify_peer.
-type cipher() :: aes_128_cbc |
@@ -1015,8 +1018,7 @@ cipher_suites(all) ->
cipher_suites(Base, Version) when Version == 'tlsv1.3';
Version == 'tlsv1.2';
Version == 'tlsv1.1';
- Version == tlsv1;
- Version == sslv3 ->
+ Version == tlsv1 ->
cipher_suites(Base, tls_record:protocol_version(Version));
cipher_suites(Base, Version) when Version == 'dtlsv1.2';
Version == 'dtlsv1'->
@@ -1034,8 +1036,7 @@ cipher_suites(Base, Version) ->
%%--------------------------------------------------------------------
cipher_suites(Base, Version, StringType) when Version == 'tlsv1.2';
Version == 'tlsv1.1';
- Version == tlsv1;
- Version == sslv3 ->
+ Version == tlsv1 ->
cipher_suites(Base, tls_record:protocol_version(Version), StringType);
cipher_suites(Base, Version, StringType) when Version == 'dtlsv1.2';
Version == 'dtlsv1'->
@@ -1121,8 +1122,6 @@ eccs() ->
%% Description: returns the curves supported for a given version of
%% ssl/tls.
%%--------------------------------------------------------------------
-eccs(sslv3) ->
- [];
eccs('dtlsv1') ->
eccs('tlsv1.1');
eccs('dtlsv1.2') ->
@@ -1532,21 +1531,13 @@ handle_options(Opts0, Role, Host) ->
%% Ensure all options are evaluated at startup
SslOpts1 = add_missing_options(SslOpts0, ?RULES),
- SslOpts = #{protocol := Protocol,
- versions := Versions}
+ SslOpts = #{protocol := Protocol}
= process_options(SslOpts1,
#{},
#{role => Role,
host => Host,
rules => ?RULES}),
-
- case Versions of
- [{3, 0}] ->
- reject_alpn_next_prot_options(SslOpts0);
- _ ->
- ok
- end,
-
+
%% Handle special options
{Sock, Emulated} = emulated_options(Protocol, SockOpts),
ConnetionCb = connection_cb(Protocol),
@@ -2290,8 +2281,7 @@ validate_versions([], Versions) ->
validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3';
Version == 'tlsv1.2';
Version == 'tlsv1.1';
- Version == tlsv1;
- Version == sslv3 ->
+ Version == tlsv1 ->
tls_validate_versions(Rest, Versions);
validate_versions([Version | Rest], Versions) when Version == 'dtlsv1';
Version == 'dtlsv1.2'->
@@ -2304,8 +2294,7 @@ tls_validate_versions([], Versions) ->
tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3';
Version == 'tlsv1.2';
Version == 'tlsv1.1';
- Version == tlsv1;
- Version == sslv3 ->
+ Version == tlsv1 ->
tls_validate_versions(Rest, Versions);
tls_validate_versions([Ver| _], Versions) ->
throw({error, {options, {Ver, {versions, Versions}}}}).
@@ -2580,25 +2569,6 @@ server_name_indication_default(Host) when is_list(Host) ->
server_name_indication_default(_) ->
undefined.
-
-reject_alpn_next_prot_options({Opts,_,_}) ->
- AlpnNextOpts = [alpn_advertised_protocols,
- alpn_preferred_protocols,
- next_protocols_advertised,
- next_protocol_selector,
- client_preferred_next_protocols],
- reject_alpn_next_prot_options(AlpnNextOpts, Opts).
-
-reject_alpn_next_prot_options([], _) ->
- ok;
-reject_alpn_next_prot_options([Opt| AlpnNextOpts], Opts) ->
- case lists:keyfind(Opt, 1, Opts) of
- {Opt, Value} ->
- throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}});
- false ->
- reject_alpn_next_prot_options(AlpnNextOpts, Opts)
- end.
-
add_filter(undefined, Filters) ->
Filters;
add_filter(Filter, Filters) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index c97884ec08..e9eb78203c 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -108,7 +108,7 @@ security_parameters_1_3(SecParams, CipherSuite) ->
%% Description: Initializes the #cipher_state according to BCA
%%-------------------------------------------------------------------
cipher_init(?RC4, IV, Key) ->
- State = crypto:stream_init(rc4, Key),
+ State = {stream_init,rc4,Key,IV},
#cipher_state{iv = IV, key = Key, state = State};
cipher_init(Type, IV, Key) when Type == ?AES_GCM;
Type == ?AES_CCM ->
@@ -137,38 +137,53 @@ nonce_seed(Seed, CipherState) ->
%%-------------------------------------------------------------------
cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
{iolist_to_binary(Fragment), CipherState};
-cipher(?RC4, CipherState = #cipher_state{state = State0}, Mac, Fragment, _Version) ->
+cipher(CipherEnum, CipherState = #cipher_state{state = {stream_init,rc4,Key,_IV}}, Mac, Fragment, Version) ->
+ State = crypto:crypto_init(rc4, Key, true),
+ cipher(CipherEnum, CipherState#cipher_state{state = State}, Mac, Fragment, Version);
+cipher(?RC4, CipherState = #cipher_state{state = State}, Mac, Fragment, _Version) ->
GenStreamCipherList = [Fragment, Mac],
- {State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList),
- {iolist_to_binary(T), CipherState#cipher_state{state = State1}};
+ T = crypto:crypto_update(State, GenStreamCipherList),
+ {iolist_to_binary(T), CipherState};
cipher(?DES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) ->
- crypto:block_encrypt(des_cbc, Key, IV, T)
+ crypto:crypto_one_time(des_cbc, Key, IV, T, true)
end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
cipher(?'3DES', CipherState, Mac, Fragment, Version) ->
- block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
- crypto:block_encrypt(des3_cbc, [K1, K2, K3], IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+ block_cipher(fun(Key, IV, T) ->
+ crypto:crypto_one_time(des_ede3_cbc, Key, IV, T, true)
+ end, block_size(des_ede3_cbc), CipherState, Mac, Fragment, Version);
cipher(?AES_CBC, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
- crypto:block_encrypt(aes_cbc128, Key, IV, T);
+ crypto:crypto_one_time(aes_128_cbc, Key, IV, T, true);
(Key, IV, T) when byte_size(Key) =:= 32 ->
- crypto:block_encrypt(aes_cbc256, Key, IV, T)
+ crypto:crypto_one_time(aes_256_cbc, Key, IV, T, true)
end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version).
aead_encrypt(Type, Key, Nonce, Fragment, AdditionalData, TagLen) ->
- crypto:block_encrypt(aead_type(Type), Key, Nonce, {AdditionalData, Fragment, TagLen}).
+ crypto:crypto_one_time_aead(aead_type(Type,size(Key)), Key, Nonce, Fragment, AdditionalData, TagLen, true).
aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AdditionalData) ->
- crypto:block_decrypt(aead_type(Type), Key, Nonce, {AdditionalData, CipherText, CipherTag}).
-
-aead_type(?AES_GCM) ->
- aes_gcm;
-aead_type(?AES_CCM) ->
- aes_ccm;
-aead_type(?AES_CCM_8) ->
- aes_ccm;
-aead_type(?CHACHA20_POLY1305) ->
+ crypto:crypto_one_time_aead(aead_type(Type,size(Key)), Key, Nonce, CipherText, AdditionalData, CipherTag, false).
+
+aead_type(?AES_GCM, 16) ->
+ aes_128_gcm;
+aead_type(?AES_GCM, 24) ->
+ aes_192_gcm;
+aead_type(?AES_GCM, 32) ->
+ aes_256_gcm;
+aead_type(?AES_CCM, 16) ->
+ aes_128_ccm;
+aead_type(?AES_CCM, 24) ->
+ aes_192_ccm;
+aead_type(?AES_CCM, 32) ->
+ aes_256_ccm;
+aead_type(?AES_CCM_8, 16) ->
+ aes_128_ccm;
+aead_type(?AES_CCM_8, 24) ->
+ aes_192_ccm;
+aead_type(?AES_CCM_8, 32) ->
+ aes_256_ccm;
+aead_type(?CHACHA20_POLY1305, _) ->
chacha20_poly1305.
build_cipher_block(BlockSz, Mac, Fragment) ->
@@ -211,12 +226,16 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV, state = IV_Cache0} = CS
%%-------------------------------------------------------------------
decipher(?NULL, _HashSz, CipherState, Fragment, _, _) ->
{Fragment, <<>>, CipherState};
-decipher(?RC4, HashSz, CipherState = #cipher_state{state = State0}, Fragment, _, _) ->
- try crypto:stream_decrypt(State0, Fragment) of
- {State, Text} ->
+decipher(CipherEnum, HashSz, CipherState = #cipher_state{state = {stream_init,rc4,Key,_IV}},
+ Fragment, Version, PaddingCheck) ->
+ State = crypto:crypto_init(rc4, Key, false),
+ decipher(CipherEnum, HashSz, CipherState#cipher_state{state = State}, Fragment, Version, PaddingCheck);
+decipher(?RC4, HashSz, CipherState = #cipher_state{state = State}, Fragment, _, _) ->
+ try crypto:crypto_update(State, Fragment) of
+ Text ->
GSC = generic_stream_cipher_from_bin(Text, HashSz),
#generic_stream_cipher{content = Content, mac = Mac} = GSC,
- {Content, Mac, CipherState#cipher_state{state = State}}
+ {Content, Mac, CipherState}
catch
_:_ ->
%% This is a DECRYPTION_FAILED but
@@ -229,17 +248,17 @@ decipher(?RC4, HashSz, CipherState = #cipher_state{state = State0}, Fragment, _,
decipher(?DES, HashSz, CipherState, Fragment, Version, PaddingCheck) ->
block_decipher(fun(Key, IV, T) ->
- crypto:block_decrypt(des_cbc, Key, IV, T)
+ crypto:crypto_one_time(des_cbc, Key, IV, T, false)
end, CipherState, HashSz, Fragment, Version, PaddingCheck);
decipher(?'3DES', HashSz, CipherState, Fragment, Version, PaddingCheck) ->
- block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
- crypto:block_decrypt(des3_cbc, [K1, K2, K3], IV, T)
+ block_decipher(fun(Key, IV, T) ->
+ crypto:crypto_one_time(des_ede3_cbc, Key, IV, T, false)
end, CipherState, HashSz, Fragment, Version, PaddingCheck);
decipher(?AES_CBC, HashSz, CipherState, Fragment, Version, PaddingCheck) ->
block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
- crypto:block_decrypt(aes_cbc128, Key, IV, T);
+ crypto:crypto_one_time(aes_128_cbc, Key, IV, T, false);
(Key, IV, T) when byte_size(Key) =:= 32 ->
- crypto:block_decrypt(aes_cbc256, Key, IV, T)
+ crypto:crypto_one_time(aes_256_cbc, Key, IV, T, false)
end, CipherState, HashSz, Fragment, Version, PaddingCheck).
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
@@ -277,8 +296,6 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%%
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
-suites({3, 0}) ->
- ssl_v3:suites();
suites({3, Minor}) ->
tls_v1:suites(Minor);
suites({_, Minor}) ->
@@ -289,9 +306,9 @@ all_suites({3, _} = Version) ->
++ chacha_suites(Version)
++ psk_suites(Version)
++ srp_suites(Version)
- ++ rc4_suites(Version)
+ ++ rsa_suites(Version)
++ des_suites(Version)
- ++ rsa_suites(Version);
+ ++ rc4_suites(Version);
all_suites(Version) ->
dtls_v1:all_suites(Version).
@@ -426,8 +443,6 @@ psk_suites_anon(0) ->
%% Description: Returns a list of the SRP cipher suites, only supported
%% if explicitly set by user.
%%--------------------------------------------------------------------
-srp_suites({3,0}) ->
- [];
srp_suites(_) ->
[?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
@@ -442,8 +457,6 @@ srp_suites(_) ->
%% Description: Returns a list of the SRP anonymous cipher suites, only supported
%% if explicitly set by user.
%%--------------------------------------------------------------------
-srp_suites_anon({3,0}) ->
- [];
srp_suites_anon(_) ->
[?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
@@ -645,24 +658,12 @@ is_acceptable_cipher(null, _Algos) ->
true;
is_acceptable_cipher(rc4_128, Algos) ->
proplists:get_bool(rc4, Algos);
-is_acceptable_cipher(des_cbc, Algos) ->
- proplists:get_bool(des_cbc, Algos);
is_acceptable_cipher('3des_ede_cbc', Algos) ->
- proplists:get_bool(des_ede3, Algos);
-is_acceptable_cipher(aes_128_cbc, Algos) ->
- proplists:get_bool(aes_cbc128, Algos);
-is_acceptable_cipher(aes_256_cbc, Algos) ->
- proplists:get_bool(aes_cbc256, Algos);
-is_acceptable_cipher(Cipher, Algos)
- when Cipher == aes_128_gcm;
- Cipher == aes_256_gcm ->
- proplists:get_bool(aes_gcm, Algos);
-is_acceptable_cipher(Cipher, Algos)
- when Cipher == aes_128_ccm;
- Cipher == aes_256_ccm;
- Cipher == aes_128_ccm_8;
- Cipher == aes_256_ccm_8 ->
- proplists:get_bool(aes_ccm, Algos);
+ proplists:get_bool(des_ede3_cbc, Algos);
+is_acceptable_cipher(aes_128_ccm_8, Algos) ->
+ proplists:get_bool(aes_128_ccm, Algos);
+is_acceptable_cipher(aes_256_ccm_8, Algos) ->
+ proplists:get_bool(aes_256_ccm, Algos);
is_acceptable_cipher(Cipher, Algos) ->
proplists:get_bool(Cipher, Algos).
@@ -734,8 +735,6 @@ hash_size(sha512) ->
mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
_Length, _Fragment) ->
<<>>;
-mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- ssl_v3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
when N =:= 1; N =:= 2; N =:= 3; N =:= 4 ->
tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
@@ -863,6 +862,7 @@ iv_size(Cipher) ->
block_size(Cipher).
block_size(Cipher) when Cipher == des_cbc;
+ Cipher == des_ede3_cbc;
Cipher == '3des_ede_cbc' ->
8;
block_size(Cipher) when Cipher == aes_128_cbc;
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 8e53be72ed..5f5505c02c 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -725,7 +725,7 @@ encode_extensions([#psk_key_exchange_modes{ke_modes = KEModes0} | Rest], Acc) ->
encode_extensions([#pre_shared_key_client_hello{
offered_psks = #offered_psks{
identities = Identities0,
- binders = Binders0} = _PSKs} | Rest], Acc) ->
+ binders = Binders0}} | Rest], Acc) ->
Identities = encode_psk_identities(Identities0),
Binders = encode_psk_binders(Binders0),
Len = byte_size(Identities) + byte_size(Binders),
@@ -948,8 +948,6 @@ cipher_suites(Suites, true) ->
%%
%% Description: use the TLS PRF to generate key material
%%--------------------------------------------------------------------
-prf({3,0}, _, _, _, _, _) ->
- {error, undefined};
prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
{ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
@@ -1797,6 +1795,10 @@ bad_key(#'DSAPrivateKey'{}) ->
bad_key(#'RSAPrivateKey'{}) ->
unacceptable_rsa_key;
bad_key(#'ECPrivateKey'{}) ->
+ unacceptable_ecdsa_key;
+bad_key(#{algorithm := rsa}) ->
+ unacceptable_rsa_key;
+bad_key(#{algorithm := ecdsa}) ->
unacceptable_ecdsa_key.
crl_check(_, false, _,_,_, _, _, _) ->
@@ -1910,13 +1912,8 @@ encrypted_premaster_secret(Secret, RSAPublicKey) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, premaster_encryption_failed))
end.
-calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
- ssl_v3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
tls_v1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
-
-calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
- ssl_v3:finished(Role, MasterSecret, lists:reverse(Handshake));
calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
@@ -1946,20 +1943,10 @@ master_secret(Version, MasterSecret,
{MasterSecret,
ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState,
ServerCipherState, Role)}.
-
-setup_keys({3,0}, _PrfAlgo, MasterSecret,
- ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_v3:setup_keys(MasterSecret, ServerRandom,
- ClientRandom, HashSize, KML, EKML, IVS);
-
setup_keys({3,N}, PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
tls_v1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
KML, IVS).
-
-calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
- ssl_v3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
-
calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
tls_v1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
@@ -3210,9 +3197,6 @@ handle_renegotiation_info(_, _RecordCB, server, #renegotiation_info{renegotiated
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation))
end
end;
-handle_renegotiation_info({3,0}, _RecordCB, client, undefined, ConnectionStates, true, _SecureRenegotation, _) ->
- {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
-
handle_renegotiation_info(_, RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
@@ -3299,8 +3283,6 @@ empty_extensions({3,4}, hello_retry_request) ->
key_share => undefined,
pre_shared_key => undefined
};
-empty_extensions({3,0}, _) ->
- empty_extensions();
empty_extensions(_, server_hello) ->
#{renegotiation_info => undefined,
alpn => undefined,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index d661de323c..565db9b1a9 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -75,10 +75,10 @@
%% Keep as interop with legacy software but do not support as default
%% tlsv1.0 and tlsv1.1 is now also considered legacy
%% tlsv1.3 is under development (experimental).
--define(ALL_AVAILABLE_VERSIONS, ['tlsv1.3', 'tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(ALL_AVAILABLE_VERSIONS, ['tlsv1.3', 'tlsv1.2', 'tlsv1.1', tlsv1]).
-define(ALL_AVAILABLE_DATAGRAM_VERSIONS, ['dtlsv1.2', dtlsv1]).
%% Defines the default versions when not specified by an ssl option.
--define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2']).
+-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.3', 'tlsv1.2']).
-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1']).
%% Versions allowed in TLSCiphertext.version (TLS 1.2 and prior) and
@@ -86,7 +86,7 @@
%% TLS 1.3 sets TLSCiphertext.legacy_record_version to 0x0303 for all records
%% generated other than an than an initial ClientHello, where it MAY also be 0x0301.
%% Thus, the allowed range is limited to 0x0300 - 0x0303.
--define(ALL_TLS_RECORD_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(ALL_TLS_RECORD_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]).
-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2']).
-define(MIN_DATAGRAM_SUPPORTED_VERSIONS, [dtlsv1]).
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
deleted file mode 100644
index 4eab60b440..0000000000
--- a/lib/ssl/src/ssl_v3.erl
+++ /dev/null
@@ -1,200 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Handles sslv3 encryption.
-%%----------------------------------------------------------------------
-
--module(ssl_v3).
-
--include("ssl_cipher.hrl").
--include("ssl_internal.hrl").
--include("ssl_record.hrl"). % MD5 and SHA
-
--export([master_secret/3, finished/3, certificate_verify/3,
- mac_hash/6, setup_keys/7,
- suites/0]).
--compile(inline).
-
-%%====================================================================
-%% Internal application API
-%%====================================================================
-
--spec master_secret(binary(), binary(), binary()) -> binary().
-
-master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
- %% draft-ietf-tls-ssl-version3-00 - 6.2.2
- %% key_block =
- %% MD5(master_secret + SHA(`A' + master_secret +
- %% ServerHello.random +
- %% ClientHello.random)) +
- %% MD5(master_secret + SHA(`BB' + master_secret +
- %% ServerHello.random +
- %% ClientHello.random)) +
- %% MD5(master_secret + SHA(`CCC' + master_secret +
- %% ServerHello.random +
- %% ClientHello.random)) + [...];
- Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
- Block.
-
--spec finished(client | server, binary(), [binary()]) -> binary().
-
-finished(Role, MasterSecret, Handshake) ->
- %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
- %% struct {
- %% opaque md5_hash[16];
- %% opaque sha_hash[20];
- %% } Finished;
- %%
- %% md5_hash MD5(master_secret + pad2 +
- %% MD5(handshake_messages + Sender +
- %% master_secret + pad1));
- %% sha_hash SHA(master_secret + pad2 +
- %% SHA(handshake_messages + Sender +
- %% master_secret + pad1));
- Sender = get_sender(Role),
- MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake),
- SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake),
- <<MD5/binary, SHA/binary>>.
-
--spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary().
-
-certificate_verify(md5sha, MasterSecret, Handshake) ->
- %% md5_hash
- %% MD5(master_secret + pad_2 +
- %% MD5(handshake_messages + master_secret + pad_1));
- %% sha_hash
- %% SHA(master_secret + pad_2 +
- %% SHA(handshake_messages + master_secret + pad_1));
-
- MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake),
- SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake),
- <<MD5/binary, SHA/binary>>;
-
-certificate_verify(sha, MasterSecret, Handshake) ->
- %% sha_hash
- %% SHA(master_secret + pad_2 +
- %% SHA(handshake_messages + master_secret + pad_1));
-
- handshake_hash(?SHA, MasterSecret, undefined, Handshake).
-
--spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
-
-mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) ->
- %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1
- %% hash(MAC_write_secret + pad_2 +
- %% hash(MAC_write_secret + pad_1 + seq_num +
- %% SSLCompressed.type + SSLCompressed.length +
- %% SSLCompressed.fragment));
- Mac = mac_hash(Method, Mac_write_secret,
- [<<?UINT64(Seq_num), ?BYTE(Type),
- ?UINT16(Length)>>, Fragment]),
- Mac.
-
--spec setup_keys(binary(), binary(), binary(),
- integer(), integer(), term(), integer()) ->
- {binary(), binary(), binary(),
- binary(), binary(), binary()}.
-
-setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
- KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom,
- 2*(HS+KML+IVS)),
- %% draft-ietf-tls-ssl-version3-00 - 6.2.2
- %% The key_block is partitioned as follows.
- %% client_write_MAC_secret[CipherSpec.hash_size]
- %% server_write_MAC_secret[CipherSpec.hash_size]
- %% client_write_key[CipherSpec.key_material]
- %% server_write_key[CipherSpec.key_material]
- %% client_write_IV[CipherSpec.IV_size] /* non-export ciphers */
- %% server_write_IV[CipherSpec.IV_size] /* non-export ciphers */
- <<ClientWriteMacSecret:HS/binary, ServerWriteMacSecret:HS/binary,
- ClientWriteKey:KML/binary, ServerWriteKey:KML/binary,
- ClientIV:IVS/binary, ServerIV:IVS/binary>> = KeyBlock,
- {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV}.
-
--spec suites() -> [ssl_cipher_format:cipher_suite()].
-
-suites() ->
- [
- ?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
- ].
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-
-hash(?MD5, Data) ->
- crypto:hash(md5, Data);
-hash(?SHA, Data) ->
- crypto:hash(sha, Data).
-
-%%pad_1(?NULL) ->
-%% "";
-pad_1(?MD5) ->
- <<"666666666666666666666666666666666666666666666666">>;
-pad_1(?SHA) ->
- <<"6666666666666666666666666666666666666666">>.
-%%pad_2(?NULL) ->
-%% "";
-pad_2(?MD5) ->
- <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"
- "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>;
-pad_2(?SHA) ->
- <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"
- "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>.
-
-mac_hash(?NULL, _Secret, _Data) ->
- <<>>;
-mac_hash(Method, Secret, Data) ->
- InnerHash = hash(Method, [Secret, pad_1(Method), Data]),
- hash(Method, [Secret, pad_2(Method), InnerHash]).
-
-handshake_hash(Method, MasterSecret, undefined, Handshake) ->
- InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]),
- hash(Method, [MasterSecret, pad_2(Method), InnerHash]);
-handshake_hash(Method, MasterSecret, Sender, Handshake) ->
- InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]),
- hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
-
-get_sender(client) -> "CLNT";
-get_sender(server) -> "SRVR".
-
-generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) ->
- gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom],
- WantedLength, 0, $A, 1, []).
-
-gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len ->
- <<Block:Wanted/binary, _/binary>> = list_to_binary(lists:reverse(Acc)),
- Block;
-gen(Secret, All, Wanted, Len, C, N, Acc) ->
- Prefix = lists:duplicate(N, C),
- SHA = crypto:hash(sha, [Prefix, All]),
- MD5 = crypto:hash(md5, [Secret, SHA]),
- gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]).
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 8c5d652035..e1244aa5b7 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -488,7 +488,7 @@ certificate_entry(DER) ->
%% 0101010101010101010101010101010101010101010101010101010101010101
sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) ->
Content = build_content(Context, THash),
- try public_key:sign(Content, HashAlgo, PrivateKey) of
+ try digitally_signed(Content, HashAlgo, PrivateKey) of
Signature ->
{ok, Signature}
catch
@@ -500,10 +500,10 @@ sign(THash, Context, HashAlgo, PrivateKey) ->
%% The length of the Salt MUST be equal to the length of the output
%% of the digest algorithm: rsa_pss_saltlen = -1
- try public_key:sign(Content, HashAlgo, PrivateKey,
- [{rsa_padding, rsa_pkcs1_pss_padding},
- {rsa_pss_saltlen, -1},
- {rsa_mgf1_md, HashAlgo}]) of
+ try digitally_signed(Content, HashAlgo, PrivateKey,
+ [{rsa_padding, rsa_pkcs1_pss_padding},
+ {rsa_pss_saltlen, -1},
+ {rsa_mgf1_md, HashAlgo}]) of
Signature ->
{ok, Signature}
catch
@@ -2320,3 +2320,30 @@ process_user_tickets([H|T], Acc, N) ->
%% (see Section 4.6.1), modulo 2^32.
obfuscate_ticket_age(TicketAge, AgeAdd) ->
(TicketAge + AgeAdd) rem round(math:pow(2,32)).
+
+
+digitally_signed(Msg, HashAlgo, PrivateKey) ->
+ digitally_signed(Msg, HashAlgo, PrivateKey, []).
+
+digitally_signed(Msg, HashAlgo, PrivateKey, Options) ->
+ try do_digitally_signed(Msg, HashAlgo, PrivateKey, Options) of
+ Signature ->
+ Signature
+ catch
+ error:_ ->
+ {error, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, bad_key(PrivateKey))}
+ end.
+
+do_digitally_signed(Msg, HashAlgo, #{algorithm := Alg} = Engine, Options) ->
+ crypto:sign(Alg, HashAlgo, Msg, maps:remove(algorithm, Engine), Options);
+do_digitally_signed(Msg, HashAlgo, Key, Options) ->
+ public_key:sign(Msg, HashAlgo, Key, Options).
+
+bad_key(#'RSAPrivateKey'{}) ->
+ unacceptable_rsa_key;
+bad_key(#'ECPrivateKey'{}) ->
+ unacceptable_ecdsa_key;
+bad_key(#{algorithm := rsa}) ->
+ unacceptable_rsa_key;
+bad_key(#{algorithm := ecdsa}) ->
+ unacceptable_ecdsa_key.
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 538c3e44fb..9d0349806a 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -353,28 +353,13 @@ supported_protocol_versions() ->
end.
supported_protocol_versions([]) ->
- Vsns = case sufficient_tlsv1_2_crypto_support() of
- true ->
- ?ALL_SUPPORTED_VERSIONS;
- false ->
- ?MIN_SUPPORTED_VERSIONS
- end,
+ Vsns = sufficient_support(?ALL_SUPPORTED_VERSIONS),
application:set_env(ssl, protocol_version, Vsns),
Vsns;
supported_protocol_versions([_|_] = Vsns) ->
- case sufficient_tlsv1_2_crypto_support() of
- true ->
- Vsns;
- false ->
- case Vsns -- ['tlsv1.2'] of
- [] ->
- ?MIN_SUPPORTED_VERSIONS;
- NewVsns ->
- NewVsns
- end
- end.
-
+ sufficient_support(Vsns).
+
-spec is_acceptable_version(tls_version()) -> boolean().
is_acceptable_version({N,_})
when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
@@ -474,7 +459,7 @@ validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length);
true ->
%% Not ?KNOWN_RECORD_TYPE(Type)
- ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+ ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, {unsupported_record_type, Type})
end.
validate_tls_record_version(_Versions, Q, _SslOpts, Acc, Type, undefined, _Length) ->
@@ -487,7 +472,7 @@ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
true ->
validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {unsupported_version, Version})
end;
{3, 4} when Version =:= {3, 3} ->
validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
@@ -495,7 +480,7 @@ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
%% Exact version match
validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length);
_ ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {unsupported_version, Version})
end.
validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) ->
@@ -668,11 +653,23 @@ highest_protocol_version() ->
lowest_protocol_version() ->
lowest_protocol_version(supported_protocol_versions()).
-sufficient_tlsv1_2_crypto_support() ->
- CryptoSupport = crypto:supports(),
- proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-
max_len([{3,4}|_])->
?TLS13_MAX_CIPHER_TEXT_LENGTH;
max_len(_) ->
?MAX_CIPHER_TEXT_LENGTH.
+
+sufficient_support(Versions) ->
+ CryptoSupport = crypto:supports(),
+ case proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)) of
+ false ->
+ Versions -- ['tlsv1.3', 'tlsv1.2'];
+ true ->
+ case proplists:get_bool(aes_gcm, proplists:get_value(ciphers, CryptoSupport)) of
+ false ->
+ Versions -- ['tlsv1.3'];
+ true ->
+ Versions
+ end
+ end.
+
+
diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl
index 951d4302e9..5e7ef9d6a9 100644
--- a/lib/ssl/src/tls_sender.erl
+++ b/lib/ssl/src/tls_sender.erl
@@ -276,6 +276,7 @@ connection({call, From}, dist_get_tls_socket,
{next_state, ?FUNCTION_NAME, StateData, [{reply, From, {ok, TLSSocket}}]};
connection({call, From}, {dist_handshake_complete, _Node, DHandle},
#data{static = #static{connection_pid = Pid} = Static} = StateData) ->
+ false = erlang:dist_ctrl_set_opt(DHandle, get_size, true),
ok = erlang:dist_ctrl_input_handler(DHandle, Pid),
ok = ssl_connection:dist_handshake_complete(Pid, DHandle),
%% From now on we execute on normal priority
@@ -287,7 +288,7 @@ connection({call, From}, {dist_handshake_complete, _Node, DHandle},
[];
Data ->
[{next_event, internal,
- {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}]
+ {application_packets,{self(),undefined},Data}}]
end]};
connection(internal, {application_packets, From, Data}, StateData) ->
send_application_data(Data, From, ?FUNCTION_NAME, StateData);
@@ -310,7 +311,7 @@ connection(info, dist_data, #data{static = #static{dist_handle = DHandle}}) ->
[];
Data ->
[{next_event, internal,
- {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}]
+ {application_packets,{self(),undefined},Data}}]
end};
connection(info, tick, StateData) ->
consume_ticks(),
@@ -623,15 +624,14 @@ dist_data(DHandle) ->
%% since the emulator will always deliver a Data
%% smaller than 4 GB, and the distribution will
%% therefore always have to use {packet,4}
- Data when is_binary(Data) ->
- Len = byte_size(Data),
- [[<<Len:32>>,Data]|dist_data(DHandle)];
- [BA,BB] = Data ->
- Len = byte_size(BA) + byte_size(BB),
- [[<<Len:32>>|Data]|dist_data(DHandle)];
- Data when is_list(Data) ->
- Len = iolist_size(Data),
- [[<<Len:32>>|Data]|dist_data(DHandle)]
+ {Len, Data} ->
+ %% Data is of type iovec(); lets keep it
+ %% as an iovec()...
+ Packet = [<<Len:32>> | Data],
+ case dist_data(DHandle) of
+ [] -> Packet;
+ More -> Packet ++ More
+ end
end.
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 381793c65d..1371ac7637 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -684,7 +684,7 @@ hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, M, N, Prev, Acc) ->
hmac_hash(?NULL, _, _) ->
<<>>;
hmac_hash(Alg, Key, Value) ->
- crypto:hmac(mac_algo(Alg), Key, Value).
+ crypto:mac(hmac, mac_algo(Alg), Key, Value).
mac_algo(Alg) when is_atom(Alg) ->
Alg;
diff --git a/lib/ssl/test/inet_crypto_dist.erl b/lib/ssl/test/inet_crypto_dist.erl
index 63c19d9438..608ebcbe89 100644
--- a/lib/ssl/test/inet_crypto_dist.erl
+++ b/lib/ssl/test/inet_crypto_dist.erl
@@ -60,7 +60,9 @@
iv = 12,
key = 16,
tag_len = 16,
- rekey_interval = 262144
+ rekey_count = 262144,
+ rekey_time = 7200000, % 2 hours
+ rekey_msg
}).
params(Socket) ->
@@ -78,10 +80,10 @@ params(Socket) ->
%%% params = brainpoolP384t1,
params = brainpoolP256t1,
public,
- private}).
-
--define(KEY_PAIR_LIFE_TIME, 3600000). % 1 hour
--define(KEY_PAIR_LIFE_COUNT, 256). % Number of connection setups
+ private,
+ life_time = 3600000, % 1 hour
+ life_count = 256 % Number of connection setups
+ }).
%% -------------------------------------------------------------------------
@@ -102,12 +104,18 @@ start_key_pair_server() ->
key_pair_server() ->
key_pair_server(undefined, undefined, undefined).
%%
-key_pair_server(KeyPair) ->
- key_pair_server(
- KeyPair,
- erlang:start_timer(?KEY_PAIR_LIFE_TIME, self(), discard),
- ?KEY_PAIR_LIFE_COUNT).
-%%
+key_pair_server(
+ #key_pair{life_time = LifeTime, life_count = LifeCount} = KeyPair) ->
+ %% Presuming: 1 < LifeCount
+ Timer =
+ case LifeCount of
+ 1 ->
+ undefined;
+ _ ->
+ erlang:start_timer(LifeTime, self(), discard)
+ end,
+ key_pair_server(KeyPair, Timer, LifeCount - 1).
+%%
key_pair_server(_KeyPair, Timer, 0) ->
cancel_timer(Timer),
key_pair_server();
@@ -138,9 +146,20 @@ generate_key_pair() ->
crypto:generate_key(Type, Params),
#key_pair{public = Public, private = Private}.
+
cancel_timer(undefined) ->
ok;
cancel_timer(Timer) ->
+ erlang_cancel_timer(Timer).
+
+start_rekey_timer(Time) ->
+ Timer = erlang:start_timer(Time, self(), rekey_time),
+ {timeout, Timer, rekey_time}.
+
+cancel_rekey_timer({timeout, Timer, rekey_time}) ->
+ erlang_cancel_timer(Timer).
+
+erlang_cancel_timer(Timer) ->
case erlang:cancel_timer(Timer) of
false ->
receive
@@ -862,11 +881,12 @@ reply({Ref, Pid}, Msg) ->
%%
%% The start message contains the two encrypted random numbers
%% this time encrypted with the session keys for verification
-%% by the other side, plus the rekey interval. The rekey interval
+%% by the other side, plus the rekey count. The rekey count
%% is just there to get an early check for if the other side's
-%% maximum rekey interal is acceptable, it is just an embryo
+%% maximum rekey count is acceptable, it is just an embryo
%% of some better check. Any side may rekey earlier but if the
-%% rekey interval is exceeded the connection fails.
+%% rekey count is exceeded the connection fails. Rekey is also
+%% triggered by a timer.
%%
%% Subsequent encrypted messages has the sequence number and the length
%% of the message as AAD data, and an incrementing IV. These messages
@@ -950,7 +970,7 @@ init_msg(
key = KeyLen,
iv = IVLen,
tag_len = TagLen,
- rekey_interval = RekeyInterval} = Params,
+ rekey_count = RekeyCount} = Params,
Secret, KeyPair, R2A, R3A, Msg) ->
%%
RLen = KeyLen + IVLen,
@@ -974,7 +994,7 @@ init_msg(
rekey_key = PubKeyB,
key = Key2A, iv = IV2A},
%%
- StartCleartext = [R2B, R3B, <<RekeyInterval:32>>],
+ StartCleartext = [R2B, R3B, <<RekeyCount:32>>],
StartMsgLen = TagLen + iolist_size(StartCleartext),
StartAAD = <<StartMsgLen:32>>,
{StartCiphertext, StartTag} =
@@ -1001,7 +1021,7 @@ start_msg(
key = Key2B,
iv = IV2B,
tag_len = TagLen,
- rekey_interval = RekeyIntervalA} = RecvParams, R2A, R3A, Msg) ->
+ rekey_count = RekeyCountA} = RecvParams, R2A, R3A, Msg) ->
%%
case Msg of
<<Tag:TagLen/binary, Ciphertext/binary>> ->
@@ -1014,10 +1034,10 @@ start_msg(
crypto:block_decrypt(
AeadCipher, Key2B, IV2B, {AAD, Ciphertext, Tag})
of
- <<R2A:RLen/binary, R3A:RLen/binary, RekeyIntervalB:32>>
- when RekeyIntervalA =< (RekeyIntervalB bsl 2),
- RekeyIntervalB =< (RekeyIntervalA bsl 2) ->
- RecvParams#params{rekey_interval = RekeyIntervalB}
+ <<R2A:RLen/binary, R3A:RLen/binary, RekeyCountB:32>>
+ when RekeyCountA =< (RekeyCountB bsl 2),
+ RekeyCountB =< (RekeyCountA bsl 2) ->
+ RecvParams#params{rekey_count = RekeyCountB}
end
end.
@@ -1071,7 +1091,10 @@ handshake(
process_flag(priority, normal),
erlang:dist_ctrl_get_data_notification(DistHandle),
output_handler(
- SendParams#params{dist_handle = DistHandle}, SendSeq);
+ SendParams#params{
+ dist_handle = DistHandle,
+ rekey_msg = start_rekey_timer(SendParams#params.rekey_time)},
+ SendSeq);
%%
{?MODULE, From, {send, Data}} ->
case
@@ -1137,9 +1160,12 @@ output_handler(Params, Seq) ->
output_handler_data(Params, Seq);
dist_tick ->
output_handler_tick(Params, Seq);
- Other ->
+ _ when Msg =:= Params#params.rekey_msg ->
+ Params_1 = output_handler_rekey(Params, Seq),
+ output_handler(Params_1, 0);
+ _ ->
%% Ignore
- _ = trace(Other),
+ _ = trace(Msg),
output_handler(Params, Seq)
end
end.
@@ -1152,9 +1178,12 @@ output_handler_data(Params, Seq) ->
output_handler_data(Params, Seq);
dist_tick ->
output_handler_data(Params, Seq);
- Other ->
+ _ when Msg =:= Params#params.rekey_msg ->
+ Params_1 = output_handler_rekey(Params, Seq),
+ output_handler_data(Params_1, 0);
+ _ ->
%% Ignore
- _ = trace(Other),
+ _ = trace(Msg),
output_handler_data(Params, Seq)
end
after 0 ->
@@ -1173,9 +1202,12 @@ output_handler_tick(Params, Seq) ->
output_handler_data(Params, Seq);
dist_tick ->
output_handler_tick(Params, Seq);
- Other ->
+ _ when Msg =:= Params#params.rekey_msg ->
+ Params_1 = output_handler_rekey(Params, Seq),
+ output_handler(Params_1, 0);
+ _ ->
%% Ignore
- _ = trace(Other),
+ _ = trace(Msg),
output_handler_tick(Params, Seq)
end
after 0 ->
@@ -1192,22 +1224,31 @@ output_handler_tick(Params, Seq) ->
end
end.
+output_handler_rekey(Params, Seq) ->
+ case encrypt_and_send_rekey_chunk(Params, Seq) of
+ #params{} = Params_1 ->
+ Params_1;
+ SendError ->
+ _ = trace(SendError),
+ death_row()
+ end.
+
output_handler_send(Params, Seq, {_, Size, _} = Q) ->
if
?CHUNK_SIZE < Size ->
- output_handler_send(Params, Seq, Q, ?CHUNK_SIZE);
+ output_handler_deq_send(Params, Seq, Q, ?CHUNK_SIZE);
true ->
case get_data(Params#params.dist_handle, Q) of
{_, 0, _} ->
{Params, Seq};
{_, Size, _} = Q_1 -> % Got no more
- output_handler_send(Params, Seq, Q_1, Size);
+ output_handler_deq_send(Params, Seq, Q_1, Size);
Q_1 ->
output_handler_send(Params, Seq, Q_1)
end
end.
-output_handler_send(Params, Seq, Q, Size) ->
+output_handler_deq_send(Params, Seq, Q, Size) ->
{Cleartext, Q_1} = deq_iovec(Size, Q),
case
encrypt_and_send_chunk(Params, Seq, [?DATA_CHUNK, Cleartext])
@@ -1358,13 +1399,30 @@ deliver_data(DistHandle, Front, Size, Rear, Bin) ->
encrypt_and_send_chunk(
#params{
+ socket = Socket, rekey_count = Seq, rekey_msg = RekeyMsg} = Params,
+ Seq, Cleartext) ->
+ %%
+ cancel_rekey_timer(RekeyMsg),
+ case encrypt_and_send_rekey_chunk(Params, Seq) of
+ #params{} = Params_1 ->
+ Result =
+ gen_tcp:send(Socket, encrypt_chunk(Params, 0, Cleartext)),
+ {Params_1, 1, Result};
+ SendError ->
+ {Params, Seq + 1, SendError}
+ end;
+encrypt_and_send_chunk(#params{socket = Socket} = Params, Seq, Cleartext) ->
+ Result = gen_tcp:send(Socket, encrypt_chunk(Params, Seq, Cleartext)),
+ {Params, Seq + 1, Result}.
+
+encrypt_and_send_rekey_chunk(
+ #params{
socket = Socket,
- rekey_interval = Seq,
rekey_key = PubKeyB,
key = Key,
iv = {IVSalt, IVNo},
hmac_algorithm = HmacAlgo} = Params,
- Seq, Cleartext) ->
+ Seq) ->
%%
KeyLen = byte_size(Key),
IVSaltLen = byte_size(IVSalt),
@@ -1380,17 +1438,13 @@ encrypt_and_send_chunk(
hmac_key_iv(
HmacAlgo, SharedSecret, [Key, IVSalt, IV],
KeyLen, IVSaltLen + 6),
- Params_1 = Params#params{key = Key_1, iv = {IVSalt_1, IVNo_1}},
- Result =
- gen_tcp:send(Socket, encrypt_chunk(Params_1, 0, Cleartext)),
- {Params_1, 1, Result};
+ Params#params{
+ key = Key_1, iv = {IVSalt_1, IVNo_1},
+ rekey_msg = start_rekey_timer(Params#params.rekey_time)};
SendError ->
- {Params, Seq + 1, SendError}
- end;
-encrypt_and_send_chunk(#params{socket = Socket} = Params, Seq, Cleartext) ->
- Result = gen_tcp:send(Socket, encrypt_chunk(Params, Seq, Cleartext)),
- {Params, Seq + 1, Result}.
-
+ SendError
+ end.
+
encrypt_chunk(
#params{
aead_cipher = AeadCipher,
@@ -1431,7 +1485,7 @@ decrypt_chunk(
block_decrypt(
#params{
rekey_key = #key_pair{public = PubKeyA} = KeyPair,
- rekey_interval = RekeyInterval} = Params,
+ rekey_count = RekeyCount} = Params,
Seq, AeadCipher, Key, IV, Data) ->
%%
case crypto:block_decrypt(AeadCipher, Key, IV, Data) of
@@ -1453,7 +1507,7 @@ block_decrypt(
end;
Chunk when is_binary(Chunk) ->
case Seq of
- RekeyInterval ->
+ RekeyCount ->
%% This was one chunk too many without rekeying
error;
_ ->
diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl
index 0e3e3daee3..299a97b76d 100644
--- a/lib/ssl/test/openssl_alpn_SUITE.erl
+++ b/lib/ssl/test/openssl_alpn_SUITE.erl
@@ -35,7 +35,6 @@
%%--------------------------------------------------------------------
all() ->
- %% Note: ALPN not supported in sslv3
case ssl_test_lib:openssl_sane_dtls_alpn() of
true ->
[
diff --git a/lib/ssl/test/openssl_cipher_suite_SUITE.erl b/lib/ssl/test/openssl_cipher_suite_SUITE.erl
index 5c10defd2f..6daf1a56b4 100644
--- a/lib/ssl/test/openssl_cipher_suite_SUITE.erl
+++ b/lib/ssl/test/openssl_cipher_suite_SUITE.erl
@@ -42,7 +42,6 @@ all_protocol_groups() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -56,7 +55,6 @@ groups() ->
{'tlsv1.2', [], kex()},
{'tlsv1.1', [], kex()},
{'tlsv1', [], kex()},
- {'sslv3', [], kex()},
{'dtlsv1.2', [], dtls_kex()},
{'dtlsv1', [], dtls_kex()},
{dhe_rsa, [],[dhe_rsa_3des_ede_cbc,
diff --git a/lib/ssl/test/openssl_client_cert_SUITE.erl b/lib/ssl/test/openssl_client_cert_SUITE.erl
index dce40b5638..a094b8ab39 100644
--- a/lib/ssl/test/openssl_client_cert_SUITE.erl
+++ b/lib/ssl/test/openssl_client_cert_SUITE.erl
@@ -41,7 +41,6 @@ groups() ->
{'tlsv1.2', [], pre_tls_1_3_protocol_groups()},
{'tlsv1.1', [], pre_tls_1_3_protocol_groups()},
{'tlsv1', [], pre_tls_1_3_protocol_groups()},
- {'sslv3', [], ssl_protocol_groups()},
{'dtlsv1.2', [], pre_tls_1_3_protocol_groups()},
{'dtlsv1', [], pre_tls_1_3_protocol_groups()},
{rsa, [], all_version_tests()},
@@ -57,7 +56,6 @@ protocol_groups() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
diff --git a/lib/ssl/test/openssl_reject_SUITE.erl b/lib/ssl/test/openssl_reject_SUITE.erl
index deefd11823..a637035f3c 100644
--- a/lib/ssl/test/openssl_reject_SUITE.erl
+++ b/lib/ssl/test/openssl_reject_SUITE.erl
@@ -36,20 +36,20 @@
all() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}].
+ {group, 'tlsv1'}
+ ].
groups() ->
[{'tlsv1.2', [], all_versions_tests()},
{'tlsv1.1', [], all_versions_tests()},
- {'tlsv1', [], all_versions_tests()},
- {'sslv3', [], all_versions_tests()}
+ {'tlsv1', [], all_versions_tests()}
].
-
+
all_versions_tests() ->
- [
+ [
erlang_client_bad_openssl_server,
- ssl2_erlang_server_openssl_client
+ ssl2_erlang_server_openssl_client,
+ ssl3_erlang_server_openssl_client
].
init_per_suite(Config0) ->
@@ -110,6 +110,14 @@ special_init(ssl2_erlang_server_openssl_client, Config) ->
false ->
{skip, "sslv2 not supported by openssl"}
end;
+special_init(ssl3_erlang_server_openssl_client, Config) ->
+ case ssl_test_lib:supports_ssl_tls_version(sslv3) of
+ true ->
+ Config;
+ false ->
+ {skip, "sslv3 not supported by openssl"}
+ end;
+
special_init(_, Config) ->
Config.
@@ -193,9 +201,34 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
ssl_test_lib:consume_port_exit(OpenSslPort),
- ssl_test_lib:check_server_alert(Server, unexpected_message),
+ ssl_test_lib:check_server_alert(Server, bad_record_mac),
process_flag(trap_exit, false).
+%%--------------------------------------------------------------------
+ssl3_erlang_server_openssl_client() ->
+ [{doc,"Test that ssl v3 clients are rejected"}].
+
+ssl3_erlang_server_openssl_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Exe = "openssl",
+ Args = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ "-ssl3", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
+ ssl_test_lib:consume_port_exit(OpenSslPort),
+ ssl_test_lib:check_server_alert(Server, bad_record_mac),
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
diff --git a/lib/ssl/test/openssl_renegotiate_SUITE.erl b/lib/ssl/test/openssl_renegotiate_SUITE.erl
index f40d0b1575..f548b75abe 100644
--- a/lib/ssl/test/openssl_renegotiate_SUITE.erl
+++ b/lib/ssl/test/openssl_renegotiate_SUITE.erl
@@ -40,14 +40,13 @@ all() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}];
false ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}]
+ {group, 'tlsv1'}
+ ]
end.
groups() ->
@@ -56,15 +55,13 @@ groups() ->
[{'tlsv1.2', [], all_versions_tests()},
{'tlsv1.1', [], all_versions_tests()},
{'tlsv1', [], all_versions_tests()},
- {'sslv3', [], all_versions_tests()},
{'dtlsv1.2', [], all_versions_tests()},
{'dtlsv1', [], all_versions_tests()}
];
false ->
[{'tlsv1.2', [], all_versions_tests()},
{'tlsv1.1', [], all_versions_tests()},
- {'tlsv1', [], all_versions_tests()},
- {'sslv3', [], all_versions_tests()}
+ {'tlsv1', [], all_versions_tests()}
]
end.
diff --git a/lib/ssl/test/openssl_server_cert_SUITE.erl b/lib/ssl/test/openssl_server_cert_SUITE.erl
index 8e97f70064..b0713ab37d 100644
--- a/lib/ssl/test/openssl_server_cert_SUITE.erl
+++ b/lib/ssl/test/openssl_server_cert_SUITE.erl
@@ -40,7 +40,6 @@ groups() ->
{'tlsv1.2', [], pre_tls_1_3_protocol_groups()},
{'tlsv1.1', [], pre_tls_1_3_protocol_groups()},
{'tlsv1', [], pre_tls_1_3_protocol_groups()},
- {'sslv3', [], ssl_protocol_groups()},
{'dtlsv1.2', [], pre_tls_1_3_protocol_groups()},
{'dtlsv1', [], pre_tls_1_3_protocol_groups()},
{rsa, [], all_version_tests()},
@@ -58,7 +57,6 @@ protocol_groups() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
diff --git a/lib/ssl/test/openssl_session_SUITE.erl b/lib/ssl/test/openssl_session_SUITE.erl
index 6f74408cb4..d524e6c039 100644
--- a/lib/ssl/test/openssl_session_SUITE.erl
+++ b/lib/ssl/test/openssl_session_SUITE.erl
@@ -39,14 +39,13 @@ all() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}];
false ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}]
+ {group, 'tlsv1'}
+ ]
end.
groups() ->
@@ -55,15 +54,13 @@ groups() ->
[{'tlsv1.2', [], tests()},
{'tlsv1.1', [], tests()},
{'tlsv1', [], tests()},
- {'sslv3', [], tests()},
{'dtlsv1.2', [], tests()},
{'dtlsv1', [], tests()}
];
false ->
[{'tlsv1.2', [], tests()},
{'tlsv1.1', [], tests()},
- {'tlsv1', [], tests()},
- {'sslv3', [], tests()}
+ {'tlsv1', [], tests()}
]
end.
diff --git a/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl b/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl
index f225065ba6..b661ec8806 100644
--- a/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl
+++ b/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl
@@ -55,14 +55,13 @@
-define('TLS_v1.2', 'tlsv1.2').
-define('TLS_v1.1', 'tlsv1.1').
-define('TLS_v1', 'tlsv1').
--define('SSL_v3', 'sslv3').
%%--------------------------------------------------------------------
%% Properties --------------------------------------------------------
%%--------------------------------------------------------------------
prop_tls_cipher_suite_rfc_name() ->
- ?FORALL({CipherSuite, TLSVersion}, ?LET(Version, tls_version(), {cipher_suite(Version), Version}),
+ ?FORALL({CipherSuite, _TLSVersion}, ?LET(Version, tls_version(), {cipher_suite(Version), Version}),
case ssl:str_to_suite(ssl:suite_to_str(CipherSuite)) of
CipherSuite ->
true;
@@ -72,7 +71,7 @@ prop_tls_cipher_suite_rfc_name() ->
).
prop_tls_cipher_suite_openssl_name() ->
- ?FORALL({CipherSuite, TLSVersion}, ?LET(Version, tls_version(), {cipher_suite(Version), Version}),
+ ?FORALL({CipherSuite, _TLSVersion}, ?LET(Version, tls_version(), {cipher_suite(Version), Version}),
case ssl:str_to_suite(ssl:suite_to_openssl_str(CipherSuite)) of
CipherSuite ->
true;
@@ -86,7 +85,7 @@ prop_tls_cipher_suite_openssl_name() ->
%% Generators -----------------------------------------------
%%--------------------------------------------------------------------
tls_version() ->
- oneof([?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1', ?'SSL_v3']).
+ oneof([?'TLS_v1.3', ?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1']).
cipher_suite(Version) ->
oneof(cipher_suites(Version)).
diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl
index f19a74898d..ea1b078892 100644
--- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl
+++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl
@@ -62,7 +62,6 @@
-define('TLS_v1.2', {3,3}).
-define('TLS_v1.1', {3,2}).
-define('TLS_v1', {3,1}).
--define('SSL_v3', {3,0}).
%%--------------------------------------------------------------------
%% Properties --------------------------------------------------------
@@ -132,14 +131,6 @@ client_hello(Version) ->
compression_methods = compressions(Version),
random = client_random(Version),
extensions = client_hello_extensions(Version)
- };
-client_hello(?'SSL_v3' = Version) ->
- #client_hello{session_id = session_id(),
- client_version = Version,
- cipher_suites = cipher_suites(Version),
- compression_methods = compressions(Version),
- random = client_random(Version),
- extensions = ssl_handshake:empty_extensions(Version, client_hello)
}.
server_hello(?'TLS_v1.3' = Version) ->
@@ -150,14 +141,6 @@ server_hello(?'TLS_v1.3' = Version) ->
compression_method = compression(Version),
extensions = server_hello_extensions(Version)
};
-server_hello(?'SSL_v3' = Version) ->
- #server_hello{server_version = Version,
- session_id = session_id(),
- random = server_random(Version),
- cipher_suite = cipher_suite(Version),
- compression_method = compression(Version),
- extensions = ssl_handshake:empty_extensions(Version, server_hello)
- };
server_hello(Version) ->
#server_hello{server_version = Version,
session_id = session_id(),
@@ -214,7 +197,7 @@ key_update() ->
%%--------------------------------------------------------------------
tls_version() ->
- oneof([?'TLS_v1.3', ?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1', ?'SSL_v3']).
+ oneof([?'TLS_v1.3', ?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1']).
cipher_suite(Version) ->
oneof(cipher_suites(Version)).
@@ -382,8 +365,6 @@ extensions(?'TLS_v1.3' = Version, MsgType = client_hello) ->
%% post_handshake_auth => PostHandshakeAuth,
signature_algs_cert => SignatureAlgorithmsCert
}));
-extensions(?'SSL_v3', client_hello) ->
- #{};
extensions(Version, client_hello) ->
?LET({
SNI,
diff --git a/lib/ssl/test/ssl_alpn_SUITE.erl b/lib/ssl/test/ssl_alpn_SUITE.erl
index 82a49e1469..f81a853bd1 100644
--- a/lib/ssl/test/ssl_alpn_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_SUITE.erl
@@ -37,7 +37,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -48,7 +47,6 @@ groups() ->
{'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()},
{'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist()},
{'tlsv1', [], alpn_tests() ++ alpn_npn_coexist()},
- {'sslv3', [], alpn_not_supported()},
{'dtlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()},
{'dtlsv1', [], alpn_tests() ++ alpn_npn_coexist()}
].
@@ -74,10 +72,6 @@ alpn_npn_coexist() ->
client_alpn_and_server_alpn_npn
].
-alpn_not_supported() ->
- [alpn_not_supported_client,
- alpn_not_supported_server
- ].
init_per_suite(Config0) ->
catch crypto:stop(),
@@ -274,30 +268,6 @@ session_reused(Config) when is_list(Config)->
ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config).
-%--------------------------------------------------------------------------------
-
-alpn_not_supported_client(Config) when is_list(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- PrefProtocols = {client_preferred_next_protocols,
- {client, [<<"http/1.0">>], <<"http/1.1">>}},
- ClientOpts = [PrefProtocols] ++ ClientOpts0,
- {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Client = ssl_test_lib:start_client_error([{node, ClientNode},
- {port, 8888}, {host, Hostname},
- {from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Client, {error,
- {options,
- {not_supported_in_sslv3, PrefProtocols}}}).
-
-%--------------------------------------------------------------------------------
-
-alpn_not_supported_server(Config) when is_list(Config)->
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
- ServerOpts = [AdvProtocols] ++ ServerOpts0,
-
- {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl
index b393ba2f9b..00f59d3cce 100644
--- a/lib/ssl/test/ssl_api_SUITE.erl
+++ b/lib/ssl/test/ssl_api_SUITE.erl
@@ -37,7 +37,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -51,7 +50,6 @@ groups() ->
{'tlsv1.2', [], gen_api_tests() ++ since_1_2() ++ handshake_paus_tests() ++ pre_1_3()},
{'tlsv1.1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3()},
{'tlsv1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3() ++ beast_mitigation_test()},
- {'sslv3', [], (gen_api_tests() -- [new_options_in_handshake]) ++ beast_mitigation_test() ++ pre_1_3()},
{'dtlsv1.2', [], (gen_api_tests() --
[invalid_keyfile, invalid_certfile, invalid_cacertfile,
invalid_options, new_options_in_handshake]) ++
diff --git a/lib/ssl/test/ssl_app_env_SUITE.erl b/lib/ssl/test/ssl_app_env_SUITE.erl
index 27fbcb8e47..233985c729 100644
--- a/lib/ssl/test/ssl_app_env_SUITE.erl
+++ b/lib/ssl/test/ssl_app_env_SUITE.erl
@@ -37,7 +37,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -48,7 +47,6 @@ groups() ->
{'tlsv1.2', [], tests()},
{'tlsv1.1', [], tests()},
{'tlsv1', [], tests()},
- {'sslv3', [], tests()},
{'dtlsv1.2', [], tests()},
{'dtlsv1', [], tests()}
].
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index fbe2d4f6b0..2ca6c7de3d 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -173,7 +173,7 @@ connect_twice(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
defaults(Config) when is_list(Config)->
Versions = ssl:versions(),
- true = lists:member(sslv3, proplists:get_value(available, Versions)),
+ false = lists:member(sslv3, proplists:get_value(available, Versions)),
false = lists:member(sslv3, proplists:get_value(supported, Versions)),
true = lists:member('tlsv1', proplists:get_value(available, Versions)),
false = lists:member('tlsv1', proplists:get_value(supported, Versions)),
@@ -321,7 +321,7 @@ cipher_suites_mix(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{ciphers, CipherSuites} | ClientOpts]}]),
+ {options, [{versions, ['tlsv1.2']},{ciphers, CipherSuites} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
@@ -406,7 +406,6 @@ eccs() ->
eccs(Config) when is_list(Config) ->
[_|_] = All = ssl:eccs(),
- [] = ssl:eccs(sslv3),
[_|_] = Tls = ssl:eccs(tlsv1),
[_|_] = Tls1 = ssl:eccs('tlsv1.1'),
[_|_] = Tls2 = ssl:eccs('tlsv1.2'),
diff --git a/lib/ssl/test/ssl_bench_test_lib.erl b/lib/ssl/test/ssl_bench_test_lib.erl
index 47bcd41608..7000683819 100644
--- a/lib/ssl/test/ssl_bench_test_lib.erl
+++ b/lib/ssl/test/ssl_bench_test_lib.erl
@@ -41,13 +41,13 @@ setup(Name) ->
lists:append([" -pa " ++ P || [P] <- PaPaths]);
_ -> []
end,
- %% io:format("Slave args: ~p~n",[SlaveArgs]),
+ %% ct:pal("Slave args: ~p~n",[SlaveArgs]),
Prog =
case os:find_executable("erl") of
false -> "erl";
P -> P
end,
- io:format("Prog = ~p~n", [Prog]),
+ ct:pal("Prog = ~p~n", [Prog]),
case net_adm:ping(Node) of
pong -> ok;
@@ -58,13 +58,13 @@ setup(Name) ->
Path = code:get_path(),
true = rpc:call(Node, code, set_path, [Path]),
ok = rpc:call(Node, ?MODULE, setup_server, [node()]),
- io:format("Client (~p) using ~ts~n",[node(), code:which(ssl)]),
+ ct:pal("Client (~p) using ~ts~n",[node(), code:which(ssl)]),
(Node =:= node()) andalso restrict_schedulers(client),
Node.
setup_server(ClientNode) ->
(ClientNode =:= node()) andalso restrict_schedulers(server),
- io:format("Server (~p) using ~ts~n",[node(), code:which(ssl)]),
+ ct:pal("Server (~p) using ~ts~n",[node(), code:which(ssl)]),
ok.
restrict_schedulers(Type) ->
diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl
index d5ca9bcf02..bcf2086cca 100644
--- a/lib/ssl/test/ssl_cert_SUITE.erl
+++ b/lib/ssl/test/ssl_cert_SUITE.erl
@@ -36,7 +36,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -47,7 +46,6 @@ groups() ->
{'tlsv1.2', [], tls_1_2_protocol_groups()},
{'tlsv1.1', [], ssl_protocol_groups()},
{'tlsv1', [], ssl_protocol_groups()},
- {'sslv3', [], ssl_protocol_groups()},
{'dtlsv1.2', [], tls_1_2_protocol_groups()},
{'dtlsv1', [], ssl_protocol_groups()},
{rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests()},
diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
index e598d662e9..afb11660e7 100644
--- a/lib/ssl/test/ssl_cipher_suite_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
@@ -35,7 +35,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -45,7 +44,6 @@ groups() ->
{'tlsv1.2', [], kex()},
{'tlsv1.1', [], kex()},
{'tlsv1', [], kex()},
- {'sslv3', [], ssl3_kex()},
{'dtlsv1.2', [], kex()},
{'dtlsv1', [], kex()},
{dhe_rsa, [],[dhe_rsa_3des_ede_cbc,
@@ -130,11 +128,6 @@ groups() ->
kex() ->
rsa() ++ ecdsa() ++ dss() ++ anonymous().
-
-ssl3_kex() ->
- ssl3_rsa() ++ ssl3_dss() ++ ssl3_anonymous().
-
-
rsa() ->
[{group, dhe_rsa},
{group, ecdhe_rsa},
@@ -143,11 +136,6 @@ rsa() ->
{group, rsa_psk}
].
-ssl3_rsa() ->
- [{group, dhe_rsa},
- {group, rsa}
- ].
-
ecdsa() ->
[{group, ecdhe_ecdsa}].
@@ -155,10 +143,6 @@ dss() ->
[{group, dhe_dss},
{group, srp_dss}].
-ssl3_dss() ->
- [{group, dhe_dss}
- ].
-
anonymous() ->
[{group, dh_anon},
{group, ecdh_anon},
@@ -168,10 +152,6 @@ anonymous() ->
{group, srp_anon}
].
-ssl3_anonymous() ->
- [{group, dh_anon}].
-
-
init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
diff --git a/lib/ssl/test/ssl_engine_SUITE.erl b/lib/ssl/test/ssl_engine_SUITE.erl
index a39a62e550..ac54a2637e 100644
--- a/lib/ssl/test/ssl_engine_SUITE.erl
+++ b/lib/ssl/test/ssl_engine_SUITE.erl
@@ -101,15 +101,18 @@ private_key(Config) when is_list(Config) ->
#{server_config := ServerConf,
client_config := ClientConf} = GenCertData =
public_key:pkix_test_data(#{server_chain =>
- #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
- peer => [{extensions, Ext},
+ #{root => [{digest, sha256},
+ {key, ssl_test_lib:hardcode_rsa_key(1)}],
+ intermediates => [[{digest, sha256},
+ {key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{extensions, Ext}, {digest, sha256},
{key, ssl_test_lib:hardcode_rsa_key(3)}
]},
client_chain =>
- #{root => [{key, ssl_test_lib:hardcode_rsa_key(4)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(5)}]],
- peer => [{key, ssl_test_lib:hardcode_rsa_key(6)}]}}),
+ #{root => [{key, ssl_test_lib:hardcode_rsa_key(4)}, {digest, sha256}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(5)},
+ {digest, sha256}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(6)},{digest, sha256}]}}),
[{server_config, FileServerConf},
{client_config, FileClientConf}] =
x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase),
diff --git a/lib/ssl/test/ssl_npn_SUITE.erl b/lib/ssl/test/ssl_npn_SUITE.erl
index 1f794075c1..94448b5721 100644
--- a/lib/ssl/test/ssl_npn_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_SUITE.erl
@@ -33,15 +33,14 @@
all() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}].
+ {group, 'tlsv1'}
+ ].
groups() ->
[
{'tlsv1.2', [], next_protocol_tests()},
{'tlsv1.1', [], next_protocol_tests()},
- {'tlsv1', [], next_protocol_tests()},
- {'sslv3', [], next_protocol_not_supported()}
+ {'tlsv1', [], next_protocol_tests()}
].
next_protocol_tests() ->
@@ -59,11 +58,6 @@ next_protocol_tests() ->
npn_handshake_session_reused
].
-next_protocol_not_supported() ->
- [npn_not_supported_client,
- npn_not_supported_server
- ].
-
init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
@@ -220,29 +214,6 @@ renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok).
%--------------------------------------------------------------------------------
-npn_not_supported_client(Config) when is_list(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- PrefProtocols = {client_preferred_next_protocols,
- {client, [<<"http/1.0">>], <<"http/1.1">>}},
- ClientOpts = [PrefProtocols] ++ ClientOpts0,
- {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Client = ssl_test_lib:start_client_error([{node, ClientNode},
- {port, 8888}, {host, Hostname},
- {from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Client, {error,
- {options,
- {not_supported_in_sslv3, PrefProtocols}}}).
-
-%--------------------------------------------------------------------------------
-npn_not_supported_server(Config) when is_list(Config)->
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
- ServerOpts = [AdvProtocols] ++ ServerOpts0,
-
- {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
-
-%--------------------------------------------------------------------------------
npn_handshake_session_reused(Config) when is_list(Config)->
ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
ClientOpts = [{client_preferred_next_protocols,
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 99cf19f3ef..7cd84781d3 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -54,7 +54,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -64,7 +63,6 @@ groups() ->
{'tlsv1.2', [], socket_packet_tests() ++ protocol_packet_tests()},
{'tlsv1.1', [], socket_packet_tests() ++ protocol_packet_tests()},
{'tlsv1', [], socket_packet_tests() ++ protocol_packet_tests()},
- {'sslv3', [], socket_packet_tests() ++ protocol_packet_tests()},
%% We will not support any packet types if the transport is
%% not reliable. We might support it for DTLS over SCTP in the future
{'dtlsv1.2', [], [reject_packet_opt]},
@@ -1132,7 +1130,7 @@ server_send_trailer(Socket, Trailer)->
client_http_decode_trailer_active(Socket) ->
receive
{ssl, Socket,
- {http_header,36,'Content-Encoding',undefined,"gzip"}} ->
+ {http_header,36,'Content-Encoding',"Content-Encoding","gzip"}} ->
ok;
Other1 ->
exit({?LINE, Other1})
@@ -1182,7 +1180,7 @@ packet_httph_bin_active(Config) when is_list(Config) ->
client_http_decode_trailer_bin_active(Socket) ->
receive
{ssl, Socket,
- {http_header,36,'Content-Encoding',undefined, <<"gzip">>}} ->
+ {http_header,36,'Content-Encoding',<<"Content-Encoding">>, <<"gzip">>}} ->
ok;
Other1 ->
exit({?LINE, Other1})
@@ -1234,7 +1232,7 @@ client_http_decode_trailer_active_once(Socket) ->
ssl:setopts(Socket, [{active, once}]),
receive
{ssl, Socket,
- {http_header,36,'Content-Encoding',undefined,"gzip"}} ->
+ {http_header,36,'Content-Encoding',"Content-Encoding","gzip"}} ->
ok;
Other1 ->
exit({?LINE, Other1})
@@ -1286,7 +1284,7 @@ client_http_decode_trailer_bin_active_once(Socket) ->
ssl:setopts(Socket, [{active, once}]),
receive
{ssl, Socket,
- {http_header,36,'Content-Encoding',undefined, <<"gzip">>}} ->
+ {http_header,36,'Content-Encoding',<<"Content-Encoding">>, <<"gzip">>}} ->
ok;
Other1 ->
exit({?LINE, Other1})
@@ -1337,7 +1335,7 @@ packet_httph_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
client_http_decode_trailer_passive(Socket) ->
- {ok,{http_header,36,'Content-Encoding',undefined,"gzip"}} = ssl:recv(Socket, 0),
+ {ok,{http_header,36,'Content-Encoding',"Content-Encoding","gzip"}} = ssl:recv(Socket, 0),
{ok, http_eoh} = ssl:recv(Socket, 0),
ok.
@@ -1377,7 +1375,7 @@ packet_httph_bin_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
client_http_decode_trailer_bin_passive(Socket) ->
- {ok,{http_header,36,'Content-Encoding',undefined,<<"gzip">>}} = ssl:recv(Socket, 0),
+ {ok,{http_header,36,'Content-Encoding',<<"Content-Encoding">>,<<"gzip">>}} = ssl:recv(Socket, 0),
{ok, http_eoh} = ssl:recv(Socket, 0),
ok.
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index 4374d9ff47..a133159c8b 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -36,8 +36,7 @@ all() ->
{group, 'tlsv1.3'},
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}
+ {group, 'tlsv1'}
].
groups() ->
@@ -45,8 +44,7 @@ groups() ->
{'tlsv1.3', [], payload_tests()},
{'tlsv1.2', [], payload_tests()},
{'tlsv1.1', [], payload_tests()},
- {'tlsv1', [], payload_tests()},
- {'sslv3', [], payload_tests()}
+ {'tlsv1', [], payload_tests()}
].
payload_tests() ->
diff --git a/lib/ssl/test/ssl_renegotiate_SUITE.erl b/lib/ssl/test/ssl_renegotiate_SUITE.erl
index 921604458a..f4d57076eb 100644
--- a/lib/ssl/test/ssl_renegotiate_SUITE.erl
+++ b/lib/ssl/test/ssl_renegotiate_SUITE.erl
@@ -39,7 +39,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -50,8 +49,7 @@ groups() ->
{'tlsv1.3', [], renegotiate_tests()},
{'tlsv1.2', [], renegotiate_tests()},
{'tlsv1.1', [], renegotiate_tests()},
- {'tlsv1', [], renegotiate_tests()},
- {'sslv3', [], ssl3_renegotiate_tests()}
+ {'tlsv1', [], renegotiate_tests()}
].
renegotiate_tests() ->
diff --git a/lib/ssl/test/ssl_session_SUITE.erl b/lib/ssl/test/ssl_session_SUITE.erl
index aa79698a72..fc31db1a83 100644
--- a/lib/ssl/test/ssl_session_SUITE.erl
+++ b/lib/ssl/test/ssl_session_SUITE.erl
@@ -40,7 +40,6 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -51,8 +50,7 @@ groups() ->
{'tlsv1.3', [], session_tests()},
{'tlsv1.2', [], session_tests()},
{'tlsv1.1', [], session_tests()},
- {'tlsv1', [], session_tests()},
- {'sslv3', [], session_tests()}
+ {'tlsv1', [], session_tests()}
].
session_tests() ->
@@ -102,25 +100,30 @@ end_per_group(GroupName, Config) ->
end.
init_per_testcase(reuse_session_expired, Config) ->
+ Versions = ssl_test_lib:protocol_version(Config),
ssl:stop(),
- application:load(ssl),
+ application:load(ssl),
ssl_test_lib:clean_env(),
+ ssl_test_lib:set_protocol_versions(Versions),
application:set_env(ssl, session_lifetime, ?EXPIRE),
- application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 30}),
Config;
init_per_testcase(_, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 15}),
Config.
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(reuse_session_expired, Config) ->
+ application:unset_env(ssl, session_lifetime),
+ Config;
+end_per_testcase(_, Config) ->
Config.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-
reuse_session() ->
[{doc,"Test reuse of sessions (short handshake)"}].
reuse_session(Config) when is_list(Config) ->
@@ -181,6 +184,7 @@ reuse_session_expired(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
receive
{Client2, SID} ->
+ end_per_testcase(?FUNCTION_NAME, Config),
ct:fail(session_reused_when_session_expired);
{Client2, _} ->
ok
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index f3b1c38d2e..e25244750e 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -27,7 +27,6 @@
-include_lib("common_test/include/ct.hrl").
--define(DELAY, 500).
-define(SLEEP, 1000).
-define(TIMEOUT, 60000).
-define(LONG_TIMEOUT, 600000).
@@ -43,7 +42,26 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-all() ->
+
+all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [{'dtlsv1.2', [], session_tests()},
+ {'dtlsv1', [], session_tests()},
+ {'tlsv1.2', [], session_tests()},
+ {'tlsv1.1', [], session_tests()},
+ {'tlsv1', [], session_tests()}
+ ].
+
+
+session_tests() ->
[session_cleanup,
session_cache_process_list,
session_cache_process_mnesia,
@@ -52,9 +70,6 @@ all() ->
save_specific_session
].
-groups() ->
- [].
-
init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
@@ -70,11 +85,28 @@ end_per_suite(_Config) ->
ssl:stop(),
application:stop(crypto).
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
+ case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ _ ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl:start(),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(session_cache_process_list, Config) ->
init_customized_session_cache(list, Config);
@@ -84,37 +116,47 @@ init_per_testcase(session_cache_process_mnesia, Config) ->
init_customized_session_cache(mnesia, Config);
init_per_testcase(session_cleanup, Config) ->
+ Versions = ssl_test_lib:protocol_version(Config),
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:set_protocol_versions(Versions),
application:set_env(ssl, session_lifetime, 5),
- application:set_env(ssl, session_delay_cleanup_time, ?DELAY),
ssl:start(),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 20}),
Config;
init_per_testcase(client_unique_session, Config) ->
ct:timetrap({seconds, 40}),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
Config;
init_per_testcase(save_specific_session, Config) ->
+ Versions = ssl_test_lib:protocol_version(Config),
ssl_test_lib:clean_start(),
+ ssl_test_lib:set_protocol_versions(Versions),
ct:timetrap({seconds, 5}),
Config;
init_per_testcase(max_table_size, Config) ->
+ Versions = ssl_test_lib:protocol_version(Config),
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:set_protocol_versions(Versions),
application:set_env(ssl, session_cache_server_max, ?MAX_TABLE_SIZE),
application:set_env(ssl, session_cache_client_max, ?MAX_TABLE_SIZE),
- application:set_env(ssl, session_delay_cleanup_time, ?DELAY),
- ssl:start(),
+ ssl:start(),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 40}),
Config.
init_customized_session_cache(Type, Config) ->
+ Versions = ssl_test_lib:protocol_version(Config),
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:set_protocol_versions(Versions),
application:set_env(ssl, session_cb, ?MODULE),
application:set_env(ssl, session_cb_init_args, [{type, Type}]),
ssl:start(),
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
catch (end_per_testcase(list_to_atom("session_cache_process" ++ atom_to_list(Type)),
Config)),
ets:new(ssl_test, [named_table, public, set]),
@@ -133,7 +175,6 @@ end_per_testcase(session_cache_process_mnesia, Config) ->
ssl:start(),
end_per_testcase(default_action, Config);
end_per_testcase(session_cleanup, Config) ->
- application:unset_env(ssl, session_delay_cleanup_time),
application:unset_env(ssl, session_lifetime),
end_per_testcase(default_action, Config);
end_per_testcase(max_table_size, Config) ->
@@ -155,8 +196,8 @@ client_unique_session() ->
"sets up many connections"}].
client_unique_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
- ServerOpts = proplists:get_value(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -253,8 +294,8 @@ save_specific_session() ->
}].
save_specific_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
- ServerOpts = proplists:get_value(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -511,7 +552,7 @@ check_timer(Timer) ->
case erlang:read_timer(Timer) of
false ->
{status, _, _, _} = sys:get_status(whereis(ssl_manager)),
- timer:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
{status, _, _, _} = sys:get_status(whereis(ssl_manager)),
ok;
Int ->
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index e58a4642fb..9cd6a7a1a5 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -84,6 +84,27 @@ init_per_suite(Config0) ->
catch _:_ ->
{skip, "Crypto did not start"}
end.
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
end_per_suite(_) ->
ssl:stop(),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 206c4c8b32..1c8ec9a313 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -2251,8 +2251,6 @@ is_tls_version('tlsv1.1') ->
true;
is_tls_version('tlsv1') ->
true;
-is_tls_version('sslv3') ->
- true;
is_tls_version(_) ->
false.
@@ -2713,13 +2711,6 @@ check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1';
_ ->
check_sane_openssl_renegotaite(Config)
end;
-check_sane_openssl_renegotaite(Config, 'sslv3') ->
- case os:cmd("openssl version") of
- "OpenSSL 1" ++ _ ->
- {skip, "Known renegotiation bug with sslv3 in OpenSSL"};
- _ ->
- check_sane_openssl_renegotaite(Config)
- end;
check_sane_openssl_renegotaite(Config, _) ->
check_sane_openssl_renegotaite(Config).
@@ -2855,6 +2846,31 @@ close_loop(Port, Time, SentClose) ->
end
end.
+portable_open_port("openssl" = Exe, Args0) ->
+ case os:getenv("WSLENV") of
+ false ->
+ AbsPath = os:find_executable(Exe),
+ ct:pal("open_port({spawn_executable, ~p}, [{args, ~p}, stderr_to_stdout]).",
+ [AbsPath, Args0]),
+ open_port({spawn_executable, AbsPath},
+ [{args, Args0}, stderr_to_stdout]);
+ _ ->
+ %% I can't get the new windows version of openssl.exe to be stable
+ %% certain server tests are failing for no reason.
+ %% This is using "linux" openssl via wslenv
+
+ Translate = fun("c:/" ++ _ = Path) ->
+ string:trim(os:cmd("wsl wslpath -u " ++ Path));
+ (Arg) ->
+ Arg
+ end,
+ Args1 = [Translate(Arg) || Arg <- Args0],
+ Args = ["/C","wsl","openssl"| Args1] ++ ["2>&1"],
+ Cmd = os:find_executable("cmd"),
+ ct:pal("open_port({spawn_executable, ~p}, [{args, ~p}, stderr_to_stdout]).", [Cmd,Args]),
+ open_port({spawn_executable, Cmd},
+ [{args, Args}, stderr_to_stdout, hide])
+ end;
portable_open_port(Exe, Args) ->
AbsPath = os:find_executable(Exe),
ct:pal("open_port({spawn_executable, ~p}, [{args, ~p}, stderr_to_stdout]).", [AbsPath, Args]),
@@ -3295,10 +3311,13 @@ digest() ->
kill_openssl() ->
case os:type() of
- {unix, _} ->
- os:cmd("pkill openssl");
{win32, _} ->
- os:cmd("cmd.exe /C \"taskkill /IM openssl.exe /F\"")
+ case os:getenv("WSLENV") of
+ false -> os:cmd("cmd.exe /C \"taskkill /IM openssl.exe /F\"");
+ _ -> os:cmd("wsl pkill openssl")
+ end;
+ _ ->
+ os:cmd("pkill openssl")
end.
hostname_format(Hostname) ->
@@ -3380,3 +3399,16 @@ bigger_buffers() ->
_ ->
[]
end.
+
+set_protocol_versions(Version) when Version == 'tlsv1';
+ Version == 'tlsv1.1';
+ Version == 'tlsv1.2' ->
+ set_protocol_versions(protocol_version, [Version]);
+set_protocol_versions(Version) when Version == 'dtlsv1';
+ Version == 'dtlsv1.2' ->
+ set_protocol_versions(dtls_protocol_version, [Version]).
+
+set_protocol_versions(_, undefined) ->
+ ok;
+set_protocol_versions(AppVar, Value) ->
+ application:set_env(ssl, AppVar, Value).
diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl
index fc0e9cf3fc..01de312a81 100644
--- a/lib/ssl/test/tls_api_SUITE.erl
+++ b/lib/ssl/test/tls_api_SUITE.erl
@@ -40,8 +40,7 @@ all() ->
{group, 'tlsv1.3'},
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}
+ {group, 'tlsv1'}
].
groups() ->
@@ -49,8 +48,7 @@ groups() ->
{'tlsv1.3', [], api_tests() -- [sockname]},
{'tlsv1.2', [], api_tests()},
{'tlsv1.1', [], api_tests()},
- {'tlsv1', [], api_tests()},
- {'sslv3', [], api_tests() ++ [ssl3_cipher_suite_limitation]}
+ {'tlsv1', [], api_tests()}
].
api_tests() ->
@@ -605,39 +603,6 @@ transport_close(Config) when is_list(Config) ->
{error, _} = ssl:send(SslS, "Hello world").
%%--------------------------------------------------------------------
-ssl3_cipher_suite_limitation() ->
- [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}].
-ssl3_cipher_suite_limitation(Config) when is_list(Config) ->
-
- {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_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.
-%%--------------------------------------------------------------------
emulated_options() ->
[{doc,"Test API function getopts/2 and setopts/2"}].
diff --git a/lib/stdlib/Makefile b/lib/stdlib/Makefile
index 3086d85445..cae3844126 100644
--- a/lib/stdlib/Makefile
+++ b/lib/stdlib/Makefile
@@ -35,3 +35,7 @@ SPECIAL_TARGETS =
# Default Subdir Targets
#
include $(ERL_TOP)/make/otp_subdir.mk
+
+DIA_PLT_APPS=compiler crypto
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 4541b4a463..1092ce3ffa 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(STDLIB_VSN)
APPLICATION=stdlib
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -89,6 +84,7 @@ XML_REF3_FILES = \
sets.xml \
shell.xml \
shell_default.xml \
+ shell_docs.xml \
slave.xml \
sofs.xml \
string.xml \
@@ -113,74 +109,12 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
-
TOP_SPECS_FILE = specs.xml
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-
-SPECS_FLAGS = -I../../include -I../../../kernel/include
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-docs: man pdf html
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
-
$(SPECDIR)/specs_erl_id_trans.xml:
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_id_trans
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+NO_CHUNKS = erl_id_trans.xml
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml
index 29edc373c7..04dad1a472 100644
--- a/lib/stdlib/doc/src/c.xml
+++ b/lib/stdlib/doc/src/c.xml
@@ -120,6 +120,64 @@
</func>
<func>
+ <name name="h" arity="1" since="OTP @OTP-16222@"/>
+ <fsummary>Module help information</fsummary>
+ <type name="h_return"/>
+ <desc>
+ <p>Print the documentation for <c>Module</c></p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="h" arity="2" since="OTP @OTP-16222@"/>
+ <fsummary>Function help information</fsummary>
+ <type name="h_return"/>
+ <type name="hf_return"/>
+ <desc>
+ <p>Print the documentation for all <c>Module:Function</c>s (regardless of arity).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="h" arity="3" since="OTP @OTP-16222@"/>
+ <fsummary>Function help information</fsummary>
+ <type name="h_return"/>
+ <type name="hf_return"/>
+ <desc>
+ <p>Print the documentation for <c>Module:Function/Arity</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="1" since="OTP @OTP-16222@"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Module</c></p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="2" since="OTP @OTP-16222@"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <type name="ht_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Type</c> in <c>Module</c> regardless of arity.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="3" since="OTP @OTP-16222@"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <type name="ht_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Type/Arity</c> in <c>Module</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="i" arity="0" since=""/>
<name name="ni" arity="0" since=""/>
<fsummary>System information.</fsummary>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index 8142e5c0aa..d487cccdfc 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -69,6 +69,25 @@
<name name="erl_parse_tree"></name>
</datatype>
<datatype>
+ <name>af_binelement(_)</name>
+ <desc>
+ <p>Abstract representation of an element of a bitstring.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name>af_generator()</name>
+ <desc>
+ <p>Abstract representation of a generator
+ or a bitstring generator.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name>af_remote_function()></name>
+ <desc>
+ <p>Abstract representation of a remote function call.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="error_description"></name>
</datatype>
<datatype>
diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml
index 0a46139db6..70922b5825 100644
--- a/lib/stdlib/doc/src/erl_pp.xml
+++ b/lib/stdlib/doc/src/erl_pp.xml
@@ -68,6 +68,10 @@
<desc>
<p>The option <c>quote_singleton_atom_types</c>
is used to add quotes to all singleton atom types.</p>
+ <p>The option <c>linewidth</c> controls the maximum line
+ width for formatted lines (defaults to 72 characters).</p>
+ <p>The option <c>indent</c> controls the
+ indention for formatted lines (defaults to 4 spaces).</p>
</desc>
</datatype>
<datatype>
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index b062c5cef3..ee66b581f8 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -288,6 +288,11 @@
writing the file. That is, absolute paths will be turned into
relative paths. There will be an info message written to the error
logger when paths are changed in this way.</p></note>
+ <warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file is
+ assumed to have been opened with the appropriate flags.</p>
+ </warning>
</desc>
</func>
@@ -349,6 +354,11 @@
<p>Prints an informational message for each extracted file.</p>
</item>
</taglist>
+ <warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file is
+ assumed to have been opened with the appropriate flags.</p>
+ </warning>
</desc>
</func>
@@ -474,10 +484,15 @@ erl_tar:close(TarDesc)</code>
finished adding files, use function <seealso marker="#close/1">
<c>close/1</c></seealso> to close the tar file.</p>
<warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file must
+ already be opened with the appropriate flags.</p>
+ </warning>
+ <warning>
<p>The <c>TarDescriptor</c> term is not a file descriptor. You are
advised not to rely on the specific contents of this term, as it
can change in future Erlang/OTP releases when more features are
- added to this module..</p>
+ added to this module.</p>
</warning>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 2d0a88fc6e..c4d39142e3 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -557,6 +557,10 @@ Error: fun containing local Erlang function calls
<item>
<p>Indicates if the table is compressed.</p>
</item>
+ <tag><c>{decentralized_counters, boolean()}</c></tag>
+ <item>
+ <p>Indicates whether the table uses <c>decentralized_counters</c>.</p>
+ </item>
<tag><c>{heir, pid() | none}</c></tag>
<item>
<p>The pid of the heir of the table, or <c>none</c> if no heir
@@ -616,6 +620,13 @@ Error: fun containing local Erlang function calls
<p>Indicates whether the table uses <c>write_concurrency</c>.</p>
</item>
</taglist>
+ <note><p>The execution time of this function is affected by
+ the <seealso marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seealso> table option.
+ The execution time is much longer when the <c>decentralized_counters</c>
+ option is set to <c>true</c> than when the <c>decentralized_counters</c>
+ option is set to <c>false</c>.</p>
+ </note>
</desc>
</func>
@@ -701,6 +712,15 @@ Error: fun containing local Erlang function calls
<p>Returns internal statistics about tables on an internal format
used by OTP test suites. Not for production use.</p></item>
</list>
+ <note>
+ <p>The execution time of this function is affected by
+ the <seealso marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seealso> table option when the second
+ argument of the function is <c>size</c> or <c>memory</c>.
+ The execution time is much longer when the <c>decentralized_counters</c>
+ option is set to <c>true</c> than when the <c>decentralized_counters</c>
+ option is set to <c>false</c>.</p>
+ </note>
</desc>
</func>
@@ -1113,7 +1133,8 @@ ets:select(Table, MatchSpec),</code>
table is named. Default values are used for omitted options.
This means that not specifying any options (<c>[]</c>) is the same
as specifying <c>[set, protected, {keypos,1}, {heir,none},
- {write_concurrency,false}, {read_concurrency,false}]</c>.</p>
+ {write_concurrency,false}, {read_concurrency,false},
+ {decentralized_counters,false}]</c>.</p>
<taglist>
<tag><c>set</c></tag>
<item>
@@ -1208,13 +1229,23 @@ ets:select(Table, MatchSpec),</code>
(and read) by concurrent processes. This is achieved to some
degree at the expense of memory consumption and the performance
of sequential access and concurrent reading.</p>
- <p>Option <c>write_concurrency</c> can be combined with option
+ <p>The <c>write_concurrency</c> option can be combined with the options
<seealso marker="#new_2_read_concurrency">
- <c>read_concurrency</c></seealso>. You typically want to combine
- these when large concurrent read bursts and large concurrent
+ <c>read_concurrency</c></seealso> and
+ <seealso marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seealso>. You typically want to combine
+ <c>write_concurrency</c> with <c>read_concurrency</c> when large
+ concurrent read bursts and large concurrent
write bursts are common; for more information, see option
<seealso marker="#new_2_read_concurrency">
- <c>read_concurrency</c></seealso>.</p>
+ <c>read_concurrency</c></seealso>. The <c>decentralized_counters</c>
+ option is turned on by default for tables of type <c>ordered_set</c>
+ with the <c>write_concurrency</c> option enabled, and the
+ <c>decentralized_counters</c> option is turned off by default for
+ all other table types.
+ For more information, see the documentation for the
+ <seealso marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seealso> option.</p>
<p>Notice that this option does not change any guarantees about
<seealso marker="#concurrency">atomicity and isolation</seealso>.
Functions that makes such promises over many objects (like
@@ -1256,7 +1287,39 @@ ets:select(Table, MatchSpec),</code>
<c>write_concurrency</c></seealso>.
You typically want to combine these when large concurrent
read bursts and large concurrent write bursts are common.</p>
- <marker id="new_2_compressed"></marker>
+ <marker id="new_2_decentralized_counters"></marker>
+ </item>
+ <tag><c>{decentralized_counters,boolean()}</c></tag>
+ <item>
+ <p>
+ Performance tuning. Defaults to <c>true</c> for tables
+ of type <c>ordered_set</c> with the
+ <seealso marker="#new_2_write_concurrency">
+ <c>write_concurrency</c></seealso> option enabled, and defaults to
+ false for all other table types. This option has no effect if
+ the <c>write_concurrency</c> option is set to <c>false</c>.</p>
+ <p>
+ When this option is set to <c>true</c>, the table is optimized for
+ frequent concurrent calls to operations that modify the tables
+ size and/or its memory consumption (e.g., <seealso
+ marker="#insert/2"><c>insert/2</c></seealso> and <seealso
+ marker="#delete/2"><c>delete/2</c></seealso>).
+ The drawback is that calls to
+ <seealso marker="#info/1"><c>info/1</c></seealso> and
+ <seealso marker="#info/2"><c>info/2</c></seealso> with <c>size</c> or
+ <c>memory</c> as the second argument can get much slower when the
+ <c>decentralized_counters</c> option is turned on.</p>
+ <p>
+ When this option is enabled the counters for the
+ table size and memory consumption are distributed over
+ several cache lines and the scheduling threads are
+ mapped to one of those cache lines. The <c>erl</c>
+ option <seealso
+ marker="erts:erl#+dcg"><c>+dcg</c></seealso> can be used
+ to control the number of cache lines that the counters
+ are distributed over.
+ </p>
+ <marker id="new_2_compressed"></marker>
</item>
<tag><c>compressed</c></tag>
<item>
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index 5df415834f..cb867f8541 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -307,5 +307,35 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
marker="kernel:kernel_app#source_search_rules"><c>source_search_rules</c></seealso>.</p>
</desc>
</func>
+ <func>
+ <name name="safe_relative_path" arity="2" since="OTP 23.0"/>
+ <fsummary>Sanitize a relative path to avoid directory traversal attacks.</fsummary>
+ <desc>
+ <p>Sanitizes the relative path by eliminating ".." and "."
+ components to protect against directory traversal attacks.
+ Either returns the sanitized path name, or the atom
+ <c>unsafe</c> if the path is unsafe.
+ The path is considered unsafe in the following circumstances:</p>
+ <list type="bulleted">
+ <item><p>The path is not relative.</p></item>
+ <item><p>A ".." component would climb up above the root of
+ the relative path.</p></item>
+ <item><p>A symbolic link in the path points above the root
+ of the relative path.</p></item>
+ </list>
+ <p><em>Examples:</em></p>
+ <pre>
+1> <input>{ok, Cwd} = file:get_cwd().</input>
+...
+2> <input>filelib:safe_relative_path("dir/sub_dir/..", Cwd).</input>
+"dir"
+3> <input>filelib:safe_relative_path("dir/..", Cwd).</input>
+[]
+4> <input>filelib:safe_relative_path("dir/../..", Cwd).</input>
+unsafe
+5> <input>filelib:safe_relative_path("/abs/path", Cwd).</input>
+unsafe</pre>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 60b7eb3436..3dca60c2f0 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -570,6 +570,10 @@ true
<item><p>A ".." component would climb up above the root of
the relative path.</p></item>
</list>
+ <warning>
+ <p>This function is deprecated. Use <seealso marker="filelib#safe_relative_path/2">
+ <c>filelib:safe_relative_path/2</c></seealso> instead for sanitizing paths.</p>
+ </warning>
<p><em>Examples:</em></p>
<pre>
1> <input>filename:safe_relative_path("dir/sub_dir/..").</input>
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 2915c4f507..f9db5ba2a8 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -50,6 +50,7 @@
gen_event module Callback module
---------------- ---------------
gen_event:start
+gen_event:start_monitor
gen_event:start_link -----> -
gen_event:add_handler
@@ -58,6 +59,7 @@ gen_event:add_sup_handler -----> Module:init/1
gen_event:notify
gen_event:sync_notify -----> Module:handle_event/2
+gen_event:send_request
gen_event:call -----> Module:handle_call/2
- -----> Module:handle_info/2
@@ -126,6 +128,15 @@ gen_event:stop -----> Module:terminate/2
<datatype>
<name name="del_handler_ret"/>
</datatype>
+ <datatype>
+ <name name="request_id"/>
+ <desc>
+ <p>
+ A request handle, see <seealso marker="#send_request/3"> <c>send_request/3</c> </seealso>
+ for details.
+ </p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
@@ -284,6 +295,40 @@ gen_event:stop -----> Module:terminate/2
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <type>
+ <v>Msg = term()</v>
+ <v>RequestId = request_id()</v>
+ <v>Result = {reply, Reply} | no_reply | {error, Error}</v>
+ <v>Reply = Error = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seealso marker="#send_request/3"><c>send_request/3</c></seealso>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function shall be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ If the specified event handler is not
+ installed, the function returns <c>{error,bad_module}</c>. If
+ the callback function fails with <c>Reason</c> or returns an
+ unexpected value <c>Term</c>, this function returns
+ <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+ respectively. If the event manager dies before or during the
+ request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+ </p>
+ </desc>
+ </func>
+
<func>
<name since="">delete_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Delete an event handler from a generic event manager.</fsummary>
@@ -348,6 +393,49 @@ gen_event:stop -----> Module:terminate/2
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">send_request(EventMgrRef, Handler, Request) -> RequestId</name>
+ <fsummary>Send a request to a generic event manager.</fsummary>
+ <type>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Handler = Module | {Module,Id}</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>&nbsp;Id = term()</v>
+ <v>Request = term()</v>
+ <v>RequestId = request_id()</v>
+ </type>
+ <desc>
+ <p>
+ Sends a request to event handler <c>Handler</c> installed in
+ event manager <c>EventMgrRef</c> and returns a handle
+ <c>RequestId</c>. The return value <c>RequestId</c> shall
+ later be used with <seealso marker="#wait_response/2">
+ <c>wait_response/2</c></seealso> or <seealso
+ marker="#check_response/2">
+ <c>check_response/2</c></seealso> in the same process to
+ fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_event:wait_response(gen_event:send_request(EventMgrRef,Handler,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seealso marker="#call/3"><c>gen_event:call(EventMgrRef,Handler,Request,Timeout)</c></seealso>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The event manager calls <seealso marker="#Module:handle_call/2">
+ <c>Module:handle_call/2</c></seealso> to handle the request.
+ </p>
+ <p>
+ <c>Request</c> is any term that is passed as one of
+ the arguments to <c>Module:handle_call/3</c>.
+ </p>
+ </desc>
+ </func>
+
<func>
<name since="">start() -> Result</name>
<name since="">start(EventMgrName | Options) -> Result</name>
@@ -436,6 +524,40 @@ gen_event:stop -----> Module:terminate/2
</func>
<func>
+ <name since="OTP @OTP-16120@">start_monitor() -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(EventMgrName | Options) -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(EventMgrName, Options) -> Result</name>
+ <fsummary>Create a stand-alone event manager process.</fsummary>
+ <type>
+ <v>EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | {error,{already_started,Pid}}</v>
+ <v>&nbsp;Pid = pid()</v>
+ </type>
+ <desc>
+ <p>Creates a stand-alone event manager process, that is, an event
+ manager that is not part of a supervision tree (and thus has
+ no supervisor) and atomically sets up a monitor to
+ the newly created process.</p>
+ <p>For a description of the arguments and return values, see
+ <seealso marker="#start_link/0"><c>start_link/0,1</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="">stop(EventMgrRef) -> ok</name>
<name since="OTP 18.0">stop(EventMgrRef, Reason, Timeout) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
@@ -546,6 +668,49 @@ gen_event:stop -----> Module:terminate/2
</func>
<func>
+ <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = request_id()</v>
+ <v>Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Result = {reply, Reply} | timeout | {error, Error}</v>
+ <v>Reply = Error = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seealso marker="#send_request/3"><c>send_request/3</c></seealso>
+ from the event manager. This function must be called from the same
+ process from which <seealso marker="#send_request/3"><c>send_request/3</c></seealso>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function must be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ If the specified event handler is not
+ installed, the function returns <c>{error,bad_module}</c>. If
+ the callback function fails with <c>Reason</c> or returns an
+ unexpected value <c>Term</c>, this function returns
+ <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+ respectively. If the event manager dies before or during the
+ request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name since="">which_handlers(EventMgrRef) -> [Handler]</name>
<fsummary>Return all event handlers installed in a generic event manager.
</fsummary>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index a4554d7657..0221319dcc 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -48,11 +48,13 @@
gen_server module Callback module
----------------- ---------------
gen_server:start
+gen_server:start_monitor
gen_server:start_link -----> Module:init/1
gen_server:stop -----> Module:terminate/2
gen_server:call
+gen_server:send_request
gen_server:multi_call -----> Module:handle_call/3
gen_server:cast
@@ -155,8 +157,8 @@ gen_server:abcast -----> Module:handle_cast/2
<item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
registered through an alternative process registry</item>
</list>
- <p><c>Request</c> is any term that is passed as one of
- the arguments to <c>Module:handle_call/3</c>.</p>
+ <p><c>Request</c> is any term that is passed as the
+ first argument to <c>Module:handle_call/3</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for a reply, or
the atom <c>infinity</c> to wait indefinitely. Defaults to
@@ -200,6 +202,43 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
+ <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = term()</v>
+ <v>Result = {reply, Reply} | no_reply | {error, {Reason, ServerRef}}</v>
+ <v>Msg = Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function must be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_server</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name since="">enter_loop(Module, Options, State)</name>
<name since="">enter_loop(Module, Options, State, ServerName)</name>
<name since="">enter_loop(Module, Options, State, Timeout)</name>
@@ -232,7 +271,7 @@ gen_server:abcast -----> Module:handle_cast/2
is needed than the <c>gen_server</c> process behavior provides.</p>
<p><c>Module</c>, <c>Options</c>, and <c>ServerName</c> have
the same meanings as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seealso>.
However, if <c>ServerName</c> is specified, the process must
have been registered accordingly <em>before</em> this function
is called.</p>
@@ -280,8 +319,8 @@ gen_server:abcast -----> Module:handle_cast/2
<c>[node()|nodes()]</c>.</p>
<p><c>Name</c> is the locally registered name of each
<c>gen_server</c> process.</p>
- <p><c>Request</c> is any term that is passed as one of
- the arguments to <c>Module:handle_call/3</c>.</p>
+ <p><c>Request</c> is any term that is passed as the first
+ argument to <c>Module:handle_call/3</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for each reply, or
the atom <c>infinity</c> to wait indefinitely. Defaults
@@ -307,12 +346,11 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
- <name since="">reply(Client, Reply) -> Result</name>
+ <name since="">reply(Client, Reply) -> ok</name>
<fsummary>Send a reply to a client.</fsummary>
<type>
<v>Client - see below</v>
<v>Reply = term()</v>
- <v>Result = term()</v>
</type>
<desc>
<p>This function can be used by a <c>gen_server</c> process to
@@ -326,8 +364,55 @@ gen_server:abcast -----> Module:handle_cast/2
the callback function. <c>Reply</c> is any term
given back to the client as the return value of
<c>call/2,3</c> or <c>multi_call/2,3,4</c>.</p>
- <p>The return value <c>Result</c> is not further defined, and
- is always to be ignored.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name since="OTP-23">send_request(ServerRef, Request) -> RequestId</name>
+ <fsummary>Sends a request to a generic server.</fsummary>
+ <type>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>RequestId = term()</v>
+ <v>Timeout = int()>0 | infinity</v>
+ <v>Request = term()</v>
+ </type>
+ <desc>
+ <p>
+ Sends a request to the <c>ServerRef</c> of the
+ <c>gen_server</c> process and returns a handle <c>RequestId</c>.
+ The return value <c>RequestId</c> shall later be used with
+ <seealso marker="#wait_response/2"> <c>wait_response/2</c></seealso> or
+ <seealso marker="#check_response/2"> <c>check_response/2</c></seealso>
+ to fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_server:wait_response(gen_server:send_request(ServerRef,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seealso marker="#call/3"><c>gen_server:call(Server,Request,Timeout)</c></seealso>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The <c>gen_server</c> process calls
+ <seealso marker="#Module:handle_call/3"> <c>Module:handle_call/3</c></seealso>
+ to handle the request.
+ </p>
+ <p><c>ServerRef</c> can be any of the following:</p>
+ <list type="bulleted">
+ <item>The pid</item>
+ <item><c>Name</c>, if the <c>gen_server</c> process is locally
+ registered</item>
+ <item><c>{Name,Node}</c>, if the <c>gen_server</c> process is locally
+ registered at another node</item>
+ <item><c>{global,GlobalName}</c>, if the <c>gen_server</c> process is
+ globally registered</item>
+ <item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
+ registered through an alternative process registry</item>
+ </list>
+ <p><c>Request</c> is any term that is passed as the first
+ argument to <c>Module:handle_call/3</c>.</p>
</desc>
</func>
@@ -466,6 +551,43 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
+ <name since="OTP @OTP-16120@">start_monitor(Module, Args, Options) -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(ServerName, Module, Args, Options) -> Result</name>
+ <fsummary>Create a standalone <c>gen_server</c> process.</fsummary>
+ <type>
+ <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Module = atom()</v>
+ <v>Args = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | ignore | {error,Error}</v>
+ <v>&nbsp;Pid = pid()</v>
+ <v>&nbsp;Error = {already_started,Pid} | term()</v>
+ </type>
+ <desc>
+ <p>Creates a standalone <c>gen_server</c> process, that is, a
+ <c>gen_server</c> process that is not part of a supervision tree
+ (and thus has no supervisor) and atomically sets up a monitor to
+ the newly created server.</p>
+ <p>For a description of arguments and return values, see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the server, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the server. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP 18.0">stop(ServerRef) -> ok</name>
<name since="OTP 18.0">stop(ServerRef, Reason, Timeout) -> ok</name>
<fsummary>Synchronously stop a generic server.</fsummary>
@@ -498,6 +620,49 @@ gen_server:abcast -----> Module:handle_cast/2
is raised.</p>
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = term()</v>
+ <v>Result = {reply, Reply} | timeout | {error, {Reason, ServerRef}}</v>
+ <v>Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+ from the <c>gen_server</c> process. This function must be called
+ from the same process from which
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function can be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_server</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
</funcs>
<section>
@@ -782,8 +947,9 @@ gen_server:abcast -----> Module:handle_cast/2
</type>
<desc>
<p>Whenever a <c>gen_server</c> process is started using
- <seealso marker="#start/3"><c>start/3,4</c></seealso> or
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
+ <seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>,
+ or <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
this function is called by the new process to initialize.</p>
<p><c>Args</c> is the <c>Args</c> argument provided to the start
function.</p>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index 39fc565ff8..466eea6964 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -176,6 +176,7 @@
gen_statem module Callback module
----------------- ---------------
gen_statem:start
+gen_statem:start_monitor
gen_statem:start_link -----> Module:init/1
Server start or code change
@@ -185,6 +186,7 @@ gen_statem:stop -----> Module:terminate/3
gen_statem:call
gen_statem:cast
+gen_statem:send_request
erlang:send
erlang:'!' -----> Module:StateName/3
Module:handle_event/4
@@ -366,6 +368,7 @@ erlang:'!' -----> Module:StateName/3
</seealso>
for
<seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>,
<seealso marker="#start_link/3"><c>start_link/3,4</c></seealso> or
<seealso marker="#enter_loop/4"><c>enter_loop/4,5,6</c></seealso>,
that may be used to automatically hibernate the server.
@@ -569,8 +572,18 @@ handle_event(_, _, State, Data) ->
<name name="start_ret"/>
<desc>
<p>
- Return value from the start functions, for example,
- <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ Return value from the <c>start()</c> and <c>start_link()</c> functions,
+ for example, <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="start_mon_ret"/>
+ <desc>
+ <p>
+ Return value from the
+ <seealso marker="#start_monitor/3"><c>start_monitor()</c></seealso>
+ functions.
</p>
</desc>
</datatype>
@@ -683,11 +696,12 @@ handle_event(_, _, State, Data) ->
<p>
External events are of 3 types:
<c>{call,<anno>From</anno>}</c>, <c>cast</c>, or <c>info</c>.
- <seealso marker="#call/2">Calls</seealso>
- (synchronous) and
- <seealso marker="#cast/2">casts</seealso>
- originate from the corresponding API functions.
+ Type <c>call</c> originates from the API functions
+ <seealso marker="#call/2"><c>call/2</c></seealso>
+ and <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
For calls, the event contains whom to reply to.
+ Type <c>cast</c> originates from the API function
+ <seealso marker="#cast/2"><c>cast/2</c></seealso>.
Type <c>info</c> originates from regular process messages sent
to the <c>gen_statem</c>.
</p>
@@ -1550,6 +1564,16 @@ handle_event(_, _, State, Data) ->
</p>
</desc>
</datatype>
+
+ <datatype>
+ <name name="request_id"/>
+ <desc>
+ <p>
+ A request handle, see <seealso marker="#send_request/2"> <c>send_request/2</c> </seealso>
+ for details.
+ </p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
@@ -1651,6 +1675,37 @@ handle_event(_, _, State, Data) ->
</func>
<func>
+ <name name="check_response" arity="2" since="OTP-23"/>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function shall be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ The return value <c><anno>Reply</anno></c> is generated when a
+ <seealso marker="#state callback"><em>state callback</em></seealso>
+ returns with
+ <c>{reply,From,<anno>Reply</anno>}</c> as one
+ <seealso marker="#type-action"><c>action()</c></seealso>,
+ and that <c><anno>Reply</anno></c> becomes the return value
+ of this function.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_statem</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="enter_loop" arity="4" since="OTP 19.1"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
@@ -1711,16 +1766,16 @@ handle_event(_, _, State, Data) ->
<p>
<c><anno>Module</anno></c>, <c><anno>Opts</anno></c>
have the same meaning as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seealso>.
</p>
<p>
If <c><anno>Server</anno></c> is <c>self()</c> an anonymous
server is created just as when using
- <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3</c></seealso>.
If <c><anno>Server</anno></c> is a
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
a named server is created just as when using
- <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.
+ <seealso marker="#start_link/4"><c>start[_link|_monitor]/4</c></seealso>.
However, the
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
name must have been registered accordingly
@@ -1779,6 +1834,48 @@ handle_event(_, _, State, Data) ->
</func>
<func>
+ <name name="send_request" arity="2" since="OTP-23"/>
+ <fsummary>Send a request to a <c>gen_statem</c>.</fsummary>
+ <desc>
+ <p>
+ Sends a request to the <c>gen_statem</c>
+ <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ and returns a handle <c><anno>RequestId</anno></c>.
+ </p>
+ <p>
+ The return value <c><anno>RequestId</anno></c> shall later be used with
+ <seealso marker="#wait_response/2"> <c>wait_response/1,2</c></seealso> or
+ <seealso marker="#check_response/2"> <c>check_response/2</c></seealso>
+ to fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_statem:wait_response(gen_statem:send_request(ServerRef,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seealso marker="#call/3"><c>gen_statem:call(Server,Request,Timeout)</c></seealso>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The <c>gen_statem</c> calls the
+ <seealso marker="#state callback"><em>state callback</em></seealso>
+ with
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>{call,From}</c> and event content
+ <c><anno>Request</anno></c>.
+ </p>
+ <p>
+ A <c>Reply</c> is generated when a
+ <seealso marker="#state callback"><em>state callback</em></seealso>
+ returns with
+ <c>{reply,From,Reply}</c> as one
+ <seealso marker="#type-action"><c>action()</c></seealso>,
+ and that <c>Reply</c> becomes the return value
+ of <seealso marker="#wait_response/2"> <c>wait_response/1,2</c></seealso> or
+ <seealso marker="#check_response/2"> <c>check_response/2</c></seealso> function.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="start" arity="3" since="OTP 19.0"/>
<name name="start" arity="4" since="OTP 19.0"/>
<fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
@@ -1922,6 +2019,35 @@ handle_event(_, _, State, Data) ->
</func>
<func>
+ <name name="start_monitor" arity="3" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="4" since="OTP @OTP-16120@"/>
+ <fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
+ <desc>
+ <p>
+ Creates a standalone <c>gen_statem</c> process according to
+ OTP design principles (using
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ primitives) and atomically sets up a monitor to
+ the newly created process.
+ As it does not get linked to the calling process,
+ this start function cannot be used by a supervisor
+ to start a child.
+ </p>
+ <p>
+ For a description of arguments and return values, see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="stop" arity="1" since="OTP 19.0"/>
<fsummary>Synchronously stop a generic server.</fsummary>
<desc>
@@ -1967,6 +2093,45 @@ handle_event(_, _, State, Data) ->
</p>
</desc>
</func>
+
+ <func>
+ <name name="wait_response" arity="1" since="OTP-23"/>
+ <name name="wait_response" arity="2" since="OTP-23"/>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+ from the <c>gen_statem</c> process. This function must be called
+ from the same process from which
+ <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely. Defaults to
+ <c>infinity</c>.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function can be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c><anno>Reply</anno></c> is generated when a
+ <seealso marker="#state callback"><em>state callback</em></seealso>
+ returns with
+ <c>{reply,From,<anno>Reply</anno>}</c> as one
+ <seealso marker="#type-action"><c>action()</c></seealso>,
+ and that <c><anno>Reply</anno></c> becomes the return value
+ of this function.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_statem</c>
+ dies before or during this function call.
+ </p>
+ </desc>
+ </func>
</funcs>
<section>
@@ -2144,7 +2309,8 @@ handle_event(_, _, State, Data) ->
<marker id="Module:init-1"/>
<p>
Whenever a <c>gen_statem</c> is started using
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>,
or
<seealso marker="#start/3"><c>start/3,4</c></seealso>,
this function is called by the new process to initialize
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index 84b5f62c7f..f05c358866 100644
--- a/lib/stdlib/doc/src/io_protocol.xml
+++ b/lib/stdlib/doc/src/io_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2016</year>
+ <year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -168,16 +168,6 @@ ok
returns it "as is".</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{put_chars, Characters}
-{put_chars, Module, Function, Args}</pre>
-
- <p>These are to behave as <c>{put_chars, latin1, Characters}</c> and
- <c>{put_chars, latin1, Module, Function, Args}</c>, respectively.</p>
</section>
<section>
@@ -332,19 +322,6 @@ eof
</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{get_until, Prompt, Module, Function, ExtraArgs}
-{get_chars, Prompt, N}
-{get_line, Prompt}</pre>
-
- <p>These are to behave as
- <c>{get_until, latin1, Prompt, Module, Function, ExtraArgs}</c>,
- <c>{get_chars, latin1, Prompt, N}</c>, and
- <c>{get_line, latin1, Prompt}</c>, respectively.</p>
</section>
<section>
@@ -637,24 +614,6 @@ request({requests, Reqs}, State) -&gt;
function applying the requests in the list one after another, returning
the last result.</p>
- <p>We need to handle backward compatibility and the
- <seealso marker="kernel:file"><c>file</c></seealso> module (which
- uses the old requests until backward compatibility with pre-R13 nodes is
- no longer needed). Notice that the I/O server does not work with a simple
- <c>file:write/2</c> if these are not added:</p>
-
- <code>
-request({put_chars,Chars}, State) -&gt;
- request({put_chars,latin1,Chars}, State);
-request({put_chars,M,F,As}, State) -&gt;
- request({put_chars,latin1,M,F,As}, State);
-request({get_chars,Prompt,N}, State) -&gt;
- request({get_chars,latin1,Prompt,N}, State);
-request({get_line,Prompt}, State) -&gt;
- request({get_line,latin1,Prompt}, State);
-request({get_until, Prompt,M,F,As}, State) -&gt;
- request({get_until,latin1,Prompt,M,F,As}, State);</code>
-
<p><c>{error, request}</c> must be returned if the request is not
recognized:</p>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index bb983903a9..b5a78ae9f1 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -86,13 +86,11 @@
</desc>
</datatype>
<datatype>
- <name name="priority_level"/>
- </datatype>
- <datatype>
- <name name="max_heap_size"/>
+ <name name="start_spawn_option"/>
<desc>
- <p>See <seealso marker="erts:erlang#process_flag_max_heap_size">
- erlang:process_flag(max_heap_size, MaxHeapSize)</seealso>.</p>
+ <p>A restricted set of <seealso marker="#type-spawn_option">spawn
+ options</seealso>. Most notably <c>monitor</c> is <em>not</em> part
+ of these options.</p>
</desc>
</datatype>
<datatype>
@@ -295,8 +293,31 @@ init(Parent) ->
<desc>
<p>Spawns a new process and initializes it as described in the
beginning of this manual page. The process is spawned using the
- <seealso marker="erts:erlang#spawn_opt/2"><c>spawn_opt</c></seealso>
+ <seealso marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt</c></seealso>
BIFs.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start" arity="3" since=""/>
+ <name name="start" arity="4" since=""/>
+ <name name="start" arity="5" since=""/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>Starts a new process synchronously. Spawns the process and
+ waits for it to start. When the process has started, it
+ <em>must</em> call
+ <seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
+ or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <p>If <c><anno>Time</anno></c> is specified as an integer, this
+ function waits for <c><anno>Time</anno></c> milliseconds for the
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
<note>
<p>Using spawn option <c>monitor</c> is not
allowed. It causes the function to fail with reason
@@ -306,29 +327,68 @@ init(Parent) ->
</func>
<func>
- <name name="start" arity="3" since=""/>
- <name name="start" arity="4" since=""/>
- <name name="start" arity="5" since=""/>
<name name="start_link" arity="3" since=""/>
<name name="start_link" arity="4" since=""/>
<name name="start_link" arity="5" since=""/>
<fsummary>Start a new process synchronously.</fsummary>
<desc>
- <p>Starts a new process synchronously. Spawns the process and
- waits for it to start. When the process has started, it
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A link is atomically set on the
+ newly spawned process. When the process has started, it
+ <em>must</em> call
+ <seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
+ or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <p>If <c><anno>Time</anno></c> is specified as an integer, this
+ function waits for <c><anno>Time</anno></c> milliseconds for the
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>If the process crashes before it has called <c>init_ack/1,2</c>,
+ <c>Ret = {error, <anno>Reason</anno>}</c> will be returned if
+ the calling process traps exits.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
+ <note>
+ <p>Using spawn option <c>monitor</c> is not
+ allowed. It causes the function to fail with reason
+ <c>badarg</c>.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start_monitor" arity="3" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="4" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="5" since="OTP @OTP-16120@"/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A monitor is atomically set on the
+ newly spawned process. When the process has started, it
<em>must</em> call
<seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
where <c>Parent</c> is the process that evaluates this
function. At this time, <c>Ret</c> is returned.</p>
- <p>If function <c>start_link/3,4,5</c> is used and
- the process crashes before it has called <c>init_ack/1,2</c>,
- <c>{error, <anno>Reason</anno>}</c> is returned if the calling
- process traps exits.</p>
<p>If <c><anno>Time</anno></c> is specified as an integer, this
function waits for <c><anno>Time</anno></c> milliseconds for the
- new process to call <c>init_ack</c>, or <c>{error, timeout}</c> is
- returned, and the process is killed.</p>
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>
+ The return value is <c>{Ret, Mon}</c> where <c>Ret</c> corresponds
+ to the <c>Ret</c> argument in the call to <c>init_ack()</c>, and
+ <c>Mon</c> is the monitor reference of the monitor that has been
+ set up.
+ </p>
+ <p>
+ A <c>'DOWN'</c> message will be delivered to the caller if
+ this function returns, and the spawned process terminates. This is
+ true also in the case when the operation times out.
+ </p>
<p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
<c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index 8d61833d1f..d12f537b07 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -84,6 +84,7 @@
<xi:include href="sets.xml"/>
<xi:include href="shell.xml"/>
<xi:include href="shell_default.xml"/>
+ <xi:include href="shell_docs.xml"/>
<xi:include href="slave.xml"/>
<xi:include href="sofs.xml"/>
<xi:include href="string.xml"/>
diff --git a/lib/stdlib/doc/src/shell_docs.xml b/lib/stdlib/doc/src/shell_docs.xml
new file mode 100644
index 0000000000..a3b6f128c3
--- /dev/null
+++ b/lib/stdlib/doc/src/shell_docs.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2020</year><year>2020</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>shell_docs</title>
+ <prepared>Lukas Larsson</prepared>
+ <responsible></responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2020-02-19</date>
+ <rev>A</rev>
+ <file>shell_docs.xml</file>
+ </header>
+ <module since="OTP @OTP-16222@">shell_docs</module>
+ <modulesummary>Functions used to render documentation for a shell.</modulesummary>
+ <description>
+ <p>This module can be used to render function and type documentation
+ to be printed in a shell. It can only render documentation of the format
+ <c>application/erlang+html</c>. For more information about this format see
+ <seealso marker="erl_docgen:doc_storage">Documentation Storage</seealso>
+ in Erl_Docgen's User's Guide.
+ </p>
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="docs_v1"/>
+ </datatype>
+ <datatype>
+ <name name="chunk_element_type"/>
+ <desc>
+ <p>
+ The HTML tags allowed in <c>application/erlang+html</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="chunk_element_attr"/>
+ <name name="chunk_element_attrs"/>
+ <name name="chunk_element"/>
+ <name name="chunk_elements"/>
+ <desc>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+
+ <func>
+ <name name="render" arity="2" since="OTP @OTP-16222@"/>
+ <fsummary>Render the documentation for a module.</fsummary>
+ <desc>
+ <p>Render the documentation for a module.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="render" arity="3" since="OTP @OTP-16222@"/>
+ <name name="render" arity="4" since="OTP @OTP-16222@"/>
+ <fsummary>Render the documentation for a function.</fsummary>
+ <desc>
+ <p>Render the documentation for a function.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="render_type" arity="2" since="OTP @OTP-16222@"/>
+ <fsummary>Render a list of all available types in a module.</fsummary>
+ <desc>
+ <p>Render a list of all available types in a module.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="render_type" arity="3" since="OTP @OTP-16222@"/>
+ <name name="render_type" arity="4" since="OTP @OTP-16222@"/>
+ <fsummary>Render the documentation of a type in a module.</fsummary>
+ <desc>
+ <p>Render the documentation of a type in a module.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="validate" arity="1" since="OTP @OTP-16222@"/>
+ <fsummary>Validate the documentation</fsummary>
+ <desc>
+ <p>This function can be used to do a basic validation of
+ the doc content of <c>application/erlang+html</c> format.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="normalize" arity="1" since="OTP @OTP-16222@"/>
+ <fsummary>Normalize the documentation</fsummary>
+ <desc>
+ <p>This function can be used to do whitespace normalization
+ of <c>application/erlang+html</c> documentation.</p>
+ </desc>
+ </func>
+
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
index fd2d625685..9b11a6941c 100644
--- a/lib/stdlib/doc/src/specs.xml
+++ b/lib/stdlib/doc/src/specs.xml
@@ -51,6 +51,7 @@
<xi:include href="../specs/specs_sets.xml"/>
<xi:include href="../specs/specs_shell.xml"/>
<xi:include href="../specs/specs_shell_default.xml"/>
+ <xi:include href="../specs/specs_shell_docs.xml"/>
<xi:include href="../specs/specs_slave.xml"/>
<xi:include href="../specs/specs_sofs.xml"/>
<xi:include href="../specs/specs_string.xml"/>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index f15b1a2dd3..1f319c97b7 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>2018</year>
+ <year>1996</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -300,6 +300,18 @@ child_spec() = #{id => child_id(), % mandatory
<name name="shutdown"/>
</datatype>
<datatype>
+ <name name="startchild_err"/>
+ </datatype>
+ <datatype>
+ <name name="startchild_ret"/>
+ </datatype>
+ <datatype>
+ <name name="startlink_err"/>
+ </datatype>
+ <datatype>
+ <name name="startlink_ret"/>
+ </datatype>
+ <datatype>
<name name="strategy"/>
</datatype>
<datatype>
diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml
index 54ea647f03..cc4186e5ef 100644
--- a/lib/stdlib/doc/src/uri_string.xml
+++ b/lib/stdlib/doc/src/uri_string.xml
@@ -322,9 +322,9 @@
<pre>
1> <input>URIMap = #{fragment => "nose", host => "example.com", path => "/over/there",</input>
1> port => 8042, query => "name=ferret", scheme => "foo", userinfo => "user"}.
-#{fragment => "top",host => "example.com",
- path => "/over/there",port => 8042,query => "?name=ferret",
- scheme => foo,userinfo => "user"}
+#{fragment => "nose",host => "example.com",
+ path => "/over/there",port => 8042,query => "name=ferret",
+ scheme => "foo",userinfo => "user"}
2> <input>uri_string:recompose(URIMap).</input>
"foo://example.com:8042/over/there?name=ferret#nose"</pre>
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index bb2ed7727a..0d36478b05 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2016</year>
+ <year>2006</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -358,6 +358,8 @@
<name name="create" arity="2" since=""/>
<name name="create" arity="3" since=""/>
<fsummary>Create a zip archive with options.</fsummary>
+ <type name="create_option"/>
+ <type name="extension_spec"/>
<desc>
<p>Creates a zip archive containing the files specified in
<c><anno>FileList</anno></c>.</p>
diff --git a/lib/stdlib/scripts/update_deprecations b/lib/stdlib/scripts/update_deprecations
new file mode 100755
index 0000000000..810a186b53
--- /dev/null
+++ b/lib/stdlib/scripts/update_deprecations
@@ -0,0 +1,149 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-mode(compile).
+
+-import(lists, [foldl/3,sort/1]).
+
+-record(st, { functions = [], types = [] }).
+
+main([_|_]=Directories) ->
+ emit(summarize(Directories)),
+ halt(0).
+
+summarize(Directories) ->
+ foldl(fun summarize_directory/2, #st{}, Directories).
+
+summarize_directory(Dir, Acc) ->
+ Files = [filename:join(Dir, F) || F <- filelib:wildcard("*.beam", Dir)],
+ foldl(fun summarize_file/2, Acc, Files).
+
+summarize_file(File, Acc) ->
+ {ok, {Module, [Chunk]}} = beam_lib:chunks(File, [attributes]),
+ {attributes, Attributes} = Chunk,
+ summarize_attributes(Attributes, Module, Acc).
+
+summarize_attributes([{deprecated, Ds} | As], Module, Acc0) ->
+ Fs = sa_1(Ds, deprecated, Module, Acc0#st.functions),
+ Acc = Acc0#st{ functions = Fs },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{removed, Rs} | As], Module, Acc0) ->
+ Fs = sa_1(Rs, removed, Module, Acc0#st.functions),
+ Acc = Acc0#st{ functions = Fs },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{deprecated_type, Ds} | As], Module, Acc0) ->
+ Ts = sa_1(Ds, deprecated, Module, Acc0#st.types),
+ Acc = Acc0#st{ types = Ts },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{removed_type, Rs} | As], Module, Acc0) ->
+ Ts = sa_1(Rs, removed, Module, Acc0#st.types),
+ Acc = Acc0#st{ types = Ts },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([_ | As], Module, Acc) ->
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([], _Module, Acc) ->
+ Acc.
+
+sa_1([{F, A, Info} | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, F, A, Info} | Acc0]);
+sa_1([{F, A} | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, F, A, undefined} | Acc0]);
+sa_1([module | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, '_', '_', undefined} | Acc0]);
+sa_1([], _Tag, _Module, Acc) ->
+ Acc.
+
+%%
+
+emit(#st{ functions = Fs, types = Ts }) ->
+ io:format("%%\n"
+ "%% WARNING: DO NOT EDIT THIS FILE.\n"
+ "%%\n"
+ "%% This file was auto-generated from attributes in the source\n"
+ "%% code.\n"
+ "%%\n"
+ "%% To add a description to a deprecation or removal attribute,\n"
+ "%% write a string after the arity:\n"
+ "%%\n"
+ "%% -deprecated([{foo,1,\"use bar/1 instead\"}]).\n"
+ "%% -deprecated_type([{gadget,1,\"use widget/1 instead\"}]).\n"
+ "%% -removed([{hello,2,\"use there/2 instead\"}]).\n"
+ "%% -removed_type([{frobnitz,1,\"use grunka/1 instead\"}]).\n"
+ "%%\n"
+ "%% Descriptions cannot be given with the `f/1` shorthand, and\n"
+ "%% it will fall back to a generic description referring the\n"
+ "%% user to the documentation.\n"
+ "%%\n"
+ "%% Use `./otp_build update_deprecations` to update this file\n"
+ "%% after adding an attribute.\n"
+ "%%\n"
+ "-module(otp_internal).\n"
+ "-include(\"otp_internal.hrl\").\n"
+ "%%\n"),
+
+ emit_function("obsolete", Fs),
+ emit_function("obsolete_type", Ts),
+
+ ok.
+
+emit_function(FuncName, Entries) ->
+ io:format("-dialyzer({no_match, ~ts/3}).\n", [FuncName]),
+ [emit_clause(FuncName, E) || E <- sort_clauses(Entries)],
+
+ io:format("~ts(_,_,_) -> no.\n\n", [FuncName]).
+
+sort_clauses(Entries) ->
+ Tagged = [{clause_order(E), E} || E <- Entries],
+ [E || {_, E} <- sort(Tagged)].
+
+%% Wildcard matches must be emitted *after* specific matches to avoid
+%% losing descriptions.
+clause_order({_Tag, _Module, F, A, _Info}) when F =/= '_', A =/= '_' -> 0;
+clause_order({_Tag, _Module, F, '_', _Info}) when F =/= '_' -> 1;
+clause_order({_Tag, _Module, '_', A, _Info}) when A =/= '_' -> 2;
+clause_order({_Tag, _Module, '_', '_', _Info}) -> 3.
+
+emit_clause(FuncName, {Tag, M, F, A, Info}) ->
+ io:format("~ts(~ts, ~ts, ~ts) ->\n"
+ " {~p, ~p};\n",
+ [FuncName, match_string(M), match_string(F), match_string(A),
+ Tag, info_string(Info)]).
+
+%%
+
+info_string(undefined) ->
+ "see the documentation for details";
+info_string(next_version) ->
+ "will be removed in the next version. "
+ "See the documentation for details";
+info_string(next_major_release) ->
+ "will be removed in the next major release. "
+ "See the documentation for details";
+info_string(eventually) ->
+ "will be removed in a future release. "
+ "See the documentation for details";
+info_string(String) when is_list(String) ->
+ String.
+
+match_string('_') -> "_";
+match_string(Term) -> io_lib:format("~p", [Term]).
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 86003c953d..c410b36964 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -112,6 +112,7 @@ MODULES= \
sets \
shell \
shell_default \
+ shell_docs \
slave \
sofs \
string \
@@ -133,7 +134,7 @@ HRL_FILES= \
../include/qlc.hrl \
../include/zip.hrl
-INTERNAL_HRL_FILES= dets.hrl erl_tar.hrl
+INTERNAL_HRL_FILES= dets.hrl erl_tar.hrl otp_internal.hrl
ERL_FILES= $(MODULES:%=%.erl)
@@ -149,6 +150,12 @@ APPUP_FILE= stdlib.appup
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+ifeq ($(TARGET),win32)
+ EXE_SUFFIX=.exe
+else
+ EXE_SUFFIX=
+endif
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -207,7 +214,7 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
$(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
unicode_util.erl: ../uc_spec/*
- escript ../uc_spec/gen_unicode_mod.escript
+ escript$(EXE_SUFFIX) ../uc_spec/gen_unicode_mod.escript
# ----------------------------------------------------
# Release Target
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index aa992f17ab..967ed33c61 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -19,6 +19,7 @@
%%
-module(beam_lib).
-behaviour(gen_server).
+-compile({nowarn_deprecated_function,{crypto,block_decrypt,4}}).
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 0362b72536..6af3951604 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -19,6 +19,8 @@
%%
-module(c).
+-include_lib("kernel/include/eep48.hrl").
+
%% Utilities to use from shell.
%% Avoid warning for local function error/2 clashing with autoimported BIF.
@@ -28,6 +30,7 @@
lc_batch/0, lc_batch/1,
i/3,pid/3,m/0,m/1,mm/0,lm/0,
bt/1, q/0,
+ h/1,h/2,h/3,ht/1,ht/2,ht/3,
erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, uptime/0,
nregs/0,pwd/0,ls/0,ls/1,cd/1,memory/1,memory/0, xm/1]).
@@ -48,6 +51,9 @@ help() ->
"cd(Dir) -- change working directory\n"
"flush() -- flush any messages sent to the shell\n"
"help() -- help info\n"
+ "h(M) -- module documentation\n"
+ "h(M,F) -- module function documentation\n"
+ "h(M,F,A) -- module function arity documentation\n"
"i() -- information about the system\n"
"ni() -- information about the networked system\n"
"i(X,Y,Z) -- information about pid <X,Y,Z>\n"
@@ -147,6 +153,82 @@ c(SrcFile, NewOpts, Filter, BeamFile, Info) ->
format("Recompiling ~ts\n", [SrcFile]),
safe_recompile(SrcFile, Options, BeamFile).
+-type h_return() :: ok | {error, missing | {unknown_format, unicode:chardata()}}.
+-type ht_return() :: h_return() | {error, type_missing}.
+-type hf_return() :: h_return() | {error, function_missing}.
+
+-spec h(module()) -> h_return().
+h(Module) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec h(module(),function()) -> hf_return().
+h(Module,Function) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Function, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec h(module(),function(),arity()) -> hf_return().
+h(Module,Function,Arity) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Function, Arity, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module()) -> h_return().
+ht(Module) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module(),Type :: atom()) -> ht_return().
+ht(Module,Type) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Type, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module(),Type :: atom(),arity()) ->
+ ht_return().
+ht(Module,Type,Arity) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Type, Arity, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+format_docs({error,_} = E) ->
+ E;
+format_docs(Docs) ->
+ format("~ts",[Docs]).
+
old_options(Info) ->
case lists:keyfind(options, 1, Info) of
{options, Opts} -> Opts;
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index ef6d1882e6..2f95f54312 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -54,7 +54,8 @@
valid_date/1,
valid_date/3]).
--deprecated([{local_time_to_universal_time,1}]).
+-deprecated([{local_time_to_universal_time,1,
+ "use calendar:local_time_to_universal_time_dst/1 instead"}]).
-define(SECONDS_PER_MINUTE, 60).
-define(SECONDS_PER_HOUR, 3600).
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index f027d05f55..6078c5e67b 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -352,9 +352,6 @@ do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) ->
%% don't blink after a $
do_op({blink,C,_}, Bef=[$$|_], Aft, Rs) ->
do_op({insert,C}, Bef, Aft, Rs);
-%do_op({blink,C,M}, Bef, [], Rs) ->
-% N = over_paren(Bef, C, M),
-% {blink,N+1,{[C|Bef],[]},[{move_rel,-(N+1)},{put_chars,[C]}|Rs]};
do_op({blink,C,M}, Bef, Aft, Rs) ->
case over_paren(Bef, C, M) of
beep ->
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index bdcefda6e5..bb6ad26d8f 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -32,35 +32,71 @@
%% function name must be on the same line. CurrentBefore is reversed
%% and over_word/3 reverses the characters it finds. In certain cases
%% possible expansions are printed.
+%%
+%% The function also handles expansion with "h(" for module and functions.
expand(Bef0) ->
{Bef1,Word,_} = edlin:over_word(Bef0, [], 0),
case over_white(Bef1, [], 0) of
- {[$:|Bef2],_White,_Nwh} ->
+ {[$,|Bef2],_White,_Nwh} ->
+ {Bef3,_White1,_Nwh1} = over_white(Bef2, [], 0),
+ {Bef4,Mod,_Nm} = edlin:over_word(Bef3, [], 0),
+ case expand_function(Bef4) of
+ help ->
+ expand_function_name(Mod, Word, ",");
+ _ ->
+ expand_module_name(Word, ",")
+ end;
+ {[$:|Bef2],_White,_Nwh} ->
{Bef3,_White1,_Nwh1} = over_white(Bef2, [], 0),
{_,Mod,_Nm} = edlin:over_word(Bef3, [], 0),
- expand_function_name(Mod, Word);
+ expand_function_name(Mod, Word, "(");
{_,_,_} ->
- expand_module_name(Word)
+ CompleteChar
+ = case expand_function(Bef1) of
+ help -> ",";
+ _ -> ":"
+ end,
+ expand_module_name(Word, CompleteChar)
end.
-expand_module_name(Prefix) ->
- match(Prefix, code:all_loaded(), ":").
+expand_function("("++Str) ->
+ case edlin:over_word(Str, [], 0) of
+ {_,"h",_} ->
+ help;
+ {_,"ht",_} ->
+ help_type;
+ _ ->
+ module
+ end;
+expand_function(_) ->
+ module.
+
+expand_module_name("",_) ->
+ {no, [], []};
+expand_module_name(Prefix,CompleteChar) ->
+ match(Prefix, [{list_to_atom(M),P} || {M,P,_} <- code:all_available()], CompleteChar).
-expand_function_name(ModStr, FuncPrefix) ->
+expand_function_name(ModStr, FuncPrefix, CompleteChar) ->
case to_atom(ModStr) of
{ok, Mod} ->
- case erlang:module_loaded(Mod) of
- true ->
- L = Mod:module_info(),
- case lists:keyfind(exports, 1, L) of
- {_, Exports} ->
- match(FuncPrefix, Exports, "(");
- _ ->
- {no, [], []}
- end;
- false ->
- {no, [], []}
- end;
+ Exports =
+ case erlang:module_loaded(Mod) of
+ true ->
+ Mod:module_info(exports);
+ false ->
+ case beam_lib:chunks(code:which(Mod), [exports]) of
+ {ok, {Mod, [{exports,E}]}} ->
+ E;
+ _ ->
+ {no, [], []}
+ end
+ end,
+ case Exports of
+ {no, [], []} ->
+ {no, [], []};
+ Exports ->
+ match(FuncPrefix, Exports, CompleteChar)
+ end;
error ->
{no, [], []}
end.
@@ -99,8 +135,10 @@ match(Prefix, Alts, Extra0) ->
{no, [], []}
end.
-flat_write(T) ->
- lists:flatten(io_lib:fwrite("~tw",[T])).
+flat_write(T) when is_atom(T) ->
+ lists:flatten(io_lib:fwrite("~tw",[T]));
+flat_write(S) ->
+ S.
%% Return the list of names L in multiple columns.
format_matches(L) ->
diff --git a/lib/stdlib/src/erl_error.erl b/lib/stdlib/src/erl_error.erl
index fdcb9e824c..5fbf5a6282 100644
--- a/lib/stdlib/src/erl_error.erl
+++ b/lib/stdlib/src/erl_error.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
%%
-module(erl_error).
--export([format_exception/6, format_exception/7,
+-export([format_exception/6, format_exception/7, format_exception/8,
format_stacktrace/4, format_stacktrace/5,
format_call/4, format_call/5, format_fun/1, format_fun/2]).
@@ -38,20 +38,34 @@ format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) ->
%% -> iolist() | unicode:charlist() (no \n at end)
%% FormatFun = fun(Term, I) -> iolist() | unicode:charlist().
-format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding)
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding) ->
+ FF = wrap_format_fun_2(FormatFun),
+ format_exception(I, Class, Reason, StackTrace, StackFun, FF, Encoding, -1).
+
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding,
+ CharsLimit)
when is_integer(I), I >= 1, is_function(StackFun, 3),
- is_function(FormatFun, 2) ->
+ is_function(FormatFun, 3), is_integer(CharsLimit) ->
S = n_spaces(I-1),
{Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace),
- Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding),
+ StLimit = if
+ CharsLimit < 0 ->
+ CharsLimit;
+ true ->
+ %% Reserve one third for the stacktrace.
+ CharsLimit div 3
+ end,
+ St = format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding, StLimit),
+ Lim = sub(sub(CharsLimit, exited(Class), latin1), St, Encoding),
+ Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding, Lim),
FormatString = case Encoding of
latin1 -> "~s~s";
_ -> "~s~ts"
end,
Expl = io_lib:fwrite(FormatString, [exited(Class), Expl0]),
- case format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding) of
+ case St of
[] -> Expl;
- Stack -> [Expl, $\n, Stack]
+ _ -> [Expl, $\n, St]
end.
%% -> iolist() (no \n at end)
@@ -63,7 +77,8 @@ format_stacktrace(I, StackTrace, StackFun, FormatFun, Encoding)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
S = n_spaces(I-1),
- format_stacktrace1(S, StackTrace, FormatFun, StackFun, Encoding).
+ FF = wrap_format_fun_2(FormatFun),
+ format_stacktrace1(S, StackTrace, FF, StackFun, Encoding, -1).
%% -> iolist() (no \n at end)
format_call(I, ForMForFun, As, FormatFun) ->
@@ -72,7 +87,8 @@ format_call(I, ForMForFun, As, FormatFun) ->
%% -> iolist() | unicode:charlist() (no \n at end)
format_call(I, ForMForFun, As, FormatFun, Enc)
when is_integer(I), I >= 1, is_list(As), is_function(FormatFun, 2) ->
- format_call("", n_spaces(I-1), ForMForFun, As, FormatFun, Enc).
+ FF = wrap_format_fun_2(FormatFun),
+ format_call("", n_spaces(I-1), ForMForFun, As, FF, Enc).
%% -> iolist() (no \n at end)
format_fun(Fun) ->
@@ -94,6 +110,9 @@ format_fun(Fun, Enc) when is_function(Fun) ->
mfa_to_string(M, F, A, Enc)
end.
+wrap_format_fun_2(FormatFun) ->
+ fun(T, I1, CL) -> {FormatFun(T, I1), CL} end.
+
analyze_exception(error, Term, Stack) ->
case {is_stacktrace(Stack), Stack, Term} of
{true, [{_,_,As,_}=MFAL|MFAs], function_clause} when is_list(As) ->
@@ -127,82 +146,83 @@ is_stacktrace(_) ->
false.
%% ERTS exit codes (some of them are also returned by erl_eval):
-explain_reason(badarg, error, [], _PF, _S, _Enc) ->
+explain_reason(badarg, error, [], _PF, _S, _Enc, _CL) ->
<<"bad argument">>;
-explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc) -> % orelse, andalso
- format_value(V, <<"bad argument: ">>, Cl, PF, S);
-explain_reason(badarith, error, [], _PF, _S, _Enc) ->
+explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc, CL) -> % orelse, andalso
+ format_value(V, <<"bad argument: ">>, Cl, PF, S, CL);
+explain_reason(badarith, error, [], _PF, _S, _Enc, _CL) ->
<<"an error occurred when evaluating an arithmetic expression">>;
-explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, Enc)
+explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, Enc, _CL)
when is_function(Fun) ->
%% Only the arity is displayed, not the arguments As.
io_lib:fwrite(<<"~ts called with ~s">>,
[format_fun(Fun, Enc), argss(length(As))]);
-explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc) ->
- format_value(Term, <<"bad function ">>, Cl, PF, S);
-explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc) ->
+explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(Term, <<"bad function ">>, Cl, PF, S, CL);
+explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc, CL) ->
Str = <<"no match of right hand side value ">>,
- format_value(Term, Str, Cl, PF, S);
-explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc) ->
+ format_value(Term, Str, Cl, PF, S, CL);
+explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc, CL) ->
%% "there is no case clause with a true guard sequence and a
%% pattern matching..."
- format_value(V, <<"no case clause matching ">>, Cl, PF, S);
-explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc) ->
+ format_value(V, <<"no case clause matching ">>, Cl, PF, S, CL);
+explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc, _CL) ->
%% Shell commands
FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]),
[<<"no function clause matching call to ">> | FAs];
-explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc) ->
+explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc, CL) ->
Str = <<"no function clause matching ">>,
- [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc),$\s|location(Loc)];
-explain_reason(if_clause, error, [], _PF, _S, _Enc) ->
+ [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc, CL),$\s|location(Loc)];
+explain_reason(if_clause, error, [], _PF, _S, _Enc, _CL) ->
<<"no true branch found when evaluating an if expression">>;
-explain_reason(noproc, error, [], _PF, _S, _Enc) ->
+explain_reason(noproc, error, [], _PF, _S, _Enc, _CL) ->
<<"no such process or port">>;
-explain_reason(notalive, error, [], _PF, _S, _Enc) ->
+explain_reason(notalive, error, [], _PF, _S, _Enc, _CL) ->
<<"the node cannot be part of a distributed system">>;
-explain_reason(system_limit, error, [], _PF, _S, _Enc) ->
+explain_reason(system_limit, error, [], _PF, _S, _Enc, _CL) ->
<<"a system limit has been reached">>;
-explain_reason(timeout_value, error, [], _PF, _S, _Enc) ->
+explain_reason(timeout_value, error, [], _PF, _S, _Enc, _CL) ->
<<"bad receive timeout value">>;
-explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc) ->
+explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc, CL) ->
%% "there is no try clause with a true guard sequence and a
%% pattern matching..."
- format_value(V, <<"no try clause matching ">>, Cl, PF, S);
-explain_reason(undef, error, [{M,F,A,_}], _PF, _S, Enc) ->
+ format_value(V, <<"no try clause matching ">>, Cl, PF, S, CL);
+explain_reason(undef, error, [{M,F,A,_}], _PF, _S, Enc, _CL) ->
%% Only the arity is displayed, not the arguments, if there are any.
io_lib:fwrite(<<"undefined function ~ts">>,
[mfa_to_string(M, F, n_args(A), Enc)]);
-explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, Enc) ->
+explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, Enc, _CL) ->
%% Give nicer reports for undefined shell functions
%% (but not when the user actively calls shell_default:F(...)).
FS = to_string(F, Enc),
io_lib:fwrite(<<"undefined shell command ~ts/~w">>, [FS, n_args(A)]);
%% Exit codes returned by erl_eval only:
-explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc) ->
+explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc, _CL) ->
io_lib:fwrite(<<"limit of number of arguments to interpreted function"
" exceeded">>, []);
-explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc) ->
- format_value(V, <<"bad filter ">>, Cl, PF, S);
-explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc) ->
- format_value(V, <<"bad generator ">>, Cl, PF, S);
-explain_reason({unbound,V}, error, [], _PF, _S, _Enc) ->
+explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(V, <<"bad filter ">>, Cl, PF, S, CL);
+explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(V, <<"bad generator ">>, Cl, PF, S, CL);
+explain_reason({unbound,V}, error, [], _PF, _S, _Enc, _CL) ->
io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
%% Exit codes local to the shell module (restricted shell):
-explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc) ->
+explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc, CL) ->
Str = <<"restricted shell module returned bad value ">>,
- format_value(V, Str, Cl, PF, S);
+ format_value(V, Str, Cl, PF, S, CL);
explain_reason({restricted_shell_disallowed,{ForMF,As}},
- exit=Cl, [], PF, S, Enc) ->
+ exit=Cl, [], PF, S, Enc, CL) ->
%% ForMF can be a fun, but not a shell fun.
Str = <<"restricted shell does not allow ">>,
- format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc);
-explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc) ->
+ format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc, CL);
+explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc, _CL) ->
<<"restricted shell starts now">>;
-explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc) ->
+explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc, _CL) ->
<<"restricted shell stopped">>;
%% Other exit code:
-explain_reason(Reason, Class, [], PF, S, _Enc) ->
- PF(Reason, (iolist_size(S)+1) + exited_size(Class)).
+explain_reason(Reason, Class, [], PF, S, _Enc, CL) ->
+ {L, _} = PF(Reason, (iolist_size(S)+1) + exited_size(Class), CL),
+ L.
n_args(A) when is_integer(A) ->
A;
@@ -218,29 +238,33 @@ argss(2) ->
argss(I) ->
io_lib:fwrite(<<"~w arguments">>, [I]).
-format_stacktrace1(S0, Stack0, PF, SF, Enc) ->
+format_stacktrace1(S0, Stack0, PF, SF, Enc, CL) ->
Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A)
end, lists:reverse(Stack0)),
S = [" " | S0],
Stack = lists:reverse(Stack1),
- format_stacktrace2(S, Stack, 1, PF, Enc).
-
-format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) ->
- [io_lib:fwrite(<<"~s~s ~ts ~ts">>,
- [sep(N, S), origin(N, M, F, A),
- mfa_to_string(M, F, A, Enc),
- location(L)])
- | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
-format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc) when is_list(As) ->
+ format_stacktrace2(S, Stack, 1, PF, Enc, CL).
+
+format_stacktrace2(_S, _Stack, _N, _PF, _Enc, _CL=0) ->
+ [];
+format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc, CL) when is_integer(A) ->
+ Cs = io_lib:fwrite(<<"~s~s ~ts ~ts">>,
+ [sep(N, S), origin(N, M, F, A),
+ mfa_to_string(M, F, A, Enc),
+ location(L)]),
+ CL1 = sub(CL, Cs, Enc),
+ [Cs | format_stacktrace2(S, Fs, N + 1, PF, Enc, CL1)];
+format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc, CL) when is_list(As) ->
A = length(As),
CalledAs = [S,<<" called as ">>],
- C = format_call("", CalledAs, {M,F}, As, PF, Enc),
- [io_lib:fwrite(<<"~s~s ~ts\n~s~ts">>,
- [sep(N, S), origin(N, M, F, A),
- mfa_to_string(M, F, A, Enc),
- CalledAs, C])
- | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
-format_stacktrace2(_S, [], _N, _PF, _Enc) ->
+ C = format_call("", CalledAs, {M,F}, As, PF, Enc, CL),
+ Cs = io_lib:fwrite(<<"~s~s ~ts\n~s~ts">>,
+ [sep(N, S), origin(N, M, F, A),
+ mfa_to_string(M, F, A, Enc),
+ CalledAs, C]),
+ CL1 = sub(CL, Enc, Cs),
+ [Cs | format_stacktrace2(S, Fs, N + 1, PF, Enc, CL1)];
+format_stacktrace2(_S, [], _N, _PF, _Enc, _CL) ->
"".
location(L) ->
@@ -264,22 +288,26 @@ origin(1, M, F, A) ->
origin(_N, _M, _F, _A) ->
<<"in call from">>.
-format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc) ->
+format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc, CL) ->
Pre1 = [Pre0 | n_spaces(exited_size(Class))],
- format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc).
+ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, CL).
format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) ->
+ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, -1).
+
+format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, CL) ->
Arity = length(As),
[ErrStr |
case is_op(ForMForFun, Arity) of
{yes,Op} ->
- format_op(ErrStr, Pre1, Op, As, PF, Enc);
+ format_op(ErrStr, Pre1, Op, As, PF, Enc, CL);
no ->
MFs = mf_to_string(ForMForFun, Arity, Enc),
I1 = string:length([Pre1,ErrStr|MFs]),
- S1 = pp_arguments(PF, As, I1, Enc),
- S2 = pp_arguments(PF, As, string:length([Pre1|MFs]), Enc),
- Long = count_nl(pp_arguments(PF, [a2345,b2345], I1, Enc)) > 0,
+ S1 = pp_arguments(PF, As, I1, Enc, CL),
+ S2 = pp_arguments(PF, As, string:length([Pre1|MFs]), Enc, CL),
+ S3 = pp_arguments(PF, [a2345,b2345], I1, Enc, CL),
+ Long = count_nl(S3) > 0,
case Long or (count_nl(S2) < count_nl(S1)) of
true ->
[$\n, Pre1, MFs, S2];
@@ -288,14 +316,15 @@ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) ->
end
end].
-format_op(ErrStr, Pre, Op, [A1], PF, _Enc) ->
+format_op(ErrStr, Pre, Op, [A1], PF, _Enc, CL) ->
OpS = io_lib:fwrite(<<"~s ">>, [Op]),
I1 = iolist_size([ErrStr,Pre,OpS]),
- [OpS | PF(A1, I1+1)];
-format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) ->
+ {S, _} = PF(A1, I1+1, CL),
+ [OpS | S];
+format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc, CL) ->
I1 = iolist_size([ErrStr,Pre]),
- S1 = PF(A1, I1+1),
- S2 = PF(A2, I1+1),
+ {S1, CL1} = PF(A1, I1+1, CL),
+ {S2, _} = PF(A2, I1+1, CL1),
OpS = atom_to_list(Op),
Pre1 = [$\n | n_spaces(I1)],
case count_nl(S1) > 0 of
@@ -304,26 +333,28 @@ format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) ->
false ->
OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]),
Size1 = iolist_size([ErrStr,Pre|OpS2]),
- {Size2,S1_2} = size(Enc, S1),
- S2_2 = PF(A2, Size1+Size2+1),
+ Size2 = size(Enc, S1),
+ {S2_2, _} = PF(A2, Size1+Size2+1, CL1),
case count_nl(S2) < count_nl(S2_2) of
true ->
- [S1_2,Pre1,OpS,Pre1|S2];
+ [S1,Pre1,OpS,Pre1|S2];
false ->
- [S1_2,OpS2|S2_2]
+ [S1,OpS2|S2_2]
end
end.
-pp_arguments(PF, As, I, Enc) ->
+pp_arguments(PF, As, I, Enc, CL) ->
case {As, printable_list(Enc, As)} of
{[Int | T], true} ->
L = integer_to_list(Int),
Ll = length(L),
A = list_to_atom(lists:duplicate(Ll, $a)),
- S0 = unicode:characters_to_list(PF([A | T], I+1), Enc),
- brackets_to_parens([$[,L,string:slice(S0, 1+Ll)], Enc);
+ {S0, _} = PF([A | T], I+1, CL),
+ S = unicode:characters_to_list(S0, Enc),
+ brackets_to_parens([$[,L,string:slice(S, 1+Ll)], Enc);
_ ->
- brackets_to_parens(PF(As, I+1), Enc)
+ {S, _CL1} = PF(As, I+1, CL),
+ brackets_to_parens(S, Enc)
end.
brackets_to_parens(S, Enc) ->
@@ -361,12 +392,12 @@ mf_to_string(F, _A, Enc) ->
FS = to_string(F, Enc),
io_lib:fwrite(<<"~ts">>, [FS]).
-format_value(V, ErrStr, Class, PF, S) ->
+format_value(V, ErrStr, Class, PF, S, CL) ->
Pre1Sz = exited_size(Class),
- S1 = PF(V, Pre1Sz + iolist_size([S, ErrStr])+1),
+ {S1, _} = PF(V, Pre1Sz + iolist_size([S, ErrStr]) + 1, CL),
[ErrStr | case count_nl(S1) of
N1 when N1 > 1 ->
- S2 = PF(V, iolist_size(S) + 1 + Pre1Sz),
+ {S2, _} = PF(V, iolist_size(S) + 1 + Pre1Sz, CL),
case count_nl(S2) < N1 of
true ->
[$\n, S, n_spaces(Pre1Sz) | S2];
@@ -413,9 +444,17 @@ to_string(A, latin1) ->
to_string(A, _) ->
io_lib:write_atom(A).
+%% Make sure T does change sign.
+sub(T, _, _Enc) when T < 0 -> T;
+sub(T, S, Enc) ->
+ sub(T, size(Enc, S)).
+
+sub(T, Sz) when T >= Sz ->
+ T - Sz;
+sub(_T, _Sz) ->
+ 0.
+
size(latin1, S) ->
- {iolist_size(S),S};
-size(_, S0) ->
- S = unicode:characters_to_list(S0, unicode),
- true = is_list(S),
- {string:length(S),S}.
+ iolist_size(S);
+size(_, S) ->
+ string:length(S).
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 2066b2f60f..aa809ab05c 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -501,13 +501,13 @@ find_maxline(LC) ->
hide_calls(LC, MaxLine) ->
LineId0 = MaxLine + 1,
- {NLC, _, D} = hide(LC, LineId0, dict:new()),
+ {NLC, _, D} = hide(LC, LineId0, maps:new()),
{NLC, D}.
%% v/1 and local calls are hidden.
hide({value,L,V}, Id, D) ->
A = erl_anno:new(Id),
- {{atom,A,ok}, Id+1, dict:store(Id, {value,L,V}, D)};
+ {{atom,A,ok}, Id+1, maps:put(Id, {value,L,V}, D)};
hide({call,L,{atom,_,N}=Atom,Args}, Id0, D0) ->
{NArgs, Id, D} = hide(Args, Id0, D0),
C = case erl_internal:bif(N, length(Args)) of
@@ -517,7 +517,7 @@ hide({call,L,{atom,_,N}=Atom,Args}, Id0, D0) ->
A = erl_anno:new(Id),
{call,A,{remote,L,{atom,L,m},{atom,L,f}},NArgs}
end,
- {C, Id+1, dict:store(Id, {call,Atom}, D)};
+ {C, Id+1, maps:put(Id, {call,Atom}, D)};
hide(T0, Id0, D0) when is_tuple(T0) ->
{L, Id, D} = hide(tuple_to_list(T0), Id0, D0),
{list_to_tuple(L), Id, D};
@@ -532,7 +532,7 @@ unhide_calls({atom,A,ok}=E, MaxLine, D) ->
L = erl_anno:line(A),
if
L > MaxLine ->
- dict:fetch(L, D);
+ map_get(L, D);
true ->
E
end;
@@ -540,7 +540,7 @@ unhide_calls({call,A,{remote,L,{atom,L,m},{atom,L,f}}=F,Args}, MaxLine, D) ->
Line = erl_anno:line(A),
if
Line > MaxLine ->
- {call,Atom} = dict:fetch(Line, D),
+ {call,Atom} = map_get(Line, D),
{call,L,Atom,unhide_calls(Args, MaxLine, D)};
true ->
{call,A,F,unhide_calls(Args, MaxLine, D)}
@@ -1163,9 +1163,19 @@ match1({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),
- fun(E, Bs) -> expr(E, Bs, none, none, none) end);
+ EvalFun = fun(E, Bs) ->
+ case erl_lint:is_guard_expr(E) of
+ true -> ok;
+ false -> throw(invalid)
+ end,
+ try
+ expr(E, Bs, none, none, none)
+ catch
+ error:{unbound, _} ->
+ throw(invalid)
+ end
+ end,
+ eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), EvalFun);
match1({bin,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
match1({op,_,'++',{nil,_},R}, Term, Bs, BBs) ->
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index d7bd15d9db..5351490b1a 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -797,9 +797,13 @@ is_simple_val(Val) ->
pattern_bin(Es0, St) ->
foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es0).
-pattern_element({bin_element,Line,Expr0,Size,Type}, {Es,St0}) ->
+pattern_element({bin_element,Line,Expr0,Size0,Type}, {Es,St0}) ->
{Expr,St1} = pattern(Expr0, St0),
- {[{bin_element,Line,Expr,Size,Type} | Es],St1}.
+ {Size,St2} = case Size0 of
+ default -> {Size0,St1};
+ _ -> expr(Size0, St1)
+ end,
+ {[{bin_element,Line,Expr,Size,Type} | Es],St2}.
%% expr_bin([Element], State) -> {[Element],State}.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 939abaff00..f5059ac710 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -245,11 +245,14 @@ bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false.
bif(abs, 1) -> true;
bif(apply, 2) -> true;
bif(apply, 3) -> true;
+bif(atom_to_binary, 1) -> true;
bif(atom_to_binary, 2) -> true;
bif(atom_to_list, 1) -> true;
bif(binary_part, 2) -> true;
bif(binary_part, 3) -> true;
+bif(binary_to_atom, 1) -> true;
bif(binary_to_atom, 2) -> true;
+bif(binary_to_existing_atom, 1) -> true;
bif(binary_to_existing_atom, 2) -> true;
bif(binary_to_integer, 1) -> true;
bif(binary_to_integer, 2) -> true;
@@ -383,8 +386,16 @@ bif(spawn_link, 1) -> true;
bif(spawn_link, 2) -> true;
bif(spawn_link, 3) -> true;
bif(spawn_link, 4) -> true;
+bif(spawn_request, 1) -> true;
+bif(spawn_request, 2) -> true;
+bif(spawn_request, 3) -> true;
+bif(spawn_request, 4) -> true;
+bif(spawn_request, 5) -> true;
+bif(spawn_request_abandon, 1) -> true;
bif(spawn_monitor, 1) -> true;
+bif(spawn_monitor, 2) -> true;
bif(spawn_monitor, 3) -> true;
+bif(spawn_monitor, 4) -> true;
bif(spawn_opt, 2) -> true;
bif(spawn_opt, 3) -> true;
bif(spawn_opt, 4) -> true;
@@ -393,6 +404,8 @@ bif(split_binary, 2) -> true;
bif(statistics, 1) -> true;
bif(term_to_binary, 1) -> true;
bif(term_to_binary, 2) -> true;
+bif(term_to_iovec, 1) -> true;
+bif(term_to_iovec, 2) -> true;
bif(throw, 1) -> true;
bif(time, 0) -> true;
bif(tl, 1) -> true;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 54b0fbd999..5163b0df1d 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1,4 +1,4 @@
-%% -*- erlang-indent-level: 4 -*-
+%%% -*- erlang-indent-level: 4 -*-
%%
%% %CopyrightBegin%
%%
@@ -33,6 +33,10 @@
-import(lists, [member/2,map/2,foldl/3,foldr/3,mapfoldl/3,all/2,reverse/1]).
+%% Removed functions
+
+-removed([{modify_line,2,"use erl_parse:map_anno/2 instead"}]).
+
%% bool_option(OnOpt, OffOpt, Default, Options) -> boolean().
%% value_option(Flag, Default, Options) -> Value.
%% value_option(Flag, Default, OnOpt, OnVal, OffOpt, OffVal, Options) ->
@@ -81,17 +85,19 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-type module_or_mfa() :: module() | mfa().
+-type gexpr_context() :: 'guard' | 'bin_seg_size' | 'map_key'.
+
-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
+ calls = maps:new(), %Who calls who
imported = [], %Actually imported functions
- used_records = sets:new() %Used record definitions
- :: sets:set(atom()),
- used_types = dict:new() %Used type definitions
- :: dict:dict(ta(), line())
+ used_records = gb_sets:new() %Used record definitions
+ :: gb_sets:set(atom()),
+ used_types = maps:new() %Used type definitions
+ :: #{ta() := line()}
}).
@@ -104,8 +110,8 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
exports=gb_sets:empty() :: gb_sets:set(fa()),%Exports
imports=[] :: orddict:orddict(fa(), module()),%Imports
compile=[], %Compile flags
- records=dict:new() %Record definitions
- :: dict:dict(atom(), {line(),Fields :: term()}),
+ records=maps:new() %Record definitions
+ :: #{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
@@ -131,17 +137,20 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
xqlc= false :: boolean(), %true if qlc.hrl included
called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
- specs = dict:new() %Type specifications
- :: dict:dict(mfa(), line()),
- callbacks = dict:new() %Callback types
- :: dict:dict(mfa(), line()),
- optional_callbacks = dict:new() %Optional callbacks
- :: dict:dict(mfa(), line()),
- types = dict:new() %Type definitions
- :: dict:dict(ta(), #typeinfo{}),
+ specs = maps:new() %Type specifications
+ :: #{mfa() => line()},
+ callbacks = maps:new() %Callback types
+ :: #{mfa() => line()},
+ optional_callbacks = maps:new() %Optional callbacks
+ :: #{mfa() => line()},
+ types = maps:new() %Type definitions
+ :: #{ta() => #typeinfo{}},
exp_types=gb_sets:empty() %Exported types
:: gb_sets:set(ta()),
- in_try_head=false :: boolean() %In a try head.
+ in_try_head=false :: boolean(), %In a try head.
+ bvt = none :: 'none' | [any()], %Variables in binary pattern
+ gexpr_context = guard %Context of guard expression
+ :: gexpr_context()
}).
-type lint_state() :: #lint{}.
@@ -183,6 +192,14 @@ format_error({invalid_deprecated,D}) ->
format_error({bad_deprecated,{F,A}}) ->
io_lib:format("deprecated function ~tw/~w undefined or not exported",
[F,A]);
+format_error({invalid_removed,D}) ->
+ io_lib:format("badly formed removed attribute ~tw", [D]);
+format_error({bad_removed,{F,A}}) when F =:= '_'; A =:= '_' ->
+ io_lib:format("at least one function matching ~tw/~w is still exported",
+ [F,A]);
+format_error({bad_removed,{F,A}}) ->
+ io_lib:format("removed function ~tw/~w is still exported",
+ [F,A]);
format_error({bad_nowarn_unused_function,{F,A}}) ->
io_lib:format("function ~tw/~w undefined", [F,A]);
format_error({bad_nowarn_bif_clash,{F,A}}) ->
@@ -231,18 +248,18 @@ format_error({redefine_bif_import,{F,A}}) ->
format_error({deprecated, MFA, ReplacementMFA, Rel}) ->
io_lib:format("~s is deprecated and will be removed in ~s; use ~s",
[format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
-format_error({deprecated, {M1, F1, A1}, String}) when is_list(String) ->
- io_lib:format("~p:~p/~p: ~s", [M1, F1, A1, String]);
+format_error({deprecated, MFA, String}) when is_list(String) ->
+ io_lib:format("~s is deprecated; ~s", [format_mfa(MFA), String]);
format_error({deprecated_type, {M1, F1, A1}, String}) when is_list(String) ->
- io_lib:format("~p:~p~s: ~s", [M1, F1, gen_type_paren(A1), String]);
+ io_lib:format("the type ~p:~p~s is deprecated; ~s",
+ [M1, F1, gen_type_paren(A1), String]);
format_error({removed, MFA, ReplacementMFA, Rel}) ->
io_lib:format("call to ~s will fail, since it was removed in ~s; "
"use ~s", [format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
format_error({removed, MFA, String}) when is_list(String) ->
- io_lib:format("~s: ~s", [format_mfa(MFA), String]);
-format_error({removed_type, MNA, ReplacementMNA, Rel}) ->
- io_lib:format("the type ~s was removed in ~s; use ~s instead",
- [format_mna(MNA), Rel, format_mna(ReplacementMNA)]);
+ io_lib:format("~s is removed; ~s", [format_mfa(MFA), String]);
+format_error({removed_type, MNA, String}) ->
+ io_lib:format("the type ~s is removed; ~s", [format_mna(MNA), String]);
format_error({obsolete_guard, {F, A}}) ->
io_lib:format("~p/~p obsolete (use is_~p/~p)", [F, A, F, A]);
format_error({obsolete_guard_overridden,Test}) ->
@@ -313,6 +330,13 @@ format_error(bittype_unit) ->
"a bit unit size must not be specified unless a size is specified too";
format_error(illegal_bitsize) ->
"illegal bit size";
+format_error({illegal_bitsize_local_call, {F,A}}) ->
+ io_lib:format("call to local/imported function ~tw/~w is illegal in a size "
+ "expression for a binary segment",
+ [F,A]);
+format_error(non_integer_bitsize) ->
+ "a size expression in a pattern evaluates to a non-integer value; "
+ "this pattern cannot possibly match";
format_error(unsized_binary_not_at_end) ->
"a binary field without size is only allowed at the end of a binary pattern";
format_error(typed_literal_string) ->
@@ -591,7 +615,7 @@ start(File, Opts) ->
Enabled = ordsets:from_list(Enabled1),
Calls = case ordsets:is_element(unused_function, Enabled) of
true ->
- dict:from_list([{{module_info,1},pseudolocals()}]);
+ maps:from_list([{{module_info,1},pseudolocals()}]);
false ->
undefined
end,
@@ -647,7 +671,14 @@ pack_warnings(Ws) ->
add_error(E, St) -> add_lint_error(E, St#lint.file, St).
-add_error(Anno, E, St) ->
+add_error(Anno, E0, #lint{gexpr_context=Context}=St) ->
+ E = case {E0,Context} of
+ {illegal_guard_expr,bin_seg_size} ->
+ illegal_bitsize;
+ {{illegal_guard_local_call,FA},bin_seg_size} ->
+ {illegal_bitsize_local_call,FA};
+ {_,_} -> E0
+ end,
{File,Location} = loc(Anno, St),
add_lint_error({Location,erl_lint,E}, File, St).
@@ -918,7 +949,8 @@ post_traversal_check(Forms, St0) ->
StE = check_unused_records(Forms, StD),
StF = check_local_opaque_types(StE),
StG = check_dialyzer_attribute(Forms, StF),
- check_callback_information(StG).
+ StH = check_callback_information(StG),
+ check_removed(Forms, StH).
%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
@@ -1030,7 +1062,7 @@ check_deprecated(Forms, St0) ->
true -> St0#lint.defined;
false -> St0#lint.exports
end,
- X = gb_sets:to_list(Exports),
+ X = ignore_predefined_funcs(gb_sets:to_list(Exports)),
#lint{module = Mod} = St0,
Bad = [{E,L} || {attribute, L, deprecated, Depr} <- Forms,
D <- lists:flatten([Depr]),
@@ -1074,7 +1106,80 @@ depr_fa(F, A, _X, _Mod) ->
deprecated_flag(next_version) -> true;
deprecated_flag(next_major_release) -> true;
deprecated_flag(eventually) -> true;
-deprecated_flag(_) -> false.
+deprecated_flag(String) -> deprecated_desc(String).
+
+deprecated_desc([Char | Str]) when is_integer(Char) -> deprecated_desc(Str);
+deprecated_desc([]) -> true;
+deprecated_desc(_) -> false.
+
+%% check_removed(Forms, State0) -> State
+
+check_removed(Forms, St0) ->
+ %% Get the correct list of exported functions.
+ Exports = case member(export_all, St0#lint.compile) of
+ true -> St0#lint.defined;
+ false -> St0#lint.exports
+ end,
+ X = ignore_predefined_funcs(gb_sets:to_list(Exports)),
+ #lint{module = Mod} = St0,
+ Bad = [{E,L} || {attribute, L, removed, Removed} <- Forms,
+ R <- lists:flatten([Removed]),
+ E <- removed_cat(R, X, Mod)],
+ foldl(fun ({E,L}, St1) ->
+ add_error(L, E, St1)
+ end, St0, Bad).
+
+removed_cat({F, A, Desc}=R, X, Mod) ->
+ case removed_desc(Desc) of
+ false -> [{invalid_removed,R}];
+ true -> removed_fa(F, A, X, Mod)
+ end;
+removed_cat({F, A}, X, Mod) ->
+ removed_fa(F, A, X, Mod);
+removed_cat(module, X, Mod) ->
+ removed_fa('_', '_', X, Mod);
+removed_cat(R, _X, _Mod) ->
+ [{invalid_removed,R}].
+
+removed_fa('_', '_', X, _Mod) ->
+ case X of
+ [_|_] -> [{bad_removed,{'_','_'}}];
+ [] -> []
+ end;
+removed_fa(F, '_', X, _Mod) when is_atom(F) ->
+ %% Don't use this syntax for built-in functions.
+ case lists:filter(fun({F1,_}) -> F1 =:= F end, X) of
+ [_|_] -> [{bad_removed,{F,'_'}}];
+ _ -> []
+ end;
+removed_fa(F, A, X, Mod) when is_atom(F), is_integer(A), A >= 0 ->
+ case lists:member({F,A}, X) of
+ true ->
+ [{bad_removed,{F,A}}];
+ false ->
+ case erlang:is_builtin(Mod, F, A) of
+ true -> [{bad_removed,{F,A}}];
+ false -> []
+ end
+ end;
+removed_fa(F, A, _X, _Mod) ->
+ [{invalid_removed,{F,A}}].
+
+removed_desc([Char | Str]) when is_integer(Char) -> removed_desc(Str);
+removed_desc([]) -> true;
+removed_desc(_) -> false.
+
+%% Ignores functions added by erl_internal:add_predefined_functions/1
+ignore_predefined_funcs([{behaviour_info,1} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([{module_info,0} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([{module_info,1} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([Other | Fs]) ->
+ [Other | ignore_predefined_funcs(Fs)];
+ignore_predefined_funcs([]) ->
+ [].
%% check_imports(Forms, State0) -> State
@@ -1134,7 +1239,7 @@ reached_functions([R|Rs], More0, Ref, Reached0) ->
true -> reached_functions(Rs, More0, Ref, Reached0);
false ->
Reached = gb_sets:add_element(R, Reached0), %It IS reached
- case dict:find(R, Ref) of
+ case maps:find(R, Ref) of
{ok,More} -> reached_functions(Rs, [More|More0], Ref, Reached);
error -> reached_functions(Rs, More0, Ref, Reached)
end
@@ -1157,10 +1262,10 @@ check_undefined_functions(#lint{called=Called0,defined=Def0}=St0) ->
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)} ||
+ UTAs = maps:keys(Used),
+ Undef = [{TA,map_get(TA, Used)} ||
TA <- UTAs,
- not dict:is_key(TA, Def),
+ not is_map_key(TA, Def),
not is_default_type(TA)],
foldl(fun ({TA,L}, St) ->
add_error(L, {undefined_type,TA}, St)
@@ -1199,7 +1304,7 @@ check_untyped_records(Forms, St0) ->
case is_warn_enabled(untyped_record, St0) of
true ->
%% Use the names of all records *defined* in the module (not used)
- RecNames = dict:fetch_keys(St0#lint.records),
+ RecNames = maps:keys(St0#lint.records),
%% these are the records with field(s) containing type info
TRecNames = [Name ||
{attribute,_,record,{Name,Fields}} <- Forms,
@@ -1207,7 +1312,7 @@ check_untyped_records(Forms, St0) ->
(_) -> false
end, Fields)],
foldl(fun (N, St) ->
- {L, Fields} = dict:fetch(N, St0#lint.records),
+ {L, Fields} = map_get(N, St0#lint.records),
case Fields of
[] -> St; % exclude records with no fields
[_|_] -> add_warning(L, {untyped_record, N}, St)
@@ -1225,12 +1330,12 @@ check_unused_records(Forms, St0) ->
%% The check is a bit imprecise in that uses from unused
%% functions count.
Usage = St0#lint.usage,
- UsedRecords = sets:to_list(Usage#usage.used_records),
- URecs = foldl(fun (Used, Recs) ->
- dict:erase(Used, Recs)
- end, St0#lint.records, UsedRecords),
+ UsedRecords = Usage#usage.used_records,
+ URecs = gb_sets:fold(fun (Used, Recs) ->
+ maps:remove(Used, Recs)
+ end, St0#lint.records, UsedRecords),
Unused = [{Name,FileLine} ||
- {Name,{FileLine,_Fields}} <- dict:to_list(URecs),
+ {Name,{FileLine,_Fields}} <- maps:to_list(URecs),
element(1, loc(FileLine, St0)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
add_warning(L, {unused_record, N}, St)
@@ -1242,27 +1347,26 @@ check_unused_records(Forms, St0) ->
check_callback_information(#lint{callbacks = Callbacks,
optional_callbacks = OptionalCbs,
defined = Defined} = St0) ->
- OptFun = fun({MFA, Line}, St) ->
- case dict:is_key(MFA, Callbacks) of
+ OptFun = fun(MFA, Line, St) ->
+ case is_map_key(MFA, Callbacks) of
true ->
St;
false ->
add_error(Line, {undefined_callback, MFA}, St)
end
end,
- St1 = lists:foldl(OptFun, St0, dict:to_list(OptionalCbs)),
+ St1 = maps:fold(OptFun, St0, OptionalCbs),
case gb_sets:is_member({behaviour_info, 1}, Defined) of
false -> St1;
true ->
- case dict:size(Callbacks) of
+ case map_size(Callbacks) of
0 -> St1;
_ ->
- CallbacksList = dict:to_list(Callbacks),
- FoldL =
- fun({Fa, Line}, St) ->
+ FoldFun =
+ fun(Fa, Line, St) ->
add_error(Line, {behaviour_info, Fa}, St)
end,
- lists:foldl(FoldL, St1, CallbacksList)
+ maps:fold(FoldFun, St1, Callbacks)
end
end.
@@ -1300,7 +1404,7 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
false ->
St2
end,
- {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St}
+ {gb_sets:add_element(TA, E), maps:put(TA, Line, U), St}
end,
{ETs0,UTs0,St0}, ETs) of
{ETs1,UTs1,St1} ->
@@ -1430,7 +1534,7 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St)
NA = {F,A},
Usage = case Cs of
undefined -> Usage0;
- _ -> Usage0#usage{calls=dict:append(Func, NA, Cs)}
+ _ -> Usage0#usage{calls=maps_prepend(Func, NA, Cs)}
end,
Anno = erl_anno:set_file(File, Line),
St#lint{called=[{NA,Anno}|Cd], usage=Usage}.
@@ -1541,7 +1645,7 @@ pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
end),
{Vt1,[],St1};
pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_Line,Fields}} ->
St1 = used_record(Name, St),
pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St1);
@@ -1625,10 +1729,10 @@ reject_invalid_alias({tuple,_,Es1}, {tuple,_,Es2}, Vt, St) ->
reject_invalid_alias_list(Es1, Es2, Vt, St);
reject_invalid_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, Vt,
#lint{records=Recs}=St) ->
- case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
- {{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
+ case Recs of
+ #{Name1 := {_Line1,Fields1}, Name2 := {_Line2,Fields2}} ->
reject_invalid_alias_rec(Pfs1, Pfs2, Fields1, Fields2, Vt, St);
- {_,_} ->
+ #{} ->
%% One or more non-existing records. (An error messages has
%% already been generated, so we are done here.)
St
@@ -1706,19 +1810,16 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) ->
is_pattern_expr_1(_Other) -> false.
pattern_map(Ps, Vt, Old, Bvt, St) ->
- foldl(fun
- ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
- {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
- ({map_field_exact,L,K,V}, {Psvt,Bvt0,St0}) ->
- case is_valid_map_key(K) of
- true ->
- {Kvt,St1} = expr(K, Vt, St0),
- {Vvt,Bvt2,St2} = pattern(V, Vt, Old, Bvt, St1),
- {vtmerge_pat(vtmerge_pat(Kvt, Vvt), Psvt), vtmerge_pat(Bvt0, Bvt2), St2};
- false ->
- {Psvt,Bvt0,add_error(L, illegal_map_key, St0)}
- end
- end, {[],[],St}, Ps).
+ foldl(fun({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,_L,K,V}, {Psvt,Bvt0,St0}) ->
+ St1 = St0#lint{gexpr_context=map_key},
+ {Kvt,St2} = gexpr(K, Vt, St1),
+ {Vvt,Bvt2,St3} = pattern(V, Vt, Old, Bvt, St2),
+ {vtmerge_pat(vtmerge_pat(Kvt, Vvt), Psvt),
+ vtmerge_pat(Bvt0, Bvt2),
+ St3}
+ end, {[],[],St}, Ps).
%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
@@ -1787,21 +1888,41 @@ pat_bit_expr(P, _Old, _Bvt, St) ->
%% Check pattern size expression, only allow really valid sizes!
pat_bit_size(default, _Vt, _Bvt, St) -> {default,[],[],St};
-pat_bit_size({atom,_Line,all}, _Vt, _Bvt, St) -> {all,[],[],St};
pat_bit_size({var,Lv,V}, Vt0, Bvt0, St0) ->
{Vt,Bvt,St1} = pat_binsize_var(V, Lv, Vt0, Bvt0, St0),
{unknown,Vt,Bvt,St1};
-pat_bit_size(Size, _Vt, _Bvt, St) ->
+pat_bit_size(Size, Vt0, Bvt0, St0) ->
Line = element(2, Size),
- case is_pattern_expr(Size) of
- true ->
- case erl_eval:partial_eval(Size) of
- {integer,Line,I} -> {I,[],[],St};
- _Other -> {unknown,[],[],add_error(Line, illegal_bitsize, St)}
- end;
- false -> {unknown,[],[],add_error(Line, illegal_bitsize, St)}
+ case erl_eval:partial_eval(Size) of
+ {integer,Line,I} -> {I,[],[],St0};
+ Expr ->
+ %% The size is an expression using operators
+ %% and/or guard BIFs calls. If the expression
+ %% happens to evaluate to a non-integer value, the
+ %% pattern will fail to match.
+ St1 = St0#lint{bvt=Bvt0,gexpr_context=bin_seg_size},
+ {Vt,#lint{bvt=Bvt}=St2} = gexpr(Size, Vt0, St1),
+ St3 = St2#lint{bvt=none,gexpr_context=St0#lint.gexpr_context},
+ St = case is_bit_size_illegal(Expr) of
+ true ->
+ %% The size is a non-integer literal or a simple
+ %% expression that does not evaluate to an
+ %% integer value. Issue a warning.
+ add_warning(Line, non_integer_bitsize, St3);
+ false -> St3
+ end,
+ {unknown,Vt,Bvt,St}
end.
+is_bit_size_illegal({atom,_,_}) -> true;
+is_bit_size_illegal({bin,_,_}) -> true;
+is_bit_size_illegal({cons,_,_,_}) -> true;
+is_bit_size_illegal({float,_,_}) -> true;
+is_bit_size_illegal({map,_,_}) -> true;
+is_bit_size_illegal({nil,_}) -> true;
+is_bit_size_illegal({tuple,_,_}) -> true;
+is_bit_size_illegal(_) -> false.
+
%% expr_bin(Line, [Element], VarTable, State, CheckFun) -> {UpdVarTable,State}.
%% Check an expression group.
@@ -2067,7 +2188,7 @@ gexpr_list(Es, Vt, St) ->
Expr :: erl_parse:abstract_expr().
is_guard_test(E) ->
- is_guard_test2(E, {dict:new(),fun(_) -> false end}).
+ is_guard_test2(E, {maps:new(),fun(_) -> false end}).
%% is_guard_test(Expression, Forms) -> boolean().
is_guard_test(Expression, Forms) ->
@@ -2115,7 +2236,7 @@ is_guard_test2(G, Info) ->
%% is_guard_expr(Expression) -> boolean().
%% Test if an expression is a guard expression.
-is_guard_expr(E) -> is_gexpr(E, []).
+is_guard_expr(E) -> is_gexpr(E, {[],fun({_,_}) -> false end}).
is_gexpr({var,_L,_V}, _Info) -> true;
is_gexpr({char,_L,_C}, _Info) -> true;
@@ -2183,7 +2304,7 @@ is_map_fields([], _Info) -> true;
is_map_fields(_T, _Info) -> false.
is_gexpr_fields(Fs, L, Name, {RDs,_}=Info) ->
- IFs = case dict:find(Name, RDs) of
+ IFs = case maps:find(Name, RDs) of
{ok,{_Line,Fields}} -> Fs ++ init_fields(Fs, L, Fields);
error -> Fs
end,
@@ -2510,73 +2631,16 @@ is_valid_call(Call) ->
_ -> true
end.
-%% is_valid_map_key(K) -> true | false
-%% variables are allowed for patterns only at the top of the tree
-
-is_valid_map_key({var,_,_}) -> true;
-is_valid_map_key(K) -> is_valid_map_key_value(K).
-is_valid_map_key_value(K) ->
- case K of
- {var,_,_} -> false;
- {char,_,_} -> true;
- {integer,_,_} -> true;
- {float,_,_} -> true;
- {string,_,_} -> true;
- {nil,_} -> true;
- {atom,_,_} -> true;
- {cons,_,H,T} ->
- is_valid_map_key_value(H) andalso
- is_valid_map_key_value(T);
- {tuple,_,Es} ->
- foldl(fun(E,B) ->
- B andalso is_valid_map_key_value(E)
- end,true,Es);
- {map,_,Arg,Ps} ->
- % only check for value expressions to be valid
- % invalid map expressions are later checked in
- % core and kernel
- is_valid_map_key_value(Arg) andalso foldl(fun
- ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve);
- (_,_) -> false
- end,true,Ps);
- {map,_,Ps} ->
- foldl(fun
- ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve);
- (_,_) -> false
- end, true, Ps);
- {record,_,_,Fs} ->
- foldl(fun
- ({record_field,_,Ke,Ve},B) ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve)
- end,true,Fs);
- {bin,_,Es} ->
- % only check for value expressions to be valid
- % invalid binary expressions are later checked in
- % core and kernel
- foldl(fun
- ({bin_element,_,E,_,_},B) ->
- B andalso is_valid_map_key_value(E)
- end,true,Es);
- Val -> is_pattern_expr(Val)
- end.
-
%% record_def(Line, RecordName, [RecField], State) -> State.
%% Add a record definition if it does not already exist. Normalise
%% so that all fields have explicit initial value.
record_def(Line, Name, Fs0, St0) ->
- case dict:is_key(Name, St0#lint.records) of
+ case is_map_key(Name, St0#lint.records) of
true -> add_error(Line, {redefine_record,Name}, St0);
false ->
{Fs1,St1} = def_fields(normalise_fields(Fs0), Name, St0),
- St2 = St1#lint{records=dict:store(Name, {Line,Fs1},
+ St2 = St1#lint{records=maps:put(Name, {Line,Fs1},
St1#lint.records)},
Types = [T || {typed_record_field, _, T} <- Fs0],
check_type({type, nowarn(), product, Types}, St2)
@@ -2627,7 +2691,7 @@ normalise_fields(Fs) ->
%% Check if a record exists. Set State.
exist_record(Line, Name, St) ->
- case dict:is_key(Name, St#lint.records) of
+ case is_map_key(Name, St#lint.records) of
true -> used_record(Name, St);
false -> add_error(Line, {undefined_record,Name}, St)
end.
@@ -2644,13 +2708,13 @@ exist_record(Line, Name, St) ->
%% {UpdatedVarTable,State}
check_record(Line, Name, St, CheckFun) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_Line,Fields}} -> CheckFun(Fields, used_record(Name, St));
error -> {[],add_error(Line, {undefined_record,Name}, St)}
end.
used_record(Name, #lint{usage=Usage}=St) ->
- UsedRecs = sets:add_element(Name, Usage#usage.used_records),
+ UsedRecs = gb_sets:add_element(Name, Usage#usage.used_records),
St#lint{usage = Usage#usage{used_records=UsedRecs}}.
%%% Record check functions.
@@ -2791,7 +2855,7 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
Info = #typeinfo{attr = Attr, line = Line},
StoreType =
fun(St) ->
- NewDefs = dict:store(TypePair, Info, TypeDefs),
+ NewDefs = maps:put(TypePair, Info, TypeDefs),
CheckType = {type, nowarn(), product, [ProtoType|Args]},
check_type(CheckType, St#lint{types=NewDefs})
end,
@@ -2811,7 +2875,7 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
end
end;
false ->
- case dict:is_key(TypePair, TypeDefs) of
+ case is_map_key(TypePair, TypeDefs) of
true ->
add_error(Line, {redefine_type, TypePair}, St0);
false ->
@@ -2833,8 +2897,8 @@ is_underspecified({type,_,any,[]}, 0) -> true;
is_underspecified(_ProtType, _Arity) -> false.
check_type(Types, St) ->
- {SeenVars, St1} = check_type(Types, dict:new(), St),
- dict:fold(fun(Var, {seen_once, Line}, AccSt) ->
+ {SeenVars, St1} = check_type(Types, maps:new(), St),
+ maps:fold(fun(Var, {seen_once, Line}, AccSt) ->
case atom_to_list(Var) of
"_"++_ -> AccSt;
_ -> add_error(Line, {singleton_typevar, Var}, AccSt)
@@ -2862,10 +2926,10 @@ check_type({atom, _L, _}, SeenVars, St) -> {SeenVars, St};
check_type({var, _L, '_'}, SeenVars, St) -> {SeenVars, St};
check_type({var, L, Name}, SeenVars, St) ->
NewSeenVars =
- case dict:find(Name, SeenVars) of
- {ok, {seen_once, _}} -> dict:store(Name, seen_multiple, SeenVars);
+ case maps:find(Name, SeenVars) of
+ {ok, {seen_once, _}} -> maps:put(Name, seen_multiple, SeenVars);
{ok, seen_multiple} -> SeenVars;
- error -> dict:store(Name, {seen_once, L}, SeenVars)
+ error -> maps:put(Name, {seen_once, L}, SeenVars)
end,
{NewSeenVars, St};
check_type({type, L, bool, []}, SeenVars, St) ->
@@ -2924,7 +2988,7 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
andalso obsolete_builtin_type(TypePair)),
St1 = case Obsolete of
{deprecated, Repl, _} when element(1, Repl) =/= Module ->
- case dict:find(TypePair, Types) of
+ case maps:find(TypePair, Types) of
{ok, _} ->
used_type(TypePair, La, St);
error ->
@@ -2953,7 +3017,7 @@ check_type(I, SeenVars, St) ->
end.
check_record_types(Line, Name, Fields, SeenVars, St) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_L,DefFields}} ->
case lists:all(fun({type, _, field_type, _}) -> true;
(_) -> false
@@ -2988,7 +3052,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
used_type(TypePair, L, #lint{usage = Usage, file = File} = St) ->
OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed),
+ UsedTypes = maps:put(TypePair, erl_anno:set_file(File, L), OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}.
is_default_type({Name, NumberOfTypeVariables}) ->
@@ -3012,8 +3076,8 @@ spec_decl(Line, MFA0, TypeSpecs, St00 = #lint{specs = Specs, module = Mod}) ->
{_M, _F, Arity} -> MFA0
end,
St0 = check_module_name(element(1, MFA), Line, St00),
- St1 = St0#lint{specs = dict:store(MFA, Line, Specs)},
- case dict:is_key(MFA, Specs) of
+ St1 = St0#lint{specs = maps:put(MFA, Line, Specs)},
+ case is_map_key(MFA, Specs) of
true -> add_error(Line, {redefine_spec, MFA0}, St1);
false ->
case MFA of
@@ -3034,8 +3098,8 @@ callback_decl(Line, MFA0, TypeSpecs,
add_error(Line, {bad_callback, MFA0}, St1);
{F, Arity} ->
MFA = {Mod, F, Arity},
- St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)},
- case dict:is_key(MFA, Callbacks) of
+ St1 = St0#lint{callbacks = maps:put(MFA, Line, Callbacks)},
+ case is_map_key(MFA, Callbacks) of
true -> add_error(Line, {redefine_callback, MFA0}, St1);
false -> check_specs(TypeSpecs, callback_wrong_arity,
Arity, St1)
@@ -3058,8 +3122,8 @@ optional_cbs(_Line, [], St) ->
optional_cbs(Line, [{F,A}|FAs], St0) ->
#lint{optional_callbacks = OptionalCbs, module = Mod} = St0,
MFA = {Mod, F, A},
- St1 = St0#lint{optional_callbacks = dict:store(MFA, Line, OptionalCbs)},
- St2 = case dict:is_key(MFA, OptionalCbs) of
+ St1 = St0#lint{optional_callbacks = maps:put(MFA, Line, OptionalCbs)},
+ St2 = case is_map_key(MFA, OptionalCbs) of
true ->
add_error(Line, {redefine_optional_callback, {F,A}}, St1);
false ->
@@ -3119,7 +3183,7 @@ check_specs_without_function(#lint{module=Mod,defined=Funcs,specs=Specs}=St) ->
end;
({_M, _F, _A}, _Line, AccSt) -> AccSt
end,
- dict:fold(Fun, St, Specs).
+ maps:fold(Fun, St, Specs).
%% This generates warnings for functions without specs; if the user has
%% specified both options, we do not generate the same warnings twice.
@@ -3137,7 +3201,7 @@ check_functions_without_spec(Forms, St0) ->
end.
add_missing_spec_warnings(Forms, St0, Type) ->
- Specs = [{F,A} || {_M,F,A} <- dict:fetch_keys(St0#lint.specs)],
+ Specs = [{F,A} || {_M,F,A} <- maps:keys(St0#lint.specs)],
Warns = %% functions + line numbers for which we should warn
case Type of
all ->
@@ -3163,7 +3227,7 @@ check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
case [File || {attribute,_L,file,{File,_Line}} <- Forms] of
[FirstFile|_] ->
D = Usage#usage.used_types,
- L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
+ L = gb_sets:to_list(ExpTs) ++ maps:keys(D),
UsedTypes = gb_sets:from_list(L),
FoldFun =
fun({{record, _}=_Type, 0}, _, AccSt) ->
@@ -3182,7 +3246,7 @@ check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
AccSt
end
end,
- dict:fold(FoldFun, St, Ts);
+ maps:fold(FoldFun, St, Ts);
[] ->
St
end.
@@ -3200,7 +3264,7 @@ check_local_opaque_types(St) ->
add_warning(FileLine, Warn, AccSt)
end
end,
- dict:fold(FoldFun, St, Ts).
+ maps:fold(FoldFun, St, Ts).
check_dialyzer_attribute(Forms, St0) ->
Vals = [{L,V} ||
@@ -3444,7 +3508,7 @@ handle_generator(P,E,Vt,Uvt,St0) ->
handle_bitstring_gen_pat({bin,_,Segments=[_|_]},St) ->
case lists:last(Segments) of
- {bin_element,Line,{var,_,_},default,Flags} when is_list(Flags) ->
+ {bin_element,Line,_,default,Flags} when is_list(Flags) ->
case member(binary, Flags) orelse member(bytes, Flags)
orelse member(bits, Flags) orelse member(bitstring, Flags) of
true ->
@@ -3590,7 +3654,13 @@ pat_binsize_var(V, Line, Vt, Bvt, St) ->
%% exported vars are probably safe, warn only if warn_export_vars is
%% set.
-expr_var(V, Line, Vt, St) ->
+expr_var(V, Line, Vt, #lint{bvt=none}=St) ->
+ do_expr_var(V, Line, Vt, St);
+expr_var(V, Line, Vt0, #lint{bvt=Bvt0}=St0) when is_list(Bvt0) ->
+ {Vt,Bvt,St} = pat_binsize_var(V, Line, Vt0, Bvt0, St0),
+ {Vt,St#lint{bvt=vtmerge(Bvt0, Bvt)}}.
+
+do_expr_var(V, Line, Vt, St) ->
case orddict:find(V, Vt) of
{ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],St};
@@ -3846,8 +3916,8 @@ deprecated_type(L, M, N, As, St) ->
false ->
St
end;
- {removed, Replacement, Rel} ->
- add_warning(L, {removed_type, {M,N,NAs}, Replacement, Rel}, St);
+ {removed, String} ->
+ add_warning(L, {removed_type, {M,N,NAs}, String}, St);
no ->
St
end.
@@ -4122,3 +4192,13 @@ no_guard_bif_clash(St,{F,A}) ->
is_imported_from_erlang(St#lint.imports,{F,A})
)
).
+
+%% maps_prepend(Key, Value, Map) -> Map.
+
+maps_prepend(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Values} ->
+ maps:put(Key, [Value|Values], Map);
+ error ->
+ maps:put(Key, [Value], Map)
+ end.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 739f786321..eab8da4ab7 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -608,6 +608,13 @@ Erlang code.
-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
abstract_type/0, form_info/0, error_info/0]).
+%% The following types are exported because they are used by syntax_tools
+-export_type([af_binelement/1, af_generator/0, af_remote_function/0]).
+
+%% Removed functions
+-removed([{set_line,2,"use erl_anno:set_line/2"},
+ {get_attributes,1,"erl_anno:{column,line,location,text}/1 instead"},
+ {get_attribute,2,"erl_anno:{column,line,location,text}/1 instead"}]).
%% Start of Abstract Format
@@ -637,7 +644,7 @@ Erlang code.
-type af_export() :: {'attribute', anno(), 'export', af_fa_list()}.
--type af_import() :: {'attribute', anno(), 'import', af_fa_list()}.
+-type af_import() :: {'attribute', anno(), 'import', {module(), af_fa_list()}}.
-type af_fa_list() :: [{function_name(), arity()}].
@@ -1455,7 +1462,19 @@ abstract(List, A, E) when is_list(List) ->
abstract(Tuple, A, E) when is_tuple(Tuple) ->
{tuple,A,abstract_tuple_list(tuple_to_list(Tuple), A, E)};
abstract(Map, A, E) when is_map(Map) ->
- {map,A,abstract_map_fields(maps:to_list(Map),A,E)}.
+ {map,A,abstract_map_fields(maps:to_list(Map),A,E)};
+abstract(Fun, A, E) when is_function(Fun) ->
+ case erlang:fun_info(Fun, type) of
+ {type, external} ->
+ Info = erlang:fun_info(Fun),
+ {module, M} = lists:keyfind(module, 1, Info),
+ {name, F} = lists:keyfind(name, 1, Info),
+ {arity, Arity} = lists:keyfind(arity, 1, Info),
+ {'fun', A, {function,
+ abstract(M, A, E),
+ abstract(F, A, E),
+ abstract(Arity, A, E)}}
+ end.
abstract_list([H|T], String, A, E) ->
case is_integer(H) andalso H >= 0 andalso E(H) of
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index daa172af50..c706a5c945 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -31,7 +31,8 @@
-import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0,
type_inop_prec/1, type_preop_prec/1]).
--define(MAXLINE, 72).
+-define(DEFAULT_LINEWIDTH, 72).
+-define(DEFAULT_INDENT, 4).
-type(hook_function() :: none
| fun((Expr :: erl_parse:abstract_expr(),
@@ -42,10 +43,13 @@
-type(option() :: {hook, hook_function()}
| {encoding, latin1 | unicode | utf8}
- | {quote_singleton_atom_types, boolean()}).
+ | {quote_singleton_atom_types, boolean()}
+ | {linewidth, pos_integer()}
+ | {indent, pos_integer()}).
-type(options() :: hook_function() | [option()]).
--record(pp, {value_fun, singleton_atom_type_fun, string_fun, char_fun}).
+-record(pp, {value_fun, singleton_atom_type_fun, string_fun, char_fun,
+ linewidth=?DEFAULT_LINEWIDTH, indent=?DEFAULT_INDENT}).
-record(options, {hook, encoding, opts}).
@@ -208,10 +212,14 @@ options(Hook) ->
state(Options) when is_list(Options) ->
Quote = proplists:get_bool(quote_singleton_atom_types, Options),
- case encoding(Options) of
- latin1 -> latin1_state(Quote);
- unicode -> unicode_state(Quote)
- end;
+ State =
+ case encoding(Options) of
+ latin1 -> latin1_state(Quote);
+ unicode -> unicode_state(Quote)
+ end,
+ Indent = proplists:get_value(indent, Options, ?DEFAULT_INDENT),
+ LineWidth = proplists:get_value(linewidth, Options, ?DEFAULT_LINEWIDTH),
+ State#pp{indent=Indent, linewidth=LineWidth};
state(_Hook) ->
latin1_state(false).
@@ -1020,7 +1028,7 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) ->
true ->
0
end,
- case same_line(I0, Sizes, NSepChars) of
+ case same_line(I0, Sizes, NSepChars, PP) of
{yes,Size} ->
Chars = if
NSepChars > 0 -> insert_sep(CharsL, $\s);
@@ -1028,9 +1036,9 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) ->
end,
{BCharsL++Chars,Size};
no ->
- CharsList = handle_step(CharsSizeL, I, ST),
+ CharsList = handle_step(CharsSizeL, I, ST, PP),
{LChars, LSize} =
- maybe_newlines(CharsList, LItems, I, NSepChars, ST),
+ maybe_newlines(CharsList, LItems, I, NSepChars, ST, PP),
{[BCharsL,LChars],nsz(LSize, I0)}
end;
f({force_nl,_ExtraInfoItem,Item}, I, ST, WT, PP) when I < 0 ->
@@ -1047,7 +1055,7 @@ f({prefer_nl,Sep,LItems}, I0, ST, WT, PP) ->
Sizes =:= [] ->
{[], 0};
true ->
- {insert_newlines(CharsSize2L, I0, ST),
+ {insert_newlines(CharsSize2L, I0, ST, PP),
nsz(lists:last(Sizes), I0)}
end;
f({value,V}, I, ST, WT, PP) ->
@@ -1071,8 +1079,6 @@ f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT, _PP) ->
f(WordName, _I, _ST, WT, _PP) when is_atom(WordName) ->
word(WordName, WT).
--define(IND, 4).
-
%% fl(ListItems, I0, ST, WT) -> [[CharsSize1,CharsSize2]]
%% ListItems = [{Item,Items}|Item]
fl([], _Sep, I0, After, ST, WT, PP) ->
@@ -1080,15 +1086,15 @@ fl([], _Sep, I0, After, ST, WT, PP) ->
fl(CItems, Sep0, I0, After, ST, WT, PP) ->
F = fun({step,Item1,Item2}, S) ->
[f(Item1, I0, ST, WT, PP),
- f([Item2,S], incr(I0, ?IND), ST, WT, PP)];
+ f([Item2,S], incr(I0, PP#pp.indent), ST, WT, PP)];
({cstep,Item1,Item2}, S) ->
{_,Sz1} = CharSize1 = f(Item1, I0, ST, WT, PP),
if
- is_integer(Sz1), Sz1 < ?IND ->
+ is_integer(Sz1), Sz1 < PP#pp.indent ->
Item2p = [leaf("\s"),Item2,S],
[consecutive(Item2p, CharSize1, I0, ST, WT, PP),{[],0}];
true ->
- [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT, PP)]
+ [CharSize1,f([Item2,S], incr(I0, PP#pp.indent), ST, WT, PP)]
end;
({reserved,Word}, S) ->
[f([Word,S], I0, ST, WT, PP),{[],0}];
@@ -1127,58 +1133,58 @@ unz1(CharSizes) ->
nonzero(CharSizes) ->
lists:filter(fun({_,Sz}) -> Sz =/= 0 end, CharSizes).
-maybe_newlines([{Chars,Size}], [], _I, _NSepChars, _ST) ->
+maybe_newlines([{Chars,Size}], [], _I, _NSepChars, _ST, _PP) ->
{Chars,Size};
-maybe_newlines(CharsSizeList, Items, I, NSepChars, ST) when I >= 0 ->
- maybe_sep(CharsSizeList, Items, I, NSepChars, nl_indent(I, ST)).
+maybe_newlines(CharsSizeList, Items, I, NSepChars, ST, PP) when I >= 0 ->
+ maybe_sep(CharsSizeList, Items, I, NSepChars, nl_indent(I, ST), PP).
-maybe_sep([{Chars1,Size1}|CharsSizeL], [Item|Items], I0, NSepChars, Sep) ->
+maybe_sep([{Chars1,Size1}|CharsSizeL], [Item|Items], I0, NSepChars, Sep, PP) ->
I1 = case classify_item(Item) of
atomic ->
I0 + Size1;
_ ->
- ?MAXLINE+1
+ PP#pp.linewidth+1
end,
- maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars, Size1, [Chars1]).
+ maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars, Size1, [Chars1], PP).
maybe_sep1([{Chars,Size}|CharsSizeL], [Item|Items],
- I0, I, Sep, NSepChars, Sz0, A) ->
+ I0, I, Sep, NSepChars, Sz0, A, PP) ->
case classify_item(Item) of
atomic when is_integer(Size) ->
Size1 = Size + 1,
I1 = I + Size1,
if
- I1 =< ?MAXLINE ->
+ I1 =< PP#pp.linewidth ->
A1 = if
NSepChars > 0 -> [Chars,$\s|A];
true -> [Chars|A]
end,
maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars,
- Sz0 + Size1, A1);
+ Sz0 + Size1, A1, PP);
true ->
A1 = [Chars,Sep|A],
maybe_sep1(CharsSizeL, Items, I0, I0 + Size, Sep,
- NSepChars, Size1, A1)
+ NSepChars, Size1, A1, PP)
end;
_ ->
A1 = [Chars,Sep|A],
- maybe_sep1(CharsSizeL, Items, I0, ?MAXLINE+1, Sep, NSepChars,
- 0, A1)
+ maybe_sep1(CharsSizeL, Items, I0, PP#pp.linewidth+1, Sep, NSepChars,
+ 0, A1, PP)
end;
-maybe_sep1(_CharsSizeL, _Items, _Io, _I, _Sep, _NSepChars, Sz, A) ->
+maybe_sep1(_CharsSizeL, _Items, _Io, _I, _Sep, _NSepChars, Sz, A, _PP) ->
{lists:reverse(A), Sz}.
-insert_newlines(CharsSizesL, I, ST) when I >= 0 ->
- {CharsL, _} = unz1(handle_step(CharsSizesL, I, ST)),
+insert_newlines(CharsSizesL, I, ST, PP) when I >= 0 ->
+ {CharsL, _} = unz1(handle_step(CharsSizesL, I, ST, PP)),
insert_nl(CharsL, I, ST).
-handle_step(CharsSizesL, I, ST) ->
+handle_step(CharsSizesL, I, ST, PP) ->
map(fun([{_C1,0},{_C2,0}]) ->
{[], 0};
([{C1,Sz1},{_C2,0}]) ->
{C1, Sz1};
([{C1,Sz1},{C2,Sz2}]) when Sz2 > 0 ->
- {insert_nl([C1,C2], I+?IND, ST),line_size([Sz1,Sz2])}
+ {insert_nl([C1,C2], I+PP#pp.indent, ST),line_size([Sz1,Sz2])}
end, CharsSizesL).
insert_nl(CharsL, I, ST) ->
@@ -1198,10 +1204,10 @@ classify_item(Atom) when is_atom(Atom) -> atomic;
classify_item({leaf, _, _}) -> atomic;
classify_item(_) -> complex.
-same_line(I0, SizeL, NSepChars) ->
+same_line(I0, SizeL, NSepChars, PP) ->
try
Size = lists:sum(SizeL) + NSepChars,
- true = incr(I0, Size) =< ?MAXLINE,
+ true = incr(I0, Size) =< PP#pp.linewidth,
{yes,Size}
catch _:_ ->
no
@@ -1269,7 +1275,7 @@ write_a_char(C, PP) ->
write_a_string(S, I, PP) when I < 0; S =:= [] ->
flat_leaf(write_string(S, PP));
write_a_string(S, I, PP) ->
- Len = erlang:max(?MAXLINE-I, ?MIN_SUBSTRING),
+ Len = erlang:max(PP#pp.linewidth-I, ?MIN_SUBSTRING),
{list,write_a_string(S, Len, Len, PP)}.
write_a_string([], _N, _Len, _PP) ->
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 4774c4bf19..0854e15177 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -66,6 +66,17 @@
token/0,
tokens_result/0]).
+%% Removed functions and types
+-removed([{set_attribute,3,"use erl_anno:set_line/2 instead"},
+ {attributes_info,'_',
+ "erl_anno:{column,line,location,text}/1 instead"},
+ {token_info,'_',
+ "erl_scan:{category,column,line,location,symbol,text}/1 instead"}]).
+
+-removed_type([{column,0,"use erl_anno:column() instead"},
+ {line,0,"use erl_anno:line() instead"},
+ {location,0,"use erl_anno:location() instead"}]).
+
%%%
%%% Defines and type definitions
%%%
@@ -249,7 +260,7 @@ string_thing(_) -> "string".
-define(WHITE_SPACE(C),
is_integer(C) andalso
(C >= $\000 andalso C =< $\s orelse C >= $\200 andalso C =< $\240)).
--define(DIGIT(C), C >= $0, C =< $9).
+-define(DIGIT(C), C >= $0 andalso C =< $9).
-define(CHAR(C), is_integer(C), C >= 0).
-define(UNICODE(C),
is_integer(C) andalso
@@ -379,7 +390,7 @@ scan1([$\%|Cs], St, Line, Col, Toks) when not St#erl_scan.comment ->
scan1([$\%=C|Cs], St, Line, Col, Toks) ->
scan_comment(Cs, St, Line, Col, Toks, [C]);
scan1([C|Cs], St, Line, Col, Toks) when ?DIGIT(C) ->
- scan_number(Cs, St, Line, Col, Toks, [C]);
+ scan_number(Cs, St, Line, Col, Toks, [C], no_underscore);
scan1("..."++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "...", '...', 3);
scan1(".."=Cs, _St, Line, Col, Toks) ->
@@ -938,27 +949,35 @@ escape_char($s) -> $\s; % \s = SPC
escape_char($d) -> $\d; % \d = DEL
escape_char(C) -> C.
-scan_number([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_number(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_number([$.,C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_fraction(Cs, St, Line, Col, Toks, [C,$.|Ncs]);
-scan_number([$.]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_number/6}};
-scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0) ->
+scan_number(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_number(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_number([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_number(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_number([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _Us) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_number(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_number([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number([$.,C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [C,$.|Ncs], Us);
+scan_number([$.]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_integer(Ncs) of
+ case catch list_to_integer(remove_digit_separators(Ncs, Us)) of
B when B >= 2, B =< 1+$Z-$A+10 ->
Bcs = Ncs++[$#],
- scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs});
+ scan_based_int(Cs, St, Line, Col, Toks, B, [], Bcs, no_underscore);
B ->
Len = length(Ncs),
scan_error({base,B}, Line, Col, Line, incr_column(Col, Len), Cs0)
end;
-scan_number([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_number/6}};
-scan_number(Cs, St, Line, Col, Toks, Ncs0) ->
+scan_number([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number(Cs, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_integer(Ncs) of
+ case catch list_to_integer(remove_digit_separators(Ncs, Us)) of
N when is_integer(N) ->
tok3(Cs, St, Line, Col, Toks, integer, Ncs, N);
_ ->
@@ -966,20 +985,33 @@ scan_number(Cs, St, Line, Col, Toks, Ncs0) ->
scan_error({illegal,integer}, Line, Col, Line, Ncol, Cs)
end.
-scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs})
- when ?DIGIT(C), C < $0+B ->
- scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs});
-scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs})
- when C >= $A, B > 10, C < $A+B-10 ->
- scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs});
-scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs})
- when C >= $a, B > 10, C < $a+B-10 ->
- scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs});
-scan_based_int([]=Cs, _St, Line, Col, Toks, State) ->
- {more,{Cs,Col,Toks,Line,State,fun scan_based_int/6}};
-scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
+remove_digit_separators(Number, no_underscore) ->
+ Number;
+remove_digit_separators(Number, with_underscore) ->
+ [C || C <- Number, C =/= $_].
+
+-define(BASED_DIGIT(C, B),
+ ((?DIGIT(C) andalso C < $0 + B)
+ orelse (C >= $A andalso B > 10 andalso C < $A + B - 10)
+ orelse (C >= $a andalso B > 10 andalso C < $a + B - 10))).
+
+scan_based_int(Cs, St, Line, Col, Toks, {B,NCs,BCs,Us}) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, NCs, BCs, Us).
+
+scan_based_int([C|Cs], St, Line, Col, Toks, B, Ncs, Bcs, Us) when
+ ?BASED_DIGIT(C, B) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, [C|Ncs], Bcs, Us);
+scan_based_int([$_,Next|Cs], St, Line, Col, Toks, B, [Prev|_]=Ncs, Bcs, _Us)
+ when ?BASED_DIGIT(Next, B) andalso ?BASED_DIGIT(Prev, B) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, [Next,$_|Ncs], Bcs,
+ with_underscore);
+scan_based_int([$_]=Cs, _St, Line, Col, Toks, B, NCs, BCs, Us) ->
+ {more,{Cs,Col,Toks,Line,{B,NCs,BCs,Us},fun scan_based_int/6}};
+scan_based_int([]=Cs, _St, Line, Col, Toks, B, NCs, BCs, Us) ->
+ {more,{Cs,Col,Toks,Line,{B,NCs,BCs,Us},fun scan_based_int/6}};
+scan_based_int(Cs, St, Line, Col, Toks, B, Ncs0, Bcs, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch erlang:list_to_integer(Ncs, B) of
+ case catch erlang:list_to_integer(remove_digit_separators(Ncs, Us), B) of
N when is_integer(N) ->
tok3(Cs, St, Line, Col, Toks, integer, Bcs++Ncs, N);
_ ->
@@ -988,32 +1020,52 @@ scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
scan_error({illegal,integer}, Line, Col, Line, Ncol, Cs)
end.
-scan_fraction([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_fraction(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_fraction([E|Cs], St, Line, Col, Toks, Ncs) when E =:= $e; E =:= $E ->
- scan_exponent_sign(Cs, St, Line, Col, Toks, [E|Ncs]);
-scan_fraction([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_fraction/6}};
-scan_fraction(Cs, St, Line, Col, Toks, Ncs) ->
- float_end(Cs, St, Line, Col, Toks, Ncs).
-
-scan_exponent_sign([C|Cs], St, Line, Col, Toks, Ncs) when C =:= $+; C =:= $- ->
- scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_exponent_sign([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_exponent_sign/6}};
-scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs) ->
- scan_exponent(Cs, St, Line, Col, Toks, Ncs).
-
-scan_exponent([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_exponent([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_exponent/6}};
-scan_exponent(Cs, St, Line, Col, Toks, Ncs) ->
- float_end(Cs, St, Line, Col, Toks, Ncs).
-
-float_end(Cs, St, Line, Col, Toks, Ncs0) ->
+scan_fraction(Cs, St, Line, Col, Toks, {Ncs,Us}) ->
+ scan_fraction(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_fraction([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_fraction([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _Us) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_fraction([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_fraction/6}};
+scan_fraction([E|Cs], St, Line, Col, Toks, Ncs, Us) when E =:= $e; E =:= $E ->
+ scan_exponent_sign(Cs, St, Line, Col, Toks, [E|Ncs], Us);
+scan_fraction([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_fraction/6}};
+scan_fraction(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ float_end(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent_sign(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent_sign([C|Cs], St, Line, Col, Toks, Ncs, Us) when
+ C =:= $+; C =:= $- ->
+ scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_exponent_sign([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent_sign/6}};
+scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_exponent([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_exponent(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_exponent([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent/6}};
+scan_exponent([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent/6}};
+scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ float_end(Cs, St, Line, Col, Toks, Ncs, Us).
+
+float_end(Cs, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_float(Ncs) of
+ case catch list_to_float(remove_digit_separators(Ncs, Us)) of
F when is_float(F) ->
tok3(Cs, St, Line, Col, Toks, float, Ncs, F);
_ ->
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 78cdd02307..cbfed30b13 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -321,22 +321,29 @@ do_open(Name, Mode) when is_list(Mode) ->
{error, {Name, Reason}}
end.
-open1({binary,Bin}, read, _Raw, Opts) when is_binary(Bin) ->
+open1({binary,Bin}=Handle, read, _Raw, Opts) when is_binary(Bin) ->
case file:open(Bin, [ram,binary,read]) of
{ok,File} ->
_ = [ram_file:uncompress(File) || lists:member(compressed, Opts)],
{ok, #reader{handle=File,access=read,func=fun file_op/2}};
- Error ->
- Error
+ {error, Reason} ->
+ {error, {Handle, Reason}}
end;
-open1({file, Fd}, read, _Raw, _Opts) ->
- Reader = #reader{handle=Fd,access=read,func=fun file_op/2},
- case do_position(Reader, {cur, 0}) of
- {ok, Pos, Reader2} ->
- {ok, Reader2#reader{pos=Pos}};
- {error, _} = Err ->
- Err
+open1({file, Fd}=Handle, read, [raw], Opts) ->
+ case not lists:member(compressed, Opts) of
+ true ->
+ Reader = #reader{handle=Fd,access=read,func=fun file_op/2},
+ case do_position(Reader, {cur, 0}) of
+ {ok, Pos, Reader2} ->
+ {ok, Reader2#reader{pos=Pos}};
+ {error, Reason} ->
+ {error, {Handle, Reason}}
+ end;
+ false ->
+ {error, {Handle, {incompatible_option, compressed}}}
end;
+open1({file, _Fd}=Handle, read, [], _Opts) ->
+ {error, {Handle, {incompatible_option, cooked}}};
open1(Name, Access, Raw, Opts) when is_list(Name) or is_binary(Name) ->
case file:open(Name, Raw ++ [binary, Access|Opts]) of
{ok, File} ->
@@ -1637,60 +1644,18 @@ write_extracted_element(#tar_header{name=Name0}=Header, Bin, Opts) ->
make_safe_path([$/|Path], Opts) ->
make_safe_path(Path, Opts);
-make_safe_path(Path, #read_opts{cwd=Cwd}) ->
- case filename:safe_relative_path(Path) of
- unsafe ->
- throw({error,{Path,unsafe_path}});
- SafePath ->
- filename:absname(SafePath, Cwd)
+make_safe_path(Path0, #read_opts{cwd=Cwd}) ->
+ case filelib:safe_relative_path(Path0, Cwd) of
+ unsafe -> throw({error,{Path0,unsafe_path}});
+ Path -> filename:absname(Path, Cwd)
end.
-safe_link_name(#tar_header{linkname=Path}, #read_opts{cwd=Cwd}) ->
- case safe_relative_path_links(Path, Cwd) of
- unsafe ->
- throw({error,{Path,unsafe_symlink}});
- SafePath ->
- SafePath
+safe_link_name(#tar_header{linkname=Path0},#read_opts{cwd=Cwd} ) ->
+ case filelib:safe_relative_path(Path0, Cwd) of
+ unsafe -> throw({error,{Path0,unsafe_symlink}});
+ Path -> Path
end.
-safe_relative_path_links(Path, Cwd) ->
- case filename:pathtype(Path) of
- relative -> safe_relative_path_links(filename:split(Path), Cwd, [], "");
- _ -> unsafe
- end.
-
-safe_relative_path_links([Segment|Segments], Cwd, PrevSegments, Acc) ->
- AccSegment = join(Acc, Segment),
- case lists:member(AccSegment, PrevSegments) of
- true ->
- unsafe;
- false ->
- case file:read_link(join(Cwd, AccSegment)) of
- {ok, LinkPath} ->
- case filename:pathtype(LinkPath) of
- relative ->
- safe_relative_path_links(filename:split(LinkPath) ++ Segments,
- Cwd, [AccSegment|PrevSegments], Acc);
- _ ->
- unsafe
- end;
-
- {error, _} ->
- case filename:safe_relative_path(join(Acc, Segment)) of
- unsafe ->
- unsafe;
- NewAcc ->
- safe_relative_path_links(Segments, Cwd,
- [AccSegment|PrevSegments], NewAcc)
- end
- end
- end;
-safe_relative_path_links([], _Cwd, _PrevSegments, Acc) ->
- Acc.
-
-join([], Path) -> Path;
-join(Left, Right) -> filename:join(Left, Right).
-
create_regular(Name, NameInArchive, Bin, Opts) ->
case write_extracted_file(Name, Bin, Opts) of
not_written ->
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 4e9ba1cc16..0e120174fe 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -780,7 +780,7 @@ interpret(Forms, HasRecs, File, Args) ->
false -> Forms;
true -> erl_expand_records:module(Forms, [])
end,
- Dict = parse_to_dict(Forms2),
+ Dict = parse_to_map(Forms2),
ArgsA = erl_parse:abstract(Args, 0),
Anno = a0(),
Call = {call,Anno,{atom,Anno,main},[ArgsA]},
@@ -824,29 +824,29 @@ format_message(F, [{Mod,E}|Es]) ->
[M|format_message(F, Es)];
format_message(_, []) -> [].
-parse_to_dict(L) -> parse_to_dict(L, dict:new()).
-
-parse_to_dict([{function,_,Name,Arity,Clauses}|T], Dict0) ->
- Dict = dict:store({local, Name,Arity}, Clauses, Dict0),
- parse_to_dict(T, Dict);
-parse_to_dict([{attribute,_,import,{Mod,Funcs}}|T], Dict0) ->
- Dict = lists:foldl(fun(I, D) ->
- dict:store({remote,I}, Mod, D)
- end, Dict0, Funcs),
- parse_to_dict(T, Dict);
-parse_to_dict([_|T], Dict) ->
- parse_to_dict(T, Dict);
-parse_to_dict([], Dict) ->
- Dict.
+parse_to_map(L) -> parse_to_map(L, maps:new()).
+
+parse_to_map([{function,_,Name,Arity,Clauses}|T], Map0) ->
+ Map = maps:put({local, Name,Arity}, Clauses, Map0),
+ parse_to_map(T, Map);
+parse_to_map([{attribute,_,import,{Mod,Funcs}}|T], Map0) ->
+ Map = lists:foldl(fun(I, D) ->
+ maps:put({remote,I}, Mod, D)
+ end, Map0, Funcs),
+ parse_to_map(T, Map);
+parse_to_map([_|T], Map) ->
+ parse_to_map(T, Map);
+parse_to_map([], Map) ->
+ Map.
code_handler(local, [file], _, File) ->
File;
-code_handler(Name, Args, Dict, File) ->
+code_handler(Name, Args, Map, File) ->
%%io:format("code handler=~p~n",[{Name, Args}]),
Arity = length(Args),
- case dict:find({local,Name,Arity}, Dict) of
+ case maps:find({local,Name,Arity}, Map) of
{ok, Cs} ->
- LF = {value,fun(I, J) -> code_handler(I, J, Dict, File) end},
+ LF = {value,fun(I, J) -> code_handler(I, J, Map, File) end},
case erl_eval:match_clause(Cs, Args,erl_eval:new_bindings(),LF) of
{Body, Bs} ->
eval_exprs(Body, Bs, LF, none, none);
@@ -854,7 +854,7 @@ code_handler(Name, Args, Dict, File) ->
erlang:error({function_clause,[{local,Name,Args}]})
end;
error ->
- case dict:find({remote,{Name,Arity}}, Dict) of
+ case maps:find({remote,{Name,Arity}}, Map) of
{ok, Mod} ->
%% io:format("Calling:~p~n",[{Mod,Name,Args}]),
apply(Mod, Name, Args);
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index e926e4fcaf..c8f8c9721f 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -155,6 +155,7 @@ give_away(_, _, _) ->
Tab :: tab(),
InfoList :: [InfoTuple],
InfoTuple :: {compressed, boolean()}
+ | {decentralized_counters, boolean()}
| {heir, pid() | none}
| {id, tid()}
| {keypos, pos_integer()}
@@ -174,7 +175,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: binary | compressed | fixed | heir | id | keypos | memory
+ Item :: binary | compressed | decentralized_counters | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
@@ -311,6 +312,7 @@ member(_, _) ->
Access :: access(),
Tweaks :: {write_concurrency, boolean()}
| {read_concurrency, boolean()}
+ | {decentralized_counters, boolean()}
| compressed,
Pos :: pos_integer(),
HeirData :: term().
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index bb86a65c72..15abb6166b 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -187,7 +187,6 @@ bin_gen_field({bin_element,Line,{string,SLine,S},Size0,Options0},
Bin0, Bs0, BBs0, Mfun, Efun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
- match_check_size(Mfun, Size1, BBs0),
{value, Size, _BBs} = Efun(Size1, BBs0),
F = fun(C, Bin, Bs, BBs) ->
bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian,
@@ -200,7 +199,6 @@ bin_gen_field({bin_element,Line,VE,Size0,Options0},
make_bit_type(Line, Size0, Options0),
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
- match_check_size(Mfun, Size1, BBs0, false),
{value, Size, _BBs} = Efun(Size1, BBs0),
bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
@@ -269,7 +267,6 @@ match_field_1({bin_element,Line,{string,SLine,S},Size0,Options0},
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
Size2 = erl_eval:partial_eval(Size1),
- match_check_size(Mfun, Size2, BBs0),
{value, Size, _BBs} = Efun(Size2, BBs0),
F = fun(C, Bin, Bs, BBs) ->
match_field(Bin, Type, Size, Unit, Sign, Endian,
@@ -283,7 +280,6 @@ match_field_1({bin_element,Line,VE,Size0,Options0},
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
Size2 = erl_eval:partial_eval(Size1),
- match_check_size(Mfun, Size2, BBs0),
{value, Size, _BBs} = Efun(Size2, BBs0),
match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
@@ -387,24 +383,3 @@ make_bit_type(_Line, Size, Type0) -> %Size evaluates to an integer or 'all'
{ok,Size,Bt} -> {Size,erl_bits:as_list(Bt)};
{error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
end.
-
-match_check_size(Mfun, Size, Bs) ->
- match_check_size(Mfun, Size, Bs, true).
-
-match_check_size(Mfun, {var,_,V}, Bs, _AllowAll) ->
- case Mfun(binding, {V,Bs}) of
- {value,_} -> ok;
- unbound -> throw(invalid) % or, rather, error({unbound,V})
- end;
-match_check_size(_, {atom,_,all}, _Bs, true) ->
- ok;
-match_check_size(_, {atom,_,all}, _Bs, false) ->
- throw(invalid);
-match_check_size(_, {atom,_,undefined}, _Bs, _AllowAll) ->
- ok;
-match_check_size(_, {integer,_,_}, _Bs, _AllowAll) ->
- ok;
-match_check_size(_, {value,_,_}, _Bs, _AllowAll) ->
- ok; %From the debugger.
-match_check_size(_, _, _Bs, _AllowAll) ->
- throw(invalid).
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index d1a5a4dc35..b4c9ffc1b9 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -25,6 +25,7 @@
-export([wildcard/3, is_dir/2, is_file/2, is_regular/2]).
-export([fold_files/6, last_modified/2, file_size/2]).
-export([find_file/2, find_file/3, find_source/1, find_source/2, find_source/3]).
+-export([safe_relative_path/2]).
%% For debugging/testing.
-export([compile_wildcard/1]).
@@ -333,6 +334,7 @@ match_part([_|_], []) ->
false.
will_always_match([accept]) -> true;
+will_always_match([double_star]) -> true;
will_always_match(_) -> false.
prepare_base(Base0) ->
@@ -340,22 +342,33 @@ prepare_base(Base0) ->
"x"++Base2 = lists:reverse(Base1),
lists:reverse(Base2).
-do_double_star(Base, [H|T], Rest, Result, Mod, Root) ->
+do_double_star(Base, [H|T], Patterns, Result0, Mod, Root) ->
Full = case Root of
- false -> filename:join(Base, H);
- true -> H
- end,
+ false -> filename:join(Base, H);
+ true -> H
+ end,
Result1 = case do_list_dir(Full, Mod) of
- {ok, Files} ->
- do_double_star(Full, Files, Rest, Result, Mod, false);
- _ -> Result
- end,
- Result2 = case Root andalso Rest == [] of
- true -> Result1;
- false -> do_wildcard_3(Full, Rest, Result1, Mod)
- end,
- do_double_star(Base, T, Rest, Result2, Mod, Root);
-do_double_star(_Base, [], _Rest, Result, _Mod, _Root) ->
+ {ok, Files} ->
+ do_double_star(Full, Files, Patterns, Result0, Mod, false);
+ _ -> Result0
+ end,
+ Result2 = case Patterns of
+ %% The root is never included in the result.
+ _ when Root -> Result1;
+
+ %% An empty pattern includes all results (except the root).
+ [] -> [Full | Result1];
+
+ %% Otherwise we check if the current entry matches
+ %% and continue recursively.
+ [Pattern | Rest] ->
+ case match_part(Pattern, H) of
+ true -> do_wildcard_2([Full], Rest, Result1, Mod);
+ false -> Result1
+ end
+ end,
+ do_double_star(Base, T, Patterns, Result2, Mod, Root);
+do_double_star(_Base, [], _Patterns, Result, _Mod, _Root) ->
Result.
do_star(Pattern, [_|Rest]=File) ->
@@ -706,3 +719,71 @@ find_regular_file([File|Files]) ->
true -> {ok, File};
false -> find_regular_file(Files)
end.
+
+-spec safe_relative_path(Filename, Cwd) -> unsafe | SafeFilename when
+ Filename :: filename_all(),
+ Cwd :: filename_all(),
+ SafeFilename :: filename_all().
+
+safe_relative_path(Path, Cwd) ->
+ case filename:pathtype(Path) of
+ relative -> safe_relative_path(filename:split(Path), Cwd, [], "");
+ _ -> unsafe
+ end.
+
+safe_relative_path([], _Cwd, _PrevLinks, Acc) ->
+ Acc;
+
+safe_relative_path([Segment | Segments], Cwd, PrevLinks, Acc) ->
+ AccSegment = join(Acc, Segment),
+ case safe_relative_path(AccSegment) of
+ unsafe ->
+ unsafe;
+ SafeAccSegment ->
+ case file:read_link(join(Cwd, SafeAccSegment)) of
+ {ok, LinkPath} ->
+ case lists:member(LinkPath, PrevLinks) of
+ true ->
+ unsafe;
+ false ->
+ case safe_relative_path(filename:split(LinkPath), Cwd, [LinkPath | PrevLinks], Acc) of
+ unsafe -> unsafe;
+ NewAcc -> safe_relative_path(Segments, Cwd, [], NewAcc)
+ end
+ end;
+ {error, _} ->
+ safe_relative_path(Segments, Cwd, PrevLinks, SafeAccSegment)
+ end
+ end.
+
+join([], Path) -> Path;
+join(Left, Right) -> filename:join(Left, Right).
+
+safe_relative_path(Path) ->
+ case filename:pathtype(Path) of
+ relative ->
+ Cs0 = filename:split(Path),
+ safe_relative_path_1(Cs0, []);
+ _ ->
+ unsafe
+ end.
+
+safe_relative_path_1(["."|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([<<".">>|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([".."|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([<<"..">>|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([H|T], Acc) ->
+ safe_relative_path_1(T, [H|Acc]);
+safe_relative_path_1([], []) ->
+ [];
+safe_relative_path_1([], Acc) ->
+ filename:join(lists:reverse(Acc)).
+
+climb(_, []) ->
+ unsafe;
+climb(T, [_|Acc]) ->
+ safe_relative_path_1(T, Acc). \ No newline at end of file
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index b7b7b562ab..b6df99621f 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -19,8 +19,8 @@
%%
-module(filename).
--deprecated({find_src,1,next_major_release}).
--deprecated({find_src,2,next_major_release}).
+-deprecated([{find_src,'_',"use filelib:find_source/1,3 instead"}]).
+-deprecated([{safe_relative_path,1,"use filelib:safe_relative_path/2 instead"}]).
%% Purpose: Provides generic manipulation of filenames.
%%
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index a7f743bd4c..be14665d80 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -28,7 +28,9 @@
%%%-----------------------------------------------------------------
-export([start/5, start/6, debug_options/2, hibernate_after/1,
name/1, unregister_name/1, get_proc_name/1, get_parent/0,
- call/3, call/4, reply/2, stop/1, stop/3]).
+ call/3, call/4, reply/2,
+ send_request/3, wait_response/2, check_response/2,
+ stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -38,7 +40,7 @@
%%-----------------------------------------------------------------
--type linkage() :: 'link' | 'nolink'.
+-type linkage() :: 'monitor' | 'link' | 'nolink'.
-type emgr_name() :: {'local', atom()}
| {'global', term()}
| {'via', Module :: module(), Name :: term()}.
@@ -53,6 +55,11 @@
| {'spawn_opt', [proc_lib:spawn_option()]}.
-type options() :: [option()].
+-type server_ref() :: pid() | atom() | {atom(), node()}
+ | {global, term()} | {via, module(), term()}.
+
+-type request_id() :: term().
+
%%-----------------------------------------------------------------
%% Starts a generic process.
%% start(GenMod, LinkP, Mod, Args, Options)
@@ -95,6 +102,13 @@ do_spawn(GenMod, link, Mod, Args, Options) ->
[GenMod, self(), self(), Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -108,6 +122,13 @@ do_spawn(GenMod, link, Name, Mod, Args, Options) ->
[GenMod, self(), self(), Name, Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Name, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Name, Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -115,6 +136,26 @@ do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time,
spawn_opts(Options)).
+
+%%
+%% Adjust monitor returns for OTP gen behaviours...
+%%
+%% If an OTP behaviour is introduced that 'init_ack's
+%% other results, this has code has to be moved out
+%% into all behaviours as well as adjusted...
+%%
+monitor_return({{ok, Pid}, Mon}) when is_pid(Pid), is_reference(Mon) ->
+ %% Successful start_monitor()...
+ {ok, {Pid, Mon}};
+monitor_return({Error, Mon}) when is_reference(Mon) ->
+ %% Failure; wait for spawned process to terminate
+ %% and release resources, then return the error...
+ receive
+ {'DOWN', Mon, process, _Pid, _Reason} ->
+ ok
+ end,
+ Error.
+
%%-----------------------------------------------------------------
%% Initiate the new process.
%% Register the name using the Rfunc function
@@ -139,7 +180,7 @@ init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
%%-----------------------------------------------------------------
%% Makes a synchronous call to a generic process.
%% Request is sent to the Pid, and the response must be
-%% {Tag, _, Reply}.
+%% {Tag, Reply}.
%%-----------------------------------------------------------------
%%% New call function which uses the new monitor BIF
@@ -192,6 +233,56 @@ get_node(Process) ->
node(Process)
end.
+-spec send_request(Name::server_ref(), Label::term(), Request::term()) -> request_id().
+send_request(Process, Label, Request) when is_pid(Process) ->
+ do_send_request(Process, Label, Request);
+send_request(Process, Label, Request) ->
+ Fun = fun(Pid) -> do_send_request(Pid, Label, Request) end,
+ try do_for_proc(Process, Fun)
+ catch exit:Reason ->
+ %% Make send_request async and fake a down message
+ Mref = erlang:make_ref(),
+ self() ! {'DOWN', Mref, process, Process, Reason},
+ Mref
+ end.
+
+do_send_request(Process, Label, Request) ->
+ Mref = erlang:monitor(process, Process),
+ erlang:send(Process, {Label, {self(), {'$gen_request_id', Mref}}, Request}, [noconnect]),
+ Mref.
+
+%%
+%% Wait for a reply to the client.
+%% Note: if timeout is returned monitors are kept.
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(Mref, Timeout)
+ when is_reference(Mref) ->
+ receive
+ {{'$gen_request_id', Mref}, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {reply, Reply};
+ {'DOWN', Mref, _, Object, Reason} ->
+ {error, {Reason, Object}}
+ after Timeout ->
+ timeout
+ end.
+
+-spec check_response(RequestId::term(), Key::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, Mref)
+ when is_reference(Mref) ->
+ case Msg of
+ {{'$gen_request_id', Mref}, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {reply, Reply};
+ {'DOWN', Mref, _, Object, Reason} ->
+ {error, {Reason, Object}};
+ _ ->
+ no_reply
+ end.
+
%%
%% Send a reply to the client.
%%
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 8213282867..8024221cab 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,13 +31,20 @@
%%% Re-written by Joe with new functional interface !
%%% Modified by Martin - uses proc_lib, sys and gen!
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
-export([start/0, start/1, start/2,
start_link/0, start_link/1, start_link/2,
+ start_monitor/0, start_monitor/1, start_monitor/2,
stop/1, stop/3,
notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
- swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/5]).
+ swap_sup_handler/3, which_handlers/1, call/3, call/4,
+ send_request/3, wait_response/2, check_response/2,
+ wake_hib/5]).
-export([init_it/6,
system_continue/3,
@@ -48,7 +55,7 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
-export_type([handler/0, handler_args/0, add_handler_ret/0,
del_handler_ret/0]).
@@ -128,11 +135,13 @@
| {'logfile', string()}.
-type option() :: {'timeout', timeout()}
| {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}
| {'hibernate_after', timeout()}.
-type emgr_ref() :: atom() | {atom(), atom()} | {'global', term()}
| {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
+-type start_mon_ret() :: {'ok', {pid(),reference()}} | {'error', term()}.
+-type request_id() :: term().
%%---------------------------------------------------------------------------
@@ -183,6 +192,20 @@ start_link(Options) when is_list(Options) ->
start_link(Name, Options) ->
gen:start(?MODULE, link, Name, ?NO_CALLBACK, [], Options).
+-spec start_monitor() -> start_mon_ret().
+start_monitor() ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], []).
+
+-spec start_monitor(emgr_name() | [option()]) -> start_mon_ret().
+start_monitor(Name) when is_tuple(Name) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], []);
+start_monitor(Options) when is_list(Options) ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], Options).
+
+-spec start_monitor(emgr_name(), [option()]) -> start_mon_ret().
+start_monitor(Name, Options) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], Options).
+
%% -spec init_it(pid(), 'self' | pid(), emgr_name(), module(), [term()], [_]) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
@@ -213,6 +236,26 @@ call(M, Handler, Query) -> call1(M, Handler, Query).
-spec call(emgr_ref(), handler(), term(), timeout()) -> term().
call(M, Handler, Query, Timeout) -> call1(M, Handler, Query, Timeout).
+-spec send_request(emgr_ref(), handler(), term()) -> request_id().
+send_request(M, Handler, Query) ->
+ gen:send_request(M, self(), {call, Handler, Query}).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), emgr_ref()}}.
+wait_response(RequestId, Timeout) ->
+ case gen:wait_response(RequestId, Timeout) of
+ {reply, {error, _} = Err} -> Err;
+ Return -> Return
+ end.
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), emgr_ref()}}.
+check_response(Msg, RequestId) ->
+ case gen:check_response(Msg, RequestId) of
+ {reply, {error, _} = Err} -> Err;
+ Return -> Return
+ end.
+
-spec delete_handler(emgr_ref(), handler(), term()) -> term().
delete_handler(M, Handler, Args) -> rpc(M, {delete_handler, Handler, Args}).
@@ -590,8 +633,10 @@ server_update(Handler1, Func, Event, SName) ->
module=>Mod1,
message=>Event},
#{domain=>[otp],
- report_cb=>fun gen_event:format_log/1,
- error_logger=>#{tag=>warning_msg}}), % warningmap??
+ report_cb=>fun gen_event:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg, % warningmap??
+ report_cb=>fun gen_event:format_log/1}}),
{ok, Handler1};
Other ->
do_terminate(Mod1, Handler1, {error, Other}, State,
@@ -752,46 +797,165 @@ report_error(Handler, Reason, State, LastIn, SName) ->
get(),State),
reason=>Reason},
#{domain=>[otp],
- report_cb=>fun gen_event:format_log/1,
- error_logger=>#{tag=>error}}).
-
-format_log(#{label:={gen_event,terminate},
- handler:=Handler,
- name:=SName,
- last_message:=LastIn,
- state:=State,
- reason:=Reason}) ->
- Reason1 =
- case Reason of
- {'EXIT',{undef,[{M,F,A,L}|MFAs]}} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- {undef,[{M,F,A,L}|MFAs]};
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- {'EXIT',Why} ->
- Why;
- _ ->
- Reason
- end,
- {"** gen_event handler ~p crashed.~n"
- "** Was installed in ~tp~n"
- "** Last event was: ~tp~n"
- "** When handler state == ~tp~n"
- "** Reason == ~tp~n",
- [Handler,SName,LastIn,State,Reason1]};
-format_log(#{label:={gen_event,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~tp~n"
- "** Unhandled message: ~tp~n",
- [Mod, Msg]}.
+ report_cb=>fun gen_event:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_event:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_event,terminate},
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason}=Report,
+ Depth) ->
+ Report#{last_message => io_lib:limit_term(LastIn, Depth),
+ state => io_lib:limit_term(State, Depth),
+ reason => io_lib:limit_term(Reason, Depth)};
+limit_report(#{label:={gen_event,no_handle_info},
+ message:=Msg}=Report,
+ Depth) ->
+ Report#{message => io_lib:limit_term(Msg, Depth)}.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_event,terminate},
+ handler:=Handler,
+ name:=SName,
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason},
+ #{single_line:=true, depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Reason1 = fix_reason(Reason),
+ Format1 = lists:append(["Generic event handler ",P," crashed. "
+ "Installed: ",P,". Last event: ",P,
+ ". State: ",P,". Reason: ",P,"."]),
+ Args1 =
+ case Depth of
+ unlimited ->
+ [Handler,SName,Reason1,LastIn,State];
+ _ ->
+ [Handler,Depth,SName,Depth,Reason1,Depth,
+ LastIn,Depth,State,Depth]
+ end,
+ {Format1, Args1};
+format_log_single(#{label:={gen_event,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={gen_event,terminate},
+ handler:=Handler,
+ name:=SName,
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason},
+ #{depth:=Depth}=FormatOpts) ->
+ Reason1 = fix_reason(Reason),
+ P = p(FormatOpts),
+ Format =
+ lists:append(["** gen_event handler ",P," crashed.\n",
+ "** Was installed in ",P,"\n",
+ "** Last event was: ",P,"\n",
+ "** When handler state == ",P,"\n",
+ "** Reason == ",P,"\n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Handler,SName,LastIn,State,Reason1];
+ _ ->
+ [Handler,Depth,SName,Depth,LastIn,Depth,State,Depth,
+ Reason1,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={gen_event,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p\n"
+ "** Unhandled message: "++P++"\n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({'EXIT',{undef,[{M,F,A,_L}|_]=MFAs}=Reason}) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',MFAs};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',MFAs}
+ end
+ end;
+fix_reason({'EXIT',Reason}) ->
+ Reason;
+fix_reason(Reason) ->
+ Reason.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
handler(Handler) when not Handler#handler.id ->
Handler#handler.module;
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 1e18710738..f4752c37d4 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -127,27 +127,9 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
-
--deprecated({start, 3, eventually}).
--deprecated({start, 4, eventually}).
--deprecated({start_link, 3, eventually}).
--deprecated({start_link, 4, eventually}).
--deprecated({stop, 1, eventually}).
--deprecated({stop, 3, eventually}).
--deprecated({send_event, 2, eventually}).
--deprecated({sync_send_event, 2, eventually}).
--deprecated({sync_send_event, 3, eventually}).
--deprecated({send_all_state_event, 2, eventually}).
--deprecated({sync_send_all_state_event, 2, eventually}).
--deprecated({sync_send_all_state_event, 3, eventually}).
--deprecated({reply, 2, eventually}).
--deprecated({start_timer, 2, eventually}).
--deprecated({send_event_after, 2, eventually}).
--deprecated({cancel_timer, 1, eventually}).
--deprecated({enter_loop, 4, eventually}).
--deprecated({enter_loop, 5, eventually}).
--deprecated({enter_loop, 6, eventually}).
+-export([format_log/1, format_log/2]).
+
+-deprecated({'_','_', "use the 'gen_statem' module instead"}).
%%% ---------------------------------------------------
%%% Interface functions.
@@ -517,8 +499,10 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTi
module=>Mod,
message=>Msg},
#{domain=>[otp],
- report_cb=>fun gen_fsm:format_log/1,
- error_logger=>#{tag=>warning_msg}}),
+ report_cb=>fun gen_fsm:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg,
+ report_cb=>fun gen_fsm:format_log/1}}),
loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
{'EXIT', What} ->
terminate(What, Name, From, Msg, Mod, StateName, StateData, []);
@@ -634,8 +618,9 @@ error_info(Reason, Name, From, Msg, StateName, StateData, Debug) ->
reason=>Reason,
client_info=>client_stacktrace(From)},
#{domain=>[otp],
- report_cb=>fun gen_fsm:format_log/1,
- error_logger=>#{tag=>error}}),
+ report_cb=>fun gen_fsm:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_fsm:format_log/1}}),
ok.
client_stacktrace(undefined) ->
@@ -655,70 +640,197 @@ client_stacktrace(Pid) when is_pid(Pid) ->
{Pid,remote}.
-format_log(#{label:={gen_fsm,terminate},
- name:=Name,
- last_message:=Msg,
- state_name:=StateName,
- state_data:=StateData,
- log:=Log,
- reason:=Reason,
- client_info:=ClientInfo}) ->
- Reason1 =
- case Reason of
- {undef,[{M,F,A,L}|MFAs]} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- Reason;
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- _ ->
- Reason
- end,
- {ClientFmt,ClientArgs} = format_client_log(ClientInfo),
- {"** State machine ~tp terminating \n" ++
- get_msg_str(Msg) ++
- "** When State == ~tp~n"
- "** Data == ~tp~n"
- "** Reason for termination ==~n** ~tp~n" ++
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_fsm,terminate},
+ last_message:=Msg,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo}=Report,
+ Depth) ->
+ Report#{last_message=>io_lib:limit_term(Msg, Depth),
+ state_data=>io_lib:limit_term(StateData, Depth),
+ log=>[io_lib:limit_term(L, Depth) || L <- Log],
+ reason=>io_lib:limit_term(Reason, Depth),
+ client_info=>limit_client_report(ClientInfo, Depth)};
+limit_report(#{label:={gen_fsm,no_handle_info},
+ message:=Msg}=Report, Depth) ->
+ Report#{message=>io_lib:limit_term(Msg, Depth)}.
+
+limit_client_report({From,{Name,Stacktrace}}, Depth) ->
+ {From,{Name,io_lib:limit_term(Stacktrace, Depth)}};
+limit_client_report(Client, _) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_fsm,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state_name:=StateName,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ FixedReason = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["State machine ",P," terminating. Reason: ",P,
+ ". Last event: ",P,
+ ". State: ",P,
+ ". Data: ",P,
+ case Log of
+ [] -> "";
+ _ -> ". Log: "++P
+ end,
+ "."]),
+ Args0 =
+ [Name,FixedReason,get_msg(Msg),StateName,StateData] ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt, Args++ClientArgs};
+format_log_single(#{label:={gen_fsm,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={gen_fsm,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state_name:=StateName,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ FixedReason = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["** State machine ",P," terminating \n"++
+ get_msg_str(Msg, P)++
+ "** When State == ",P,"~n",
+ "** Data == ",P,"~n",
+ "** Reason for termination ==~n** ",P,"~n",
+ case Log of
+ [] -> [];
+ _ -> "** Log ==~n**"++P++"~n"
+ end]),
+ Args0 =
+ [Name|get_msg(Msg)] ++
+ [StateName,StateData,FixedReason |
case Log of
[] -> [];
- _ -> "** Log ==~n** ~tp~n"
- end ++ ClientFmt,
- [Name|error_logger:limit_term(get_msg(Msg))] ++
- [StateName,
- error_logger:limit_term(StateData),
- error_logger:limit_term(Reason1) |
- case Log of
- [] -> [];
- _ -> [[error_logger:limit_term(D) || D <- Log]]
- end] ++ ClientArgs};
-format_log(#{label:={gen_fsm,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~p~n"
- "** Unhandled message: ~tp~n",
- [Mod, error_logger:limit_term(Msg)]}.
-
-get_msg_str({'$gen_event', _Event}) ->
- "** Last event in was ~tp~n";
-get_msg_str({'$gen_sync_event', _From, _Event}) ->
- "** Last sync event in was ~tp from ~tw~n";
-get_msg_str({'$gen_all_state_event', _Event}) ->
- "** Last event in was ~tp (for all states)~n";
-get_msg_str({'$gen_sync_all_state_event', _From, _Event}) ->
- "** Last sync event in was ~tp (for all states) from ~tw~n";
-get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
- "** Last timer event in was ~tp~n";
-get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
- "** Last timer event in was ~tp~n";
-get_msg_str(_Msg) ->
- "** Last message in was ~tp~n".
+ _ -> [Log]
+ end],
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt,Args++ClientArgs};
+format_log_multi(#{label:={gen_fsm,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p~n"
+ "** Unhandled message: "++P++"~n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({undef,[{M,F,A,L}|MFAs]}=Reason) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',[{M,F,A,L}|MFAs]};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',[{M,F,A,L}|MFAs]}
+ end
+ end;
+fix_reason(Reason) ->
+ Reason.
+
+get_msg_str({'$gen_event', _Event}, P) ->
+ "** Last event in was "++P++"~n";
+get_msg_str({'$gen_sync_event', _From, _Event}, P) ->
+ "** Last sync event in was "++P++" from ~tw~n";
+get_msg_str({'$gen_all_state_event', _Event}, P) ->
+ "** Last event in was "++P++" (for all states)~n";
+get_msg_str({'$gen_sync_all_state_event', _From, _Event}, P) ->
+ "** Last sync event in was "++P++" (for all states) from "++P++"~n";
+get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}, P) ->
+ "** Last timer event in was "++P++"~n";
+get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}, P) ->
+ "** Last timer event in was "++P++"~n";
+get_msg_str(_Msg, P) ->
+ "** Last message in was "++P++"~n".
get_msg({'$gen_event', Event}) -> [Event];
get_msg({'$gen_sync_event', {From,_Tag}, Event}) -> [Event,From];
@@ -728,16 +840,53 @@ get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> [{timeout, Ref, Msg}];
get_msg({timeout, _Ref, {'$gen_event', Event}}) -> [Event];
get_msg(Msg) -> [Msg].
-format_client_log(undefined) ->
+format_client_log_single(undefined, _, _) ->
+ {"", []};
+format_client_log_single({Pid,dead}, _, _) ->
+ {" Client ~0p is dead.", [Pid]};
+format_client_log_single({Pid,remote}, _, _) ->
+ {" Client ~0p is remote on node ~0p.", [Pid,node(Pid)]};
+format_client_log_single({_Pid,{Name,Stacktrace0}}, P, Depth) ->
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0, 4),
+ Format = lists:append([" Client ",P," stacktrace: ",P,"."]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format, Args}.
+
+format_client_log(undefined, _, _) ->
{"", []};
-format_client_log({From,dead}) ->
- {"** Client ~p is dead~n", [From]};
-format_client_log({From,remote}) ->
- {"** Client ~p is remote on node ~p~n", [From, node(From)]};
-format_client_log({_From,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
+format_client_log({Pid,dead}, _, _) ->
+ {"** Client ~p is dead~n", [Pid]};
+format_client_log({Pid,remote}, _, _) ->
+ {"** Client ~p is remote on node ~p~n", [Pid,node(Pid)]};
+format_client_log({_Pid,{Name,Stacktrace}}, P, Depth) ->
+ Format = lists:append(["** Client ",P," stacktrace~n** ",P,"~n"]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%%-----------------------------------------------------------------
%% Status information
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index c7b6406f54..e49961a5f0 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -19,6 +19,11 @@
%%
-module(gen_server).
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%%% ---------------------------------------------------
%%%
%%% The idea behind THIS server is that the user module
@@ -89,8 +94,10 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ start_monitor/3, start_monitor/4,
stop/1, stop/3,
call/2, call/3,
+ send_request/2, wait_response/2, check_response/2,
cast/2, reply/2,
abcast/2, abcast/3,
multi_call/2, multi_call/3, multi_call/4,
@@ -105,7 +112,7 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
%% Internal exports
-export([init_it/6]).
@@ -116,6 +123,16 @@
STACKTRACE(),
element(2, erlang:process_info(self(), current_stacktrace))).
+
+-type server_ref() ::
+ pid()
+ | (LocalName :: atom())
+ | {Name :: atom(), Node :: atom()}
+ | {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), ViaName :: term()}.
+
+-type request_id() :: term().
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -188,6 +205,12 @@ start_link(Mod, Args, Options) ->
start_link(Name, Mod, Args, Options) ->
gen:start(?MODULE, link, Name, Mod, Args, Options).
+start_monitor(Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Mod, Args, Options).
+
+start_monitor(Name, Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Name, Mod, Args, Options).
+
%% -----------------------------------------------------------------
%% Stop a generic server and wait for it to terminate.
@@ -224,6 +247,25 @@ call(Name, Request, Timeout) ->
end.
%% -----------------------------------------------------------------
+%% Send a request to a generic server and return a Key which should be
+%% used with wait_response/2 or check_response/2 to fetch the
+%% result of the request.
+
+-spec send_request(Name::server_ref(), Request::term()) -> request_id().
+send_request(Name, Request) ->
+ gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+ gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+ gen:check_response(Msg, RequestId).
+
+%% -----------------------------------------------------------------
%% Make a cast to a generic server.
%% -----------------------------------------------------------------
cast({global,Name}, Request) ->
@@ -249,7 +291,8 @@ cast_msg(Request) -> {'$gen_cast',Request}.
%% Send a reply to the client.
%% -----------------------------------------------------------------
reply({To, Tag}, Reply) ->
- catch To ! {Tag, Reply}.
+ catch To ! {Tag, Reply},
+ ok.
%% -----------------------------------------------------------------
%% Asynchronous broadcast, returns nothing, it's just send 'n' pray
@@ -646,8 +689,10 @@ try_dispatch(Mod, Func, Msg, State) ->
module=>Mod,
message=>Msg},
#{domain=>[otp],
- report_cb=>fun gen_server:format_log/1,
- error_logger=>#{tag=>warning_msg}}),
+ report_cb=>fun gen_server:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg,
+ report_cb=>fun gen_server:format_log/1}}),
{ok, {noreply, State}};
true ->
{'EXIT', error, R, Stacktrace}
@@ -894,8 +939,9 @@ error_info(Reason, Name, From, Msg, Mod, State, Debug) ->
reason=>Reason,
client_info=>client_stacktrace(From)},
#{domain=>[otp],
- report_cb=>fun gen_server:format_log/1,
- error_logger=>#{tag=>error}}),
+ report_cb=>fun gen_server:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_server:format_log/1}}),
ok.
client_stacktrace(undefined) ->
@@ -914,63 +960,236 @@ client_stacktrace(From) when is_pid(From), node(From) =:= node() ->
client_stacktrace(From) when is_pid(From) ->
{From,remote}.
-format_log(#{label:={gen_server,terminate},
- name:=Name,
- last_message:=Msg,
- state:=State,
- log:=Log,
- reason:=Reason,
- client_info:=Client}) ->
- Reason1 =
- case Reason of
- {undef,[{M,F,A,L}|MFAs]} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- Reason;
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- _ ->
- Reason
- end,
- {ClientFmt,ClientArgs} = format_client_log(Client),
- [LimitedMsg,LimitedState,LimitedReason|LimitedLog] =
- [error_logger:limit_term(D) || D <- [Msg,State,Reason1|Log]],
- {"** Generic server ~tp terminating \n"
- "** Last message in was ~tp~n"
- "** When Server state == ~tp~n"
- "** Reason for termination ==~n** ~tp~n" ++
- case LimitedLog of
- [] -> [];
- _ -> "** Log ==~n** ~tp~n"
- end ++ ClientFmt,
- [Name, LimitedMsg, LimitedState, LimitedReason] ++
- case LimitedLog of
- [] -> [];
- _ -> [LimitedLog]
- end ++ ClientArgs};
-format_log(#{label:={gen_server,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~p~n"
- "** Unhandled message: ~tp~n",
- [Mod, error_logger:limit_term(Msg)]}.
-
-format_client_log(undefined) ->
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report,Depth),FormatOpts).
+
+limit_report(Report,unlimited) ->
+ Report;
+limit_report(#{label:={gen_server,terminate},
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client}=Report,
+ Depth) ->
+ Report#{last_message=>io_lib:limit_term(Msg,Depth),
+ state=>io_lib:limit_term(State,Depth),
+ log=>[io_lib:limit_term(L,Depth)||L<-Log],
+ reason=>io_lib:limit_term(Reason,Depth),
+ client_info=>limit_client_report(Client,Depth)};
+limit_report(#{label:={gen_server,no_handle_info},
+ message:=Msg}=Report,Depth) ->
+ Report#{message=>io_lib:limit_term(Msg,Depth)}.
+
+limit_client_report({From,{Name,Stacktrace}},Depth) ->
+ {From,{Name,io_lib:limit_term(Stacktrace,Depth)}};
+limit_client_report(Client,_) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default,FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_server,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format1 = lists:append(["Generic server ",P," terminating. Reason: ",P,
+ ". Last message: ", P, ". State: ",P,"."]),
+ {ServerLogFormat,ServerLogArgs} = format_server_log_single(Log,FormatOpts),
+ {ClientLogFormat,ClientLogArgs} = format_client_log_single(Client,FormatOpts),
+
+ Args1 =
+ case Depth of
+ unlimited ->
+ [Name,fix_reason(Reason),Msg,State];
+ _ ->
+ [Name,Depth,fix_reason(Reason),Depth,Msg,Depth,State,Depth]
+ end,
+ {Format1++ServerLogFormat++ClientLogFormat,
+ Args1++ServerLogArgs++ClientLogArgs};
+format_log_single(#{label:={gen_server,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={gen_server,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client},
+ #{depth:=Depth}=FormatOpts) ->
+ Reason1 = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log(Client,FormatOpts),
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ ["** Generic server ",P," terminating \n"
+ "** Last message in was ",P,"~n"
+ "** When Server state == ",P,"~n"
+ "** Reason for termination ==~n** ",P,"~n"] ++
+ case Log of
+ [] -> [];
+ _ -> ["** Log ==~n** ["|
+ lists:join(",~n ",lists:duplicate(length(Log),P))]++
+ ["]~n"]
+ end) ++ ClientFmt,
+ Args =
+ case Depth of
+ unlimited ->
+ [Name, Msg, State, Reason1] ++
+ case Log of
+ [] -> [];
+ _ -> Log
+ end ++ ClientArgs;
+ _ ->
+ [Name, Depth, Msg, Depth, State, Depth, Reason1, Depth] ++
+ case Log of
+ [] -> [];
+ _ -> lists:flatmap(fun(L) -> [L, Depth] end, Log)
+ end ++ ClientArgs
+ end,
+ {Format,Args};
+format_log_multi(#{label:={gen_server,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p~n"
+ "** Unhandled message: "++P++"~n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({undef,[{M,F,A,L}|MFAs]}=Reason) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',[{M,F,A,L}|MFAs]};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',[{M,F,A,L}|MFAs]}
+ end
+ end;
+fix_reason(Reason) ->
+ Reason.
+
+format_server_log_single([],_) ->
+ {"",[]};
+format_server_log_single(Log,FormatOpts) ->
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Log];
+ Depth ->
+ [Log, Depth]
+ end,
+ {" Log: "++p(FormatOpts),Args}.
+
+format_client_log_single(undefined,_) ->
+ {"",[]};
+format_client_log_single({From,dead},_) ->
+ {" Client ~0p is dead.",[From]};
+format_client_log_single({From,remote},_) ->
+ {" Client ~0p is remote on node ~0p.", [From, node(From)]};
+format_client_log_single({_From,{Name,Stacktrace0}},FormatOpts) ->
+ P = p(FormatOpts),
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0,4),
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Name, Stacktrace];
+ Depth ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {" Client "++P++" stacktrace: "++P++".", Args}.
+
+format_client_log(undefined,_) ->
{"", []};
-format_client_log({From,dead}) ->
+format_client_log({From,dead},_) ->
{"** Client ~p is dead~n", [From]};
-format_client_log({From,remote}) ->
+format_client_log({From,remote},_) ->
{"** Client ~p is remote on node ~p~n", [From, node(From)]};
-format_client_log({_From,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
+format_client_log({_From,{Name,Stacktrace}},FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["** Client ",P," stacktrace~n",
+ "** ",P,"~n"]),
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Name, Stacktrace];
+ Depth ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%%-----------------------------------------------------------------
%% Status information
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 885c6ef031..acedd6daaa 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -21,11 +21,18 @@
-include("logger.hrl").
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%% API
-export(
[start/3,start/4,start_link/3,start_link/4,
+ start_monitor/3,start_monitor/4,
stop/1,stop/3,
cast/2,call/2,call/3,
+ send_request/2,wait_response/1,wait_response/2,check_response/2,
enter_loop/4,enter_loop/5,enter_loop/6,
reply/1,reply/2]).
@@ -47,7 +54,7 @@
[wakeup_from_hibernate/3]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
%% Type exports for templates and callback modules
-export_type(
@@ -58,7 +65,8 @@
event_handler_result/1,
reply_action/0,
enter_action/0,
- action/0]).
+ action/0
+ ]).
%% Old types, not advertised
-export_type(
[state_function_result/0,
@@ -257,6 +265,7 @@
Replies :: [reply_action()] | reply_action(),
NewData :: data()}.
+-type request_id() :: term().
%% The state machine init function. It is called only once and
%% the server is not running until this function has returned
@@ -453,12 +462,16 @@ timeout_event_type(Type) ->
| {'via', RegMod :: module(), ViaName :: term()}.
-type start_opt() ::
{'timeout', Time :: timeout()}
- | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}
| enter_loop_opt().
-type start_ret() ::
{'ok', pid()}
| 'ignore'
| {'error', term()}.
+-type start_mon_ret() ::
+ {'ok', {pid(),reference()}}
+ | 'ignore'
+ | {'error', term()}.
-type enter_loop_opt() ::
{'hibernate_after', HibernateAfterTimeout :: timeout()}
| {'debug', Dbgs :: [sys:debug_option()]}.
@@ -493,6 +506,20 @@ start_link(Module, Args, Opts) ->
start_link(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, link, ServerName, Module, Args, Opts).
+%% Start and monitor a state machine
+-spec start_monitor(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, Module, Args, Opts).
+%%
+-spec start_monitor(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, ServerName, Module, Args, Opts).
+
%% Stop a state machine
-spec stop(ServerRef :: server_ref()) -> ok.
stop(ServerRef) ->
@@ -551,6 +578,26 @@ call(ServerRef, Request, {_, _} = Timeout) ->
call(ServerRef, Request, Timeout) ->
call_clean(ServerRef, Request, Timeout, Timeout).
+-spec send_request(ServerRef::server_ref(), Request::term()) ->
+ RequestId::request_id().
+send_request(Name, Request) ->
+ gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id()) ->
+ {reply, Reply::term()} | {error, {term(), server_ref()}}.
+wait_response(RequestId) ->
+ gen:wait_response(RequestId, infinity).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+ gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+ gen:check_response(Msg, RequestId).
+
%% Reply from a state machine callback to whom awaits in call/2
-spec reply([reply_action()] | reply_action()) -> ok.
reply({reply,From,Reply}) ->
@@ -2329,8 +2376,10 @@ error_info(
reason=>{Class,Reason,Stacktrace},
client_info=>client_stacktrace(Q)},
#{domain=>[otp],
- report_cb=>fun gen_statem:format_log/1,
- error_logger=>#{tag=>error}}).
+ report_cb=>fun gen_statem:format_log/2,
+ error_logger=>
+ #{tag=>error,
+ report_cb=>fun gen_statem:format_log/1}}).
client_stacktrace([]) ->
undefined;
@@ -2356,42 +2405,155 @@ client_stacktrace([_|_]) ->
undefined.
-format_log(#{label:={gen_statem,terminate},
- name:=Name,
- queue:=Q,
- postponed:=Postponed,
- callback_mode:=CallbackMode,
- state_enter:=StateEnter,
- state:=FmtData,
- timeouts:=Timeouts,
- log:=Log,
- reason:={Class,Reason,Stacktrace},
- client_info:=ClientInfo}) ->
- {FixedReason,FixedStacktrace} =
- case Stacktrace of
- [{M,F,Args,_}|ST]
- when Class =:= error, Reason =:= undef ->
- case code:is_loaded(M) of
- false ->
- {{'module could not be loaded',M},ST};
- _ ->
- Arity =
- if
- is_list(Args) ->
- length(Args);
- is_integer(Args) ->
- Args
- end,
- case erlang:function_exported(M, F, Arity) of
- true ->
- {Reason,Stacktrace};
- false ->
- {{'function not exported',{M,F,Arity}},ST}
- end
- end;
- _ -> {Reason,Stacktrace}
- end,
- {ClientFmt,ClientArgs} = format_client_log(ClientInfo),
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_statem,terminate},
+ queue:=Q,
+ postponed:=Postponed,
+ state:=FmtData,
+ timeouts:=Timeouts,
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo}=Report,
+ Depth) ->
+ Report#{queue =>
+ case Q of
+ [Event|Events] ->
+ [io_lib:limit_term(Event, Depth)
+ |io_lib:limit_term(Events, Depth)];
+ _ -> []
+ end,
+ postponed =>
+ case Postponed of
+ [] -> [];
+ _ -> io_lib:limit_term(Postponed, Depth)
+ end,
+ state => io_lib:limit_term(FmtData, Depth),
+ timeouts =>
+ case Timeouts of
+ {0,_} -> Timeouts;
+ _ -> io_lib:limit_term(Timeouts, Depth)
+ end,
+ log =>
+ case Log of
+ [] -> [];
+ _ -> [io_lib:limit_term(T, Depth) || T <- Log]
+ end,
+ reason =>
+ {Class,
+ io_lib:limit_term(Reason, Depth),
+ io_lib:limit_term(Stacktrace, Depth)},
+ client_info => limit_client_info(ClientInfo, Depth)}.
+
+
+limit_client_info({Pid,{Name,Stacktrace}}, Depth) ->
+ {Pid,{Name,io_lib:limit_term(Stacktrace, Depth)}};
+limit_client_info(Client, _Depth) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default,FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_statem,terminate},
+ name:=Name,
+ queue:=Q,
+ %% postponed
+ %% callback_mode
+ %% state_enter
+ state:=FmtData,
+ %% timeouts
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {FixedReason,FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
+ {ClientFmt,ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["State machine ",P," terminating. Reason: ",P,
+ case FixedStacktrace of
+ [] -> "";
+ _ -> ". Stack: "++P
+ end,
+ case Q of
+ [] -> "";
+ _ -> ". Last event: "++P
+ end,
+ ". State: ",P,
+ case Log of
+ [] -> "";
+ _ -> ". Log: "++P
+ end,
+ "."]),
+ Args0 =
+ [Name,FixedReason] ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end ++
+ case Q of
+ [] -> [];
+ [Event|_] -> [Event]
+ end ++
+ [FmtData] ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt, Args++ClientArgs};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={gen_statem,terminate},
+ name:=Name,
+ queue:=Q,
+ postponed:=Postponed,
+ callback_mode:=CallbackMode,
+ state_enter:=StateEnter,
+ state:=FmtData,
+ timeouts:=Timeouts,
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {FixedReason,FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
+ {ClientFmt,ClientArgs} = format_client_log(ClientInfo, P, Depth),
CBMode =
case StateEnter of
true ->
@@ -2399,74 +2561,145 @@ format_log(#{label:={gen_statem,terminate},
false ->
CallbackMode
end,
- {"** State machine ~tp terminating~n" ++
- case Q of
- [] -> "";
- _ -> "** Last event = ~tp~n"
- end ++
- "** When server state = ~tp~n" ++
- "** Reason for termination = ~w:~tp~n" ++
- "** Callback mode = ~p~n" ++
+ Format =
+ lists:append(
+ ["** State machine ",P," terminating~n",
+ case Q of
+ [] -> "";
+ _ -> "** Last event = "++P++"~n"
+ end,
+ "** When server state = ",P,"~n",
+ "** Reason for termination = ",P,":",P,"~n",
+ "** Callback mode = ",P,"~n",
+ case Q of
+ [_,_|_] -> "** Queued = "++P++"~n";
+ _ -> ""
+ end,
+ case Postponed of
+ [] -> "";
+ _ -> "** Postponed = "++P++"~n"
+ end,
+ case FixedStacktrace of
+ [] -> "";
+ _ -> "** Stacktrace =~n** "++P++"~n"
+ end,
+ case Timeouts of
+ {0,_} -> "";
+ _ -> "** Time-outs: "++P++"~n"
+ end,
+ case Log of
+ [] -> "";
+ _ -> "** Log =~n** "++P++"~n"
+ end]),
+ Args0 =
+ [Name |
case Q of
- [_,_|_] -> "** Queued = ~tp~n";
- _ -> ""
- end ++
- case Postponed of
- [] -> "";
- _ -> "** Postponed = ~tp~n"
- end ++
- case FixedStacktrace of
- [] -> "";
- _ -> "** Stacktrace =~n** ~tp~n"
- end ++
- case Timeouts of
- {0,_} -> "";
- _ -> "** Time-outs: ~p~n"
- end ++
- case Log of
- [] -> "";
- _ -> "** Log =~n** ~tp~n"
- end ++ ClientFmt,
- [Name |
- case Q of
- [] -> [];
- [Event|_] -> [error_logger:limit_term(Event)]
- end] ++
- [error_logger:limit_term(FmtData),
- Class,error_logger:limit_term(FixedReason),
- CBMode] ++
- case Q of
- [_|[_|_] = Events] -> [error_logger:limit_term(Events)];
- _ -> []
- end ++
- case Postponed of
- [] -> [];
- _ -> [error_logger:limit_term(Postponed)]
- end ++
- case FixedStacktrace of
[] -> [];
- _ -> [error_logger:limit_term(FixedStacktrace)]
- end ++
- case Timeouts of
- {0,_} -> [];
- _ -> [error_logger:limit_term(Timeouts)]
- end ++
- case Log of
- [] -> [];
- _ -> [[error_logger:limit_term(T) || T <- Log]]
- end ++ ClientArgs}.
+ [Event|_] -> [Event]
+ end] ++
+ [FmtData,
+ Class,FixedReason,
+ CBMode] ++
+ case Q of
+ [_|[_|_] = Events] -> [Events];
+ _ -> []
+ end ++
+ case Postponed of
+ [] -> [];
+ _ -> [Postponed]
+ end ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end ++
+ case Timeouts of
+ {0,_} -> [];
+ _ -> [Timeouts]
+ end ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt,Args++ClientArgs}.
+
+fix_reason(Class, Reason, Stacktrace) ->
+ case Stacktrace of
+ [{M,F,Args,_}|ST]
+ when Class =:= error, Reason =:= undef ->
+ case code:is_loaded(M) of
+ false ->
+ {{'module could not be loaded',M},ST};
+ _ ->
+ Arity =
+ if
+ is_list(Args) ->
+ length(Args);
+ is_integer(Args) ->
+ Args
+ end,
+ case erlang:function_exported(M, F, Arity) of
+ true ->
+ {Reason,Stacktrace};
+ false ->
+ {{'function not exported',{M,F,Arity}},ST}
+ end
+ end;
+ _ -> {Reason,Stacktrace}
+ end.
-format_client_log(undefined) ->
+format_client_log_single(undefined, _, _) ->
{"", []};
-format_client_log({Pid,dead}) ->
+format_client_log_single({Pid,dead}, _, _) ->
+ {" Client ~0p is dead.", [Pid]};
+format_client_log_single({Pid,remote}, _, _) ->
+ {" Client ~0p is remote on node ~0p.", [Pid,node(Pid)]};
+format_client_log_single({_Pid,{Name,Stacktrace0}}, P, Depth) ->
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0, 4),
+ Format = lists:append([" Client ",P," stacktrace: ",P,"."]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format, Args}.
+
+format_client_log(undefined, _, _) ->
+ {"", []};
+format_client_log({Pid,dead}, _, _) ->
{"** Client ~p is dead~n", [Pid]};
-format_client_log({Pid,remote}) ->
- {"** Client ~p is remote on node ~p~n", [Pid, node(Pid)]};
-format_client_log({_Pid,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
-
+format_client_log({Pid,remote}, _, _) ->
+ {"** Client ~p is remote on node ~p~n", [Pid,node(Pid)]};
+format_client_log({_Pid,{Name,Stacktrace}}, P, Depth) ->
+ Format = lists:append(["** Client ",P," stacktrace~n** ",P,"~n"]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%% Call Module:format_status/2 or return a default value
format_status(
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 63c9a6bddf..1848aa3628 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -106,7 +106,6 @@ nl() ->
IoDevice :: device().
nl(Io) ->
-% o_request(Io, {put_chars,io_lib:nl()}).
o_request(Io, nl, nl).
-spec columns() -> {'ok', pos_integer()} | {'error', 'enotsup'}.
@@ -255,8 +254,6 @@ read(Io, Prompt) ->
case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
{ok,Toks,_EndLine} ->
erl_parse:parse_term(Toks);
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(read, Reason), [Io, Prompt]);
{error,E,_EndLine} ->
{error,E};
{eof,_EndLine} ->
@@ -352,12 +349,7 @@ fread(Prompt, Format) ->
| server_no_data().
fread(Io, Prompt, Format) ->
- case request(Io, {fread,Prompt,Format}) of
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(fread, Reason), [Io, Prompt, Format]);
- Other ->
- Other
- end.
+ request(Io, {fread,Prompt,Format}).
-spec format(Format) -> 'ok' when
Format :: format().
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 21d66c5529..e2823b70f2 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -78,7 +78,7 @@
%% Utilities for collecting characters.
-export([collect_chars/3, collect_chars/4,
- collect_line/2, collect_line/3, collect_line/4,
+ collect_line/3, collect_line/4,
get_until/3, get_until/4]).
%% The following functions were used by Yecc's include-file.
@@ -851,6 +851,7 @@ collect_chars({binary,Stack,N}, Data,latin1, _) ->
end;
collect_chars({list,Stack,N}, Data, _,_) ->
collect_chars_list(Stack, N, Data);
+
%% collect_chars(Continuation, MoreChars, Count)
%% Returns:
%% {done,Result,RestChars}
@@ -881,32 +882,6 @@ collect_chars_list(Stack, N, []) ->
collect_chars_list(Stack,N, [H|T]) ->
collect_chars_list([H|Stack], N-1, T).
-%% collect_line(Continuation, MoreChars)
-%% Returns:
-%% {done,Result,RestChars}
-%% {more,Continuation}
-%%
-%% XXX Can be removed when compatibility with pre-R12B-5 nodes
-%% is no longer required.
-%%
-collect_line([], Chars) ->
- collect_line1(Chars, []);
-collect_line({SoFar}, More) ->
- collect_line1(More, SoFar).
-
-collect_line1([$\r, $\n|Rest], Stack) ->
- collect_line1([$\n|Rest], Stack);
-collect_line1([$\n|Rest], Stack) ->
- {done,lists:reverse([$\n|Stack], []),Rest};
-collect_line1([C|Rest], Stack) ->
- collect_line1(Rest, [C|Stack]);
-collect_line1(eof, []) ->
- {done,eof,[]};
-collect_line1(eof, Stack) ->
- {done,lists:reverse(Stack, []),[]};
-collect_line1([], Stack) ->
- {more,{Stack}}.
-
%% collect_line(State, Data, _). New in R9C.
%% Returns:
%% {stop,Result,RestData}
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 77f02eafe0..838d412d0c 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -895,9 +895,6 @@ write_string(S, _Uni) ->
io_lib:write_string(S, $"). %"
expand({_, _, _Dots=0, no_more} = If, _T, _Dd) -> If;
-%% expand({{list,L}, _Len, _, no_more}, T, Dd) ->
-%% {NL, NLen, NDots} = expand_list(L, T, Dd, 2),
-%% {{list,NL}, NLen, NDots, no_more};
expand({{tuple,IsTagged,L}, _Len, _, no_more}, T, Dd) ->
{NL, NLen, NDots} = expand_list(L, T, Dd, 2),
{{tuple,IsTagged,NL}, NLen, NDots, no_more};
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 51965ddb57..49d6a12eb2 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -100,6 +100,7 @@ merge(_,_) -> erlang:nif_error(undef).
put(_,_,_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:remove/2
-spec remove(Key,Map1) -> Map2 when
Key :: term(),
Map1 :: map(),
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index fa34f19637..57439c515e 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,574 +1,438 @@
%%
-%% %CopyrightBegin%
+%% WARNING: DO NOT EDIT THIS FILE.
%%
-%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%% This file was auto-generated from attributes in the source
+%% code.
%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
+%% To add a description to a deprecation or removal attribute,
+%% write a string after the arity:
%%
-%% http://www.apache.org/licenses/LICENSE-2.0
+%% -deprecated([{foo,1,"use bar/1 instead"}]).
+%% -deprecated_type([{gadget,1,"use widget/1 instead"}]).
+%% -removed([{hello,2,"use there/2 instead"}]).
+%% -removed_type([{frobnitz,1,"use grunka/1 instead"}]).
%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
+%% Descriptions cannot be given with the `f/1` shorthand, and
+%% it will fall back to a generic description referring the
+%% user to the documentation.
%%
-%% %CopyrightEnd%
+%% Use `./otp_build update_deprecations` to update this file
+%% after adding an attribute.
%%
-module(otp_internal).
-
--export([obsolete/3, obsolete_type/3]).
-
-%%----------------------------------------------------------------------
-
+-include("otp_internal.hrl").
+%%
-dialyzer({no_match, obsolete/3}).
-
--type tag() :: 'deprecated' | 'removed'. %% | 'experimental'.
--type mfas() :: mfa() | {atom(), atom(), [byte()]}.
--type release() :: string().
-
--spec obsolete(module(), atom(), arity()) ->
- 'no' | {tag(), string()} | {tag(), mfas(), release()}.
-
-obsolete(Module, Name, Arity) ->
- case obsolete_1(Module, Name, Arity) of
- {deprecated=Tag,{_,_,_}=Replacement} ->
- {Tag,Replacement,"a future release"};
- {_,String}=Ret when is_list(String) ->
- Ret;
- {_,_,_}=Ret ->
- Ret;
- no ->
- no
- end.
-
-obsolete_1(net, call, 4) ->
- {deprecated, {rpc, call, 4}};
-obsolete_1(net, cast, 4) ->
- {deprecated, {rpc, cast, 4}};
-obsolete_1(net, broadcast, 3) ->
- {deprecated, {rpc, eval_everywhere, 3}};
-obsolete_1(net, ping, 1) ->
- {deprecated, {net_adm, ping, 1}};
-obsolete_1(net, sleep, 1) ->
- {deprecated, "Use 'receive after T -> ok end' instead"};
-obsolete_1(net, relay, 1) ->
- {deprecated, {slave, relay, 1}};
-
-
-obsolete_1(erlang, now, 0) ->
- {deprecated,
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."};
-
-obsolete_1(calendar, local_time_to_universal_time, 1) ->
- {deprecated, {calendar, local_time_to_universal_time_dst, 1}};
-
-%% *** STDLIB added in OTP 22 ***
-
-obsolete_1(sys, get_debug, 3) ->
- {deprecated,
- "Deprecated function. "
- "Incorrectly documented and in fact only for internal use. "
- "Can often be replaced with sys:get_log/1."};
-
-%% *** STDLIB added in OTP 20 ***
-
-obsolete_1(gen_fsm, start, 3) ->
- {deprecated, {gen_statem, start, 3}};
-obsolete_1(gen_fsm, start, 4) ->
- {deprecated, {gen_statem, start, 4}};
-
-obsolete_1(gen_fsm, start_link, 3) ->
- {deprecated, {gen_statem, start_link, 3}};
-obsolete_1(gen_fsm, start_link, 4) ->
- {deprecated, {gen_statem, start_link, 4}};
-
-obsolete_1(gen_fsm, stop, 1) ->
- {deprecated, {gen_statem, stop, 1}};
-obsolete_1(gen_fsm, stop, 3) ->
- {deprecated, {gen_statem, stop, 3}};
-
-obsolete_1(gen_fsm, enter_loop, 4) ->
- {deprecated, {gen_statem, enter_loop, 4}};
-obsolete_1(gen_fsm, enter_loop, 5) ->
- {deprecated, {gen_statem, enter_loop, 5}};
-obsolete_1(gen_fsm, enter_loop, 6) ->
- {deprecated, {gen_statem, enter_loop, 6}};
-
-obsolete_1(gen_fsm, reply, 2) ->
- {deprecated, {gen_statem, reply, 2}};
-
-obsolete_1(gen_fsm, send_event, 2) ->
- {deprecated, {gen_statem, cast, 2}};
-obsolete_1(gen_fsm, send_all_state_event, 2) ->
- {deprecated, {gen_statem, cast, 2}};
-
-obsolete_1(gen_fsm, sync_send_event, 2) ->
- {deprecated, {gen_statem, call, 2}};
-obsolete_1(gen_fsm, sync_send_event, 3) ->
- {deprecated, {gen_statem, call, 3}};
-
-obsolete_1(gen_fsm, sync_send_all_state_event, 2) ->
- {deprecated, {gen_statem, call, 2}};
-obsolete_1(gen_fsm, sync_send_all_state_event, 3) ->
- {deprecated, {gen_statem, call, 3}};
-
-obsolete_1(gen_fsm, start_timer, 2) ->
- {deprecated, {erlang, start_timer, 3}};
-obsolete_1(gen_fsm, cancel_timer, 1) ->
- {deprecated, {erlang, cancel_timer, 1}};
-obsolete_1(gen_fsm, send_event_after, 2) ->
- {deprecated, {erlang, send_after, 3}};
-
-%% *** CRYPTO added in OTP 20 ***
-
-obsolete_1(crypto, rand_uniform, 2) ->
- {deprecated, {rand, uniform, 1}};
-
-%% *** CRYPTO added in OTP 19 ***
-
-obsolete_1(crypto, rand_bytes, 1) ->
- {removed, {crypto, strong_rand_bytes, 1}, "20.0"};
-
-%% *** CRYPTO added in R16B01 ***
-
-obsolete_1(crypto, md4, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-obsolete_1(crypto, md5, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-obsolete_1(crypto, sha, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-
-obsolete_1(crypto, md4_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-obsolete_1(crypto, md5_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-obsolete_1(crypto, sha_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-
-obsolete_1(crypto, md4_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-obsolete_1(crypto, md5_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-obsolete_1(crypto, sha_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-
-obsolete_1(crypto, md4_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-obsolete_1(crypto, md5_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-obsolete_1(crypto, sha_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-
-obsolete_1(crypto, md5_mac, 2) ->
- {removed, {crypto, hmac, 3}, "20.0"};
-obsolete_1(crypto, sha_mac, 2) ->
- {removed, {crypto, hmac, 3}, "20.0"};
-obsolete_1(crypto, sha_mac, 3) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-
-obsolete_1(crypto, sha_mac_96, 2) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-obsolete_1(crypto, md5_mac_96, 2) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-
-obsolete_1(crypto, rsa_sign, 2) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, rsa_sign, 3) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, rsa_verify, 3) ->
- {removed, {crypto, verify, 5}, "20.0"};
-obsolete_1(crypto, rsa_verify, 4) ->
- {removed, {crypto, verify, 5}, "20.0"};
-
-obsolete_1(crypto, dss_sign, 2) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, dss_sign, 3) ->
- {removed, {crypto, sign, 4}, "20.0"};
-
-obsolete_1(crypto, dss_verify, 3) ->
- {removed, {crypto, verify, 5}, "20.0"};
-obsolete_1(crypto, dss_verify, 4) ->
- {removed, {crypto, verify, 5}, "20.0"};
-
-obsolete_1(crypto, mod_exp, 3) ->
- {removed, {crypto, mod_pow, 3}, "20.0"};
-
-obsolete_1(crypto, dh_compute_key, 3) ->
- {removed, {crypto, compute_key, 4}, "20.0"};
-obsolete_1(crypto, dh_generate_key, 1) ->
- {removed, {crypto, generate_key, 2}, "20.0"};
-obsolete_1(crypto, dh_generate_key, 2) ->
- {removed, {crypto, generate_key, 3}, "20.0"};
-
-obsolete_1(crypto, des_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cbc_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des_ecb_encrypt, 2) ->
- {removed, {crypto, block_encrypt, 3}, "20.0"};
-obsolete_1(crypto, des_ede3_cbc_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des_cfb_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cfb_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ecb_encrypt, 2) ->
- {removed, {crypto, block_encrypt, 3}, "20.0"};
-obsolete_1(crypto, blowfish_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_cfb64_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ofb64_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cfb_128_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_128_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_256_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_40_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-
-obsolete_1(crypto, des_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cbc_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des_ecb_decrypt, 2) ->
- {removed, {crypto, block_decrypt, 3}, "20.0"};
-obsolete_1(crypto, des_ede3_cbc_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des_cfb_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cfb_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ecb_decrypt, 2) ->
- {removed, {crypto, block_decrypt, 3}, "20.0"};
-obsolete_1(crypto, blowfish_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_cfb64_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ofb64_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cfb_128_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_128_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_256_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_40_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-
-obsolete_1(crypto, aes_ctr_stream_decrypt, 2) ->
- {removed, {crypto, stream_decrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_stream_encrypt, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_decrypt, 3) ->
- {removed, {crypto, stream_decrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_encrypt, 3) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, rc4_encrypt, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, rc4_encrypt_with_state, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_stream_init, 2) ->
- {removed, {crypto, stream_init, 3}, "20.0"};
-obsolete_1(crypto, rc4_set_key, 1) ->
- {removed, {crypto, stream_init, 2}, "20.0"};
-
-obsolete_1(crypto, rsa_private_decrypt, 3) ->
- {removed, {crypto, private_decrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_public_decrypt, 3) ->
- {removed, {crypto, public_decrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_private_encrypt, 3) ->
- {removed, {crypto, private_encrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_public_encrypt, 3) ->
- {removed, {crypto, public_encrypt, 4}, "20.0"};
-
-obsolete_1(crypto, des_cfb_ivec, 2) ->
- {removed, {crypto, next_iv, 3}, "20.0"};
-obsolete_1(crypto,des_cbc_ivec, 1) ->
- {removed, {crypto, next_iv, 2}, "20.0"};
-obsolete_1(crypto, aes_cbc_ivec, 1) ->
- {removed, {crypto, next_iv, 2}, "20.0"};
-
-obsolete_1(crypto,info, 0) ->
- {removed, {crypto, module_info, 0}, "20.0"};
-
-obsolete_1(crypto, strong_rand_mpint, 3) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-obsolete_1(crypto, erlint, 1) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-obsolete_1(crypto, mpint, 1) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-
-
-%% *** SNMP ***
-
-obsolete_1(snmp, N, A) ->
- case is_snmp_agent_function(N, A) of
- false ->
- no;
- true ->
- {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 OTP 18); use \"new\" format instead"};
-
-
-%% *** MEGACO ***
-
-obsolete_1(megaco, format_versions, 1) ->
- {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"};
-
-
-%% *** OS-MON-MIB ***
-
-%% FIXME: Remove this warning in OTP 24.
-obsolete_1(os_mon_mib, _, _) ->
- {removed, "was removed in 22.0"};
-
-obsolete_1(auth, is_auth, 1) ->
- {deprecated, {net_adm, ping, 1}};
-obsolete_1(auth, cookie, 0) ->
- {deprecated, {erlang, get_cookie, 0}};
-obsolete_1(auth, cookie, 1) ->
- {deprecated, {erlang, set_cookie, 2}};
-obsolete_1(auth, node_cookie, 1) ->
- {deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-obsolete_1(auth, node_cookie, 2) ->
- {deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-
-%% Added in R16
-obsolete_1(wxCalendarCtrl, enableYearChange, _) -> %% wx bug documented?
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxDC, computeScaleAndOrigin, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxClientDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPaintDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxWindowDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGraphicsRenderer, createLinearGradientBrush, 7) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGraphicsRenderer, createRadialGradientBrush, 8) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGridCellEditor, endEdit, 4) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGridCellEditor, paintBackground, 3) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxIdleEvent, canSend, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxMDIClientWindow, new, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxMDIClientWindow, new, 2) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPostScriptDC, getResolution, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPostScriptDC, setResolution, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxCursor, new, 3) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxCursor, new, 4) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-
-%% Added in OTP 17.
-obsolete_1(asn1ct, decode,3) ->
- {removed,"removed; use Mod:decode/2 instead"};
-obsolete_1(asn1ct, encode, 2) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1ct, encode, 3) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, decode,3) ->
- {removed,"removed; use Mod:decode/2 instead"};
-obsolete_1(asn1rt, encode, 2) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, encode, 3) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, info, 1) ->
- {removed,"removed; use Mod:info/0 instead"};
-obsolete_1(asn1rt, utf8_binary_to_list, 1) ->
- {removed,{unicode,characters_to_list,1},"OTP 20"};
-obsolete_1(asn1rt, utf8_list_to_binary, 1) ->
- {removed,{unicode,characters_to_binary,1},"OTP 20"};
-
-%% Added in OTP 18.
-obsolete_1(core_lib, get_anno, 1) ->
- {removed,{cerl,get_ann,1},"19"};
-obsolete_1(core_lib, set_anno, 2) ->
- {removed,{cerl,set_ann,2},"19"};
-obsolete_1(core_lib, is_literal, 1) ->
- {removed,{cerl,is_literal,1},"19"};
-obsolete_1(core_lib, is_literal_list, 1) ->
- {removed,"removed; use lists:all(fun cerl:is_literal/1, L)"
- " instead"};
-obsolete_1(core_lib, literal_value, 1) ->
- {removed,{core_lib,concrete,1},"19"};
-obsolete_1(erl_scan, set_attribute, 3) ->
- {removed,{erl_anno,set_line,2},"19.0"};
-obsolete_1(erl_scan, attributes_info, 1) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_scan, attributes_info, 2) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_scan, token_info, 1) ->
- {removed,"removed in 19.0; use "
- "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
-obsolete_1(erl_scan, token_info, 2) ->
- {removed,"removed in 19.0; use "
- "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
-obsolete_1(erl_parse, set_line, 2) ->
- {removed,{erl_anno,set_line,2},"19.0"};
-obsolete_1(erl_parse, get_attributes, 1) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_parse, get_attribute, 2) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_lint, modify_line, 2) ->
- {removed,{erl_parse,map_anno,2},"19.0"};
-obsolete_1(ssl, negotiated_next_protocol, 1) ->
- {removed,"removed in 20.0; use ssl:negotiated_protocol/1 instead"};
-obsolete_1(ssl, connection_info, 1) ->
- {removed, "removed in 20.0; use ssl:connection_information/[1,2] instead"};
-
-obsolete_1(httpd_conf, check_enum, 2) ->
- {deprecated, "deprecated; use lists:member/2 instead"};
-obsolete_1(httpd_conf, clean, 1) ->
- {deprecated, "deprecated; use sting:strip/1 instead or possible the re module"};
-obsolete_1(httpd_conf, custom_clean, 3) ->
- {deprecated, "deprecated; use sting:strip/3 instead or possible the re module"};
-obsolete_1(httpd_conf, is_directory, 1) ->
- {deprecated, "deprecated; use filelib:is_dir/1 instead"};
-obsolete_1(httpd_conf, is_file, 1) ->
- {deprecated, "deprecated; use filelib:is_file/1 instead"};
-obsolete_1(httpd_conf, make_integer, 1) ->
- {deprecated, "deprecated; use erlang:list_to_integer/1 instead"};
-
-%% Added in OTP 19.
-
-obsolete_1(random, _, _) ->
- {deprecated, "the 'random' module is deprecated; "
- "use the 'rand' module instead"};
-obsolete_1(code, rehash, 0) ->
- {deprecated, "deprecated because the code path cache feature has been removed"};
-obsolete_1(queue, lait, 1) ->
- {deprecated, {queue,liat,1}};
-
-%% Removed in OTP 19.
-
-obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 ->
- {removed, {rpc, multi_server_call, A}, "19.0"};
-
-%% Added in OTP 20.
-
-obsolete_1(filename, find_src, 1) ->
- {deprecated, "deprecated; use filelib:find_source/1 instead"};
-obsolete_1(filename, find_src, 2) ->
- {deprecated, "deprecated; use filelib:find_source/3 instead"};
-
-obsolete_1(erlang, get_stacktrace, 0) ->
- {deprecated, "deprecated; use the new try/catch syntax for retrieving the stack backtrace"};
-
-%% Removed in OTP 20.
-
-obsolete_1(erlang, hash, 2) ->
- {removed, {erlang, phash2, 2}, "20.0"};
-
-%% Add in OTP 21.
-
-obsolete_1(ssl, ssl_accept, 1) ->
- {deprecated, "deprecated; use ssl:handshake/1 instead"};
-obsolete_1(ssl, ssl_accept, 2) ->
- {deprecated, "deprecated; use ssl:handshake/2 instead"};
-obsolete_1(ssl, ssl_accept, 3) ->
- {deprecated, "deprecated; use ssl:handshake/3 instead"};
-obsolete_1(otp_mib, F, _) when F =:= load; F =:= unload ->
- {deprecated, "deprecated; functionality will be removed in a future release"};
-
-%% not obsolete
-
-obsolete_1(_, _, _) ->
- no.
-
--spec is_snmp_agent_function(atom(), byte()) -> boolean().
-
-is_snmp_agent_function(c, 1) -> true;
-is_snmp_agent_function(c, 2) -> true;
-is_snmp_agent_function(compile, 3) -> true;
-is_snmp_agent_function(is_consistent, 1) -> true;
-is_snmp_agent_function(mib_to_hrl, 1) -> true;
-is_snmp_agent_function(change_log_size, 1) -> true;
-is_snmp_agent_function(log_to_txt, 2) -> true;
-is_snmp_agent_function(log_to_txt, 3) -> true;
-is_snmp_agent_function(log_to_txt, 4) -> true;
-is_snmp_agent_function(current_request_id, 0) -> true;
-is_snmp_agent_function(current_community, 0) -> true;
-is_snmp_agent_function(current_address, 0) -> true;
-is_snmp_agent_function(current_context, 0) -> true;
-is_snmp_agent_function(current_net_if_data, 0) -> true;
-is_snmp_agent_function(get_symbolic_store_db, 0) -> true;
-is_snmp_agent_function(name_to_oid, 1) -> true;
-is_snmp_agent_function(name_to_oid, 2) -> true;
-is_snmp_agent_function(oid_to_name, 1) -> true;
-is_snmp_agent_function(oid_to_name, 2) -> true;
-is_snmp_agent_function(int_to_enum, 2) -> true;
-is_snmp_agent_function(int_to_enum, 3) -> true;
-is_snmp_agent_function(enum_to_int, 2) -> true;
-is_snmp_agent_function(enum_to_int, 3) -> true;
-is_snmp_agent_function(get, 2) -> true;
-is_snmp_agent_function(info, 1) -> true;
-is_snmp_agent_function(load_mibs, 2) -> true;
-is_snmp_agent_function(unload_mibs, 2) -> true;
-is_snmp_agent_function(dump_mibs, 0) -> true;
-is_snmp_agent_function(dump_mibs, 1) -> true;
-is_snmp_agent_function(register_subagent, 3) -> true;
-is_snmp_agent_function(unregister_subagent, 2) -> true;
-is_snmp_agent_function(send_notification, 3) -> true;
-is_snmp_agent_function(send_notification, 4) -> true;
-is_snmp_agent_function(send_notification, 5) -> true;
-is_snmp_agent_function(send_notification, 6) -> true;
-is_snmp_agent_function(send_trap, 3) -> true;
-is_snmp_agent_function(send_trap, 4) -> true;
-is_snmp_agent_function(add_agent_caps, 2) -> true;
-is_snmp_agent_function(del_agent_caps, 1) -> true;
-is_snmp_agent_function(get_agent_caps, 0) -> true;
-is_snmp_agent_function(_, _) -> false.
-
--dialyzer({no_match, obsolete_type/3}).
-
--spec obsolete_type(module(), atom(), arity()) ->
- 'no' | {tag(), string()} | {tag(), mfas(), release()}.
+obsolete(auth, cookie, 0) ->
+ {deprecated, "use erlang:get_cookie/0 instead"};
+obsolete(auth, cookie, 1) ->
+ {deprecated, "use erlang:set_cookie/2 instead"};
+obsolete(auth, is_auth, 1) ->
+ {deprecated, "use net_adm:ping/1 instead"};
+obsolete(calendar, local_time_to_universal_time, 1) ->
+ {deprecated, "use calendar:local_time_to_universal_time_dst/1 instead"};
+obsolete(code, rehash, 0) ->
+ {deprecated, "the code path cache feature has been removed"};
+obsolete(crypto, block_decrypt, 3) ->
+ {deprecated, "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 instead"};
+obsolete(crypto, block_decrypt, 4) ->
+ {deprecated, "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto_(dyn_iv)?_init + crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead"};
+obsolete(crypto, block_encrypt, 3) ->
+ {deprecated, "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 instead"};
+obsolete(crypto, block_encrypt, 4) ->
+ {deprecated, "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto_(dyn_iv)?_init + crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead"};
+obsolete(crypto, cmac, 3) ->
+ {deprecated, "use crypto:mac/4 instead"};
+obsolete(crypto, cmac, 4) ->
+ {deprecated, "use crypto:macN/5 instead"};
+obsolete(crypto, hmac, 3) ->
+ {deprecated, "use crypto:mac/4 instead"};
+obsolete(crypto, hmac, 4) ->
+ {deprecated, "use crypto:macN/5 instead"};
+obsolete(crypto, hmac_final, 1) ->
+ {deprecated, "use crypto:mac_final/1 instead"};
+obsolete(crypto, hmac_final_n, 2) ->
+ {deprecated, "use crypto:mac_finalN/2 instead"};
+obsolete(crypto, hmac_init, 2) ->
+ {deprecated, "use crypto:mac_init/3 instead"};
+obsolete(crypto, hmac_update, 2) ->
+ {deprecated, "use crypto:mac_update/2 instead"};
+obsolete(crypto, poly1305, 2) ->
+ {deprecated, "use crypto:mac/3 instead"};
+obsolete(crypto, rand_uniform, 2) ->
+ {deprecated, "use rand:rand_uniform/1 instead"};
+obsolete(crypto, stream_decrypt, 2) ->
+ {deprecated, "use crypto:crypto_update/2 instead"};
+obsolete(crypto, stream_encrypt, 2) ->
+ {deprecated, "use crypto:crypto_update/2 instead"};
+obsolete(erlang, get_stacktrace, 0) ->
+ {deprecated, "use the new try/catch syntax for retrieving the stack backtrace"};
+obsolete(erlang, now, 0) ->
+ {deprecated, "see the \"Time and Time Correction in Erlang\" chapter of the ERTS User's Guide for more information"};
+obsolete(filename, safe_relative_path, 1) ->
+ {deprecated, "use filelib:safe_relative_path/2 instead"};
+obsolete(http_uri, decode, 1) ->
+ {deprecated, "use uri_string functions instead"};
+obsolete(http_uri, encode, 1) ->
+ {deprecated, "use uri_string functions instead"};
+obsolete(http_uri, parse, 1) ->
+ {deprecated, "use uri_string functions instead"};
+obsolete(http_uri, parse, 2) ->
+ {deprecated, "use uri_string functions instead"};
+obsolete(http_uri, scheme_defaults, 0) ->
+ {deprecated, "use uri_string functions instead"};
+obsolete(httpd, parse_query, 1) ->
+ {deprecated, "use uri_string:dissect_query/1 instead"};
+obsolete(megaco, format_versions, 1) ->
+ {deprecated, "use megaco:print_version_info/0,1 instead"};
+obsolete(net, broadcast, 3) ->
+ {deprecated, "use rpc:eval_everywhere/3 instead"};
+obsolete(net, call, 4) ->
+ {deprecated, "use rpc:call/4 instead"};
+obsolete(net, cast, 4) ->
+ {deprecated, "use rpc:cast/4 instead"};
+obsolete(net, ping, 1) ->
+ {deprecated, "use net_adm:ping/1 instead"};
+obsolete(net, relay, 1) ->
+ {deprecated, "use slave:relay/1 instead"};
+obsolete(net, sleep, 1) ->
+ {deprecated, "use 'receive after T -> ok end' instead"};
+obsolete(queue, lait, 1) ->
+ {deprecated, "use queue:liat/1 instead"};
+obsolete(snmp, add_agent_caps, 2) ->
+ {deprecated, "use snmpa:add_agent_caps/2 instead"};
+obsolete(snmp, c, 1) ->
+ {deprecated, "use snmpa:c/1 instead"};
+obsolete(snmp, c, 2) ->
+ {deprecated, "use snmpa:c/2 instead"};
+obsolete(snmp, change_log_size, 1) ->
+ {deprecated, "use snmpa:change_log_size/1 instead"};
+obsolete(snmp, compile, 3) ->
+ {deprecated, "use snmpa:compile/3 instead"};
+obsolete(snmp, current_address, 0) ->
+ {deprecated, "use snmpa:current_address/0 instead"};
+obsolete(snmp, current_community, 0) ->
+ {deprecated, "use snmpa:current_community/0 instead"};
+obsolete(snmp, current_context, 0) ->
+ {deprecated, "use snmpa:current_context/0 instead"};
+obsolete(snmp, current_net_if_data, 0) ->
+ {deprecated, "use snmpa:current_net_if_data/0 instead"};
+obsolete(snmp, current_request_id, 0) ->
+ {deprecated, "use snmpa:current_request_id/0 instead"};
+obsolete(snmp, del_agent_caps, 1) ->
+ {deprecated, "use snmpa:del_agent_caps/1 instead"};
+obsolete(snmp, dump_mibs, 0) ->
+ {deprecated, "use snmpa:dump_mibs/0 instead"};
+obsolete(snmp, dump_mibs, 1) ->
+ {deprecated, "use snmpa:dump_mibs/1 instead"};
+obsolete(snmp, enum_to_int, 2) ->
+ {deprecated, "use snmpa:enum_to_int/2 instead"};
+obsolete(snmp, enum_to_int, 3) ->
+ {deprecated, "use snmpa:enum_to_int/3 instead"};
+obsolete(snmp, get, 2) ->
+ {deprecated, "use snmpa:get/2 instead"};
+obsolete(snmp, get_agent_caps, 0) ->
+ {deprecated, "use snmpa:get_agent_caps/0 instead"};
+obsolete(snmp, get_symbolic_store_db, 0) ->
+ {deprecated, "use snmpa:get_symbolic_store_db/0 instead"};
+obsolete(snmp, info, 1) ->
+ {deprecated, "use snmpa:info/1 instead"};
+obsolete(snmp, int_to_enum, 2) ->
+ {deprecated, "use snmpa:int_to_enum/2 instead"};
+obsolete(snmp, int_to_enum, 3) ->
+ {deprecated, "use snmpa:int_to_enum/3 instead"};
+obsolete(snmp, is_consistent, 1) ->
+ {deprecated, "use snmpa:is_consistent/1 instead"};
+obsolete(snmp, load_mibs, 2) ->
+ {deprecated, "use snmpa:load_mibs/2 instead"};
+obsolete(snmp, log_to_txt, 2) ->
+ {deprecated, "use snmpa:log_to_txt/2 instead"};
+obsolete(snmp, log_to_txt, 3) ->
+ {deprecated, "use snmpa:log_to_txt/3 instead"};
+obsolete(snmp, log_to_txt, 4) ->
+ {deprecated, "use snmpa:log_to_txt/4 instead"};
+obsolete(snmp, mib_to_hrl, 1) ->
+ {deprecated, "use snmpa:mib_to_hrl/1 instead"};
+obsolete(snmp, name_to_oid, 1) ->
+ {deprecated, "use snmpa:name_to_oid/1 instead"};
+obsolete(snmp, name_to_oid, 2) ->
+ {deprecated, "use snmpa:name_to_oid/2 instead"};
+obsolete(snmp, oid_to_name, 1) ->
+ {deprecated, "use snmpa:oid_to_name/1 instead"};
+obsolete(snmp, oid_to_name, 2) ->
+ {deprecated, "use snmpa:oid_to_name/2 instead"};
+obsolete(snmp, register_subagent, 3) ->
+ {deprecated, "use snmpa:register_subagent/3 instead"};
+obsolete(snmp, send_notification, 3) ->
+ {deprecated, "use snmpa:send_notification/3 instead"};
+obsolete(snmp, send_notification, 4) ->
+ {deprecated, "use snmpa:send_notification/4 instead"};
+obsolete(snmp, send_notification, 5) ->
+ {deprecated, "use snmpa:send_notification/5 instead"};
+obsolete(snmp, send_notification, 6) ->
+ {deprecated, "use snmpa:send_notification/6 instead"};
+obsolete(snmp, send_trap, 3) ->
+ {deprecated, "use snmpa:send_trap/3 instead"};
+obsolete(snmp, send_trap, 4) ->
+ {deprecated, "use snmpa:send_trap/4 instead"};
+obsolete(snmp, unload_mibs, 2) ->
+ {deprecated, "use snmpa:unload_mibs/2 instead"};
+obsolete(snmp, unregister_subagent, 2) ->
+ {deprecated, "use snmpa:unregister_subagent/2 instead"};
+obsolete(snmpa, old_info_format, 1) ->
+ {deprecated, "use \"new\" format instead"};
+obsolete(sys, get_debug, 3) ->
+ {deprecated, "incorrectly documented and only for internal use. Can often be replaced with sys:get_log/1"};
+obsolete(wxClientDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxCursor, new, 3) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxCursor, new, 4) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxDC, computeScaleAndOrigin, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGraphicsRenderer, createLinearGradientBrush, 7) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGraphicsRenderer, createRadialGradientBrush, 8) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGridCellEditor, endEdit, 4) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGridCellEditor, paintBackground, 3) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxIdleEvent, canSend, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxMDIClientWindow, new, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxMDIClientWindow, new, 2) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPaintDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPostScriptDC, getResolution, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPostScriptDC, setResolution, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxWindowDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(core_lib, get_anno, 1) ->
+ {removed, "use cerl:get_ann/1 instead"};
+obsolete(core_lib, is_literal, 1) ->
+ {removed, "use cerl:is_literal/1 instead"};
+obsolete(core_lib, is_literal_list, 1) ->
+ {removed, "use cerl:is_literal_list/1 instead"};
+obsolete(core_lib, literal_value, 1) ->
+ {removed, "use cerl:concrete/1 instead"};
+obsolete(core_lib, set_anno, 2) ->
+ {removed, "use cerl:set_ann/2 instead"};
+obsolete(crypto, aes_cbc_128_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cbc_128_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_cbc_256_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cbc_256_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_cbc_ivec, 2) ->
+ {removed, "use crypto:next_iv/2 instead"};
+obsolete(crypto, aes_cfb_128_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cfb_128_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_ctr_decrypt, 3) ->
+ {removed, "use crypto:stream_decrypt/2 instead"};
+obsolete(crypto, aes_ctr_encrypt, 3) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_decrypt, 2) ->
+ {removed, "use crypto:stream_decrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_encrypt, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_init, 2) ->
+ {removed, "use crypto:stream_init/3 instead"};
+obsolete(crypto, blowfish_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, blowfish_cfb64_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_cfb64_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, blowfish_ecb_decrypt, 2) ->
+ {removed, "use crypto:block_decrypt/3 instead"};
+obsolete(crypto, blowfish_ecb_encrypt, 2) ->
+ {removed, "use crypto:block_encrypt/3 instead"};
+obsolete(crypto, blowfish_ofb64_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_ofb64_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_cbc_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des3_cbc_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_cfb_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des3_cfb_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_ede3_cbc_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des_cbc_ivec, 2) ->
+ {removed, "use crypto:next_iv/2 instead"};
+obsolete(crypto, des_cfb_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cfb_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des_cfb_ivec, 2) ->
+ {removed, "use crypto:next_iv/3 instead"};
+obsolete(crypto, des_ecb_decrypt, 2) ->
+ {removed, "use crypto:block_decrypt/3 instead"};
+obsolete(crypto, des_ecb_encrypt, 2) ->
+ {removed, "use crypto:block_encrypt/3 instead"};
+obsolete(crypto, des_ede3_cbc_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, dh_compute_key, 3) ->
+ {removed, "use crypto:compute_key/4 instead"};
+obsolete(crypto, dh_generate_key, 1) ->
+ {removed, "use crypto:generate_key/2 instead"};
+obsolete(crypto, dh_generate_key, 2) ->
+ {removed, "use crypto:generate_key/3 instead"};
+obsolete(crypto, erlint, 1) ->
+ {removed, "only needed by other removed functions"};
+obsolete(crypto, info, 0) ->
+ {removed, "use crypto:module_info/0 instead"};
+obsolete(crypto, md4, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, md4_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, md4_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, md4_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, md5, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, md5_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, md5_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, md5_mac, 2) ->
+ {removed, "use crypto:hmac/3 instead"};
+obsolete(crypto, md5_mac_96, 2) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, md5_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, mod_exp, 3) ->
+ {removed, "use crypto:mod_pow/3 instead"};
+obsolete(crypto, mpint, 1) ->
+ {removed, "only needed by other removed functions"};
+obsolete(crypto, rand_bytes, 1) ->
+ {removed, "use crypto:strong_rand_bytes/1 instead"};
+obsolete(crypto, rc2_40_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, rc2_40_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, rc2_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, rc2_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, rc4_encrypt, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, rc4_encrypt_with_state, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, rc4_set_key, 2) ->
+ {removed, "use crypto:stream_init/2 instead"};
+obsolete(crypto, sha, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, sha_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, sha_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, sha_mac, 2) ->
+ {removed, "use crypto:hmac/3 instead"};
+obsolete(crypto, sha_mac, 3) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, sha_mac_96, 2) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, sha_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, strong_rand_mpint, 3) ->
+ {removed, "only needed by other removed functions"};
+obsolete(erl_lint, modify_line, 2) ->
+ {removed, "use erl_parse:map_anno/2 instead"};
+obsolete(erl_parse, get_attribute, 2) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_parse, get_attributes, 1) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_parse, set_line, 2) ->
+ {removed, "use erl_anno:set_line/2"};
+obsolete(erl_scan, set_attribute, 3) ->
+ {removed, "use erl_anno:set_line/2 instead"};
+obsolete(erlang, hash, 2) ->
+ {removed, "use erlang:phash2/2 instead"};
+obsolete(httpd_conf, check_enum, 2) ->
+ {removed, "use lists:member/2 instead"};
+obsolete(httpd_conf, clean, 1) ->
+ {removed, "use sting:strip/1 instead or possibly the re module"};
+obsolete(httpd_conf, custom_clean, 3) ->
+ {removed, "use sting:strip/1 instead or possibly the re module"};
+obsolete(httpd_conf, is_directory, 1) ->
+ {removed, "use filelib:is_dir/1 instead"};
+obsolete(httpd_conf, is_file, 1) ->
+ {removed, "use filelib:is_file/1 instead"};
+obsolete(httpd_conf, make_integer, 1) ->
+ {removed, "use erlang:list_to_integer/1 instead"};
+obsolete(rpc, safe_multi_server_call, 2) ->
+ {removed, "use rpc:multi_server_call/2 instead"};
+obsolete(rpc, safe_multi_server_call, 3) ->
+ {removed, "use rpc:multi_server_call/3 instead"};
+obsolete(ssl, connection_info, 1) ->
+ {removed, "use ssl:connection_information/[1,2] instead"};
+obsolete(ssl, negotiated_next_protocol, 1) ->
+ {removed, "use ssl:negotiated_protocol/1 instead"};
+obsolete(auth, node_cookie, _) ->
+ {deprecated, "use erlang:set_cookie/2 and net_adm:ping/1 instead"};
+obsolete(crypto, next_iv, _) ->
+ {deprecated, "see the 'New and Old API' chapter of the CRYPTO User's guide"};
+obsolete(crypto, stream_init, _) ->
+ {deprecated, "use crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 or crypto:crypto_one_time/4 instead"};
+obsolete(filename, find_src, _) ->
+ {deprecated, "use filelib:find_source/1,3 instead"};
+obsolete(ssl, ssl_accept, _) ->
+ {deprecated, "use ssl_handshake/1,2,3 instead"};
+obsolete(wxCalendarCtrl, enableYearChange, _) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(asn1ct, decode, _) ->
+ {removed, "use Mod:decode/2 instead"};
+obsolete(asn1ct, encode, _) ->
+ {removed, "use Mod:encode/2 instead"};
+obsolete(crypto, dss_sign, _) ->
+ {removed, "use crypto:sign/4 instead"};
+obsolete(crypto, dss_verify, _) ->
+ {removed, "use crypto:verify/5 instead"};
+obsolete(crypto, rsa_sign, _) ->
+ {removed, "use crypto:sign/4 instead"};
+obsolete(crypto, rsa_verify, _) ->
+ {removed, "use crypto:verify/5 instead"};
+obsolete(erl_scan, attributes_info, _) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_scan, token_info, _) ->
+ {removed, "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
+obsolete(gen_fsm, _, _) ->
+ {deprecated, "use the 'gen_statem' module instead"};
+obsolete(pg2, _, _) ->
+ {deprecated, "the 'pg2' module is deprecated and scheduled for removal in OTP 24; use 'pg' instead."};
+obsolete(random, _, _) ->
+ {deprecated, "use the 'rand' module instead"};
+obsolete(os_mon_mib, _, _) ->
+ {removed, "this module was removed in OTP 22.0"};
+obsolete(_,_,_) -> no.
-dialyzer({no_match, obsolete_type/3}).
-obsolete_type(Module, Name, NumberOfVariables) ->
- case obsolete_type_1(Module, Name, NumberOfVariables) of
- {deprecated=Tag,{_,_,_}=Replacement} ->
- {Tag,Replacement,"in a future release"};
- {_,String}=Ret when is_list(String) ->
- Ret;
- {_,_,_}=Ret ->
- Ret;
- no ->
- no
- end.
+obsolete_type(erl_scan, column, 0) ->
+ {removed, "use erl_anno:column() instead"};
+obsolete_type(erl_scan, line, 0) ->
+ {removed, "use erl_anno:line() instead"};
+obsolete_type(erl_scan, location, 0) ->
+ {removed, "use erl_anno:location() instead"};
+obsolete_type(_,_,_) -> no.
-obsolete_type_1(erl_scan,column,0) ->
- {removed,{erl_anno,column,0},"19.0"};
-obsolete_type_1(erl_scan,line,0) ->
- {removed,{erl_anno,line,0},"19.0"};
-obsolete_type_1(erl_scan,location,0) ->
- {removed,{erl_anno,location,0},"19.0"};
-obsolete_type_1(_,_,_) ->
- no.
diff --git a/lib/stdlib/src/otp_internal.hrl b/lib/stdlib/src/otp_internal.hrl
new file mode 100644
index 0000000000..ace1fa5cc1
--- /dev/null
+++ b/lib/stdlib/src/otp_internal.hrl
@@ -0,0 +1,36 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This file is included by the file "otp_internal.erl", which is
+%% auto-generated by stdlib/scripts/update_deprecations
+%%
+
+-export([obsolete/3, obsolete_type/3]).
+
+-type tag() :: 'deprecated' | 'removed'. %% | 'experimental'.
+-type mfas() :: mfa() | {atom(), atom(), [byte()]}.
+-type release() :: string().
+
+-spec obsolete(module(), atom(), arity()) ->
+ 'no' | {tag(), string()} | {tag(), mfas(), release()}.
+
+-spec obsolete_type(module(), atom(), arity()) ->
+ 'no' | {tag(), string()} | {tag(), mfas(), release()}.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index cfbaf8b242..58e6faf950 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
spawn/3, spawn_link/3, spawn/4, spawn_link/4,
spawn_opt/2, spawn_opt/3, spawn_opt/4, spawn_opt/5,
start/3, start/4, start/5, start_link/3, start_link/4, start_link/5,
+ start_monitor/3, start_monitor/4, start_monitor/5,
hibernate/3,
init_ack/1, init_ack/2,
init_p/3,init_p/5,format/1,format/2,format/3,report_cb/2,
@@ -39,25 +40,21 @@
-export([wake_up/3]).
-export_type([spawn_option/0]).
+-export_type([start_spawn_option/0]).
-include("logger.hrl").
%%-----------------------------------------------------------------------------
--type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
--type max_heap_size() :: non_neg_integer() |
- #{ size => non_neg_integer(),
- kill => true,
- error_logger => true}.
--type spawn_option() :: 'link'
- | 'monitor'
- | {'priority', priority_level()}
- | {'max_heap_size', max_heap_size()}
- | {'min_heap_size', non_neg_integer()}
- | {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}
- | {'message_queue_data',
- 'off_heap' | 'on_heap' | 'mixed' }.
+-type start_spawn_option() :: 'link'
+ | {'priority', erlang:priority_level()}
+ | {'max_heap_size', erlang:max_heap_size()}
+ | {'min_heap_size', non_neg_integer()}
+ | {'min_bin_vheap_size', non_neg_integer()}
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data', erlang:message_queue_data() }.
+
+-type spawn_option() :: erlang:spawn_opt_option().
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
@@ -65,6 +62,14 @@
%%-----------------------------------------------------------------------------
+-define(VERIFY_NO_MONITOR_OPT(M, F, A, T, Opts),
+ case lists:member(monitor, Opts) of
+ true -> erlang:error(badarg, [M,F,A,T,Opts]);
+ false -> ok
+ end).
+
+%%-----------------------------------------------------------------------------
+
-spec spawn(Fun) -> pid() when
Fun :: function().
@@ -141,17 +146,16 @@ spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ancestors = get_ancestors(),
erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]).
--spec spawn_opt(Fun, SpawnOpts) -> pid() when
+-spec spawn_opt(Fun, SpawnOpts) -> pid() | {pid(), reference()} when
Fun :: function(),
SpawnOpts :: [spawn_option()].
spawn_opt(F, Opts) when is_function(F) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts).
--spec spawn_opt(Node, Function, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Function, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Function :: function(),
SpawnOpts :: [spawn_option()].
@@ -159,10 +163,9 @@ spawn_opt(F, Opts) when is_function(F) ->
spawn_opt(Node, F, Opts) when is_function(F) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts).
--spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Module :: module(),
Function :: atom(),
Args :: [term()],
@@ -171,10 +174,9 @@ spawn_opt(Node, F, Opts) when is_function(F) ->
spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
--spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Module :: module(),
Function :: atom(),
@@ -184,30 +186,13 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
-%% OTP-6345
-%% monitor spawn_opt option is currently not possible to use
-check_for_monitor(SpawnOpts) ->
- case lists:member(monitor, SpawnOpts) of
- true ->
- erlang:error(badarg);
- false ->
- false
- end.
-
spawn_mon(M,F,A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
erlang:spawn_monitor(?MODULE, init_p, [Parent,Ancestors,M,F,A]).
-spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
- Parent = get_my_name(),
- Ancestors = get_ancestors(),
- check_for_monitor(Opts),
- erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], [monitor|Opts]).
-
-spec hibernate(Module, Function, Args) -> no_return() when
Module :: module(),
Function :: atom(),
@@ -216,14 +201,6 @@ spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
erlang:hibernate(?MODULE, wake_up, [M, F, A]).
-ensure_link(SpawnOpts) ->
- case lists:member(link, SpawnOpts) of
- true ->
- SpawnOpts;
- false ->
- [link|SpawnOpts]
- end.
-
-spec init_p(pid(), [pid()], function()) -> term().
init_p(Parent, Ancestors, Fun) when is_function(Fun) ->
@@ -299,20 +276,32 @@ start(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_mon(M, F, A),
- sync_wait_mon(PidRef, Timeout).
+ sync_start(spawn_mon(M, F, A), Timeout).
-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_spawn_option()],
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_opt_mon(M, F, A, SpawnOpts),
- sync_wait_mon(PidRef, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]), Timeout).
+
+sync_start({Pid, Ref}, Timeout) ->
+ receive
+ {ack, Pid, Return} ->
+ erlang:demonitor(Ref, [flush]),
+ Return;
+ {'DOWN', Ref, process, Pid, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ erlang:demonitor(Ref, [flush]),
+ kill_flush(Pid),
+ {error, timeout}
+ end.
-spec start_link(Module, Function, Args) -> Ret when
Module :: module(),
@@ -331,60 +320,88 @@ start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn_link(M, F, A),
- sync_wait(Pid, Timeout).
+ sync_start_link(?MODULE:spawn_link(M, F, A), Timeout).
-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_spawn_option()],
Ret :: term() | {error, Reason :: term()}.
start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)),
- sync_wait(Pid, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_link(?MODULE:spawn_opt(M, F, A, [link|SpawnOpts]), Timeout).
-sync_wait(Pid, Timeout) ->
+sync_start_link(Pid, Timeout) ->
receive
{ack, Pid, Return} ->
- Return;
+ Return;
{'EXIT', Pid, Reason} ->
- {error, Reason}
+ {error, Reason}
after Timeout ->
- unlink(Pid),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {error, timeout}
end.
-sync_wait_mon({Pid, Ref}, Timeout) ->
+-spec start_monitor(Module, Function, Args) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+ start_monitor(M, F, A, infinity).
+
+-spec start_monitor(Module, Function, Args, Time) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
+ sync_start_monitor(spawn_mon(M, F, A), Timeout).
+
+-spec start_monitor(Module, Function, Args, Time, SpawnOpts) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ SpawnOpts :: [start_spawn_option()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M,F,A,Timeout,SpawnOpts) when is_atom(M),
+ is_atom(F),
+ is_list(A) ->
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_monitor(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]),
+ Timeout).
+
+sync_start_monitor({Pid, Ref}, Timeout) ->
receive
{ack, Pid, Return} ->
- erlang:demonitor(Ref, [flush]),
- Return;
- {'DOWN', Ref, _Type, Pid, Reason} ->
- {error, Reason};
- {'EXIT', Pid, Reason} -> %% link as spawn_opt?
- erlang:demonitor(Ref, [flush]),
- {error, Reason}
+ {Return, Ref};
+ {'DOWN', Ref, process, Pid, Reason} = Down ->
+ self() ! Down,
+ {{error, Reason}, Ref}
after Timeout ->
- erlang:demonitor(Ref, [flush]),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {{error, timeout}, Ref}
end.
--spec flush(pid()) -> 'true'.
+-spec kill_flush(Pid) -> 'ok' when
+ Pid :: pid().
-flush(Pid) ->
- receive
- {'EXIT', Pid, _} ->
- true
- after 0 ->
- true
- end.
+kill_flush(Pid) ->
+ unlink(Pid),
+ exit(Pid, kill),
+ receive {'EXIT', Pid, _} -> ok after 0 -> ok end,
+ ok.
-spec init_ack(Parent, Ret) -> 'ok' when
Parent :: pid(),
@@ -784,20 +801,114 @@ format(CrashReport, Encoding, Depth) ->
encoding => Encoding,
single_line => false}).
-do_format([OwnReport,LinkReport], #{single_line:=Single}=Extra) ->
+do_format([OwnReport,LinkReport], Extra) ->
+ #{encoding:=Enc, single_line:=Single, chars_limit:=Limit0} = Extra,
Indent = if Single -> "";
true -> " "
end,
- MyIndent = Indent ++ Indent,
- Sep = nl(Single,"; "),
- OwnFormat = format_report(OwnReport, MyIndent, Extra),
- LinkFormat = lists:join(Sep,format_link_report(LinkReport, MyIndent, Extra)),
Nl = nl(Single," "),
- Str = io_lib:format("~scrasher:"++Nl++"~ts"++Sep++"~sneighbours:"++Nl++"~ts",
- [Indent,OwnFormat,Indent,LinkFormat]),
- lists:flatten(Str).
+ Sep = nl(Single, report_separator()),
+ {PartLimit, Limit} =
+ case Limit0 of
+ unlimited ->
+ {Limit0, Limit0};
+ _ when is_integer(Limit0) ->
+ %% HardcodedSize is the length of the hardcoded heading +
+ %% separators in the final format string below,
+ %% including neighbours. Just make sure the limit
+ %% does not become negative.
+ Num = length(OwnReport),
+ HardcodedSize = (length(Indent) + length("crasher")
+ + length(Nl) + length(Sep)
+ + (length(Sep) * Num)),
+ Limit1 = max(Limit0-HardcodedSize, 1),
+
+ %% Divide the available characters over all report
+ %% parts. Spend one third of the characters on the
+ %% crash reason, and let the rest of the elements
+ %% (including the neighbours) share the other two
+ %% thirds. This is to make sure we see a good part of
+ %% the crash reason. Most of the other elements in the
+ %% crasher's report are quite small, so we don't loose
+ %% a lot of info from these anyway.
+ EL = Limit1 div 3,
+ PL = (Limit1-EL) div (Num),
+ {PL, Limit1}
+ end,
+ LinkFormat = format_link_reports(LinkReport, Indent, Extra, PartLimit),
+ LinkFormatSize = size(Enc, LinkFormat),
+
+ OwnFormat = format_own_report(OwnReport, Indent, Extra,
+ LinkFormatSize, PartLimit, Limit),
+ io_lib:format("~scrasher:"++Nl++"~ts"++Sep++"~ts",
+ [Indent,OwnFormat,LinkFormat]).
+
+format_own_report(OwnReport, Indent, Extra, LinkFormatSize, PartLimit, Limit0) ->
+ MyIndent = Indent ++ Indent,
+ case separate_error_info(OwnReport) of
+ {First,{Class,Reason,StackTrace},Rest} ->
+ F = format_report(First, MyIndent, Extra, PartLimit),
+ R = format_report(Rest, MyIndent, Extra, PartLimit),
+ #{encoding:=Enc, single_line:=Single} = Extra,
+ Sep = nl(Single, part_separator()),
+ Limit = case Limit0 of
+ unlimited ->
+ Limit0;
+ _ when is_integer(Limit0) ->
+ %% Some of the report parts are quite small,
+ %% and we can use the leftover chars to show
+ %% more of the error_info part.
+ SizeOfOther = (size(Enc, F)
+ +size(Enc, R)
+ -length(Sep)*(length(F)+length(R))
+ +LinkFormatSize),
+ max(Limit0-SizeOfOther, 1)
+ end,
+ EI = format_exception(Class, Reason, StackTrace, Extra, Limit),
+ lists:join(Sep, [F, EI, R]);
+ no ->
+ Limit = case Limit0 of
+ unlimited ->
+ Limit0;
+ _ when is_integer(Limit0) ->
+ max(Limit0-LinkFormatSize, 1)
+ end,
+ format_report(OwnReport, MyIndent, Extra, Limit)
+ end.
-format_link_report([Link|Reps], Indent0, #{single_line:=Single}=Extra) ->
+separate_error_info(Report) ->
+ try
+ lists:splitwith(fun(A) -> element(1, A) =/= error_info end, Report)
+ of
+ {First, [{error_info,ErrorInfo}|Rest]} ->
+ {First,ErrorInfo,Rest};
+ _ -> no
+ catch _:_ -> no
+ end.
+
+%% If the size of the total report is limited by chars_limit, then
+%% print only the pids.
+format_link_reports(LinkReports, Indent, Extra, PartLimit)
+ when is_integer(PartLimit) ->
+ #{encoding:=Enc, depth:=Depth, single_line:=Single} = Extra,
+ Pids = [P || {neighbour,[{pid,P}|_]} <- LinkReports],
+ {P,Tl} = p(Enc,Depth),
+ Width = if Single -> "0";
+ true -> ""
+ end,
+ io_lib:format(Indent++"neighbours: ~"++Width++P,
+ [Pids|Tl],
+ [{chars_limit,PartLimit}]);
+format_link_reports(LinkReports, Indent, Extra, PartLimit) ->
+ #{single_line:=Single} = Extra,
+ MyIndent = Indent ++ Indent,
+ LinkFormat =
+ lists:join(nl(Single, report_separator()),
+ format_link_report(LinkReports, MyIndent, Extra, PartLimit)),
+ [Indent,"neighbours:",nl(Single," "),LinkFormat].
+
+format_link_report([Link|Reps], Indent0, Extra, PartLimit) ->
+ #{single_line:=Single} = Extra,
Rep = case Link of
{neighbour,Rep0} -> Rep0;
_ -> Link
@@ -806,63 +917,70 @@ format_link_report([Link|Reps], Indent0, #{single_line:=Single}=Extra) ->
true -> Indent0
end,
LinkIndent = [" ",Indent],
- [[Indent,"neighbour:",nl(Single," "),format_report(Rep, LinkIndent, Extra)]|
- format_link_report(Reps, Indent, Extra)];
-format_link_report(Rep, Indent, Extra) ->
- format_report(Rep, Indent, Extra).
-
-format_report(Rep, Indent, #{single_line:=Single}=Extra) when is_list(Rep) ->
- lists:join(nl(Single,", "),format_rep(Rep, Indent, Extra));
-format_report(Rep, Indent0, #{encoding:=Enc,depth:=Depth,
- chars_limit:=Limit,single_line:=Single}) ->
+ [[Indent,"neighbour:",nl(Single," "),
+ format_report(Rep, LinkIndent, Extra, PartLimit)]|
+ format_link_report(Reps, Indent, Extra, PartLimit)];
+format_link_report(Rep, Indent, Extra, PartLimit) ->
+ format_report(Rep, Indent, Extra, PartLimit).
+
+format_report(Rep, Indent, Extra, Limit) when is_list(Rep) ->
+ #{single_line:=Single} = Extra,
+ lists:join(nl(Single, part_separator()),
+ format_rep(Rep, Indent, Extra, Limit));
+format_report(Rep, Indent0, Extra, Limit) ->
+ #{encoding:=Enc, depth:=Depth, single_line:=Single} = Extra,
{P,Tl} = p(Enc,Depth),
{Indent,Width} = if Single -> {"","0"};
true -> {Indent0,""}
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
io_lib:format("~s~"++Width++P, [Indent, Rep | Tl], Opts).
-format_rep([{initial_call,InitialCall}|Rep], Indent, Extra) ->
- [format_mfa(Indent, InitialCall, Extra)|format_rep(Rep, Indent, Extra)];
-format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Indent, Extra) ->
- [format_exception(Class, Reason, StackTrace, Extra)|
- format_rep(Rep, Indent, Extra)];
-format_rep([{Tag,Data}|Rep], Indent, Extra) ->
- [format_tag(Indent, Tag, Data, Extra)|format_rep(Rep, Indent, Extra)];
-format_rep(_, _, _Extra) ->
+format_rep([{initial_call,InitialCall}|Rep], Indent, Extra, Limit) ->
+ [format_mfa(Indent, InitialCall, Extra, Limit)|
+ format_rep(Rep, Indent, Extra, Limit)];
+format_rep([{Tag,Data}|Rep], Indent, Extra, Limit) ->
+ [format_tag(Indent, Tag, Data, Extra, Limit)|
+ format_rep(Rep, Indent, Extra, Limit)];
+format_rep(_, _, _Extra, _Limit) ->
[].
-format_exception(Class, Reason, StackTrace,
- #{encoding:=Enc,depth:=Depth,chars_limit:=Limit,
- single_line:=Single}=Extra) ->
- PF = pp_fun(Extra),
+format_exception(Class, Reason, StackTrace, Extra, Limit) ->
+ #{encoding:=Enc,depth:=Depth, single_line:=Single} = Extra,
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
if Single ->
{P,Tl} = p(Enc,Depth),
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
[atom_to_list(Class), ": ",
io_lib:format("~0"++P,[{Reason,StackTrace}|Tl],Opts)];
true ->
+ %% Notice that each call to PF uses chars_limit, which
+ %% means that the total size of the formatted exception
+ %% can exceed the limit a lot.
+ PF = pp_fun(Extra, Enc),
EI = " ",
- [EI, erl_error:format_exception(1+length(EI), Class, Reason,
- StackTrace, StackFun, PF, Enc)]
+ Lim = case Limit of
+ unlimited -> -1;
+ _ -> Limit
+ end,
+ FE = erl_error:format_exception(1+length(EI), Class, Reason,
+ StackTrace, StackFun, PF, Enc,
+ Lim),
+ [EI, FE]
end.
-format_mfa(Indent0, {M,F,Args}=StartF, #{encoding:=Enc,single_line:=Single}=Extra) ->
+format_mfa(Indent0, {M,F,Args}=StartF, Extra, Limit) ->
+ #{encoding:=Enc,single_line:=Single} = Extra,
Indent = if Single -> "";
true -> Indent0
end,
try
A = length(Args),
- [Indent,"initial call: ",atom_to_list(M),$:,to_string(F, Enc),$/,
+ [Indent,"initial call: ",to_string(M, Enc),$:,to_string(F, Enc),$/,
integer_to_list(A)]
catch
error:_ ->
- format_tag(Indent, initial_call, StartF, Extra)
+ format_tag(Indent, initial_call, StartF, Extra, Limit)
end.
to_string(A, latin1) ->
@@ -870,27 +988,25 @@ to_string(A, latin1) ->
to_string(A, _) ->
io_lib:write_atom(A).
-pp_fun(#{encoding:=Enc,depth:=Depth,chars_limit:=Limit,single_line:=Single}) ->
+pp_fun(Extra, Enc) ->
+ #{encoding:=Enc,depth:=Depth, single_line:=Single} = Extra,
{P,Tl} = p(Enc, Depth),
Width = if Single -> "0";
true -> ""
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
- fun(Term, I) ->
- io_lib:format("~" ++ Width ++ "." ++ integer_to_list(I) ++ P,
- [Term|Tl], Opts)
+ fun(Term, I, Limit) ->
+ S = io_lib:format("~" ++ Width ++ "." ++ integer_to_list(I) ++ P,
+ [Term|Tl], [{chars_limit, Limit}]),
+ {S, sub(Limit, S, Enc)}
end.
-format_tag(Indent0, Tag, Data, #{encoding:=Enc,depth:=Depth,chars_limit:=Limit,single_line:=Single}) ->
+format_tag(Indent0, Tag, Data, Extra, Limit) ->
+ #{encoding:=Enc,depth:=Depth,single_line:=Single} = Extra,
{P,Tl} = p(Enc, Depth),
{Indent,Width} = if Single -> {"","0"};
true -> {Indent0,""}
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
io_lib:format("~s~" ++ Width ++ "p: ~" ++ Width ++ ".18" ++ P,
[Indent, Tag, Data|Tl], Opts).
@@ -902,12 +1018,35 @@ p(Encoding, Depth) ->
P = modifier(Encoding) ++ Letter,
{P, Tl}.
+report_separator() -> "; ".
+
+part_separator() -> ", ".
+
+chars_limit_opt(CharsLimit) ->
+ [{chars_limit, CharsLimit} || is_integer(CharsLimit)].
+
modifier(latin1) -> "";
modifier(_) -> "t".
nl(true,Else) -> Else;
nl(false,_) -> "\n".
+%% Make sure T does change sign.
+sub(T, _, _Enc) when T < 0 -> T;
+sub(T, E, Enc) ->
+ Sz = size(Enc, E),
+ if
+ T >= Sz ->
+ T - Sz;
+ true ->
+ 0
+ end.
+
+size(latin1, S) ->
+ iolist_size(S);
+size(_, S) ->
+ string:length(S).
+
%%% -----------------------------------------------------------
%%% Stop a process and wait for it to terminate
%%% -----------------------------------------------------------
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index 3ce68887ae..9216c3bdb3 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -220,7 +220,7 @@ get_value(Key, [P | Ps], Default) ->
{_, Value} ->
Value;
_ ->
- %% Don</code>t continue the search!
+ %% Don't continue the search!
Default
end;
true ->
@@ -419,7 +419,7 @@ substitute_aliases_1([], P) ->
%% <p>Example: <code>substitute_negations([{no_foo, foo}], L)</code>
%% will replace any atom <code>no_foo</code> or tuple <code>{no_foo,
%% true}</code> in <code>L</code> with <code>{foo, false}</code>, and
-%% any other tuple <code>{no_foo, ...}</code> with <code>foo</code.</p>
+%% any other tuple <code>{no_foo, ...}</code> with <code>foo</code>.</p>
%%
%% @see get_bool/2
%% @see substitute_aliases/2
@@ -639,24 +639,24 @@ normalize(L, []) ->
Rest :: [term()].
split(List, Keys) ->
- {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []),
- {[lists:reverse(dict:fetch(K, Store)) || K <- Keys],
+ {Store, Rest} = split(List, maps:from_list([{K, []} || K <- Keys]), []),
+ {[lists:reverse(map_get(K, Store)) || K <- Keys],
lists:reverse(Rest)}.
split([P | Ps], Store, Rest) ->
if is_atom(P) ->
- case dict:is_key(P, Store) of
+ case is_map_key(P, Store) of
true ->
- split(Ps, dict_prepend(P, P, Store), Rest);
+ split(Ps, maps_prepend(P, P, Store), Rest);
false ->
split(Ps, Store, [P | Rest])
end;
tuple_size(P) >= 1 ->
%% Note that Key does not have to be an atom in this case.
Key = element(1, P),
- case dict:is_key(Key, Store) of
+ case is_map_key(Key, Store) of
true ->
- split(Ps, dict_prepend(Key, P, Store), Rest);
+ split(Ps, maps_prepend(Key, P, Store), Rest);
false ->
split(Ps, Store, [P | Rest])
end;
@@ -666,5 +666,5 @@ split([P | Ps], Store, Rest) ->
split([], Store, Rest) ->
{Store, Rest}.
-dict_prepend(Key, Val, Dict) ->
- dict:store(Key, [Val | dict:fetch(Key, Dict)], Dict).
+maps_prepend(Key, Val, Dict) ->
+ Dict#{Key := [Val | map_get(Key, Dict)]}.
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index a1c1117e31..713ed1f896 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -785,7 +785,7 @@ merge_binding_structs(Bs1, Bs2) ->
aux_name1(Name, N, AllNames) ->
SN = name_suffix(Name, N),
- case sets:is_element(SN, AllNames) of
+ case gb_sets:is_member(SN, AllNames) of
true -> aux_name1(Name, N + 1, AllNames);
false -> {SN, N}
end.
@@ -1357,7 +1357,7 @@ flatten_abstr(E, VN, _Vars, Body) ->
{VN, Body, E}.
abstract_vars(Abstract) ->
- sets:from_list(ordsets:to_list(vars(Abstract))).
+ gb_sets:from_list(ordsets:to_list(vars(Abstract))).
collect([]=L) ->
L;
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index 4a39f8ae9d..7cf631d85d 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -511,7 +511,7 @@ used_genvar_check(FormsNoShadows, State) ->
Acc0 = {State#state.intro_vars, [{atom, anno0(), true}]},
{_, {[], Exprs}} = qual_fold(F, Acc0, [], FormsNoShadows, State),
FunctionNames = [Name || {function, _, Name, _, _} <- FormsNoShadows],
- UniqueFName = qlc:aux_name(used_genvar, 1, sets:from_list(FunctionNames)),
+ UniqueFName = qlc:aux_name(used_genvar, 1, gb_sets:from_list(FunctionNames)),
A = anno0(),
{function,A,UniqueFName,0,[{clause,A,[],[],lists:reverse(Exprs)}]}.
@@ -613,8 +613,8 @@ q_intro_vars(QId, [{QId, IVs} | QsIVs], IVsSoFar) -> {QsIVs, IVs ++ IVsSoFar}.
transform(FormsNoShadows, State) ->
_ = erlang:system_flag(backtrace_depth, 500),
IntroVars = State#state.intro_vars,
- AllVars = sets:from_list(ordsets:to_list(qlc:vars(FormsNoShadows))),
- ?DEBUG("AllVars = ~p~n", [sets:to_list(AllVars)]),
+ AllVars = gb_sets:from_list(ordsets:to_list(qlc:vars(FormsNoShadows))),
+ ?DEBUG("AllVars = ~p~n", [gb_sets:to_list(AllVars)]),
F1 = fun(QId, {generate,_,P,LE}, Foo, {GoI,SI}) ->
{{QId,GoI,SI,{gen,P,LE}},Foo,{GoI + 3, SI + 2}};
(QId, F, Foo, {GoI,SI}) ->
@@ -632,10 +632,10 @@ transform(FormsNoShadows, State) ->
{_,Source0} = qual_fold(fun(_QId, {generate,_,_P,_E}=Q, Dict, Foo) ->
{Q,Dict,Foo};
(QId, F, Dict, Foo) ->
- {F,dict:store(QId, F, Dict),Foo}
- end, dict:new(), [], FormsNoShadows, State),
+ {F,maps:put(QId, F, Dict),Foo}
+ end, maps:new(), [], FormsNoShadows, State),
{_,Source} = qlc_mapfold(fun(Id, {lc,_L,E,_Qs}=LC, Dict) ->
- {LC,dict:store(Id, E, Dict)}
+ {LC,maps:put(Id, E, Dict)}
end, Source0, FormsNoShadows, State),
@@ -685,7 +685,7 @@ transform(FormsNoShadows, State) ->
FunW = {'fun',L,{clauses,[{clause,L,AsW,[],
[{match,L,{var,L,Fun},FunC},
{call,L,{var,L,Fun},As0}]}]}},
- {ok, OrigE0} = dict:find(Id, Source),
+ OrigE0 = map_get(Id, Source),
OrigE = undo_no_shadows(OrigE0, State),
QCode = qcode(OrigE, XQCs, Source, L, State),
Qdata = qdata(XQCs, L),
@@ -2361,7 +2361,7 @@ qcode(E, QCs, Source, L, State) ->
qcode([{_QId, {_QIvs, {{gen,P,_LE,_GV}, GoI, _SI}}} | QCs], Source, State) ->
[{GoI,undo_no_shadows(P, State)} | qcode(QCs, Source, State)];
qcode([{QId, {_QIVs, {{fil,_F}, GoI, _SI}}} | QCs], Source, State) ->
- {ok,OrigF} = dict:find(QId, Source),
+ OrigF = map_get(QId, Source),
[{GoI,undo_no_shadows(OrigF, State)} | qcode(QCs, Source, State)];
qcode([], _Source, _State) ->
[].
@@ -2666,12 +2666,12 @@ no_shadows(Forms0, State) ->
%%
%% The original names of variables are kept in a table in State.
%% undo_no_shadows/2 re-creates the original code.
- AllVars = sets:from_list(ordsets:to_list(qlc:vars(Forms0))),
- ?DEBUG("nos AllVars = ~p~n", [sets:to_list(AllVars)]),
+ AllVars = gb_sets:from_list(ordsets:to_list(qlc:vars(Forms0))),
+ ?DEBUG("nos AllVars = ~p~n", [gb_sets:to_list(AllVars)]),
VFun = fun(_Id, LC, Vs) -> nos(LC, Vs) end,
LI = ets:new(?APIMOD,[]),
UV = ets:new(?APIMOD,[]),
- D0 = dict:new(),
+ D0 = maps:new(),
S1 = {LI, D0, UV, AllVars, [], State},
_ = qlc_mapfold(VFun, S1, Forms0, State),
?DEBUG("UsedIntroVars = ~p~n", [ets:match_object(UV, '_')]),
@@ -2781,7 +2781,7 @@ nos_var(Anno, Name, State) ->
end.
used_var(V, Vs, UV) ->
- case dict:find(V, Vs) of
+ case maps:find(V, Vs) of
{ok,Value} ->
VN = qlc:name_suffix(V, Value),
_ = ets:update_counter(UV, VN, 1),
@@ -2796,10 +2796,10 @@ next_var(V, Vs, AllVars, LI, UV) ->
end,
true = ets:insert(LI, {V, NValue}),
VN = qlc:name_suffix(V, NValue),
- case sets:is_element(VN, AllVars) of
+ case gb_sets:is_member(VN, AllVars) of
true -> next_var(V, Vs, AllVars, LI, UV);
false -> true = ets:insert(UV, {VN, 0}),
- NVs = dict:store(V, NValue, Vs),
+ NVs = maps:put(V, NValue, Vs),
{VN, NVs}
end.
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index 11c0aa8d2b..9fe3782f92 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -37,7 +37,7 @@
%% Mis-spelled, deprecated.
-export([lait/1]).
--deprecated([lait/1]).
+-deprecated([{lait,1,"use queue:liat/1 instead"}]).
%%--------------------------------------------------------------------------
%% Efficient implementation of double ended fifo queues
diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl
index 46dabb4323..8d6a35f031 100644
--- a/lib/stdlib/src/random.erl
+++ b/lib/stdlib/src/random.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
%%
-module(random).
--deprecated(module).
+-deprecated({'_','_',"use the 'rand' module instead"}).
%% Reasonable random number generator.
%% The method is attributed to B. A. Wichmann and I. D. Hill
diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl
index a0c1d98513..e65340e663 100644
--- a/lib/stdlib/src/shell_default.erl
+++ b/lib/stdlib/src/shell_default.erl
@@ -28,6 +28,7 @@
erlangrc/1,bi/1, regs/0, flush/0,pwd/0,ls/0,ls/1,cd/1,
y/1, y/2,
xm/1, bt/1, q/0,
+ h/1, h/2, h/3, ht/1, ht/2, ht/3,
ni/0, nregs/0]).
-export([ih/0,iv/0,im/0,ii/1,ii/2,iq/1,ini/1,ini/2,inq/1,ib/2,ib/3,
@@ -43,7 +44,13 @@ help() ->
format("e(N) -- repeat the expression in query <N>\n"),
format("f() -- forget all variable bindings\n"),
format("f(X) -- forget the binding of variable X\n"),
- format("h() -- history\n"),
+ format("h() -- history\n"),
+ format("h(Mod) -- help about module\n"),
+ format("h(Mod,Func)-- help about function in module\n"),
+ format("h(Mod,Func,Arity) -- help about function with arity in module\n"),
+ format("ht(Mod) -- help about a module's types\n"),
+ format("ht(Mod,Func) -- help about type in module\n"),
+ format("ht(Mod,Func,Arity) -- help about type with arity in module\n"),
format("history(N) -- set how many previous commands to keep\n"),
format("results(N) -- set how many previous command results to keep\n"),
format("catch_exception(B) -- how exceptions are handled\n"),
@@ -76,6 +83,12 @@ c(File, Opt, Filter) -> c:c(File, Opt, Filter).
cd(D) -> c:cd(D).
erlangrc(X) -> c:erlangrc(X).
flush() -> c:flush().
+h(M) -> c:h(M).
+h(M,F) -> c:h(M,F).
+h(M,F,A) -> c:h(M,F,A).
+ht(M) -> c:ht(M).
+ht(M,F) -> c:ht(M,F).
+ht(M,F,A) -> c:ht(M,F,A).
i() -> c:i().
i(X,Y,Z) -> c:i(X,Y,Z).
l(Mod) -> c:l(Mod).
diff --git a/lib/stdlib/src/shell_docs.erl b/lib/stdlib/src/shell_docs.erl
new file mode 100644
index 0000000000..6e56030fca
--- /dev/null
+++ b/lib/stdlib/src/shell_docs.erl
@@ -0,0 +1,684 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(shell_docs).
+
+-include("eep48.hrl").
+
+-export([render/2, render/3, render/4]).
+-export([render_type/2, render_type/3, render_type/4]).
+
+%% Used by chunks.escript in erl_docgen
+-export([validate/1, normalize/1]).
+
+%% Convinience functions
+-export([get_doc/1, get_doc/3, get_type_doc/3]).
+
+-record(config, { docs,
+ io_opts = io:getopts(),
+ io_columns = element(2,io:columns())
+ }).
+
+-define(ALL_ELEMENTS,[a,p,h1,h2,h3,i,br,em,pre,code,ul,ol,li,dl,dt,dd]).
+%% inline elements are:
+-define(INLINE,[i,br,em,code,a]).
+-define(IS_INLINE(ELEM),(((ELEM) =:= a) orelse ((ELEM) =:= code)
+ orelse ((ELEM) =:= i) orelse ((ELEM) =:= br)
+ orelse ((ELEM) =:= em))).
+%% non-inline elements are:
+-define(BLOCK,[p,pre,ul,ol,li,dl,dt,dd,h1,h2,h3]).
+-define(IS_BLOCK(ELEM),not ?IS_INLINE(ELEM)).
+-define(IS_PRE(ELEM),(((ELEM) =:= pre))).
+
+-type chunk_element_type() :: a | p | i | br | em | pre | code | ul |
+ ol | li | dl | dt | dd.
+-type chunk_element_attr() :: {atom(),unicode:chardata()}.
+-type chunk_element_attrs() :: [chunk_element_attr()].
+-type chunk_element() :: {chunk_element_type(),chunk_element_attrs(),
+ chunk_elements()} | binary().
+-type chunk_elements() :: [chunk_element()].
+-type docs_v1() :: #docs_v1{}.
+
+
+-spec validate(Module) -> ok when
+ Module :: module() | docs_v1().
+%% Simple validation of erlang doc chunk. Check that all tags are supported and
+%% that the signature is correct.
+validate(Module) when is_atom(Module) ->
+ {ok, Doc} = code:get_doc(Module),
+ validate(Doc);
+validate(#docs_v1{ module_doc = MDocs, docs = AllDocs }) ->
+
+ %% Check some macro in-variants
+ AE = lists:sort(?ALL_ELEMENTS),
+ AE = lists:sort(?INLINE ++ ?BLOCK),
+ true = lists:all(fun(Elem) -> ?IS_INLINE(Elem) end, ?INLINE),
+ true = lists:all(fun(Elem) -> ?IS_BLOCK(Elem) end, ?BLOCK),
+
+ _ = maps:map(fun(_Key,MDoc) -> validate(MDoc) end, MDocs),
+ lists:map(fun({_,_Anno, Sig, Docs, _Meta}) ->
+ case lists:all(fun erlang:is_binary/1, Sig) of
+ false -> throw({invalid_signature,Sig});
+ true -> ok
+ end,
+ maps:map(fun(_Key,Doc) -> validate(Doc) end, Docs)
+ end, AllDocs);
+validate([H|T]) when is_tuple(H) ->
+ _ = validate(H),
+ validate(T);
+validate({Tag,Attr,Content}) ->
+ case lists:member(Tag,?ALL_ELEMENTS) of
+ false ->
+ throw({invalid_tag,Tag});
+ true ->
+ ok
+ end,
+ true = is_list(Attr),
+ validate(Content);
+validate([Chars | T]) when is_binary(Chars) ->
+ validate(T);
+validate([]) ->
+ ok.
+
+%% Follows algorithm described here:
+%% * https://medium.com/@patrickbrosset/when-does-white-space-matter-in-html-b90e8a7cdd33
+%% which in turn follows this:
+%% * https://www.w3.org/TR/css-text-3/#white-space-processing
+-spec normalize(Docs) -> NormalizedDocs when
+ Docs :: chunk_elements(),
+ NormalizedDocs :: chunk_elements().
+normalize(Docs) ->
+ Trimmed = normalize_trim(Docs,true),
+ normalize_space(Trimmed).
+
+normalize_trim(Bin,true) when is_binary(Bin) ->
+ %% Remove any whitespace (except \n) before or after a newline
+ NoSpace = re:replace(Bin,"[^\\S\n]*\n+[^\\S\n]*","\n",[global]),
+ %% Replace any tabs with space
+ NoTab = re:replace(NoSpace,"\t"," ",[global]),
+ %% Replace any newlines with space
+ NoNewLine = re:replace(NoTab,"\\v"," ",[global]),
+ %% Replace any sequences of \s with a single " "
+ re:replace(NoNewLine,"\\s+"," ",[global,{return,binary}]);
+normalize_trim(Bin,false) when is_binary(Bin) ->
+ Bin;
+normalize_trim([{pre,Attr,Content}|T],Trim) ->
+ [{pre,Attr,normalize_trim(Content,false)} | normalize_trim(T,Trim)];
+normalize_trim([{Tag,Attr,Content}|T],Trim) ->
+ [{Tag,Attr,normalize_trim(Content,Trim)} | normalize_trim(T,Trim)];
+normalize_trim([<<>>|T],Trim) ->
+ normalize_trim(T,Trim);
+normalize_trim([B1,B2|T],Trim) when is_binary(B1),is_binary(B2) ->
+ normalize_trim([<<B1/binary,B2/binary>> | T],Trim);
+normalize_trim([H|T],Trim) ->
+ [normalize_trim(H,Trim) | normalize_trim(T,Trim)];
+normalize_trim([],_Trim) ->
+ [].
+
+%% We want to remove any duplicate spaces, even if they
+%% cross into other inline elements.
+%% For non-inline elements we just need to make sure that any
+%% leading or trailing spaces are stripped.
+normalize_space([{Pre,Attr,Content}|T]) when ?IS_PRE(Pre) ->
+ [{Pre,Attr,trim_first_and_last(Content,$\n)} | normalize_space(T)];
+normalize_space([{Block,Attr,Content}|T]) when ?IS_BLOCK(Block) ->
+ [{Block,Attr,trim_first_and_last(trim_inline(Content),$ )} | normalize_space(T)];
+normalize_space([E|T]) ->
+ [E|normalize_space(T)];
+normalize_space([]) ->
+ [].
+
+trim_inline(Content) ->
+ {NewContent,_} = trim_inline(Content,false),
+ NewContent.
+trim_inline([Bin|T],false) when is_binary(Bin) ->
+ LastElem = binary:at(Bin,byte_size(Bin)-1),
+ {NewT, NewState} = trim_inline(T,LastElem =:= $ ),
+ {[Bin | NewT],NewState};
+trim_inline([<<" ">>|T],true) ->
+ trim_inline(T,false);
+trim_inline([<<" ",Bin/binary>>|T],true) when is_binary(Bin) ->
+ trim_inline([Bin | T],false);
+trim_inline([Bin|T],true) when is_binary(Bin) ->
+ trim_inline([Bin|T],false);
+trim_inline([{Elem,Attr,Content}|T],TrimSpace) when ?IS_INLINE(Elem) ->
+ {NewContent,ContentTrimSpace} = trim_inline(Content,TrimSpace),
+ {NewT,TTrimSpace} = trim_inline(T,ContentTrimSpace),
+ {[{Elem,Attr,NewContent} | NewT], TTrimSpace};
+trim_inline([{Elem1,_A1,_C1} = B1,<<" ">>,{Elem2,_A2,_C2} = B2|T],TrimSpace)
+ when ?IS_BLOCK(Elem1),?IS_BLOCK(Elem2) ->
+ trim_inline([B1,B2|T],TrimSpace);
+trim_inline([{Elem,_Attr,_Content} = Block|T],_TrimSpace) when ?IS_BLOCK(Elem) ->
+ [NewBlock] = normalize_space([Block]),
+ {NewT,TTrimSpace} = trim_inline(T,false),
+ {[NewBlock | NewT], TTrimSpace};
+trim_inline([],TrimSpace) ->
+ {[],TrimSpace}.
+
+
+%% This function removes the first and last What from the content.
+%% This is complicated by the fact that the first or last element
+%% may not have any binary, or have the binary deeply nested within.
+trim_first_and_last(Content, What) when What < 256 ->
+ {NewContent,_State} = trim_last(trim_first(Content,What),What),
+ NewContent.
+
+trim_first(Content,What) ->
+ {NewContent,_State} = trim_first(Content,false,What),
+ NewContent.
+trim_first([Bin|T],false,What) when is_binary(Bin) ->
+ case Bin of
+ <<What>> ->
+ {T,true};
+ <<What,NewBin/binary>> ->
+ {[NewBin|T],true};
+ Bin ->
+ {[Bin|T],true}
+ end;
+trim_first([{Elem,Attr,Content} = Tag|T],false,What) ->
+ case trim_first(Content,false,What) of
+ {NewContent,true} ->
+ {[{Elem,Attr,NewContent}|T],true};
+ {Content,false} ->
+ {NewT,NewState} = trim_first(T,false,What),
+ {[Tag | NewT],NewState}
+ end;
+trim_first([],false,_What) ->
+ {[],false}.
+
+trim_last([Bin | T],What) when is_binary(Bin) ->
+ case trim_last(T,What) of
+ {NewT,true} ->
+ {[Bin | NewT],true};
+ {T,false} ->
+ PreSz = byte_size(Bin)-1,
+ case Bin of
+ <<What>> -> {T,true};
+ <<NewBin:PreSz/binary,What>> ->
+ {[NewBin|T],true};
+ Bin ->
+ {[Bin|T],true}
+ end
+ end;
+trim_last([{Elem,Attr,Content} = Tag|T],What) ->
+ case trim_last(T,What) of
+ {NewT,true} ->
+ {[Tag | NewT],true};
+ {T,false} ->
+ {NewContent,NewState} = trim_last(Content,What),
+ {[{Elem,Attr,NewContent}|T],NewState}
+ end;
+trim_last([],_What) ->
+ {[],false}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API function for dealing with the function documentation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec get_doc(Module :: module()) -> chunk_elements().
+get_doc(Module) ->
+ {ok, #docs_v1{ module_doc = ModuleDoc } } = code:get_doc(Module),
+ get_local_doc(Module, ModuleDoc).
+
+-spec get_doc(Module :: module(), Function, Arity) ->
+ [{{Function,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
+ Function :: function(),
+ Arity :: arity(),
+ Anno :: erl_anno:anno(),
+ Signature :: [binary()],
+ Metadata :: #{}.
+get_doc(Module, Function, Arity) ->
+ {ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
+ FnFunctions =
+ lists:filter(fun({{function, F, A},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs),
+
+ [{F,A,S,get_local_doc({F,A},D),M} || {F,A,S,D,M} <- FnFunctions].
+
+-spec render(Module :: module(), Docs :: docs_v1()) -> unicode:chardata().
+render(Module, #docs_v1{ module_doc = ModuleDoc, metadata = MD } = D) ->
+ render_docs([["\t",atom_to_binary(Module)]],
+ get_local_doc(Module, ModuleDoc), MD, D).
+
+-spec render(Module :: module(), Function :: function(), Docs :: docs_v1()) ->
+ unicode:chardata() | {error,function_missing}.
+render(_Module, Function, #docs_v1{ docs = Docs } = D) ->
+ render_function(
+ lists:filter(fun({{function, F, _},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function;
+ (_) ->
+ false
+ end, Docs), D).
+-spec render(Module :: module(), Function :: function(), Arity :: arity(),
+ Docs :: docs_v1()) -> unicode:chardata() | {error,function_missing}.
+render(_Module, Function, Arity, #docs_v1{ docs = Docs } = D) ->
+ render_function(
+ lists:filter(fun({{function, F, A},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs), D).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API function for dealing with the type documentation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec get_type_doc(Module :: module(), Type :: atom(), Arity :: arity()) ->
+ [{{Type,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
+ Type :: atom(),
+ Arity :: arity(),
+ Anno :: erl_anno:anno(),
+ Signature :: [binary()],
+ Metadata :: #{}.
+get_type_doc(Module, Type, Arity) ->
+ {ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
+ FnFunctions =
+ lists:filter(fun({{type, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs),
+ [{F,A,S,get_local_doc(F, D),M} || {F,A,S,D,M} <- FnFunctions].
+
+-spec render_type(Module :: module(), Docs :: docs_v1()) -> unicode:chardata().
+render_type(Module, #docs_v1{ docs = Docs } = D) ->
+ render_type_signatures(Module,
+ lists:filter(fun({{type, _, _},_Anno,_Sig,_Doc,_Meta}) ->
+ true;
+ (_) ->
+ false
+ end, Docs), D).
+
+-spec render_type(Module :: module(), Type :: atom(), Docs :: docs_v1()) ->
+ unicode:chardata() | {error,type_missing}.
+render_type(_Module, Type, #docs_v1{ docs = Docs } = D) ->
+ render_type_docs(
+ lists:filter(fun({{type, T, _},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type;
+ (_) ->
+ false
+ end, Docs), D).
+
+-spec render_type(Module :: module(), Type :: atom(), Arity :: arity(),
+ Docs :: docs_v1()) -> unicode:chardata() | {error,type_missing}.
+render_type(_Module, Type, Arity, #docs_v1{ docs = Docs } = D) ->
+ render_type_docs(
+ lists:filter(fun({{type, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs), D).
+
+
+%% Get the docs in the correct locale if it exists.
+get_local_doc(MissingMod, Docs) when is_atom(MissingMod) ->
+ get_local_doc(atom_to_binary(MissingMod), Docs);
+get_local_doc({F,A}, Docs) ->
+ get_local_doc(unicode:characters_to_binary(io_lib:format("~tp/~p",[F,A])), Docs);
+get_local_doc(_Missing, #{ <<"en">> := Docs }) ->
+ %% English if it exists
+ normalize(Docs);
+get_local_doc(_Missing, ModuleDoc) when map_size(ModuleDoc) > 0 ->
+ %% Otherwise take first alternative found
+ normalize(maps:get(hd(maps:keys(ModuleDoc)), ModuleDoc));
+get_local_doc(Missing, hidden) ->
+ [{p,[],[<<"The documentation for ">>,Missing,
+ <<" is hidden. This probably means that it is internal "
+ "and not to be used by other applications.">>]}];
+get_local_doc(Missing, None) when None =:= none; None =:= #{} ->
+ [{p,[],[<<"There is no documentation for ">>,Missing]}].
+
+%%% Functions for rendering reference documentation
+render_function([], _D) ->
+ {error,function_missing};
+render_function(FDocs, D) ->
+ [render_docs(render_signature(Func), get_local_doc({F,A},Doc), Meta, D)
+ || {{_,F,A},_Anno,_Sig,Doc,Meta} = Func <- lists:sort(FDocs)].
+
+%% Render the signature of either function or a type, or anything else really.
+render_signature({{_Type,_F,_A},_Anno,_Sig,_Docs,#{ signature := Specs }}) ->
+ [erl_pp:attribute(Spec,[{encoding,utf8}]) || Spec <- Specs];
+render_signature({{_Type,_F,_A},_Anno,Sigs,_Docs,_Meta}) ->
+ [Sig || Sig <- Sigs].
+
+render_since(#{ since := Vsn }) ->
+ ["\n\nSince: ",Vsn];
+render_since(_) ->
+ [].
+
+render_docs(Headers, DocContents, MD, D = #config{}) ->
+ init_ansi(D),
+ try
+ {Doc,_} = render_docs(DocContents,[],0,2,D),
+ [sansi(bold),
+ [io_lib:format("~n~ts",[Header]) || Header <- Headers],
+ ransi(bold),
+ render_since(MD),
+ io_lib:format("~n~n~ts",[Doc])]
+ after
+ clean_ansi()
+ end;
+render_docs(Headers, DocContents, MD, D) ->
+ render_docs(Headers, DocContents, MD, #config{ docs = D }).
+
+%%% Functions for rendering type documentation
+render_type_signatures(Module, Types, D = #config{}) ->
+ init_ansi(D),
+ try
+ [sansi(bold),"\t",atom_to_list(Module),ransi(bold),"\n\n",
+ [render_signature(Type) || Type <- Types ]]
+ after
+ clean_ansi()
+ end;
+render_type_signatures(Module, Types, D) ->
+ render_type_signatures(Module, Types, #config{ docs = D }).
+
+render_type_docs([], _D) ->
+ {error,type_missing};
+render_type_docs(Types, #config{} = D) when is_list(Types) ->
+ [render_type_docs(Type, D) || Type <- Types];
+render_type_docs({{_,F,A},_,_Sig,Docs,Meta} = Type, #config{} = D) ->
+ render_docs(render_signature(Type), get_local_doc({F,A},Docs), Meta, D);
+render_type_docs(Docs, D) ->
+ render_type_docs(Docs, #config{ docs = D }).
+
+%%% General rendering functions
+render_docs(Elems,State,Pos,Ind,D) when is_list(Elems) ->
+ lists:mapfoldl(fun(Elem,P) ->
+% io:format("Elem: ~p (~p) (~p,~p)~n",[Elem,State,P,Ind]),
+ render_docs(Elem,State,P,Ind,D)
+ end,Pos,Elems);
+render_docs(Elem,State,Pos,Ind,D) ->
+ render_element(Elem,State,Pos,Ind,D).
+
+
+%%% The function is the main element rendering function
+%%%
+%%% Elem: The current element to process
+%%% Stack: A stack of element names to see where we are in the dom
+%%% Pos: The current print position on the current line
+%%% Ind: How much the text should be indented after a newline
+%%% Config: The renderer's configuration
+%%%
+%%% Each element is responsible for putting new lines AFTER itself
+%%% The indents are done either by render_words when a newline happens
+%%% or when a new element is to be rendered and Pos < Ind.
+%%%
+%%% Any block elements (i.e. p, ul, li etc) are responsible for trimming
+%%% extra new lines. eg. <ul><li><p>content</p></li></ul> should only
+%%% have two newlines at the end.
+-spec render_element(Elem :: chunk_element(),
+ Stack :: [chunk_element_type()],
+ Pos :: non_neg_integer(),
+ Indent :: non_neg_integer(),
+ Config :: #config{}) ->
+ {unicode:chardata(), Pos :: non_neg_integer()}.
+
+render_element({IgnoreMe,_,Content}, State, Pos, Ind,D)
+ when IgnoreMe =:= a; IgnoreMe =:= anno ->
+ render_docs(Content, State, Pos, Ind,D);
+
+%% Catch h1, h2 and h3 before the padding is done as there reset padding
+render_element({h1,_,Content},State,0 = Pos,_Ind,D) ->
+ trimnlnl(render_element({code,[],[{em,[],Content}]}, State, Pos, 0, D));
+render_element({h2,_,Content},State,0 = Pos,_Ind,D) ->
+ trimnlnl(render_element({em,[],Content}, State, Pos, 0, D));
+render_element({h3,_,Content},State,Pos,_Ind,D) when Pos =< 2 ->
+ trimnlnl(render_element({code,[],Content}, State, Pos, 2, D));
+
+render_element({p,_Attr,_Content} = E,State,Pos,Ind,D) when Pos > Ind ->
+ {Docs,NewPos} = render_element(E,State,0,Ind,D),
+ {["\n",Docs],NewPos};
+render_element({p,[{class,What}],Content},State,Pos,Ind,D) ->
+ {Docs,_} = render_docs(Content, [p|State], 0, Ind+2, D),
+ trimnlnl([pad(Ind - Pos),string:titlecase(What),":\n",Docs]);
+render_element({p,_,Content},State,Pos,Ind,D) ->
+ trimnlnl(render_docs(Content, [p|State], Pos, Ind,D));
+
+render_element(Elem,State,Pos,Ind,D) when Pos < Ind ->
+% io:format("Pad: ~p~n",[Ind - Pos]),
+ {Docs,NewPos} = render_element(Elem,State,Ind,Ind,D),
+
+ {[pad(Ind - Pos), Docs],NewPos};
+
+render_element({code,_,Content},[pre|_] = State,Pos,Ind,D) ->
+ %% When code is within a pre we don't emit any underline
+ render_docs(Content, [code|State], Pos, Ind,D);
+render_element({code,_,Content},State,Pos,Ind,D) ->
+ Underline = sansi(underline),
+ {Docs, NewPos} = render_docs(Content, [code|State], Pos, Ind,D),
+ {[Underline,Docs,ransi(underline)], NewPos};
+
+render_element({i,_,Content},State,Pos,Ind,D) ->
+ %% Just ignore i as ansi does not have cursive style
+ render_docs(Content, State, Pos, Ind,D);
+
+render_element({br,[],[]},_State,_Pos,_Ind,_D) ->
+ nl("");
+
+render_element({em,_,Content},State,Pos,Ind,D) ->
+ Bold = sansi(bold),
+ {Docs, NewPos} = render_docs(Content, State, Pos, Ind,D),
+ {[Bold,Docs,ransi(bold)], NewPos};
+
+render_element({pre,_,Content},State,Pos,Ind,D) ->
+ %% For pre we make sure to respect the newlines in pre
+ trimnlnl(render_docs(Content, [pre|State], Pos, Ind+2, D));
+
+render_element({ul,[{class,"types"}],Content},State,_Pos,Ind,D) ->
+ {Docs, _} = render_docs(Content, [types|State], 0, Ind+2, D),
+ trimnlnl(["Types:\n", Docs]);
+render_element({li,Attr,Content},[types|_] = State,Pos,Ind,C) ->
+ Doc =
+ case {proplists:get_value(name, Attr),proplists:get_value(class, Attr)} of
+ {undefined,Class} when Class =:= undefined; Class =:= "type" ->
+ %% Inline html for types
+ render_docs(Content,[type|State],Pos,Ind,C);
+ {_,"description"} ->
+ %% Inline html for type descriptions
+ render_docs(Content,[type|State],Pos,Ind+2,C);
+ {Name,_} ->
+ %% Try to render from type metadata
+ case render_type_signature(list_to_atom(Name),C) of
+ undefined when Content =:= [] ->
+ %% Failed and no content, emit place-holder
+ {["-type ",Name,"() :: term()."],0};
+ undefined ->
+ %% Failed with metadata, render the content
+ render_docs(Content,[type|State],Pos,Ind,C);
+ Type ->
+ %% Emit the erl_pp typespec
+ {Type,0}
+ end
+ end,
+ trimnl(Doc);
+render_element({ul,[],Content},State,Pos,Ind,D) ->
+ render_docs(Content, [l|State], Pos, Ind,D);
+render_element({ol,[],Content},State,Pos,Ind,D) ->
+ %% For now ul and ol does the same thing
+ render_docs(Content, [l|State], Pos, Ind,D);
+render_element({li,[],Content},[l | _] = State, Pos, Ind,D) ->
+ Bullet = get_bullet(State, proplists:get_value(encoding, D#config.io_opts)),
+ BulletLen = string:length(Bullet),
+ {Docs, _NewPos} = render_docs(Content, [li | State], Pos + BulletLen,Ind + BulletLen, D),
+ trimnlnl([Bullet,Docs]);
+
+render_element({dl,_,Content},State,Pos,Ind,D) ->
+ render_docs(Content, [dl|State], Pos, Ind,D);
+render_element({dt,_,Content},[dl | _] = State,Pos,Ind,D) ->
+ Underline = sansi(underline),
+ {Docs, _NewPos} = render_docs(Content, [li | State], Pos, Ind, D),
+ {[Underline,Docs,ransi(underline),":","\n"], 0};
+render_element({dd,_,Content},[dl | _] = State,Pos,Ind,D) ->
+ trimnlnl(render_docs(Content, [li | State], Pos, Ind + 2, D));
+
+render_element(B, State, Pos, Ind,#config{ io_columns = Cols }) when is_binary(B) ->
+ case lists:member(pre,State) of
+ true ->
+ Pre = string:replace(B,"\n",["\n",pad(Ind)],all),
+ {Pre, Pos + lastline(Pre)};
+ _ ->
+ render_words(split_to_words(B),State,Pos,Ind,[[]],Cols)
+ end;
+
+render_element({Tag,Attr,Content}, State, Pos, Ind,D) ->
+ throw({unhandled,{Tag,Attr,Content,Pos,Ind}}),
+ render_docs(Content, State, Pos, Ind,D).
+
+render_words(Words,[_,types|State],Pos,Ind,Acc,Cols) ->
+ %% When we render words and are in the types->type state we indent
+ %% the extra lines two additional spaces to make it look nice
+ render_words(Words,State,Pos,Ind+2,Acc,Cols);
+render_words([Word|T],State,Pos,Ind,Acc,Cols) when is_binary(Word) ->
+ WordLength = string:length(Word),
+ NewPos = WordLength + Pos,
+ if
+ NewPos > (Cols - 10 - Ind) ->
+ %% Word does not fit, time to add a newline and also pad to Indent level
+ render_words(T,State,WordLength+Ind+1,Ind,[[[pad(Ind), Word]]|Acc],Cols);
+ true ->
+ %% Word does fit on line
+ [Line | LineAcc] = Acc,
+ %% Add + 1 to length for space
+ NewPosSpc = NewPos+1,
+ render_words(T,State,NewPosSpc,Ind,[[Word|Line]|LineAcc],Cols)
+ end;
+render_words([],_State,Pos,_Ind,Acc,_Cols) ->
+ Lines = lists:join(
+ $\n,lists:map(fun(RevLine) ->
+ Line = lists:reverse(RevLine),
+ lists:join($ ,Line)
+ end,lists:reverse(Acc))),
+ {iolist_to_binary(Lines), Pos}.
+
+render_type_signature(Name, #config{ docs = #docs_v1{ metadata = #{ types := AllTypes }}}) ->
+ case [Type || Type = {TName,_} <- maps:keys(AllTypes), TName =:= Name] of
+ [] ->
+ undefined;
+ Types ->
+ [erl_pp:attribute(maps:get(Type, AllTypes)) || Type <- Types]
+ end.
+
+%% Pad N spaces, disabling any ansi formatting while doing so
+pad(N) ->
+ Pad = lists:duplicate(N," "),
+ case ansi() of
+ undefined ->
+ Pad;
+ Ansi ->
+ ["\033[0m",Pad,Ansi]
+ end.
+
+get_bullet(_State,latin1) ->
+ <<" * ">>;
+get_bullet(State,unicode) ->
+ %% Fancy bullet point logic!
+ lists:nth(length([l || l <- State]),
+ [<<" • "/utf8>>,<<" ○ "/utf8>>,
+ <<" ◼ "/utf8>>,<<" ◻ "/utf8>>]).
+
+% Look for the length of the last line of a string
+lastline(Str) ->
+ LastStr = case string:find(Str,"\n",trailing) of
+ nomatch ->
+ Str;
+ Match ->
+ tl(string:next_codepoint(Match))
+ end,
+ string:length(LastStr).
+
+split_to_words(B) ->
+ binary:split(B,[<<" ">>],[global]).
+
+%% These functions make sure that we trim extra newlines added
+%% by the renderer. For example if we do <li><p></p></li>
+%% that would add 4 \n at after the last </li>. This is trimmed
+%% here to only be 2 \n
+trimnlnl({Chars, _Pos}) ->
+ nl(nl(string:trim(Chars, trailing, "\n")));
+trimnlnl(Chars) ->
+ nl(nl(string:trim(Chars, trailing, "\n"))).
+trimnl({Chars, _Pos}) ->
+ nl(string:trim(Chars, trailing, "\n")).
+nl({Chars, _Pos}) ->
+ nl(Chars);
+nl(Chars) ->
+ {[Chars,"\n"],0}.
+
+%% We keep the current ansi state in the pdict so that we know
+%% what to disable and enable when doing padding
+init_ansi(#config{ io_opts = Opts }) ->
+ %% We use this as our heuristic to see if we should print ansi or not
+ case {application:get_env(kernel, shell_docs_ansi),
+ proplists:is_defined(echo, Opts) andalso
+ proplists:is_defined(expand_fun, Opts),
+ os:type()} of
+ {{ok,false}, _, _} ->
+ put(ansi, noansi);
+ {{ok,true}, _, _} ->
+ put(ansi, []);
+ {_, _, {win32,_}} ->
+ put(ansi, noansi);
+ {_, true,_} ->
+ put(ansi, []);
+ {_, false,_} ->
+ put(ansi, noansi)
+ end.
+
+clean_ansi() ->
+ case get(ansi) of
+ [] -> erase(ansi);
+ noansi -> erase(ansi)
+ end,
+ ok.
+
+%% Set ansi
+sansi(Type) -> sansi(Type, get(ansi)).
+sansi(_Type, noansi) ->
+ [];
+sansi(Type, Curr) ->
+ put(ansi,[Type | Curr]),
+ ansi(get(ansi)).
+
+%% Clear ansi
+ransi(Type) -> ransi(Type, get(ansi)).
+ransi(_Type, noansi) ->
+ [];
+ransi(Type, Curr) ->
+ put(ansi,proplists:delete(Type,Curr)),
+ case ansi(get(ansi)) of
+ undefined ->
+ "\033[0m";
+ Ansi ->
+ Ansi
+ end.
+
+ansi() -> ansi(get(ansi)).
+ansi(noansi) -> undefined;
+ansi(Curr) ->
+ case lists:usort(Curr) of
+ [] ->
+ undefined;
+ [bold] ->
+ "\033[;1m";
+ [underline] ->
+ "\033[;;4m";
+ [bold,underline] ->
+ "\033[;1;4m"
+ end.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 6ade386159..9334a06ca2 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -92,6 +92,7 @@
sets,
shell,
shell_default,
+ shell_docs,
slave,
sofs,
string,
@@ -108,6 +109,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.6.2","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-@OTP-15251@","erts-@OTP-15251@","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 1ac7334830..acb9dd1970 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,6 +32,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, format_status/2]).
+%% logger callback
+-export([format_log/1, format_log/2]).
+
%% For release_handler only
-export([get_callback_module/1]).
@@ -44,14 +47,17 @@
{reason,Reason},
{offender,extract_child(Child)}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor:format_log/2,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
error_logger=>#{tag=>error_report,
- type=>supervisor_report}})).
+ type=>supervisor_report,
+ report_cb=>fun supervisor:format_log/1}})).
%%--------------------------------------------------------------------------
--export_type([sup_flags/0, child_spec/0, startchild_ret/0, strategy/0]).
+-export_type([sup_flags/0, child_spec/0, strategy/0,
+ startchild_ret/0, startchild_err/0,
+ startlink_ret/0, startlink_err/0]).
%%--------------------------------------------------------------------------
@@ -122,7 +128,7 @@
strategy :: strategy() | 'undefined',
children = {[],#{}} :: children(), % Ids in start order
dynamics :: {'maps', #{pid() => list()}}
- | {'sets', sets:set(pid())}
+ | {'mapsets', #{pid() => []}}
| 'undefined',
intensity :: non_neg_integer() | 'undefined',
period :: pos_integer() | 'undefined',
@@ -924,21 +930,21 @@ monitor_child(Pid) ->
terminate_dynamic_children(State) ->
Child = get_dynamic_child(State),
{Pids, EStack0} = monitor_dynamic_children(Child,State),
- Sz = sets:size(Pids),
+ Sz = maps:size(Pids),
EStack = case Child#child.shutdown of
brutal_kill ->
- sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, kill) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
infinity ->
- sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, shutdown) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
Time ->
- sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, shutdown) end, ok, Pids),
TRef = erlang:start_timer(Time, self(), kill),
wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
end,
%% Unroll stacked errors and report them
- dict:fold(fun(Reason, Ls, _) ->
+ maps:fold(fun(Reason, Ls, _) ->
?report_error(shutdown_error, Reason,
Child#child{pid=Ls}, State#state.name)
end, ok, EStack).
@@ -947,15 +953,15 @@ monitor_dynamic_children(Child,State) ->
dyn_fold(fun(P,{Pids, EStack}) when is_pid(P) ->
case monitor_child(P) of
ok ->
- {sets:add_element(P, Pids), EStack};
+ {maps:put(P, P, Pids), EStack};
{error, normal} when not (?is_permanent(Child)) ->
{Pids, EStack};
{error, Reason} ->
- {Pids, dict:append(Reason, P, EStack)}
+ {Pids, maps_prepend(Reason, P, EStack)}
end;
(?restarting(_), {Pids, EStack}) ->
{Pids, EStack}
- end, {sets:new(), dict:new()}, State).
+ end, {maps:new(), maps:new()}, State).
wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
EStack;
@@ -973,36 +979,44 @@ wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, killed} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
- TRef, dict:append(Reason, Pid, EStack))
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
+ TRef, maps_prepend(Reason, Pid, EStack))
end;
wait_dynamic_children(Child, Pids, Sz, TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, shutdown} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, {shutdown, _}} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, normal} when not (?is_permanent(Child)) ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
- TRef, dict:append(Reason, Pid, EStack));
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
+ TRef, maps_prepend(Reason, Pid, EStack));
{timeout, TRef, kill} ->
- sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, kill) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack)
end.
+maps_prepend(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Values} ->
+ maps:put(Key, [Value|Values], Map);
+ error ->
+ maps:put(Key, [Value], Map)
+ end.
+
%%-----------------------------------------------------------------
%% Access #state.children
%%-----------------------------------------------------------------
@@ -1420,9 +1434,159 @@ report_progress(Child, SupName) ->
report=>[{supervisor,SupName},
{started,extract_child(Child)}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor:format_log/2,
logger_formatter=>#{title=>"PROGRESS REPORT"},
- error_logger=>#{tag=>info_report,type=>progress}}).
+ error_logger=>#{tag=>info_report,
+ type=>progress,
+ report_cb=>fun supervisor:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(LogReport) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(LogReport, Depth), FormatOpts).
+
+limit_report(LogReport, unlimited) ->
+ LogReport;
+limit_report(#{label:={supervisor,progress},
+ report:=[{supervisor,_}=Supervisor,{started,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {started,limit_child_report(Child, Depth)}]};
+limit_report(#{label:={supervisor,_Error},
+ report:=[{supervisor,_}=Supervisor,{errorContext,Ctxt},
+ {reason,Reason},{offender,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {errorContext,io_lib:limit_term(Ctxt, Depth)},
+ {reason,io_lib:limit_term(Reason, Depth)},
+ {offender,limit_child_report(Child, Depth)}]}.
+
+limit_child_report(Report, Depth) ->
+ io_lib:limit_term(Report, Depth).
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},{started,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {ChildFormat,ChildArgs} = format_child_log_single(Child, "Started:"),
+ Format = "Supervisor: "++P++".",
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName];
+ _ ->
+ [SupName,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Supervisor: ",P,". Context: ",P,
+ ". Reason: ",P,"."]),
+ {ChildFormat,ChildArgs} = format_child_log_single(Child, "Offender:"),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},
+ {started,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " started: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Child];
+ _ ->
+ [SupName,Depth,Child,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " errorContext: ",P,"~n",
+ " reason: ",P,"~n",
+ " offender: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason,Child];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth,Child,Depth]
+ end,
+ {Format,Args}.
+
+format_child_log_single(Child, Tag) ->
+ {id,Id} = lists:keyfind(id, 1, Child),
+ case lists:keyfind(pid, 1, Child) of
+ false ->
+ {nb_children,NumCh} = lists:keyfind(nb_children, 1, Child),
+ {" ~s id=~w,nb_children=~w.", [Tag,Id,NumCh]};
+ T when is_tuple(T) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {" ~s id=~w,pid=~w.", [Tag,Id,Pid]}
+ end.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
format_status(terminate, [_PDict, State]) ->
State;
@@ -1431,36 +1595,41 @@ format_status(_, [_PDict, State]) ->
{supervisor, [{"Callback", State#state.module}]}].
%%%-----------------------------------------------------------------
-%%% Dynamics database access
-dyn_size(#state{dynamics = {Mod,Db}}) ->
- Mod:size(Db).
+%%% Dynamics database access.
+%%%
+%%% Store all dynamic children in a map with the pid as the key. If
+%%% the children are permanent, store the start arguments as the value,
+%%% otherwise store [] as the value.
+%%%
-dyn_erase(Pid,#state{dynamics={sets,Db}}=State) ->
- State#state{dynamics={sets,sets:del_element(Pid,Db)}};
-dyn_erase(Pid,#state{dynamics={maps,Db}}=State) ->
+dyn_size(#state{dynamics = {_Kind,Db}}) ->
+ map_size(Db).
+
+dyn_erase(Pid,#state{dynamics={_Kind,Db}}=State) ->
State#state{dynamics={maps,maps:remove(Pid,Db)}}.
-dyn_store(Pid,_,#state{dynamics={sets,Db}}=State) ->
- State#state{dynamics={sets,sets:add_element(Pid,Db)}};
-dyn_store(Pid,Args,#state{dynamics={maps,Db}}=State) ->
- State#state{dynamics={maps,Db#{Pid => Args}}}.
+dyn_store(Pid,Args,#state{dynamics={Kind,Db}}=State) ->
+ case Kind of
+ mapsets ->
+ %% Children are temporary. The start arguments
+ %% will not be needed again. Store [].
+ State#state{dynamics={mapsets,Db#{Pid => []}}};
+ maps ->
+ %% Children are permanent and may be restarted.
+ %% Store the start arguments.
+ State#state{dynamics={maps,Db#{Pid => Args}}}
+ end.
-dyn_fold(Fun,Init,#state{dynamics={sets,Db}}) ->
- sets:fold(Fun,Init,Db);
-dyn_fold(Fun,Init,#state{dynamics={maps,Db}}) ->
+dyn_fold(Fun,Init,#state{dynamics={_Kind,Db}}) ->
maps:fold(fun(Pid,_,Acc) -> Fun(Pid,Acc) end, Init, Db).
-dyn_map(Fun, #state{dynamics={sets,Db}}) ->
- lists:map(Fun, sets:to_list(Db));
-dyn_map(Fun, #state{dynamics={maps,Db}}) ->
+dyn_map(Fun, #state{dynamics={_Kind,Db}}) ->
lists:map(Fun, maps:keys(Db)).
-dyn_exists(Pid, #state{dynamics={sets, Db}}) ->
- sets:is_element(Pid, Db);
-dyn_exists(Pid, #state{dynamics={maps, Db}}) ->
- maps:is_key(Pid, Db).
+dyn_exists(Pid, #state{dynamics={_Kind, Db}}) ->
+ is_map_key(Pid, Db).
-dyn_args(_Pid, #state{dynamics={sets, _Db}}) ->
+dyn_args(_Pid, #state{dynamics={mapsets, _Db}}) ->
{ok,undefined};
dyn_args(Pid, #state{dynamics={maps, Db}}) ->
maps:find(Pid, Db).
@@ -1469,6 +1638,6 @@ dyn_init(State) ->
dyn_init(get_dynamic_child(State),State).
dyn_init(Child,State) when ?is_temporary(Child) ->
- State#state{dynamics={sets,sets:new()}};
+ State#state{dynamics={mapsets,maps:new()}};
dyn_init(_Child,State) ->
State#state{dynamics={maps,maps:new()}}.
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index 21ba6f53af..abbfb404a5 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-export([code_change/3]).
+%% logger callback
+-export([format_log/1, format_log/2]).
-callback init(Args :: term()) ->
{ok, Pid :: pid(), State :: term()} | ignore | {error, Error :: term()}.
@@ -136,9 +138,12 @@ report_progress(Pid, Mod, StartArgs, SupName) ->
{started, [{pid, Pid},
{mfa, {Mod, init, [StartArgs]}}]}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor_bridge:format_log/2,
logger_formatter=>#{title=>"PROGRESS REPORT"},
- error_logger=>#{tag=>info_report,type=>progress}}).
+ error_logger=>#{tag=>info_report,
+ type=>progress,
+ report_cb=>
+ fun supervisor_bridge:format_log/1}}).
report_error(Error, Reason, #state{name = Name, pid = Pid, mod = Mod}) ->
?LOG_ERROR(#{label=>{supervisor,error},
@@ -147,6 +152,167 @@ report_error(Error, Reason, #state{name = Name, pid = Pid, mod = Mod}) ->
{reason, Reason},
{offender, [{pid, Pid}, {mod, Mod}]}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor_bridge:format_log/2,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
- error_logger=>#{tag=>error_report,type=>supervisor_report}}).
+ error_logger=>#{tag=>error_report,
+ type=>supervisor_report,
+ report_cb=>
+ fun supervisor_bridge:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(LogReport) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(LogReport, Depth), FormatOpts).
+
+limit_report(LogReport, unlimited) ->
+ LogReport;
+limit_report(#{label:={supervisor,progress},
+ report:=[{supervisor,_}=Supervisor,{started,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {started,limit_child_report(Child, Depth)}]};
+limit_report(#{label:={supervisor,error},
+ report:=[{supervisor,_}=Supervisor,{errorContext,Ctxt},
+ {reason,Reason},{offender,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {errorContext,io_lib:limit_term(Ctxt, Depth)},
+ {reason,io_lib:limit_term(Reason, Depth)},
+ {offender,io_lib:limit_term(Child, Depth)}]}.
+
+limit_child_report(ChildReport, Depth) ->
+ {mfa,{M,F,[As]}} = lists:keyfind(mfa, 1, ChildReport),
+ NewMFAs = {M,F,[io_lib:limit_term(As, Depth)]},
+ lists:keyreplace(mfa, 1, ChildReport, {mfa,NewMFAs}).
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},{started,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {ChildFormat,ChildArgs} =
+ format_child_log_progress_single(Child, "Started:", FormatOpts),
+ Format = "Supervisor: "++P++".",
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName];
+ _ ->
+ [SupName,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Supervisor: ",P,". Context: ",P,
+ ". Reason: ",P,"."]),
+ {ChildFormat,ChildArgs} =
+ format_child_log_error_single(Child, "Offender:"),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},
+ {started,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " started: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Child];
+ _ ->
+ [SupName,Depth,Child,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " errorContext: ",P,"~n",
+ " reason: ",P,"~n",
+ " offender: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason,Child];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth,Child,Depth]
+ end,
+ {Format,Args}.
+
+format_child_log_progress_single(Child, Tag, FormatOpts) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {mfa,MFAs} = lists:keyfind(mfa, 1, Child),
+ Args =
+ case maps:get(depth, FormatOpts) of
+ unlimited ->
+ [MFAs];
+ Depth ->
+ [MFAs, Depth]
+ end,
+ {" ~s pid=~w,mfa="++p(FormatOpts)++".",[Tag,Pid]++Args}.
+
+format_child_log_error_single(Child, Tag) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {mod,Mod} = lists:keyfind(mod, 1, Child),
+ {" ~s pid=~w,mod=~w.",[Tag,Pid,Mod]}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 93bf4743d2..e803b749f7 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -31,7 +31,10 @@
install/2, install/3, remove/2, remove/3]).
-export([handle_system_msg/6, handle_system_msg/7, handle_debug/4,
print_log/1, get_log/1, get_debug/3, debug_options/1, suspend_loop_hib/6]).
--deprecated([{get_debug,3,eventually}]).
+
+-deprecated([{get_debug,3,
+ "incorrectly documented and only for internal use. Can often "
+ "be replaced with sys:get_log/1"}]).
%%-----------------------------------------------------------------
%% Types
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index a922bf3fbe..8f703d9d1a 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -53,6 +53,10 @@
%% for debugging, to turn off catch
-define(CATCH, catch).
+%% Debug.
+-define(SHOW_GP_BIT_11(B, F), ok).
+%%-define(SHOW_GP_BIT_11(B, F), io:format("F = ~.16#, B = ~lp\n", [F, B])).
+
%% option sets
-record(unzip_opts, {
output, % output object (fun)
@@ -138,6 +142,10 @@
-define(PKWARE_RESERVED, 11).
-define(BZIP2_COMPRESSED, 12).
+%% Version 2.0, attribute compatibility type 3 (Unix)
+-define(VERSION_MADE_BY, 20 bor (3 bsl 8)).
+-define(GP_BIT_11, 16#800). % Filename and file comment UTF-8 encoded.
+
%% zip-file records
-define(LOCAL_FILE_MAGIC,16#04034b50).
-define(LOCAL_FILE_HEADER_SZ,(4+2+2+2+2+2+4+4+4+2+2)).
@@ -160,6 +168,7 @@
-define(CENTRAL_DIR_DIGITAL_SIG_MAGIC, 16#05054b50).
-define(CENTRAL_DIR_DIGITAL_SIG_SZ, (4+2)).
+-define(CENTRAL_FILE_EXT_ATTRIBUTES, 8#644 bsl 16).
-define(CENTRAL_FILE_MAGIC, 16#02014b50).
-record(cd_file_header, {version_made_by,
@@ -191,12 +200,16 @@
zip_comment_length}).
--type create_option() :: memory | cooked | verbose | {comment, string()}
- | {cwd, file:filename()}
- | {compress, extension_spec()}
- | {uncompress, extension_spec()}.
+-type create_option() :: memory | cooked | verbose
+ | {comment, Comment ::string()}
+ | {cwd, CWD :: file:filename()}
+ | {compress, What :: extension_spec()}
+ | {uncompress, What :: extension_spec()}.
-type extension() :: string().
--type extension_spec() :: all | [extension()] | {add, [extension()]} | {del, [extension()]}.
+-type extension_spec() :: all
+ | [Extension :: extension()]
+ | {add, [Extension :: extension()]}
+ | {del, [Extension :: extension()]}.
-type filename() :: file:filename().
-type zip_comment() :: #zip_comment{}.
@@ -277,8 +290,11 @@ do_openzip_get(_, _) ->
throw(einval).
file_name_search(Name,Files) ->
- case lists:dropwhile(fun({ZipFile,_}) -> ZipFile#zip_file.name =/= Name end,
- Files) of
+ Fun = fun({ZipFile,_}) ->
+ not string:equal(ZipFile#zip_file.name, Name,
+ _IgnoreCase = false, _Norm = nfc)
+ end,
+ case lists:dropwhile(Fun, Files) of
[ZFile|_] -> ZFile;
[] -> false
end.
@@ -429,12 +445,7 @@ zip(F, Files) -> zip(F, Files, []).
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 :: file:filename(),
+ Option :: create_option(),
RetValue :: {ok, FileName :: file:name()}
| {ok, {FileName :: file:name(), binary()}}
| {error, Reason :: term()}).
@@ -622,9 +633,11 @@ get_zip_opt([Unknown | _Rest], _Opts) ->
%% feedback funs
silent(_) -> ok.
-verbose_unzip(FN) -> io:format("extracting: ~tp\n", [FN]).
+verbose_unzip(FN) ->
+ io:format("extracting: ~ts\n", [io_lib:write_string(FN)]).
-verbose_zip(FN) -> io:format("adding: ~tp\n", [FN]).
+verbose_zip(FN) ->
+ io:format("adding: ~ts\n", [io_lib:write_string(FN)]).
%% file filter funs
all(_) -> true.
@@ -655,7 +668,10 @@ get_zip_options(Files, Options) ->
compress = all,
uncompress = Suffixes
},
- get_zip_opt(Options, Opts).
+ Opts1 = #zip_opts{comment = Comment} = get_zip_opt(Options, Opts),
+ %% UTF-8 encode characters in the interval from 127 to 255.
+ {Comment1, _} = encode_string(Comment),
+ Opts1#zip_opts{comment = Comment1}.
get_unzip_options(F, Options) ->
Opts = #unzip_opts{file_filter = fun all/1,
@@ -850,16 +866,18 @@ put_z_files([F | Rest], Z, Out0, Pos0,
regular -> FileInfo#file_info.size;
directory -> 0
end,
- FileName = get_filename(F, Type),
+ FileName0 = get_filename(F, Type),
+ %% UTF-8 encode characters in the interval from 127 to 255.
+ {FileName, GPFlag} = encode_string(FileName0),
CompMethod = get_comp_method(FileName, UncompSize, Opts, Type),
- LH = local_file_header_from_info_method_name(FileInfo, UncompSize, CompMethod, FileName),
+ LH = local_file_header_from_info_method_name(FileInfo, UncompSize, CompMethod, FileName, GPFlag),
BLH = local_file_header_to_bin(LH),
B = [<<?LOCAL_FILE_MAGIC:32/little>>, BLH],
Out1 = Output({write, B}, Out0),
Out2 = Output({write, FileName}, Out1),
{Out3, CompSize, CRC} = put_z_file(CompMethod, UncompSize, Out2, F1,
0, Input, Output, OpO, Z, Type),
- FB(FileName),
+ FB(FileName0),
Patch = <<CRC:32/little, CompSize:32/little>>,
Out4 = Output({pwrite, Pos0 + ?LOCAL_FILE_HEADER_CRC32_OFFSET, Patch}, Out3),
Out5 = Output({seek, eof, 0}, Out4),
@@ -1012,7 +1030,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
uncomp_size = UncompSize,
file_name_length = FileNameLength,
extra_field_length = ExtraFieldLength} = LH,
- #cd_file_header{version_made_by = 20,
+ #cd_file_header{version_made_by = ?VERSION_MADE_BY,
version_needed = VersionNeeded,
gp_flag = GPFlag,
comp_method = CompMethod,
@@ -1026,7 +1044,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
file_comment_length = 0, % FileCommentLength,
disk_num_start = 0, % DiskNumStart,
internal_attr = 0, % InternalAttr,
- external_attr = 0, % ExternalAttr,
+ external_attr = ?CENTRAL_FILE_EXT_ATTRIBUTES, % ExternalAttr,
local_header_offset = Pos}.
cd_file_header_to_bin(
@@ -1103,10 +1121,10 @@ eocd_to_bin(#eocd{disk_num = DiskNum,
%% put together a local file header
local_file_header_from_info_method_name(#file_info{mtime = MTime},
UncompSize,
- CompMethod, Name) ->
+ CompMethod, Name, GPFlag) ->
{ModDate, ModTime} = dos_date_time_from_datetime(MTime),
#local_file_header{version_needed = 20,
- gp_flag = 0,
+ gp_flag = GPFlag,
comp_method = CompMethod,
last_mod_time = ModTime,
last_mod_date = ModDate,
@@ -1270,7 +1288,9 @@ get_central_dir(In0, RawIterator, Input) ->
In2 = Input({seek, bof, EOCD#eocd.offset}, In1),
N = EOCD#eocd.entries,
Acc0 = [],
- Out0 = RawIterator(EOCD, "", binary_to_list(BComment), <<>>, Acc0),
+ %% There is no encoding flag for the archive comment.
+ Comment = heuristic_to_string(BComment),
+ Out0 = RawIterator(EOCD, "", Comment, <<>>, Acc0),
get_cd_loop(N, In2, RawIterator, Input, Out0).
get_cd_loop(0, In, _RawIterator, _Input, Acc) ->
@@ -1286,20 +1306,32 @@ get_cd_loop(N, In0, RawIterator, Input, Acc0) ->
ExtraLen = CD#cd_file_header.extra_field_length,
CommentLen = CD#cd_file_header.file_comment_length,
ToRead = FileNameLen + ExtraLen + CommentLen,
+ GPFlag = CD#cd_file_header.gp_flag,
{B2, In2} = Input({read, ToRead}, In1),
{FileName, Comment, BExtra} =
- get_name_extra_comment(B2, FileNameLen, ExtraLen, CommentLen),
+ get_name_extra_comment(B2, FileNameLen, ExtraLen, CommentLen, GPFlag),
Acc1 = RawIterator(CD, FileName, Comment, BExtra, Acc0),
get_cd_loop(N-1, In2, RawIterator, Input, Acc1).
-get_name_extra_comment(B, FileNameLen, ExtraLen, CommentLen) ->
- case B of
- <<BFileName:FileNameLen/binary,
- BExtra:ExtraLen/binary,
- BComment:CommentLen/binary>> ->
- {binary_to_list(BFileName), binary_to_list(BComment), BExtra};
- _ ->
- throw(bad_central_directory)
+get_name_extra_comment(B, FileNameLen, ExtraLen, CommentLen, GPFlag) ->
+ try
+ <<BFileName:FileNameLen/binary,
+ BExtra:ExtraLen/binary,
+ BComment:CommentLen/binary>> = B,
+ {binary_to_chars(BFileName, GPFlag),
+ %% Appendix D says: "If general purpose bit 11 is unset, the
+ %% file name and comment should conform to the original ZIP
+ %% character encoding." However, it seems that at least Linux
+ %% zip(1) encodes the comment without setting bit 11 if the
+ %% filename is 7-bit ASCII. If bit 11 is set,
+ %% binary_to_chars/1 could (should?) be called (it can fail),
+ %% but the choice is to employ heuristics in this case too
+ %% (it does not fail).
+ heuristic_to_string(BComment),
+ BExtra}
+ catch
+ _:_ ->
+ throw(bad_central_directory)
end.
%% get end record, containing the offset to the central directory
@@ -1428,7 +1460,8 @@ get_z_file(In0, Z, Input, Output, OpO, FB,
LH#local_file_header.crc32}
end,
{BFileN, In3} = Input({read, FileNameLen + ExtraLen}, In1),
- {FileName, _} = get_file_name_extra(FileNameLen, ExtraLen, BFileN),
+ {FileName, _} =
+ get_file_name_extra(FileNameLen, ExtraLen, BFileN, GPFlag),
ReadAndWrite =
case check_valid_location(CWD, FileName) of
{true,FileName1} ->
@@ -1488,12 +1521,13 @@ check_dir_level([".." | Parts], Level) ->
check_dir_level([_Dir | Parts], Level) ->
check_dir_level(Parts, Level+1).
-get_file_name_extra(FileNameLen, ExtraLen, B) ->
- case B of
- <<BFileName:FileNameLen/binary, BExtra:ExtraLen/binary>> ->
- {binary_to_list(BFileName), BExtra};
- _ ->
- throw(bad_file_header)
+get_file_name_extra(FileNameLen, ExtraLen, B, GPFlag) ->
+ try
+ <<BFileName:FileNameLen/binary, BExtra:ExtraLen/binary>> = B,
+ {binary_to_chars(BFileName, GPFlag), BExtra}
+ catch
+ _:_ ->
+ throw(bad_file_header)
end.
%% get compressed or stored data
@@ -1597,6 +1631,38 @@ skip_bin(B, Pos) when is_binary(B) ->
_ -> <<>>
end.
+binary_to_chars(B, GPFlag) ->
+ ?SHOW_GP_BIT_11(B, GPFlag band ?GP_BIT_11),
+ case GPFlag band ?GP_BIT_11 of
+ 0 ->
+ binary_to_list(B);
+ ?GP_BIT_11 ->
+ case unicode:characters_to_list(B) of
+ List when is_list(List) ->
+ List
+ end
+ end.
+
+heuristic_to_string(B) when is_binary(B) ->
+ case unicode:characters_to_binary(B) of
+ B ->
+ unicode:characters_to_list(B);
+ _ ->
+ binary_to_list(B)
+ end.
+
+encode_string(String) ->
+ case lists:any(fun(C) -> C > 127 end, String) of
+ true ->
+ case unicode:characters_to_binary(String) of
+ B when is_binary(B) ->
+ {binary_to_list(B), ?GP_BIT_11};
+ _ ->
+ throw({bad_unicode, String})
+ end;
+ false ->
+ {String, 0}
+ end.
%% ZIP header manipulations
eocd_and_comment_from_bin(<<DiskNum:16/little,
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 7f7a0834ba..4b923c5fe2 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -34,11 +34,6 @@ MODULES= \
escript_SUITE \
ets_SUITE \
ets_tough_SUITE \
- expand_test \
- expand_test1 \
- unicode_expand \
- ExpandTestCaps \
- ExpandTestCaps1 \
filelib_SUITE \
file_sorter_SUITE \
filename_SUITE \
@@ -79,6 +74,7 @@ MODULES= \
supervisor_deadlock \
naughty_child \
shell_SUITE \
+ shell_docs_SUITE \
supervisor_SUITE \
supervisor_bridge_SUITE \
sys_SUITE \
diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl
index 8f999f7cad..a85fd86ab9 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -39,6 +39,9 @@ new(Mod, Eq) ->
end.
empty(Mod) ->
+ %% The dict module might not be loaded since it not used by
+ %% anything in the core parts of Erlang/OTP.
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, new, 0) of
false -> Mod:empty();
true -> Mod:new()
@@ -48,6 +51,7 @@ to_list(Mod, D) ->
Mod:to_list(D).
from_list(Mod, L) ->
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, from_orddict, 1) of
false ->
Mod:from_list(L);
@@ -63,6 +67,7 @@ from_list(Mod, L) ->
%% Store new value into dictionary or update previous value in dictionary.
enter(Mod, Key, Val, Dict) ->
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, store, 3) of
false ->
Mod:enter(Key, Val, Dict);
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 5c2b1965ba..dd6b25a531 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -27,6 +27,7 @@
-include_lib("common_test/include/ct.hrl").
init_per_testcase(_Case, Config) ->
+ cleanup(),
Config.
end_per_testcase(_Case, _Config) ->
@@ -44,10 +45,6 @@ groups() ->
[].
init_per_suite(Config) ->
- (catch code:delete(expand_test)),
- (catch code:delete(expand_test1)),
- (catch code:delete('ExpandTestCaps')),
- (catch code:delete('ExpandTestCaps1')),
Config.
end_per_suite(_Config) ->
@@ -59,9 +56,15 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+cleanup() ->
+ [try
+ code:purge(M),
+ code:delete(M)
+ catch _:_ -> ok end || M <- [expand_test, expand_test1,
+ 'ExpandTestCaps', 'ExpandTestCaps2']].
normal(Config) when is_list(Config) ->
- {module,expand_test} = c:l(expand_test),
+ {module,expand_test} = compile_and_load(Config,expand_test),
%% These tests might fail if another module with the prefix
%% "expand_" happens to also be loaded.
{yes, "test:", []} = do_expand("expand_"),
@@ -80,8 +83,8 @@ normal(Config) when is_list(Config) ->
%% Normal module name, some function names using quoted atoms.
quoted_fun(Config) when is_list(Config) ->
- {module,expand_test} = c:l(expand_test),
- {module,expand_test1} = c:l(expand_test1),
+ {module,expand_test} = compile_and_load(Config,expand_test),
+ {module,expand_test1} = compile_and_load(Config,expand_test1),
%% should be no colon after test this time
{yes, "test", []} = do_expand("expand_"),
{no, [], []} = do_expand("expandXX_"),
@@ -112,7 +115,7 @@ quoted_fun(Config) when is_list(Config) ->
ok.
quoted_module(Config) when is_list(Config) ->
- {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ {module,'ExpandTestCaps'} = compile_and_load(Config,'ExpandTestCaps'),
{yes, "Caps':", []} = do_expand("'ExpandTest"),
{no,[],
[{"a_fun_name",1},
@@ -125,8 +128,8 @@ quoted_module(Config) when is_list(Config) ->
ok.
quoted_both(Config) when is_list(Config) ->
- {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
- {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'),
+ {module,'ExpandTestCaps'} = compile_and_load(Config,'ExpandTestCaps'),
+ {module,'ExpandTestCaps1'} = compile_and_load(Config,'ExpandTestCaps1'),
%% should be no colon (or quote) after test this time
{yes, "Caps", []} = do_expand("'ExpandTest"),
{no,[],[{"'#weird-fun-name'",0},
@@ -229,7 +232,7 @@ check_trailing([I|Str], ArityStr, Suffix, Dots) ->
end.
unicode(Config) when is_list(Config) ->
- {module,unicode_expand} = c:l('unicode_expand'),
+ {module,unicode_expand} = compile_and_load(Config,'unicode_expand'),
{no,[],[{"'кlирилли́ческий атом'",0},
{"'кlирилли́ческий атом'",1},
{"'кlирилли́ческий атомB'",1},
@@ -253,3 +256,10 @@ do_expand(String) ->
do_format(StringList) ->
lists:flatten(edlin_expand:format_matches(StringList)).
+
+compile_and_load(Config,Module) ->
+ Filename = filename:join(
+ proplists:get_value(data_dir,Config),
+ atom_to_list(Module)),
+ {ok,Module,Bin} = compile:file(Filename, [binary]),
+ code:load_binary(Module, Filename, Bin).
diff --git a/lib/stdlib/test/ExpandTestCaps.erl b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl
index 7fb1107ae0..7fb1107ae0 100644
--- a/lib/stdlib/test/ExpandTestCaps.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl
diff --git a/lib/stdlib/test/ExpandTestCaps1.erl b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl
index 400a17b137..400a17b137 100644
--- a/lib/stdlib/test/ExpandTestCaps1.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl
diff --git a/lib/stdlib/test/expand_test.erl b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl
index 5544923a12..5544923a12 100644
--- a/lib/stdlib/test/expand_test.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl
diff --git a/lib/stdlib/test/expand_test1.erl b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl
index abefaefcfd..abefaefcfd 100644
--- a/lib/stdlib/test/expand_test1.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl
diff --git a/lib/stdlib/test/unicode_expand.erl b/lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl
index 41f741fa84..41f741fa84 100644
--- a/lib/stdlib/test/unicode_expand.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c7556f6f7e..88e09c3d21 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -49,7 +49,8 @@
eep37/1,
eep43/1,
otp_15035/1,
- otp_16439/1]).
+ otp_16439/1,
+ otp_14708/1]).
%%
%% Define to run outside of test server
@@ -89,7 +90,7 @@ all() ->
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
otp_8133, otp_10622, otp_13228, otp_14826,
funs, try_catch, eval_expr_5, zero_width,
- eep37, eep43, otp_15035, otp_16439].
+ eep37, eep43, otp_15035, otp_16439, otp_14708].
groups() ->
[].
@@ -1678,6 +1679,53 @@ otp_16439(Config) when is_list(Config) ->
ok.
+%% Test guard expressions in keys for maps and in sizes in binary matching.
+
+otp_14708(Config) when is_list(Config) ->
+ check(fun() -> X = 42, #{{tag,X} := V} = #{{tag,X} => a}, V end,
+ "begin X = 42, #{{tag,X} := V} = #{{tag,X} => a}, V end.",
+ a),
+ check(fun() ->
+ T = {x,y,z},
+ Map = #{x => 99, y => 100},
+ #{element(1, T) := V1, element(2, T) := V2} = Map,
+ {V1, V2}
+ end,
+ "begin
+ T = {x,y,z},
+ Map = #{x => 99, y => 100},
+ #{element(1, T) := V1, element(2, T) := V2} = Map,
+ {V1, V2}
+ end.",
+ {99, 100}),
+ error_check("#{term_to_binary(42) := _} = #{}.", illegal_guard_expr),
+
+ check(fun() ->
+ <<Sz:16,Body:(Sz-1)/binary>> = <<4:16,1,2,3>>,
+ Body
+ end,
+ "begin
+ <<Sz:16,Body:(Sz-1)/binary>> = <<4:16,1,2,3>>,
+ Body
+ end.",
+ <<1,2,3>>),
+ check(fun() ->
+ Sizes = #{0 => 3, 1 => 7},
+ <<SzTag:1,Body:(map_get(SzTag, Sizes))/binary>> =
+ <<1:1,1,2,3,4,5,6,7>>,
+ Body
+ end,
+ "begin
+ Sizes = #{0 => 3, 1 => 7},
+ <<SzTag:1,Body:(map_get(SzTag, Sizes))/binary>> =
+ <<1:1,1,2,3,4,5,6,7>>,
+ Body
+ end.",
+ <<1,2,3,4,5,6,7>>),
+ error_check("<<X:(process_info(self()))>> = <<>>.", illegal_bitsize),
+
+ ok.
+
%% Check the string in different contexts: as is; in fun; from compiled code.
check(F, String, Result) ->
check1(F, String, Result),
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 3885f6648d..257546d6a4 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -702,9 +702,13 @@ otp_7078(Config) when is_list(Config) ->
-record(otp_7101, {a,b,c=[],d=[],e=[]}).
+id(I) -> I.
+
%% OTP-7101. Record update: more than one call to setelement/3.
otp_7101(Config) when is_list(Config) ->
- Rec = #otp_7101{},
+ %% Ensure the compiler won't do any funny constant propagation tricks.
+ id(#otp_7101{a=a,b=b,c=c,d=d,e=e}),
+ Rec = id(#otp_7101{}),
%% Spawn a tracer process to count the number of setelement/3 calls.
%% The tracer will forward all trace messages to us.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 38d07249fd..c27b928a5a 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -69,7 +69,7 @@
stacktrace_syntax/1,
otp_14285/1, otp_14378/1,
external_funs/1,otp_15456/1,otp_15563/1,
- unused_type/1]).
+ unused_type/1,removed/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -91,7 +91,7 @@ all() ->
otp_11851, otp_11879, otp_13230,
record_errors, otp_11879_cont, non_latin1_module, otp_14323,
stacktrace_syntax, otp_14285, otp_14378, external_funs,
- otp_15456, otp_15563, unused_type].
+ otp_15456, otp_15563, unused_type, removed].
groups() ->
[{unused_vars_warn, [],
@@ -469,7 +469,7 @@ unused_vars_warn_lc(Config) when is_list(Config) ->
">>,
[warn_unused_vars],
{warnings,[{6,erl_lint,{unused_var,'C1'}},
- {7,sys_core_fold,no_clause_match},
+ {7,sys_core_fold,no_clause_match},
{9,erl_lint,{unused_var,'C3'}}]}},
{lc21,
@@ -1298,6 +1298,10 @@ unsized_binary_in_bin_gen_pattern(Config) when is_list(Config) ->
<< <<X,Tail/bits>> || <<X,Tail/bits>> <= Bin >>;
t({bc,bitstring,Bin}) ->
<< <<X,Tail/bits>> || <<X,Tail/bitstring>> <= Bin >>;
+ t({bc,binary_lit,Bin}) ->
+ << <<X>> || <<X,1/binary>> <= Bin >>;
+ t({bc,bytes_lit,Bin}) ->
+ << <<X>> || <<X,a/bytes>> <= Bin >>;
t({lc,binary,Bin}) ->
[ {X,Tail} || <<X,Tail/binary>> <= Bin ];
t({lc,bytes,Bin}) ->
@@ -1305,17 +1309,25 @@ unsized_binary_in_bin_gen_pattern(Config) when is_list(Config) ->
t({lc,bits,Bin}) ->
[ {X,Tail} || <<X,Tail/bits>> <= Bin ];
t({lc,bitstring,Bin}) ->
- [ {X,Tail} || <<X,Tail/bitstring>> <= Bin ].">>,
+ [ {X,Tail} || <<X,Tail/bitstring>> <= Bin ];
+ t({lc,bits_lit,Bin}) ->
+ [ X || <<X,42/bits>> <= Bin ];
+ t({lc,bitstring_lit,Bin}) ->
+ [ X || <<X,b/bitstring>> <= Bin ].">>,
[],
{errors,
[{2,erl_lint,unsized_binary_in_bin_gen_pattern},
{4,erl_lint,unsized_binary_in_bin_gen_pattern},
{6,erl_lint,unsized_binary_in_bin_gen_pattern},
{8,erl_lint,unsized_binary_in_bin_gen_pattern},
- {10,erl_lint,unsized_binary_in_bin_gen_pattern},
- {12,erl_lint,unsized_binary_in_bin_gen_pattern},
- {14,erl_lint,unsized_binary_in_bin_gen_pattern},
- {16,erl_lint,unsized_binary_in_bin_gen_pattern}],
+ {10,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {12,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {14,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {16,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {18,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {20,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {22,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {24,erl_lint,unsized_binary_in_bin_gen_pattern}],
[]}}],
[] = run(Config, Ts),
ok.
@@ -2061,8 +2073,9 @@ otp_5362(Config) when is_list(Config) ->
{error,
[{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}],
[{4,erl_lint,{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."}}]}},
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}},
{otp_5362_5,
<<"-compile(nowarn_deprecated_function).
-compile(nowarn_bif_clash).
@@ -2119,8 +2132,9 @@ otp_5362(Config) when is_list(Config) ->
{nowarn_bif_clash,{spawn,1}}]}, % has no effect
{warnings,
[{5,erl_lint,{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."}}]}},
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}},
{otp_5362_9,
<<"-include_lib(\"stdlib/include/qlc.hrl\").
@@ -2150,13 +2164,15 @@ otp_5362(Config) when is_list(Config) ->
[],
{warnings,
[{1,erl_lint,{deprecated,{calendar,local_time_to_universal_time,1},
- {calendar,local_time_to_universal_time_dst,1}, "a future release"}}]}},
+ "use calendar:local_time_to_universal_time_dst/1 "
+ "instead"}}]}},
{call_removed_function,
<<"t(X) -> erlang:hash(X, 10000).">>,
[],
{warnings,
- [{1,erl_lint,{removed,{erlang,hash,2},{erlang,phash2,2},"20.0"}}]}},
+ [{1,erl_lint,{removed,{erlang,hash,2},
+ "use erlang:phash2/2 instead"}}]}},
{nowarn_call_removed_function_1,
<<"t(X) -> erlang:hash(X, 10000).">>,
@@ -2173,7 +2189,7 @@ otp_5362(Config) when is_list(Config) ->
[],
{warnings,[{1,erl_lint,
{removed,{os_mon_mib,any_function_really,1},
- "was removed in 22.0"}}]}},
+ "this module was removed in OTP 22.0"}}]}},
{nowarn_call_removed_module,
<<"t(X) -> os_mon_mib:any_function_really(X).">>,
@@ -2212,7 +2228,6 @@ otp_15456(Config) when is_list(Config) ->
warn_deprecated_function]},
{warnings,[{5,erl_lint,
{deprecated,{random,seed,3},
- "the 'random' module is deprecated; "
"use the 'rand' module instead"}}]}},
%% {nowarn_unused_function,[{M,F,A}]} can be given
@@ -3645,6 +3660,7 @@ bin_syntax_errors(Config) ->
Ts = [{bin_syntax_errors,
<<"t(<<X:bad_size>>) -> X;
t(<<_:(x ! y)/integer>>) -> ok;
+ t(<<_:(l())/integer>>) -> ok;
t(<<X:all/integer>>) -> X;
t(<<X/bad_type>>) -> X;
t(<<X/unit:8>>) -> X;
@@ -3653,22 +3669,30 @@ bin_syntax_errors(Config) ->
t(<<(x ! y):8/integer>>) -> ok;
t(X) ->
{<<X/binary-integer>>,<<X/signed-unsigned-integer>>,
- <<X/little-big>>,<<X/unit:4-unit:8>>}.
+ <<X/little-big>>,<<X/unit:4-unit:8>>};
+ t(<<_:{A,B}>>) -> ok.
+
+ l() ->
+ foo.
">>,
[],
- {error,[{1,erl_lint,illegal_bitsize},
- {2,erl_lint,illegal_bitsize},
- {3,erl_lint,illegal_bitsize},
- {4,erl_lint,{undefined_bittype,bad_type}},
- {5,erl_lint,bittype_unit},
- {7,erl_lint,illegal_pattern},
+ {error,[{2,erl_lint,illegal_bitsize},
+ {3,erl_lint,{illegal_bitsize_local_call,{l,0}}},
+ {5,erl_lint,{undefined_bittype,bad_type}},
+ {6,erl_lint,bittype_unit},
{8,erl_lint,illegal_pattern},
- {10,erl_lint,{bittype_mismatch,integer,binary,"type"}},
- {10,erl_lint,{bittype_mismatch,unsigned,signed,"sign"}},
- {11,erl_lint,{bittype_mismatch,8,4,"unit"}},
- {11,erl_lint,{bittype_mismatch,big,little,"endianness"}}
+ {9,erl_lint,illegal_pattern},
+ {11,erl_lint,{bittype_mismatch,integer,binary,"type"}},
+ {11,erl_lint,{bittype_mismatch,unsigned,signed,"sign"}},
+ {12,erl_lint,{bittype_mismatch,8,4,"unit"}},
+ {12,erl_lint,{bittype_mismatch,big,little,"endianness"}},
+ {13,erl_lint,{unbound_var,'A'}},
+ {13,erl_lint,{unbound_var,'B'}}
],
- [{6,erl_lint,{bad_bitsize,"float"}}]}}
+ [{1,erl_lint,non_integer_bitsize},
+ {4,erl_lint,non_integer_bitsize},
+ {7,erl_lint,{bad_bitsize,"float"}},
+ {13,erl_lint,non_integer_bitsize}]}}
],
[] = run(Config, Ts),
ok.
@@ -3779,7 +3803,7 @@ maps(Config) ->
">>,
[],
{errors,[{4,erl_lint,illegal_map_construction},
- {6,erl_lint,illegal_map_key}],[]}},
+ {6,erl_lint,{unbound_var,'V'}}],[]}},
{unused_vars_with_empty_maps,
<<"t(Foo, Bar, Baz) -> {#{},#{}}.">>,
[warn_unused_variables],
@@ -4128,9 +4152,9 @@ otp_14378(Config) ->
[],
{warnings,[{4,erl_lint,
{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction"
- " in Erlang\" chapter of the ERTS User's Guide"
- " for more information."}}]}}],
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}}],
[] = run(Config, Ts),
ok.
@@ -4291,6 +4315,30 @@ otp_15563(Config) when is_list(Config) ->
[] = run(Config, Ts),
ok.
+removed(Config) when is_list(Config) ->
+ Ts = [{removed,
+ <<"-removed([{nonexistent,1,\"hi\"}]). %% okay since it doesn't exist
+ -removed([frutt/0]). %% okay since frutt/0 is not exported
+ -removed([t/0]). %% not okay since t/0 is exported
+ -removed([{t,'_'}]). %% not okay since t/0 is exported
+ -removed([{'_','_'}]). %% not okay since t/0 is exported
+ -removed([{{badly,formed},1}]).
+ -removed('badly formed').
+ -export([t/0]).
+ frutt() -> ok.
+ t() -> ok.
+ ">>,
+ {[]},
+ {error,[{3,erl_lint,{bad_removed,{t,0}}},
+ {4,erl_lint,{bad_removed,{t,'_'}}},
+ {5,erl_lint,{bad_removed,{'_','_'}}},
+ {6,erl_lint,{invalid_removed,{{badly,formed},1}}},
+ {7,erl_lint,{invalid_removed,'badly formed'}}],
+ [{9,erl_lint,{unused_function,{frutt,0}}}]}}
+ ],
+ [] = run(Config, Ts),
+ ok.
+
format_error(E) ->
lists:flatten(erl_lint:format_error(E)).
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 48152243f8..61eccdd1b5 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -47,6 +47,7 @@
hook/1,
neg_indent/1,
maps_syntax/1,
+ format_options/1,
quoted_atom_types/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
@@ -76,7 +77,8 @@ groups() ->
[{expr, [],
[func, call, recs, try_catch, if_then, receive_after,
bits, head_tail, cond1, block, case1, ops,
- messages, maps_syntax, quoted_atom_types
+ messages, maps_syntax, quoted_atom_types,
+ format_options
]},
{attributes, [], [misc_attrs, import_export, dialyzer_attrs]},
{tickets, [],
@@ -545,6 +547,36 @@ import_export(Config) when is_list(Config) ->
compile(Config, Ts),
ok.
+format_options(Config) when is_list(Config) ->
+ "case 1 of\n"
+ " 2 ->\n"
+ " 3;\n"
+ " 4 ->\n"
+ " 5\n"
+ "end" = flat_parse_and_pp_expr("case 1 of 2 -> 3; 4 -> 5 end", 0, [{indent, 2}]),
+
+ "-spec foo(bar(),\n"
+ " qux()) ->\n"
+ " T |\n"
+ " baz(T)\n"
+ " when\n"
+ " T ::\n"
+ " tuple().\n" =
+ lists:flatten(
+ parse_and_pp_forms(
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().",
+ [{indent, 2}, {linewidth, 20}]
+ )
+ ),
+
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().\n" =
+ lists:flatten(
+ parse_and_pp_forms(
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().",
+ [{indent, 2}, {linewidth, 1000}]
+ )
+ ).
+
misc_attrs(Config) when is_list(Config) ->
ok = pp_forms(<<"-module(m). ">>),
ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk,"
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index aca5b1e54f..162e2a0a3d 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-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,7 +23,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_11807/1]).
+ otp_10990/1, otp_10992/1, otp_11807/1, otp_16480/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -58,7 +58,7 @@ suite() ->
all() ->
[{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992,
- otp_11807].
+ otp_11807, otp_16480].
groups() ->
[{error, [], [error_1, error_2]}].
@@ -300,6 +300,32 @@ integers() ->
Ts = [{integer,{1,1},I}],
test_string(S, Ts)
end || S <- [[N] || N <- lists:seq($0, $9)] ++ ["2323","000"] ],
+ UnderscoreSamples =
+ [{"123_456", 123456},
+ {"123_456_789", 123456789},
+ {"1_2", 12}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{integer, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["123_",
+ "123__",
+ "123_456_",
+ "123__456",
+ "_123",
+ "__123"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{integer, _, _}], _} ->
+ error({unexpected_integer, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("_123", [{var,{1,1},'_123'}]),
+ test_string("123_", [{integer,{1,1},123},{var,{1,4},'_'}]),
ok.
base_integers() ->
@@ -315,13 +341,19 @@ base_integers() ->
{error,{{1,1},erl_scan,{base,1}},{1,2}} =
erl_scan:string("1#000", {1,1}, []),
+ {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"),
+ {error,{{1,1},erl_scan,{base,1000}},{1,6}} =
+ erl_scan:string("1_000#000", {1,1}, []),
+
test_string("12#bc", [{integer,{1,1},11},{atom,{1,5},c}]),
[begin
Str = BS ++ "#" ++ S,
- {error,{1,erl_scan,{illegal,integer}},1} =
- erl_scan:string(Str)
- end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ],
+ E = 2 + length(BS),
+ {error,{{1,1},erl_scan,{illegal,integer}},{1,E}} =
+ erl_scan:string(Str, {1,1}, [])
+ end || {BS,S} <- [{"3","3"},{"15","f"},{"12","c"},
+ {"1_5","f"},{"1_2","c"}] ],
{ok,[{integer,1,239},{'@',1}],1} = erl_scan_string("16#ef@"),
{ok,[{integer,{1,1},239},{'@',{1,6}}],{1,7}} =
@@ -329,6 +361,36 @@ base_integers() ->
{ok,[{integer,{1,1},14},{atom,{1,5},g@}],{1,7}} =
erl_scan_string("16#eg@", {1,1}, []),
+ UnderscoreSamples =
+ [{"16#1234_ABCD_EF56", 16#1234abcdef56},
+ {"2#0011_0101_0011", 2#001101010011},
+ {"1_6#123ABC", 16#123abc},
+ {"1_6#123_ABC", 16#123abc},
+ {"16#abcdef", 16#ABCDEF}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{integer, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["16_#123ABC",
+ "16#123_",
+ "16#_123",
+ "16#ABC_",
+ "16#_ABC",
+ "2#_0101",
+ "1__6#ABC",
+ "16#AB__CD"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{integer, _, _}], _} ->
+ error({unexpected_integer, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("16#123_", [{integer,{1,1},291},{var,{1,7},'_'}]),
+ test_string("_16#ABC", [{var,{1,1},'_16'},{'#',{1,4}},{var,{1,5},'ABC'}]),
ok.
floats() ->
@@ -344,12 +406,44 @@ floats() ->
erl_scan:string("1.0e400"),
{error,{{1,1},erl_scan,{illegal,float}},{1,8}} =
erl_scan:string("1.0e400", {1,1}, []),
+ {error,{{1,1},erl_scan,{illegal,float}},{1,9}} =
+ erl_scan:string("1.0e4_00", {1,1}, []),
[begin
{error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S),
{error,{{1,1},erl_scan,{illegal,float}},{1,_}} =
erl_scan:string(S, {1,1}, [])
end || S <- ["1.14Ea"]],
+ UnderscoreSamples =
+ [{"123_456.789", 123456.789},
+ {"123.456_789", 123.456789},
+ {"1.2_345e10", 1.2345e10},
+ {"1.234e1_06", 1.234e106},
+ {"12_34.56_78e1_6", 1234.5678e16},
+ {"12_34.56_78e-1_8", 1234.5678e-18}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{float, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["123_.456",
+ "123._456",
+ "123.456_",
+ "123._",
+ "1._23e10",
+ "1.23e_10",
+ "1.23e10_"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{float, _, _}], _} ->
+ error({unexpected_float, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("123._", [{integer,{1,1},123},{'.',{1,4}},{var,{1,5},'_'}]),
+ test_string("1.23_e10", [{float,{1,1},1.23},{var,{1,5},'_e10'}]),
ok.
dots() ->
@@ -1103,6 +1197,11 @@ otp_11807(Config) when is_list(Config) ->
(catch erl_parse:abstract("string", [{encoding,bad}])),
ok.
+otp_16480(Config) when is_list(Config) ->
+ F = fun mod:func/19,
+ F = erl_parse:normalise(erl_parse_abstract(F)),
+ ok.
+
test_string(String, ExpectedWithCol) ->
{ok, ExpectedWithCol, _EndWithCol} = erl_scan_string(String, {1, 1}, []),
Expected = [ begin
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 7f349c1bb5..9912da0c6b 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -40,14 +40,19 @@
-export([lookup_element_mult/1]).
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
- select_bound_chunk/1,
- t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
+ select_bound_chunk/1, t_delete_all_objects/1, t_test_ms/1,
t_select_delete/1,t_select_replace/1,t_select_replace_next_bug/1,t_ets_dets/1]).
+-export([t_insert_list/1, t_insert_list_bag/1, t_insert_list_duplicate_bag/1,
+ t_insert_list_set/1, t_insert_list_delete_set/1,
+ t_insert_list_parallel/1, t_insert_list_delete_parallel/1,
+ t_insert_list_kill_process/1]).
-export([test_table_size_concurrency/1,test_table_memory_concurrency/1,
- test_delete_table_while_size_snapshot/1, test_delete_table_while_size_snapshot_helper/0]).
+ test_delete_table_while_size_snapshot/1, test_delete_table_while_size_snapshot_helper/1,
+ test_decentralized_counters_setting/1]).
-export([ordered/1, ordered_match/1, interface_equality/1,
- fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
+ fixtable_next/1, fixtable_iter_bag/1,
+ fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]).
-export([update_counter_with_default/1]).
-export([update_counter_with_default_bad_pos/1]).
@@ -128,8 +133,8 @@ all() ->
{group, delete}, firstnext, firstnext_concurrent, slot,
{group, match}, t_match_spec_run,
{group, lookup_element}, {group, misc}, {group, files},
- {group, heavy}, ordered, ordered_match,
- interface_equality, fixtable_next, fixtable_insert,
+ {group, heavy}, {group, insert_list}, ordered, ordered_match,
+ interface_equality, fixtable_next, fixtable_iter_bag, fixtable_insert,
rename, rename_unnamed, evil_rename, update_element,
update_counter, evil_update_counter,
update_counter_with_default,
@@ -139,7 +144,7 @@ all() ->
match_heavy, {group, fold}, member, t_delete_object,
select_bound_chunk,
t_init_table, t_whitebox, t_delete_all_objects,
- t_insert_list, t_test_ms, t_select_delete, t_select_replace,
+ t_test_ms, t_select_delete, t_select_replace,
t_select_replace_next_bug,
t_ets_dets, memory, t_select_reverse, t_bucket_disappears,
t_named_select, select_fixtab_owner_change,
@@ -162,11 +167,12 @@ all() ->
take,
whereis_table,
delete_unfix_race,
- test_throughput_benchmark,
- {group, benchmark},
+ %test_throughput_benchmark,
+ %{group, benchmark},
test_table_size_concurrency,
test_table_memory_concurrency,
- test_delete_table_while_size_snapshot].
+ test_delete_table_while_size_snapshot,
+ test_decentralized_counters_setting].
groups() ->
@@ -197,7 +203,12 @@ groups() ->
meta_lookup_named_read, meta_lookup_named_write,
meta_newdel_unnamed, meta_newdel_named]},
{benchmark, [],
- [long_throughput_benchmark]}].
+ [long_throughput_benchmark]},
+ {insert_list, [],
+ [t_insert_list, t_insert_list_set, t_insert_list_bag,
+ t_insert_list_duplicate_bag, t_insert_list_delete_set,
+ t_insert_list_parallel, t_insert_list_delete_parallel,
+ t_insert_list_kill_process]}].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -1167,7 +1178,7 @@ t_insert_new(Config) when is_list(Config) ->
L),
verify_etsmem(EtsMem).
-%% Test ets:insert/2 with list of objects.
+%% Test ets:insert/2 with list of objects into duplicate bag table.
t_insert_list(Config) when is_list(Config) ->
EtsMem = etsmem(),
repeat_for_opts(fun t_insert_list_do/1),
@@ -1179,6 +1190,245 @@ t_insert_list_do(Opts) ->
del_one_by_one_dbag_2(T,4000,0),
ets:delete(T).
+% Insert a long list twice in a bag
+t_insert_list_bag(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ T = ets:new(t, [bag]),
+ ListSize = 25000,
+ List = [ {N} || N <- lists:seq(1, ListSize)],
+ ets:insert(T, List),
+ ets:insert(T, List),
+ ListSize = ets:info(T, size),
+ ets:delete(T),
+ verify_etsmem(EtsMem).
+
+% Insert a long list twice in a duplicate_bag
+t_insert_list_duplicate_bag(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ T = ets:new(t, [duplicate_bag]),
+ ListSize = 25000,
+ List = [ {N} || N <- lists:seq(1, ListSize)],
+ ets:insert(T, List),
+ ets:insert(T, List),
+ DoubleListSize = ListSize * 2,
+ DoubleListSize = ets:info(T, size),
+ ets:delete(T),
+ verify_etsmem(EtsMem).
+
+%% Test ets:insert/2 with list of objects into set tables.
+t_insert_list_set(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_set_do/1, [set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_set_do(Opts) ->
+ Nr = 2,
+ t_insert_list_set_do(Opts, fun ets_insert_with_check/2, Nr, 1, Nr+1),
+ t_insert_list_set_do(Opts, fun ets_insert_with_check/2, Nr*2, 2, Nr*2),
+ InsertNewWithCheck =
+ fun(T,E) ->
+ Res = ets:insert_new(T,E),
+ Seq = element(1, lists:nth(1, E)),
+ case Seq rem 2 =:= 0 of
+ true -> Res = false;
+ false -> Res = true
+ end
+ end,
+ t_insert_list_set_do(Opts, InsertNewWithCheck, Nr, 1, Nr),
+ t_insert_list_set_do(Opts, fun ets:insert_new/2, Nr*2, 2, Nr*2),
+ ok.
+
+t_insert_list_set_do(Opts, InsertFun, Nr, Step, ExpectedSize) ->
+ T = ets_new(x,Opts),
+ [InsertFun(T,[{X,X}, {X+1,X}]) || X <- lists:seq(1,Nr,Step)],
+ ExpectedSize = ets:info(T,size),
+ ets:delete(T).
+
+%% Test ets:insert/2 with list of objects into set tables in parallel.
+t_insert_list_parallel(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_parallel_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+ets_insert_with_check(Table, ToInsert) ->
+ true = ets:insert(Table, ToInsert),
+ true.
+
+ets_insert_new_with_check(Table, ToInsert) ->
+ ExpectedRes =
+ case put(is_first_insert_for_list, true) of
+ undefined -> true;
+ true -> false
+ end,
+ ExpectedRes = ets:insert_new(Table, ToInsert),
+ ExpectedRes.
+
+t_insert_list_parallel_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_parallel_do(Opts, I, 2, 100, 5000),
+ t_insert_list_parallel_do(Opts, I, 10, 100, 500),
+ t_insert_list_parallel_do(Opts, I, 1000, 100, 50),
+ t_insert_list_parallel_do(Opts, I, 50000, 3, 1)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]].
+
+t_insert_list_parallel_do(Opts, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ T = ets_new(x,Opts),
+ t_insert_list_parallel_do_helper(self(), T, 0, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess),
+ receive done -> ok end,
+ ExpectedSize = ListLength * NrOfProcesses,
+ ExpectedSize = length(ets:match_object(T, {'$0', '$1'})),
+ ExpectedSize = ets:info(T, size),
+ ets:delete(T),
+ ok.
+
+t_insert_list_delete_parallel(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_delete_parallel_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_delete_parallel_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_delete_parallel_do(Opts, I, 30, 32, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 300, 8, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 3000, 4, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 9000, 4, 1000000)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]],
+ ok.
+
+t_insert_list_delete_parallel_do(Opts, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ T = ets_new(x,Opts),
+ CompletedInsertsCtr = counters:new(1,[]),
+ NewInsertFun =
+ fun(Table, ToInsert) ->
+ try
+ InsertFun(Table, ToInsert),
+ counters:add(CompletedInsertsCtr, 1, 1)
+ catch
+ error:badarg -> put(stop,yes)
+ end
+ end,
+ Self = self(),
+ spawn(fun()->
+ t_insert_list_parallel_do_helper(self(), T, 0, NewInsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess),
+ receive done -> Self ! done_parallel_insert end
+ end),
+ receive after 3 -> ok end,
+ spawn(fun()->
+ spawn(fun()->
+ receive after 7 -> ok end,
+ ets:delete(T),
+ Self ! done_delete
+ end)
+ end),
+ receive done_delete -> ok end,
+ receive done_parallel_insert -> ok end,
+ io:format("~p/~p completed",
+ [counters:get(CompletedInsertsCtr, 1),
+ NrOfProcesses * NrOfInsertsPerProcess]).
+
+
+t_insert_list_parallel_do_helper(Parent, T, StartKey, InsertFun, ListLength, 1, NrOfInsertsPerProcess) ->
+ try
+ repeat(fun()->
+ case get(stop) of
+ yes -> throw(end_repeat);
+ _ -> ok
+ end,
+ InsertFun(T,[{X,X} || X <- lists:seq(StartKey,StartKey+ListLength-1,1)])
+ end, NrOfInsertsPerProcess)
+ catch
+ throw:end_repeat -> ok
+ end,
+ Parent ! done;
+t_insert_list_parallel_do_helper(Parent, T, StartKey, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ Self = self(),
+ spawn(fun() ->
+ t_insert_list_parallel_do_helper(Self,
+ T,
+ StartKey,
+ InsertFun,
+ ListLength,
+ NrOfProcesses div 2,
+ NrOfInsertsPerProcess) end),
+ spawn(fun() ->
+ t_insert_list_parallel_do_helper(Self,
+ T,
+ StartKey + ListLength*(NrOfProcesses div 2),
+ InsertFun,
+ ListLength,
+ (NrOfProcesses div 2) + (NrOfProcesses rem 2),
+ NrOfInsertsPerProcess)
+ end),
+ receive done -> ok end,
+ receive done -> ok end,
+ Parent ! done.
+
+t_insert_list_delete_set(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_delete_set_do/1, [[public],set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_delete_set_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_delete_set_do(Opts, I, 1000000, 1, 1),
+ t_insert_list_delete_set_do(Opts, I, 100000, 10, 5),
+ t_insert_list_delete_set_do(Opts, I, 10000, 100, 50),
+ t_insert_list_delete_set_do(Opts, I, 1000, 1000, 500)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]],
+ ok.
+
+
+t_insert_list_delete_set_do(Opts, InsertFun, ListLength, NrOfTables, NrOfInserts) ->
+ CompletedInsertsCtr = counters:new(1,[]),
+ Parent = self(),
+ [(fun() ->
+ T = ets_new(x,Opts),
+ spawn(
+ fun() ->
+ try
+ repeat(
+ fun() ->
+ InsertFun(T,[{Z,Z} ||
+ Z <- lists:seq(1,ListLength)]),
+ counters:add(CompletedInsertsCtr, 1, 1)%,
+ end, NrOfInserts)
+ catch
+ error:badarg -> ok
+ end,
+ Parent ! done
+ end),
+ receive after 1 -> ok end,
+ ets:delete(T)
+ end)() || _ <- lists:seq(1,NrOfTables)],
+ [receive done -> ok end || _ <- lists:seq(1,NrOfTables)],
+ io:format("~p/~p completed",
+ [counters:get(CompletedInsertsCtr, 1),
+ NrOfTables * NrOfInserts]).
+
+
+t_insert_list_kill_process(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_kill_process_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+
+t_insert_list_kill_process_do(Opts) ->
+ [(fun(I) ->
+ [(fun(Time) ->
+ T = ets_new(x,Opts),
+ List = lists:seq(1,600000),
+ TupleList = [{E,E} || E <- List],
+ Pid = spawn(fun() -> I(T, TupleList) end),
+ receive after Time -> ok end,
+ exit(Pid, kill),
+ ets:delete(T)
+ end)(TheTime) || TheTime <- [1,3,5] ++ lists:seq(7,29,7)]
+ end)(InsertFun) || InsertFun <- [fun ets:insert/2,
+ fun ets:insert_new/2]],
+ ok.
%% Test interface of ets:test_ms/2.
t_test_ms(Config) when is_list(Config) ->
@@ -2494,6 +2744,135 @@ do_fixtable_next(Tab) ->
false = ets:info(Tab, fixed),
ets:delete(Tab).
+%% Check that iteration of bags find all live objects and nothing else.
+fixtable_iter_bag(Config) when is_list(Config) ->
+ repeat_for_opts(fun fixtable_iter_do/1,
+ [write_concurrency,[bag,duplicate_bag]]).
+
+fixtable_iter_do(Opts) ->
+ EtsMem = etsmem(),
+ do_fixtable_iter_bag(ets_new(fixtable_iter_bag,Opts)),
+ verify_etsmem(EtsMem).
+
+do_fixtable_iter_bag(T) ->
+ MaxValues = 4,
+ %% Create 1 to MaxValues objects for each key
+ %% and then delete every possible combination of those objects
+ %% in every possible order.
+ %% Then test iteration returns all live objects and nothing else.
+
+ CrDelOps = [begin
+ Values = lists:seq(1,N),
+ %% All ways of deleting any number of the Values in any order
+ Combos = combs(Values),
+ DeleteOps = concat_lists([perms(C) || C <- Combos]),
+ {N, DeleteOps}
+ end
+ || N <- lists:seq(1,MaxValues)],
+
+ %%io:format("~p\n", [CrDelOps]),
+
+ NKeys = lists:foldl(fun({_, DeleteOps}, Cnt) ->
+ Cnt + length(DeleteOps)
+ end,
+ 0,
+ CrDelOps),
+
+ io:format("Create ~p keys\n", [NKeys]),
+
+ %% Fixate even before inserts just to maintain small table size
+ %% and increase likelyhood of different keys in same bucket.
+ ets:safe_fixtable(T,true),
+ InsRes = [begin
+ [begin
+ Key = {NValues,ValueList},
+ [begin
+ Tpl = {Key, V},
+ %%io:format("Insert object ~p", [Tpl]),
+ ets:insert(T, Tpl),
+ Tpl
+ end
+ || V <- lists:seq(1,NValues)]
+ end
+ || ValueList <- DeleteOps]
+ end
+ || {NValues, DeleteOps} <- CrDelOps],
+
+ Inserted = lists:flatten(InsRes),
+ InSorted = lists:sort(Inserted),
+ InSorted = lists:usort(Inserted), %% No duplicates
+ NObjs = length(Inserted),
+
+ DelRes = [begin
+ [begin
+ Key = {NValues,ValueList},
+ [begin
+ Tpl = {Key, V},
+ %%io:format("Delete object ~p", [Tpl]),
+ ets:delete_object(T, Tpl),
+ Tpl
+ end
+ || V <- ValueList]
+ end
+ || ValueList <- DeleteOps]
+ end
+ || {NValues, DeleteOps} <- CrDelOps],
+
+ Deleted = lists:flatten(DelRes),
+ DelSorted = lists:sort(Deleted),
+ DelSorted = lists:usort(Deleted), %% No duplicates
+ NDels = length(Deleted),
+
+ %% Nr of keys where all values were deleted.
+ NDeletedKeys = lists:sum([factorial(N) || N <- lists:seq(1,MaxValues)]),
+
+ CountKeysFun = fun Me(K1, Cnt) ->
+ case ets:next(T, K1) of
+ '$end_of_table' ->
+ Cnt;
+ K2 ->
+ Objs = ets:lookup(T, K2),
+ [{{NValues, ValueList}, _V} | _] = Objs,
+ ExpectedLive = NValues - length(ValueList),
+ ExpectedLive = length(Objs),
+ Me(K2, Cnt+1)
+ end
+ end,
+
+ ExpectedKeys = NKeys - NDeletedKeys,
+ io:format("Expected keys: ~p\n", [ExpectedKeys]),
+ FoundKeys = CountKeysFun(ets:first(T), 1),
+ io:format("Found keys: ~p\n", [FoundKeys]),
+ ExpectedKeys = FoundKeys,
+
+ ExpectedObjs = NObjs - NDels,
+ io:format("Expected objects: ~p\n", [ExpectedObjs]),
+ FoundObjs = ets:select_count(T, [{{'_','_'}, [], [true]}]),
+ io:format("Found objects: ~p\n", [FoundObjs]),
+ ExpectedObjs = FoundObjs,
+
+ ets:delete(T).
+
+%% All permutations of list
+perms([]) -> [[]];
+perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
+
+%% All combinations of picking the element (or not) from list
+combs([]) -> [[]];
+combs([H|T]) ->
+ Tcombs = combs(T),
+ Tcombs ++ [[H | C] || C <- Tcombs].
+
+factorial(0) -> 1;
+factorial(N) when N > 0 ->
+ N * factorial(N - 1).
+
+concat_lists([]) ->
+ [];
+concat_lists([H|T]) ->
+ H ++ concat_lists(T).
+
+
%% Check inserts of deleted keys in fixed bags.
fixtable_insert(Config) when is_list(Config) ->
Combos = [[Type,{write_concurrency,WC}] || Type<- [bag,duplicate_bag],
@@ -2558,15 +2937,17 @@ write_concurrency(Config) when is_list(Config) ->
Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]),
- Yes7 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
- Yes8 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]),
- Yes9 = ets_new(foo,[ordered_set,{write_concurrency,true}]),
- Yes10 = ets_new(foo,[{write_concurrency,true},ordered_set,public]),
- Yes11 = ets_new(foo,[{write_concurrency,true},ordered_set,protected]),
+ NoCentCtrs = {decentralized_counters,false},
+ Yes7 = ets_new(foo,[ordered_set,public,{write_concurrency,true},NoCentCtrs]),
+ Yes8 = ets_new(foo,[ordered_set,protected,{write_concurrency,true},NoCentCtrs]),
+ Yes9 = ets_new(foo,[ordered_set,{write_concurrency,true},NoCentCtrs]),
+ Yes10 = ets_new(foo,[{write_concurrency,true},ordered_set,public,NoCentCtrs]),
+ Yes11 = ets_new(foo,[{write_concurrency,true},ordered_set,protected,NoCentCtrs]),
Yes12 = ets_new(foo,[set,{write_concurrency,false},
- {write_concurrency,true},ordered_set,public]),
+ {write_concurrency,true},ordered_set,public,NoCentCtrs]),
Yes13 = ets_new(foo,[private,public,set,{write_concurrency,false},
- {write_concurrency,true},ordered_set]),
+ {write_concurrency,true},ordered_set,NoCentCtrs]),
+ Yes14 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
No4 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]),
No5 = ets_new(foo,[ordered_set,public,{write_concurrency,false}]),
No6 = ets_new(foo,[ordered_set,protected,{write_concurrency,false}]),
@@ -2578,6 +2959,7 @@ write_concurrency(Config) when is_list(Config) ->
YesMem = ets:info(Yes1,memory),
NoHashMem = ets:info(No1,memory),
YesTreeMem = ets:info(Yes7,memory),
+ YesYesTreeMem = ets:info(Yes14,memory),
NoTreeMem = ets:info(No4,memory),
io:format("YesMem=~p NoHashMem=~p NoTreeMem=~p YesTreeMem=~p\n",[YesMem,NoHashMem,
NoTreeMem,YesTreeMem]),
@@ -2603,10 +2985,17 @@ write_concurrency(Config) when is_list(Config) ->
NoHashMem = ets:info(No8,memory),
NoHashMem = ets:info(No9,memory),
- true = YesMem > NoHashMem orelse erlang:system_info(schedulers) == 1,
- true = YesMem > NoTreeMem orelse erlang:system_info(schedulers) == 1,
true = YesMem > YesTreeMem,
- true = YesTreeMem < NoTreeMem orelse erlang:system_info(schedulers) == 1,
+
+ case erlang:system_info(schedulers) > 1 of
+ true ->
+ true = YesMem > NoHashMem,
+ true = YesMem > NoTreeMem,
+ true = YesTreeMem < NoTreeMem,
+ true = YesYesTreeMem > YesTreeMem;
+ _ ->
+ one_scheduler_only
+ end,
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])),
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])),
@@ -2614,7 +3003,7 @@ write_concurrency(Config) when is_list(Config) ->
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])),
lists:foreach(fun(T) -> ets:delete(T) end,
- [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,Yes7,Yes8,Yes9,Yes10,Yes11,Yes12,Yes13,
+ [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,Yes7,Yes8,Yes9,Yes10,Yes11,Yes12,Yes13,Yes14,
No1,No2,No3,No4,No5,No6,No7,No8,No9]),
verify_etsmem(EtsMem),
ok.
@@ -4497,6 +4886,8 @@ info_do(Opts) ->
{value, {protection, Protection}} =
lists:keysearch(protection, 1, Res),
{value, {id, Tab}} = lists:keysearch(id, 1, Res),
+ {value, {decentralized_counters, _DecentralizedCtrs}} =
+ lists:keysearch(decentralized_counters, 1, Res),
%% Test 'binary'
[] = ?ets_info(Tab, binary, SlavePid),
@@ -4610,7 +5001,10 @@ size_loop(_T, 0, _, _) ->
size_loop(T, I, PrevSize, WhatToTest) ->
Size = ets:info(T, WhatToTest),
case Size < PrevSize of
- true -> ct:fail("Bad ets:info/2");
+ true ->
+ io:format("Bad ets:info/2 (got ~p expected >=~p)",
+ [Size, PrevSize]),
+ ct:fail("Bad ets:info/2)");
_ -> ok
end,
size_loop(T, I -1, Size, WhatToTest).
@@ -4622,13 +5016,17 @@ add_loop(T, I) ->
add_loop(T, I -1).
-test_table_counter_concurrency(WhatToTest) ->
+test_table_counter_concurrency(WhatToTest, TableOptions) ->
IntStatePrevOn =
erts_debug:set_internal_state(available_internal_state, true),
ItemsToAdd = 1000000,
SizeLoopSize = 1000,
- T = ets:new(k, [public, ordered_set, {write_concurrency, true}]),
- erts_debug:set_internal_state(ets_debug_random_split_join, {T, false}),
+ T = ets:new(k, TableOptions),
+ case lists:member(ordered_set, TableOptions) of
+ true ->
+ erts_debug:set_internal_state(ets_debug_random_split_join, {T, false});
+ false -> ok
+ end,
0 = ets:info(T, size),
P = self(),
SpawnedSizeProcs =
@@ -4659,14 +5057,18 @@ test_table_size_concurrency(Config) when is_list(Config) ->
case erlang:system_info(schedulers) of
1 -> {skip,"Only valid on smp > 1 systems"};
_ ->
- test_table_counter_concurrency(size)
+ BaseOptions = [public, {write_concurrency, true}],
+ test_table_counter_concurrency(size, [set | BaseOptions]),
+ test_table_counter_concurrency(size, [ordered_set | BaseOptions])
end.
test_table_memory_concurrency(Config) when is_list(Config) ->
case erlang:system_info(schedulers) of
1 -> {skip,"Only valid on smp > 1 systems"};
_ ->
- test_table_counter_concurrency(memory)
+ BaseOptions = [public, {write_concurrency, true}],
+ test_table_counter_concurrency(memory, [set | BaseOptions]),
+ test_table_counter_concurrency(memory, [ordered_set | BaseOptions])
end.
%% Tests that calling the ets:delete operation on a table T with
@@ -4677,15 +5079,20 @@ test_delete_table_while_size_snapshot(Config) when is_list(Config) ->
%% depend on that pids are ordered in creation order which is no
%% longer the case when many processes have been started before
Node = start_slave(),
- ok = rpc:call(Node, ?MODULE, test_delete_table_while_size_snapshot_helper, []),
+ [ok = rpc:call(Node,
+ ?MODULE,
+ test_delete_table_while_size_snapshot_helper,
+ [TableType])
+ || TableType <- [set, ordered_set]],
test_server:stop_node(Node),
ok.
-test_delete_table_while_size_snapshot_helper()->
+test_delete_table_while_size_snapshot_helper(TableType) ->
TopParent = self(),
repeat_par(
fun() ->
- Table = ets:new(t, [public, ordered_set,
+ Table = ets:new(t, [public, TableType,
+ {decentralized_counters, true},
{write_concurrency, true}]),
Parent = self(),
NrOfSizeProcs = 100,
@@ -4693,7 +5100,7 @@ test_delete_table_while_size_snapshot_helper()->
|| _ <- lists:seq(1, NrOfSizeProcs)],
timer:sleep(1),
ets:delete(Table),
- [receive
+ [receive
table_gone -> ok;
Problem -> TopParent ! Problem
end || _ <- Pids]
@@ -4738,6 +5145,64 @@ repeat_par_help(FunToRepeat, NrOfTimes, OrgNrOfTimes) ->
end),
repeat_par_help(FunToRepeat, NrOfTimes-1, OrgNrOfTimes).
+test_decentralized_counters_setting(Config) when is_list(Config) ->
+ case erlang:system_info(schedulers) of
+ 1 -> {skip,"Only relevant when the number of shedulers > 1"};
+ _ -> EtsMem = etsmem(),
+ do_test_decentralized_counters_setting(set),
+ do_test_decentralized_counters_setting(ordered_set),
+ do_test_decentralized_counters_default_setting(),
+ verify_etsmem(EtsMem)
+ end.
+
+do_test_decentralized_counters_setting(TableType) ->
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage),
+ lists:foreach(
+ fun(OptList) ->
+ T1 = ets:new(t1, [public, TableType] ++ OptList ++ [TableType]),
+ check_decentralized_counters(T1, false, FlxCtrMemUsage),
+ ets:delete(T1)
+ end,
+ [[{write_concurrency, false}],
+ [{write_concurrency, true}, {decentralized_counters, false}]]),
+ lists:foreach(
+ fun(OptList) ->
+ T1 = ets:new(t1, [public,
+ TableType,
+ {write_concurrency, true}] ++ OptList ++ [TableType]),
+ check_decentralized_counters(T1, true, FlxCtrMemUsage),
+ ets:delete(T1),
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage)
+ end,
+ [[{decentralized_counters, true}]]),
+ ok.
+
+do_test_decentralized_counters_default_setting() ->
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage),
+ Set = ets:new(t1, [public, {write_concurrency, true}]),
+ check_decentralized_counters(Set, false, FlxCtrMemUsage),
+ ets:delete(Set),
+ Set2 = ets:new(t1, [public, set, {write_concurrency, true}]),
+ check_decentralized_counters(Set2, false, FlxCtrMemUsage),
+ ets:delete(Set2),
+ OrdSet = ets:new(t1, [public, ordered_set, {write_concurrency, true}]),
+ check_decentralized_counters(OrdSet, true, FlxCtrMemUsage),
+ ets:delete(OrdSet),
+ ok.
+
+check_decentralized_counters(T, ExpectedState, InitMemUsage) ->
+ case {ExpectedState, erts_debug:get_internal_state(flxctr_memory_usage)} of
+ {false, notsup} -> ok;
+ {false, X} -> InitMemUsage = X;
+ {true, notsup} -> ok;
+ {true, X} when X > InitMemUsage -> ok;
+ {true, _} -> ct:fail("Decentralized counter not used.")
+ end,
+ ExpectedState = ets:info(T, decentralized_counters).
+
%% Test various duplicate_bags stuff.
dups(Config) when is_list(Config) ->
repeat_for_opts(fun dups_do/1).
@@ -5023,7 +5488,7 @@ tabfile_ext4(Config) when is_list(Config) ->
{error,Y} = ets:file2tab(FName,[{verify,true}]),
ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
{X,Y}
- end || N <- lists:seq(500,600)],
+ end || N <- lists:seq(700,800)],
io:format("~p~n",[Res]),
file:delete(FName)
end),
@@ -5804,13 +6269,13 @@ otp_7665_act(Tab,Min,Max,DelNr) ->
true = ets:insert(Tab, List1),
true = ets:safe_fixtable(Tab, true),
true = ets:delete_object(Tab, {key,DelNr}),
- List2 = lists:delete({key,DelNr}, List1),
+ List2 = lists:sort(lists:delete({key,DelNr}, List1)),
%% Now verify that we find all remaining objects
- List2 = ets:lookup(Tab,key),
- EList2 = lists:map(fun({key,N})-> N end,
- List2),
- EList2 = ets:lookup_element(Tab,key,2),
+ List2 = lists:sort(ets:lookup(Tab,key)),
+ EList2 = lists:sort(lists:map(fun({key,N})-> N end,
+ List2)),
+ EList2 = lists:sort(ets:lookup_element(Tab,key,2)),
true = ets:delete(Tab, key),
[] = ets:lookup(Tab, key),
true = ets:safe_fixtable(Tab, false),
@@ -6835,7 +7300,8 @@ take(Config) when is_list(Config) ->
%% Same with bag.
T3 = ets_new(c, [bag]),
ets:insert(T3, [{1,1},{1,2},{3,3}]),
- [{1,1},{1,2}] = ets:take(T3, 1),
+ R = lists:sort([{1,1},{1,2}]),
+ R = lists:sort(ets:take(T3, 1)),
[{3,3}] = ets:take(T3, 3),
[] = ets:tab2list(T3),
ets:delete(T1),
@@ -7598,31 +8064,70 @@ my_tab_to_list(Ts,Key, Acc) ->
wait_for_memory_deallocations() ->
try
+ erts_debug:set_internal_state(wait, thread_progress),
erts_debug:set_internal_state(wait, deallocations)
catch
error:undef ->
erts_debug:set_internal_state(available_internal_state, true),
- wait_for_memory_deallocations()
+ wait_for_memory_deallocations();
+ error:badarg ->
+ %% The emulator we run on does not have the wait internal state
+ %% so we just sleep some time instead...
+ timer:sleep(100)
end.
etsmem() ->
% The following is done twice to avoid an inconsistent memory
% "snapshot" (see verify_etsmem/2).
lists:foldl(
- fun(_,_) ->
+ fun(AttemptNr, PrevEtsMem) ->
+ AllTabsExceptions = [logger, code],
+ %% The logger table is excluded from the AllTabs list
+ %% below because it uses decentralized counters to keep
+ %% track of the size and the memory counters. This cause
+ %% ets:info(T,size) and ets:info(T,memory) to trigger
+ %% allocations and frees that may change the amount of
+ %% memory that is allocated for ETS.
+ %%
+ %% The code table is excluded from the list below
+ %% because the amount of memory allocated for it may
+ %% change if the tested code loads a new module.
+ AllTabs =
+ lists:sort(
+ [begin
+ try ets:info(T, decentralized_counters) of
+ true ->
+ ct:fail("Background ETS table (~p) that "
+ "uses decentralized counters (Add exception?)",
+ [ets:info(T,name)]);
+ _ -> ok
+ catch _:_ ->
+ ok
+ end,
+ {T,
+ ets:info(T,name),
+ ets:info(T,size),
+ ets:info(T,memory),
+ ets:info(T,type)}
+ end
+ || T <- ets:all(),
+ not lists:member(ets:info(T, name), AllTabsExceptions)]),
wait_for_memory_deallocations(),
-
- AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size),
- ets:info(T,memory),ets:info(T,type)}
- end, ets:all()),
-
EtsAllocSize = erts_debug:alloc_blocks_size(ets_alloc),
ErlangMemoryEts = try erlang:memory(ets) catch error:notsup -> notsup end,
-
- Mem = {ErlangMemoryEts, EtsAllocSize},
- {Mem, AllTabs}
+ FlxCtrMemUsage = try erts_debug:get_internal_state(flxctr_memory_usage) catch error:badarg -> notsup end,
+ Mem = {ErlangMemoryEts, EtsAllocSize, FlxCtrMemUsage},
+ EtsMem = {Mem, AllTabs},
+ case PrevEtsMem of
+ first -> ok;
+ _ when PrevEtsMem =:= EtsMem -> ok;
+ _ ->
+ io:format("etsmem(): Change in attempt ~p~n~nbefore:~n~p~n~nafter:~n~p~n~n",
+ [AttemptNr, PrevEtsMem, EtsMem])
+ end,
+ EtsMem
end,
- not_used,
+ first,
lists:seq(1,2)).
verify_etsmem(MI) ->
diff --git a/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html b/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
index 27d6849c60..239877c257 100644
--- a/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
+++ b/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
@@ -4,7 +4,7 @@
<!-- %% -->
<!-- %% %CopyrightBegin% -->
<!-- %% -->
-<!-- %% Copyright Ericsson AB and Kjell Winblad 1996-2018. All Rights Reserved. -->
+<!-- %% Copyright Ericsson AB and Kjell Winblad 1996-2019. All Rights Reserved. -->
<!-- %% -->
<!-- %% Licensed under the Apache License, Version 2.0 (the "License"); -->
<!-- %% you may not use this file except in compliance with the License. -->
@@ -44,6 +44,12 @@
<br>
<textarea id="dataField" rows="4" cols="50">#bench_data_placeholder</textarea>
<br>
+ <input type="checkbox" id="throughputPlot" checked> Include Throughput Plot
+ <br>
+ <input type="checkbox" id="betterThanWorstPlot"> Include % More Throughput Than Worst Plot
+ <br>
+ <input type="checkbox" id="worseThanBestPlot"> Include % Less Throughput Than Best Plot
+ <br>
<input type="checkbox" id="barPlot"> Bar Plot
<br>
<input type="checkbox" id="sameSpacing" checked> Same X Spacing Between Points
@@ -148,10 +154,52 @@
}
return data;
}
+ function toCompareData(dataParam, compareWithWorst) {
+ var data = $.extend(true, [], dataParam);
+ var worstSoFarMap = {};
+ var defaultSoFarValue = compareWithWorst ? Number.MAX_VALUE : Number.MIN_VALUE;
+ function getWorstBestSoFar(x){
+ return worstSoFarMap[x] === undefined ? defaultSoFarValue : worstSoFarMap[x];
+ }
+ function setWorstBestSoFar(x, y){
+ return worstSoFarMap[x] = y;
+ }
+ function lessOrGreaterThan(n1, n2){
+ return compareWithWorst ? n1 < n2 : n1 > n2;
+ }
+ $.each(data, function(i, allResConfig) {
+ $.each(allResConfig.y, function(index, res) {
+ var xName = allResConfig.x[index];
+ if(lessOrGreaterThan(res, getWorstBestSoFar(xName))){
+ setWorstBestSoFar(xName, res);
+ }
+ });
+ });
+ $.each(data, function(i, allResConfig) {
+ $.each(allResConfig.y, function(index, res) {
+ var xName = allResConfig.x[index];
+ if(compareWithWorst){
+ allResConfig.y[index] = ((res / getWorstBestSoFar(xName))-1.0) * 100;
+ }else{
+ allResConfig.y[index] = (1.0 -(res / getWorstBestSoFar(xName))) * 100;
+ }
+ });
+ });
+ return data;
+ }
+ function toBetterThanWorstData(data){
+ return toCompareData(data, true);
+ }
+ function toWorseThanBestData(data){
+ return toCompareData(data, false);
+ }
function plotGraphs(){
var insertPlaceholder = $("#insertPlaceholder");
var sameSpacing = $('#sameSpacing').is(":checked");
var barPlot = $('#barPlot').is(":checked");
+ var throughputPlot = $('#throughputPlot').is(":checked");
+ var betterThanWorstPlot = $('#betterThanWorstPlot').is(":checked");
+ var worseThanBestPlot = $('#worseThanBestPlot').is(":checked");
var lines = $("#dataField").val();
$('.showCheck').each(function() {
var item = $(this);
@@ -188,42 +236,59 @@
plotGraph(lines, sameSpacing, barPlot, prefix));
}
}
+ var nrOfGraphs = 0;
+ function plotScenario(name, plotType) {
+ var data = scenarioDataMap[name];
+ var yAxisTitle = undefined;
+ nrOfGraphs = nrOfGraphs + 1;
+ $("<div class='added' id='graph" + nrOfGraphs + "'>")
+ .insertBefore(insertPlaceholder);
+ $("<button type='button' class='added' id='fullscreenButton" + nrOfGraphs + "'>Fill screen</button>")
+ .insertBefore(insertPlaceholder);
+ $("<span class='added'><br><hr><br></span>")
+ .insertBefore(insertPlaceholder);
+ if (plotType === 'throughput') {
+ yAxisTitle = 'Operations/Second';
+ } else if (plotType === 'better_than_worst') {
+ yAxisTitle = '% More Throughput Than Worst';
+ data = toBetterThanWorstData(data);
+ } else {
+ yAxisTitle = '% Less Throughput Than Best';
+ data = toWorseThanBestData(data);
+ }
+ var layout = {
+ title: name,
+ xaxis: {
+ title: '# of Processes'
+ },
+ yaxis: {
+ title: yAxisTitle
+ }
+ };
+ $("#fullscreenButton" + nrOfGraphs).click(
+ function () {
+ $('#graph' + nrOfGraphs).replaceWith(
+ $("<div class='added' id='graph" + nrOfGraphs + "'>"));
+ layout = $.extend({}, layout, {
+ width: $(window).width() - 40,
+ height: $(window).height() - 40
+ });
+ Plotly.newPlot('graph' + nrOfGraphs, data, layout);
+ });
+ Plotly.newPlot('graph' + nrOfGraphs, data, layout);
+ }
$.each(scenarioList,
- function( index, name ) {
- var nrOfGraphs = index + 1;
- var data = scenarioDataMap[name];
- $( "<div class='added' id='graph"+nrOfGraphs+"'>")
- .insertBefore( insertPlaceholder );
- $( "<button type='button' class='added' id='fullscreenButton"+nrOfGraphs+"'>Fill screen</button>")
- .insertBefore( insertPlaceholder );
- $( "<span class='added'><br><hr><br></span>")
- .insertBefore( insertPlaceholder );
- var layout = {
- title:name,
- xaxis: {
- title: '# of Processes'
- },
- yaxis: {
- title: 'Operations/Second'
- }
-
- };
-
- $("#fullscreenButton"+nrOfGraphs).click(
- function(){
- $('#graph'+nrOfGraphs).replaceWith(
- $("<div class='added' id='graph"+nrOfGraphs+"'>"));
- layout = $.extend({}, layout, {
- width:$(window).width()-40,
- height:$(window).height()-40
- });
- Plotly.newPlot('graph'+nrOfGraphs, data, layout);
- });
- Plotly.newPlot('graph'+nrOfGraphs, data, layout);
-
- });
-
-
+ function (index, name) {
+ if (throughputPlot) {
+ plotScenario(name, 'throughput');
+ }
+ if (betterThanWorstPlot) {
+ plotScenario(name, 'better_than_worst');
+ }
+ if (worseThanBestPlot) {
+ plotScenario(name, 'worse_than_best');
+ }
+ });
}
$(document).ready(function(){
$('#renderButton').click(
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 527d083eaa..3a1ca9b28a 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -26,7 +26,8 @@
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1,
wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1,
- find_source/1, find_source_subdir/1]).
+ find_source/1, find_source_subdir/1, safe_relative_path/1,
+ safe_relative_path_links/1]).
-import(lists, [foreach/2]).
@@ -49,7 +50,8 @@ all() ->
[wildcard_one, wildcard_two, wildcard_errors,
fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink,
wildcard_symlink, is_file_symlink, file_props_symlink,
- find_source, find_source_subdir].
+ find_source, find_source_subdir, safe_relative_path,
+ safe_relative_path_links].
groups() ->
[].
@@ -647,3 +649,167 @@ find_source_subdir(Config) when is_list(Config) ->
{ok, SrcFile} = filelib:find_file(SrcName, BeamDir),
ok.
+
+safe_relative_path(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Root = filename:join(PrivDir, "filelib_SUITE_safe_relative_path"),
+ ok = file:make_dir(Root),
+ ok = file:set_cwd(Root),
+
+ ok = file:make_dir("a"),
+ ok = file:set_cwd("a"),
+ ok = file:make_dir("b"),
+ ok = file:set_cwd("b"),
+ ok = file:make_dir("c"),
+
+ ok = file:set_cwd(Root),
+
+ "a" = test_srp("a"),
+ "a/b" = test_srp("a/b"),
+ "a/b" = test_srp("a/./b"),
+ "a/b" = test_srp("a/./b/."),
+
+ "" = test_srp("a/.."),
+ "" = test_srp("a/./.."),
+ "" = test_srp("a/../."),
+ "a" = test_srp("a/b/.."),
+ "a" = test_srp("a/../a"),
+ "a" = test_srp("a/../a/../a"),
+ "a/b/c" = test_srp("a/../a/b/c"),
+
+ unsafe = test_srp("a/../.."),
+ unsafe = test_srp("a/../../.."),
+ unsafe = test_srp("a/./../.."),
+ unsafe = test_srp("a/././../../.."),
+ unsafe = test_srp("a/b/././../../.."),
+
+ unsafe = test_srp(PrivDir), %Absolute path.
+
+ ok.
+
+test_srp(RelPath) ->
+ Res = do_test_srp(RelPath),
+ Res = case do_test_srp(list_to_binary(RelPath)) of
+ Bin when is_binary(Bin) ->
+ binary_to_list(Bin);
+ Other ->
+ Other
+ end.
+
+do_test_srp(RelPath) ->
+ {ok,Root} = file:get_cwd(),
+ ok = file:set_cwd(RelPath),
+ {ok,Cwd} = file:get_cwd(),
+ ok = file:set_cwd(Root),
+ case filelib:safe_relative_path(RelPath, Cwd) of
+ unsafe ->
+ true = length(Cwd) < length(Root),
+ unsafe;
+ "" ->
+ "";
+ SafeRelPath ->
+ ok = file:set_cwd(SafeRelPath),
+ {ok,Cwd} = file:get_cwd(),
+ true = length(Cwd) >= length(Root),
+ ok = file:set_cwd(Root),
+ SafeRelPath
+ end.
+
+safe_relative_path_links(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = filename:join(PrivDir, "filelib_SUITE_safe_relative_path_links"),
+ ok = file:make_dir(BaseDir),
+ try
+ case check_symlink_support(BaseDir) of
+ true ->
+ simple_test(BaseDir),
+ inside_directory_test(BaseDir),
+ nested_links_test(BaseDir),
+ loop_test(BaseDir),
+ loop_with_parent_test(BaseDir),
+ revist_links_test(BaseDir);
+ false ->
+ {skipped, "This platform/user can't create symlinks."}
+ end
+ after
+ %% This test leaves some rather nasty links that may screw with
+ %% z_SUITE's core file search, so we must make sure everything's
+ %% removed regardless of what happens.
+ rm_rf(BaseDir)
+ end.
+
+check_symlink_support(BaseDir) ->
+ Canary = filename:join(BaseDir, "symlink_canary"),
+ Link = filename:join(BaseDir, "symlink_canary_link"),
+ ok = file:write_file(Canary, <<"chirp">>),
+ ok =:= file:make_symlink(Canary, Link).
+
+simple_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "simple_test")),
+ file:make_symlink("..", filename:join(BaseDir, "simple_test/link")),
+
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "simple_test")),
+ "file" = filelib:safe_relative_path("file", filename:join(BaseDir, "simple_test/link")).
+
+inside_directory_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "inside_directory_test")),
+ file:make_symlink("..", filename:join(BaseDir, "inside_directory_test/link")),
+
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "inside_directory_test")),
+ "file" = filelib:safe_relative_path("file", filename:join(BaseDir, "inside_directory_test/link")).
+
+nested_links_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "nested_links_test")),
+ file:make_dir(filename:join(BaseDir, "nested_links_test/a")),
+ file:make_symlink("a/b/c", filename:join(BaseDir, "nested_links_test/link")),
+ file:make_symlink("..", filename:join(BaseDir, "nested_links_test/a/b")),
+
+ "c/file" = filelib:safe_relative_path("link/file", filename:join(BaseDir, "nested_links_test")),
+
+ file:delete(filename:join(BaseDir, "nested_links_test/a/b")),
+ file:make_symlink("../..", filename:join(BaseDir, "nested_links_test/a/b")),
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "nested_links_test")).
+
+loop_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "loop_test")),
+
+ file:make_symlink("b", filename:join(BaseDir, "loop_test/c")),
+ file:make_symlink("c", filename:join(BaseDir, "loop_test/b")),
+
+ unsafe = filelib:safe_relative_path("c", filename:join(BaseDir, "loop_test")).
+
+loop_with_parent_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "loop_with_parent_test")),
+ file:make_dir(filename:join(BaseDir, "loop_with_parent_test/bar")),
+
+ file:make_symlink("../bar/foo", filename:join(BaseDir, "loop_with_parent_test/bar/foo")),
+
+ unsafe = filelib:safe_relative_path("bar/foo", filename:join(BaseDir, "loop_with_parent_test")).
+
+revist_links_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "revist_links_test")),
+
+ file:make_symlink(".", filename:join(BaseDir, "revist_links_test/x")),
+ file:make_symlink("x", filename:join(BaseDir, "revist_links_test/y")),
+ file:make_symlink("y", filename:join(BaseDir, "revist_links_test/z")),
+
+ "file" = filelib:safe_relative_path("x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("y/x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/y/x/y/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/y/z/x/y/z/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/x/y/y/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/z/y/x/./z/foo/../x/./y/file", filename:join(BaseDir, "revist_links_test")).
+
+rm_rf(Dir) ->
+ case file:read_link_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok, Content} = file:list_dir_all(Dir),
+ [ rm_rf(filename:join(Dir,C)) || C <- Content ],
+ file:del_dir(Dir),
+ ok;
+ {ok, #file_info{}} ->
+ file:delete(Dir);
+ _ ->
+ ok
+ end.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index f284eb1ed6..846394d366 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -886,7 +886,7 @@ t_nativename_bin(Config) when is_list(Config) ->
safe_relative_path(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Root = filename:join(PrivDir, ?FUNCTION_NAME),
+ Root = filename:join(PrivDir, "filename_SUITE_safe_relative_path"),
ok = file:make_dir(Root),
ok = file:set_cwd(Root),
@@ -1081,7 +1081,10 @@ check_basedir_xdg([Type|Types]) ->
Opt = #{os=>linux},
Key = basedir_xdg_env(Type),
io:format("type: ~p~n", [Type]),
- Home = os:getenv("HOME"),
+ Home = case os:getenv("WSLENV") of
+ false -> os:getenv("HOME");
+ _ -> os:getenv("USERPROFILE")
+ end,
NDir = "/some/absolute/path",
DefPath = basedir_xdg_def(Type,Home,Name),
EnvPath = case Type of
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 880b10117c..cb292cf01f 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@
start_opt/1,
undef_init/1, undef_handle_call/1, undef_handle_event/1,
undef_handle_info/1, undef_code_change/1, undef_terminate/1,
- undef_in_terminate/1]).
+ undef_in_terminate/1, format_log_1/1, format_log_2/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -40,7 +40,8 @@ all() ->
[start, {group, test_all}, hibernate, auto_hibernate,
call_format_status, call_format_status_anon, error_format_status,
get_state, replace_state,
- start_opt, {group, undef_callbacks}, undef_in_terminate].
+ start_opt, {group, undef_callbacks}, undef_in_terminate,
+ format_log_1, format_log_2].
groups() ->
[{test_all, [],
@@ -112,6 +113,11 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid1),
ok = gen_event:stop(Pid1),
+ {ok, {Pid1b,Mon1b}} = gen_event:start_monitor(), %anonymous
+ [] = gen_event:which_handlers(Pid1b),
+ ok = gen_event:stop(Pid1b),
+ receive {'DOWN',Mon1b,process,Pid1b,_} -> ok end,
+
{ok, Pid2} = gen_event:start(?LMGR),
[] = gen_event:which_handlers(my_dummy_name),
[] = gen_event:which_handlers(Pid2),
@@ -122,21 +128,45 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid3),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid3b,Mon3b}} = gen_event:start_monitor(?LMGR),
+ [] = gen_event:which_handlers(my_dummy_name),
+ [] = gen_event:which_handlers(Pid3b),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon3b,process,Pid3b,_} -> ok end,
+
{ok, Pid4} = gen_event:start_link(?GMGR),
[] = gen_event:which_handlers(?GMGR),
[] = gen_event:which_handlers(Pid4),
ok = gen_event:stop(?GMGR),
+ {ok, {Pid4b,Mon4b}} = gen_event:start_monitor(?GMGR),
+ [] = gen_event:which_handlers(?GMGR),
+ [] = gen_event:which_handlers(Pid4b),
+ ok = gen_event:stop(?GMGR),
+ receive {'DOWN',Mon4b,process,Pid4b,_} -> ok end,
+
{ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers(Pid5),
ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ {ok, {Pid5b,Mon5b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers(Pid5b),
+ ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ receive {'DOWN',Mon5b,process,Pid5b,_} -> ok end,
+
{ok, _} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start(?LMGR),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid5c,Mon5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start(?LMGR),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon5c,process,Pid5c,_} -> ok end,
+
{ok, Pid6} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start(?GMGR),
@@ -148,6 +178,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid6b,Mon6b}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start(?GMGR),
+
+ ok = gen_event:stop(?GMGR, shutdown, 10000),
+ receive
+ {'DOWN', Mon6b, process, Pid6b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
{ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
@@ -159,6 +200,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid7b,Mon7b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
+
+ exit(Pid7b, shutdown),
+ receive
+ {'DOWN', Mon7b, process, Pid7b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
process_flag(trap_exit, OldFl),
ok.
@@ -763,27 +815,49 @@ sync_notify(Config) when is_list(Config) ->
ok.
call(Config) when is_list(Config) ->
+ Async = fun(Mgr,H,Req) ->
+ try
+ Promise = gen_event:send_request(Mgr,H,Req),
+ gen_event:wait_response(Promise, infinity)
+ catch _:Reason ->
+ {'did_exit', Reason}
+ end
+ end,
{ok,_} = gen_event:start({local, my_dummy_handler}),
ok = gen_event:add_handler(my_dummy_handler, dummy_h, [self()]),
ok = gen_event:add_handler(my_dummy_handler, {dummy_h, 1}, [self()]),
[{dummy_h, 1}, dummy_h] = gen_event:which_handlers(my_dummy_handler),
{'EXIT',_} = (catch gen_event:call(non_exist, dummy_h, hejsan)),
- {error, bad_module} =
- gen_event:call(my_dummy_handler, bad_h, hejsan),
+ {error, _} = Async(non_exist, dummy_h, hejsan),
+ {error, bad_module} = gen_event:call(my_dummy_handler, bad_h, hejsan),
+ {error, bad_module} = Async(my_dummy_handler, bad_h, hejsan),
+
{ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
- {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1},
- hejsan),
- {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan,
- 10000),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, dummy_h, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan, 10000),
{'EXIT', {timeout, _}} =
(catch gen_event:call(my_dummy_handler, dummy_h, hejsan, 0)),
flush(),
+ P1 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+ timeout = gen_event:wait_response(P1, 0),
+ {reply, {ok, hejhopp}} = gen_event:wait_response(P1, infinity),
+
+ flush(),
+ P2 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+ no_reply = gen_event:check_response({other,msg}, P2),
+ {reply, {ok, hejhopp}} = receive Msg -> gen_event:check_response(Msg, P2)
+ after 1000 -> exit(tmo) end,
+
ok = gen_event:delete_handler(my_dummy_handler, {dummy_h, 1}, []),
{ok, swapped} = gen_event:call(my_dummy_handler, dummy_h,
{swap_call,dummy1_h,swap}),
[dummy1_h] = gen_event:which_handlers(my_dummy_handler),
- {error, bad_module} =
- gen_event:call(my_dummy_handler, dummy_h, hejsan),
+ {error, bad_module} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
+ {error, bad_module} = Async(my_dummy_handler, dummy_h, hejsan),
ok = gen_event:call(my_dummy_handler, dummy1_h, delete_call),
receive
{dummy1_h, removed} ->
@@ -1221,3 +1295,209 @@ fake_upgrade(Pid, Mod) ->
Ret = sys:change_code(Pid, Mod, old_vsn, []),
ok = sys:resume(Pid),
Ret.
+
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Handler = my_handler,
+ Name = self(),
+ Report = #{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term},
+ {F1, A1} = gen_event:format_log(Report),
+ FExpected1 = "** gen_event handler ~tp crashed.\n"
+ "** Was installed in ~tp\n"
+ "** Last event was: ~tp\n"
+ "** When handler state == ~tp\n"
+ "** Reason == ~tp\n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Handler,Name,Term,Term,Term] = A1,
+
+ Warning = #{label=>{gen_event,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_event:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p\n"
+ "** Unhandled message: ~tp\n",
+ ct:log("WF1: ~ts~nWA1: ~tp", [WF1,WA1]),
+ WFExpected1 = WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_event:format_log(#{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term}),
+ FExpected2 = "** gen_event handler ~tP crashed.\n"
+ "** Was installed in ~tP\n"
+ "** Last event was: ~tP\n"
+ "** When handler state == ~tP\n"
+ "** Reason == ~tP\n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ [Handler,Depth,Name,Depth,Limited,Depth,Limited,Depth,Limited,Depth] = A2,
+
+ {WF2,WA2} = gen_event:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p\n"
+ "** Unhandled message: ~tP\n",
+ ct:log("WF2: ~ts~nWA2: ~tp", [WF2,WA2]),
+ WFExpected2 = WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Handler = my_handler,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed in "++NameStr++"\n"
+ "** Last event was: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** When handler state == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Warning = #{label=>{gen_event,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning, FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("WStr1: ~ts", [WStr1]),
+ ct:log("length(WStr1): ~p", [WL1]),
+ true = WExpected1 =:= WStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed in " ++ NameStr ++ "\n"
+ "** Last event was: [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When handler state == [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason == [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ WStr2 = flatten_format_log(Warning, FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("WStr2: ~ts", [WStr2]),
+ ct:log("length(WStr2): ~p", [WL2]),
+ true = WExpected2 =:= WStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning, WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr3: ~ts", [WStr3]),
+ ct:log("length(WStr3): ~p", [WL3]),
+ true = lists:prefix(WExpected3, WStr3),
+ true = WL3 < WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+
+ Expected4 = "Generic event handler my_handler crashed. "
+ "Installed: "++NameStr++". "
+ "Last event: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ WStr4 = flatten_format_log(Warning, FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_event_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("WStr4: ~ts", [WStr4]),
+ ct:log("length(WStr4): ~p", [WL4]),
+ true = WExpected4 =:= WStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Generic event handler my_handler crashed. "
+ "Installed: "++NameStr++". "
+ "Last event: [1,2,3,4,5,6,7,8,9|...]. "
+ "State: [1,2,3,4,5,6,7,8,9|...]. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ WStr5 = flatten_format_log(Warning, FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_event_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("WStr5: ~ts", [WStr5]),
+ ct:log("length(WStr5): ~p", [WL5]),
+ true = WExpected5 =:= WStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Generic event handler my_handler crashed. Installed: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning, WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_event_SUITE. ",
+ ct:log("WStr6: ~ts", [WStr6]),
+ ct:log("length(WStr6): ~p", [WL6]),
+ true = lists:prefix(WExpected6, WStr6),
+ true = WL6 < WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_event:format_log(Report, Format)).
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 62d9d0e0ae..6840184c74 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -46,6 +46,8 @@
-export([hibernate/1,auto_hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
+-export([format_log_1/1, format_log_2/1]).
+
-export([enter_loop/1]).
%% Exports for apply
@@ -69,7 +71,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, start}, {group, abnormal}, shutdown,
{group, sys}, hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
- undef_in_handle_info, undef_in_terminate].
+ undef_in_handle_info, undef_in_terminate,{group,format_log}].
groups() ->
[{start, [],
@@ -83,7 +85,8 @@ groups() ->
get_state, replace_state]},
{undef_callbacks, [],
[undef_handle_event, undef_handle_sync_event, undef_handle_info,
- undef_init, undef_code_change, undef_terminate1, undef_terminate2]}].
+ undef_init, undef_code_change, undef_terminate1, undef_terminate2]},
+ {format_log, [], [format_log_1, format_log_2]}].
init_per_suite(Config) ->
Config.
@@ -1018,6 +1021,236 @@ undef_in_terminate(Config) when is_list(Config) ->
ok
end.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Name = self(),
+ Report = #{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ {F1,A1} = gen_fsm:format_log(Report),
+ FExpected1 = "** State machine ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When State == ~tp~n"
+ "** Data == ~tp~n"
+ "** Reason for termination ==~n** ~tp~n"
+ "** Log ==~n**~tp~n"
+ "** Client ~tp stacktrace~n** ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1=F1,
+
+ [Name,Term,Name,Term,Term,[Term],clientname,[]] = A1,
+
+ Warning = #{label=>{gen_fsm,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_fsm:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tp~n",
+ ct:log("WF1: ~ts~nWA1: ~tp", [WF1,WA1]),
+ WFExpected1=WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_fsm:format_log(#{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}}),
+ FExpected2 = "** State machine ~tP terminating \n"
+ "** Last message in was ~tP~n"
+ "** When State == ~tP~n"
+ "** Data == ~tP~n"
+ "** Reason for termination ==~n** ~tP~n"
+ "** Log ==~n**~tP~n"
+ "** Client ~tP stacktrace~n** ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2=F2,
+
+ [Name,Depth,Limited,Depth,Name,Depth,Limited,Depth,Limited,
+ Depth,[Limited],Depth,clientname,Depth,[],Depth] = A2,
+
+ {WF2,WA2} = gen_fsm:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tP~n",
+ ct:log("WF2: ~ts~nWA2: ~tp", [WF2,WA2]),
+ WFExpected2=WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine "++NameStr++" terminating \n"
+ "** Last message in was [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** When State == "++NameStr++"\n"
+ "** Data == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason for termination ==\n"
+ "** [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Log ==\n"
+ "**[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]\n"
+ "** Client clientname stacktrace\n"
+ "** []\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Warning = #{label=>{gen_fsm,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning, FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_fsm_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("WStr1: ~ts", [WStr1]),
+ ct:log("length(WStr1): ~p", [WL1]),
+ true = WExpected1 =:= WStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine "++NameStr++" terminating \n"
+ "** Last message in was [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When State == "++NameStr++"\n"
+ "** Data == [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination ==\n"
+ "** [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Log ==\n"
+ "**[[1,2,3,4,5,6,7,8|...]]\n"
+ "** Client clientname stacktrace\n"
+ "** []\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ WStr2 = flatten_format_log(Warning, FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_fsm_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("WStr2: ~ts", [WStr2]),
+ ct:log("length(WStr2): ~p", [WL2]),
+ true = WExpected2 =:= WStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine "++NameStr++" terminating \n"
+ "** Last ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning, WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_fsm_SUITE",
+ ct:log("WStr3: ~ts", [WStr3]),
+ ct:log("length(WStr3): ~p", [WL3]),
+ true = lists:prefix(WExpected3, WStr3),
+ true = WL3 < WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine "++NameStr++" terminating. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Last event: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "State: "++NameStr++". "
+ "Data: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Log: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "Client clientname stacktrace: [].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ WStr4 = flatten_format_log(Warning, FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("WStr4: ~ts", [WStr4]),
+ ct:log("length(WStr4): ~p", [WL4]),
+ true = WExpected4 =:= WStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine "++NameStr++" terminating. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Last event: [[1,2,3,4,5,6,7,8|...]]. "
+ "State: "++NameStr++". "
+ "Data: [1,2,3,4,5,6,7,8,9|...]. "
+ "Log: [[1,2,3,4,5,6,7,8|...]]. "
+ "Client clientname stacktrace: [].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ WStr5 = flatten_format_log(Warning, FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("WStr5: ~ts", [WStr5]),
+ ct:log("length(WStr5): ~p", [WL5]),
+ true = WExpected5 =:= WStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine "++NameStr++" terminating. Reason: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning, WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr6: ~ts", [WStr6]),
+ ct:log("length(WStr6): ~p", [WL6]),
+ true = lists:prefix(WExpected6, WStr6),
+ true = WL6 < WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_fsm:format_log(Report, Format)).
+
%%
%% Functionality check
%%
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index e29195e895..8015126b0d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -26,7 +26,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([start/1, crash/1, call/1, cast/1, cast_fast/1,
+-export([start/1, crash/1, call/1, send_request/1, cast/1, cast_fast/1,
continue/1, info/1, abcast/1, multicall/1, multicall_down/1,
call_remote1/1, call_remote2/1, call_remote3/1,
call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
@@ -38,7 +38,9 @@
undef_handle_call/1, undef_handle_cast/1, undef_handle_info/1,
undef_init/1, undef_code_change/1, undef_terminate1/1,
undef_terminate2/1, undef_in_terminate/1, undef_in_handle_info/1,
- undef_handle_continue/1
+ undef_handle_continue/1,
+
+ format_log_1/1, format_log_2/1
]).
-export([stop1/1, stop2/1, stop3/1, stop4/1, stop5/1, stop6/1, stop7/1,
@@ -61,7 +63,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [start, {group,stop}, crash, call, cast, cast_fast, info, abcast,
+ [start, {group,stop}, crash, call, send_request, cast, cast_fast, info, abcast,
continue, multicall, multicall_down, call_remote1, call_remote2,
call_remote3, call_remote_n1, call_remote_n2,
call_remote_n3, spec_init,
@@ -71,7 +73,8 @@ all() ->
call_format_status, error_format_status, terminate_crash_format,
get_state, replace_state,
call_with_huge_message_queue, {group, undef_callbacks},
- undef_in_terminate, undef_in_handle_info].
+ undef_in_terminate, undef_in_handle_info,
+ format_log_1, format_log_2].
groups() ->
[{stop, [],
@@ -104,7 +107,8 @@ init_per_testcase(Case, Config) when Case == call_remote1;
Case == call_remote3;
Case == call_remote_n1;
Case == call_remote_n2;
- Case == call_remote_n3 ->
+ Case == call_remote_n3;
+ Case == send_request ->
{ok,N} = start_node(hubba),
[{node,N} | Config];
@@ -161,6 +165,18 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% anonymous monitored
+ {ok, {Pid1b, Mon1b}} =
+ gen_server:start_monitor(gen_server_SUITE, [], []),
+ ok = gen_server:call(Pid1b, started_p),
+ ok = gen_server:call(Pid1b, stop),
+ receive
+ {'DOWN', Mon1b, process, Pid1b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% local register
{ok, Pid2} =
gen_server:start({local, my_test_name},
@@ -191,6 +207,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% local register monitored
+ {ok, {Pid3b, Mon3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, started_p),
+ {error, {already_started, Pid3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, stop),
+ receive
+ {'DOWN', Mon3b, process, Pid3b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% global register
{ok, Pid4} =
gen_server:start({global, my_test_name},
@@ -219,6 +251,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% global register monitored
+ {ok, {Pid5b, Mon5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, started_p),
+ {error, {already_started, Pid5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, stop),
+ receive
+ {'DOWN', Mon5b, process, Pid5b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% via register
dummy_via:reset(),
{ok, Pid6} =
@@ -459,6 +507,90 @@ call(Config) when is_list(Config) ->
ok.
%% --------------------------------------
+%% Test gen_server:send_request.
+%% --------------------------------------
+
+send_request(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ {ok, Pid} = gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ Async = fun(Process, Req) ->
+ try
+ Promise = gen_server:send_request(Process, Req),
+ gen_server:wait_response(Promise, infinity)
+ catch _:Reason:ST ->
+ {'did_exit', Reason, ST}
+ end
+ end,
+ {reply,ok} = Async(my_test_name, started_p),
+
+ {reply,delayed} = Async(Pid, {delayed_answer,1}),
+
+ %% two requests within a specified time.
+ Promise1 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ Promise2 = gen_server:send_request(my_test_name, next_call),
+ {reply, ok} = gen_server:wait_response(Promise1, infinity),
+ {reply, ok} = gen_server:wait_response(Promise2, infinity),
+
+ Promise3 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ no_reply = gen_server:check_response({foo, bar}, Promise3),
+ receive {{'$gen_request_id',Ref},_} = Msg when is_reference(Ref) ->
+ {reply, ok} = gen_server:check_response(Msg, Promise3)
+ after 1000 ->
+ %% Format not yet doumented so it might be ok
+ %% This test is just to make you aware that you have changed it
+ exit(message_format_changed)
+ end,
+ timer:sleep(1500),
+
+ {reply, false} = Async(my_test_name, next_call),
+
+ %% timeout
+ Promise5 = gen_server:send_request(my_test_name, {delayed_answer,50}),
+ timeout = gen_server:wait_response(Promise5, 0),
+ {reply, delayed} = gen_server:wait_response(Promise5, infinity),
+
+ %% bad return value in the gen_server loop from handle_call.
+ {error,{{bad_return_value, badreturn},_}} = Async(my_test_name, badreturn),
+
+ %% Test other error cases
+ {error, {noproc,_}} = Async(Pid, started_p),
+ {error, {noproc,_}} = Async(my_test_name, started_p),
+ {error, {noconnection, _}} = Async({my_test_name, foo@node}, started_p),
+
+ {error, {noproc,_}} = Async({global, non_existing}, started_p),
+ catch exit(whereis(dummy_via), foo),
+ {'EXIT', {badarg,_}} =
+ (catch gen_server:send_request({via, dummy_via, non_existing}, started_p)),
+
+ %% Remote nodes
+ Via = dummy_via:reset(),
+ Remote = proplists:get_value(node,Config),
+ {ok, RPid} = rpc:call(Remote, gen_server, start, [{global, remote}, ?MODULE, [], []]),
+ dummy_via:register_name(remote, RPid),
+ {reply, ok} = Async(RPid, started_p),
+ {reply, ok} = Async({global, remote}, started_p),
+ {reply, ok} = Async({via, dummy_via, remote}, started_p),
+ {error, {shutdown, _}} = Async({global, remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({global, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, non_existing}, started_p),
+
+ {ok, _} = rpc:call(Remote, gen_server, start, [{local, remote}, ?MODULE, [], []]),
+ {reply, ok} = Async({remote, Remote}, started_p),
+ {error, {shutdown, _}} = Async({remote, Remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({remote, Remote}, started_p),
+
+ %% Cleanup
+ catch exit(Via, foo2),
+ receive {'EXIT', Via, foo2} -> ok end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
+
+%% --------------------------------------
%% Test handle_continue.
%% --------------------------------------
@@ -1528,6 +1660,208 @@ wait_until_processed(Pid, Message, N) ->
ok
end.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel,error_logger_format_depth),
+ application:unset_env(kernel,error_logger_format_depth),
+ Term = lists:seq(1,15),
+ Name = self(),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ {F1,A1} = gen_server:format_log(Report),
+ FExpected1 = "** Generic server ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When Server state == ~tp~n"
+ "** Reason for termination ==~n** ~tp~n"
+ "** Client ~tp stacktrace~n"
+ "** ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp",[F1,A1]),
+ FExpected1=F1,
+ [Name,Term,Term,Term,clientname,[]] = A1,
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_server:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tp~n",
+ ct:log("WF1: ~ts~nWA1: ~tp",[WF1,WA1]),
+ WFExpected1=WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel,error_logger_format_depth,Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_server:format_log(#{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}}),
+ FExpected2 = "** Generic server ~tP terminating \n"
+ "** Last message in was ~tP~n"
+ "** When Server state == ~tP~n"
+ "** Reason for termination ==~n** ~tP~n"
+ "** Client ~tP stacktrace~n"
+ "** ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp",[F2,A2]),
+ FExpected2=F2,
+ [Name,Depth,Limited,Depth,Limited,Depth,Limited,Depth,
+ clientname,Depth,[],Depth] = A2,
+
+ {WF2,WA2} = gen_server:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tP~n",
+ ct:log("WF2: ~ts~nWA2: ~tp",[WF2,WA2]),
+ WFExpected2=WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel,error_logger_format_depth);
+ _ ->
+ application:set_env(kernel,error_logger_format_depth,FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report,FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str1: ~ts",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning,FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr1: ~ts",[WStr1]),
+ ct:log("length(WStr1): ~p",[WL1]),
+ true = lists:prefix(WExpected1,WStr1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report,FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str2: ~ts",[Str2]),
+ ct:log("length(Str2): ~p",[L2]),
+ true = lists:prefix(Expected2,Str2),
+ true = L2<L1,
+
+ WStr2 = flatten_format_log(Warning,FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr2: ~ts",[WStr2]),
+ ct:log("length(WStr2): ~p",[WL2]),
+ true = lists:prefix(WExpected2,WStr2),
+ true = WL2<WL1,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report,FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str3: ~ts",[Str3]),
+ ct:log("length(Str3): ~p",[L3]),
+ true = lists:prefix(Expected3,Str3),
+ true = L3<L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning,WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr3: ~ts",[WStr3]),
+ ct:log("length(WStr3): ~p",[WL3]),
+ true = lists:prefix(WExpected3,WStr3),
+ true = WL3<WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report,FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str4: ~ts",[Str4]),
+ ct:log("length(Str4): ~p",[L4]),
+ true = lists:prefix(Expected4,Str4),
+ true = L4<L1,
+
+ WStr4 = flatten_format_log(Warning,FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr4: ~ts",[WStr4]),
+ ct:log("length(WStr4): ~p",[WL4]),
+ true = lists:prefix(WExpected4,WStr4),
+ true = WL4<WL1,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report,FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str5: ~ts",[Str5]),
+ ct:log("length(Str5): ~p",[L5]),
+ true = lists:prefix(Expected5,Str5),
+ true = L5<L4,
+
+ WStr5 = flatten_format_log(Warning,FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr5: ~ts",[WStr5]),
+ ct:log("length(WStr5): ~p",[WL5]),
+ true = lists:prefix(WExpected5,WStr5),
+ true = WL5<WL4,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report,FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str6: ~ts",[Str6]),
+ ct:log("length(Str6): ~p",[L6]),
+ true = lists:prefix(Expected6,Str6),
+ true = L6<L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning,WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr6: ~ts",[WStr6]),
+ ct:log("length(WStr6): ~p",[WL6]),
+ true = lists:prefix(WExpected6,WStr6),
+ true = WL6<WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_server:format_log(Report, Format)).
+
%%--------------------------------------------------------------
%% Help functions to spec_init_*
start_link(Init, Options) ->
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 0586575736..741ce6211f 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -42,7 +42,7 @@ all() ->
event_types, generic_timers, code_change,
{group, sys},
hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
- undef_in_terminate].
+ undef_in_terminate, {group, format_log}].
groups() ->
[{start, [], tcs(start)},
@@ -53,7 +53,8 @@ groups() ->
{abnormal_handle_event, [], tcs(abnormal)},
{sys, [], tcs(sys)},
{sys_handle_event, [], tcs(sys)},
- {undef_callbacks, [], tcs(undef_callbacks)}].
+ {undef_callbacks, [], tcs(undef_callbacks)},
+ {format_log, [], tcs(format_log)}].
tcs(start) ->
[start1, start2, start3, start4, start5, start6, start7,
@@ -69,7 +70,9 @@ tcs(sys) ->
get_state, replace_state];
tcs(undef_callbacks) ->
[undef_code_change, undef_terminate1, undef_terminate2,
- function_clause_after_change_callback_module].
+ function_clause_after_change_callback_module];
+tcs(format_log) ->
+ [format_log_1, format_log_2].
init_per_suite(Config) ->
Config.
@@ -140,8 +143,18 @@ start1(Config) ->
%% ?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
%%process_flag(trap_exit, OldFl),
- ok = verify_empty_msgq().
+ ok = verify_empty_msgq(),
+ {ok,{Pid1,Mon1}} = gen_statem:start_monitor(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ stop_it(Pid1),
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+ ok = verify_empty_msgq().
+
%% anonymous w. shutdown
start2(Config) ->
%% Dont link when shutdown
@@ -197,7 +210,7 @@ start6(Config) ->
ok = verify_empty_msgq().
-%% global register linked
+%% global register linked & monitored
start7(Config) ->
STM = {global,my_stm},
@@ -207,6 +220,8 @@ start7(Config) ->
gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
{error,{already_started,Pid}} =
gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
ok = do_func_test(Pid),
ok = do_sync_func_test(Pid),
@@ -214,6 +229,28 @@ start7(Config) ->
ok = do_sync_func_test(STM),
stop_it(STM),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(STM),
+ ok = do_sync_func_test(STM),
+ stop_it(STM),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
@@ -237,7 +274,7 @@ start8(Config) ->
%%process_flag(trap_exit, OldFl),
ok = verify_empty_msgq().
-%% local register linked
+%% local register linked & monitored
start9(Config) ->
%%OldFl = process_flag(trap_exit, true),
Name = my_stm,
@@ -255,6 +292,24 @@ start9(Config) ->
stop_it(Pid),
%%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(Name),
+ ok = do_sync_func_test(Name),
+ stop_it(Pid1),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
%% global register
@@ -1827,6 +1882,306 @@ next_events(Config) ->
?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1,15),
+ Name = self(),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report1 = simple_report(Name, Term, Reason),
+ Report2 = elaborate_report(Name, Term, Reason),
+
+ {F1,A1} = gen_statem:format_log(Report1),
+ ct:log("F1: ~ts~nA1: ~tp",[F1,A1]),
+ FExpected1 = "** State machine ~tp terminating~n"
+ "** When server state = ~tp~n"
+ "** Reason for termination = ~tp:~tp~n"
+ "** Callback mode = ~tp~n",
+ FExpected1 = F1,
+ [Name,Term,error,Reason,state_functions] = A1,
+
+ {F3,A3} = gen_statem:format_log(Report2),
+ ct:log("F3: ~ts~nA3: ~tp",[F3,A3]),
+ FExpected3 = "** State machine ~tp terminating~n"
+ "** Last event = ~tp~n"
+ "** When server state = ~tp~n"
+ "** Reason for termination = ~tp:~tp~n"
+ "** Callback mode = ~tp~n"
+ "** Queued = ~tp~n"
+ "** Postponed = ~tp~n"
+ "** Stacktrace =~n** ~tp~n"
+ "** Time-outs: ~tp~n"
+ "** Log =~n** ~tp~n"
+ "** Client ~tp stacktrace~n"
+ "** ~tp~n",
+ FExpected3 = F3,
+ Stacktrace = stacktrace(),
+ [Name,Term,Term,error,Reason,[state_functions,state_enter],[Term],
+ [{internal,Term}],Stacktrace,{1,[{timeout,message}]},[Term],Name,[]] = A3,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_statem:format_log(Report1),
+ ct:log("F2: ~ts~nA2: ~tp",[F2,A2]),
+ FExpected2 = "** State machine ~tP terminating~n"
+ "** When server state = ~tP~n"
+ "** Reason for termination = ~tP:~tP~n"
+ "** Callback mode = ~tP~n",
+ FExpected2 = F2,
+ [Name,Depth,Limited,Depth,error,Depth,Reason,
+ Depth,state_functions,Depth] = A2,
+
+ {F4,A4} = gen_statem:format_log(Report2),
+ ct:log("F4: ~ts~nA4: ~tp",[F4,A4]),
+ FExpected4 = "** State machine ~tP terminating~n"
+ "** Last event = ~tP~n"
+ "** When server state = ~tP~n"
+ "** Reason for termination = ~tP:~tP~n"
+ "** Callback mode = ~tP~n"
+ "** Queued = ~tP~n"
+ "** Postponed = ~tP~n"
+ "** Stacktrace =~n** ~tP~n"
+ "** Time-outs: ~tP~n"
+ "** Log =~n** ~tP~n"
+ "** Client ~tP stacktrace~n"
+ "** ~tP~n",
+ FExpected4 = F4,
+ LimitedPostponed = [{internal,[1,2,3,4,5,6,'...']}],
+ LimitedStacktrace = io_lib:limit_term(Stacktrace, Depth),
+ LimitedQueue = io_lib:limit_term([Term], Depth),
+ [Name,Depth,Limited,Depth,Limited,Depth,error,Depth,Reason,Depth,
+ [state_functions,state_enter],Depth,LimitedQueue,Depth,
+ LimitedPostponed,Depth,LimitedStacktrace,Depth,{1,[{timeout,message}]},
+ Depth,[Limited],Depth,Name,Depth,[],Depth] = A4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ format_log_2_simple(),
+ format_log_2_elaborate(),
+ ok.
+
+format_log_2_simple() ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report = simple_report(Name, Term, Reason),
+
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback mode = state_functions\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ Expected1 = Str1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback mode = state_functions\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ Expected4 = Str4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "State: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ Expected5 = Str5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>100},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+format_log_2_elaborate() ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report = elaborate_report(Name, Term, Reason),
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = lists:prefix(Expected1, Str1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback mode = [state_functions,state_enter]\n"
+ "** Queued = [[1,2,3,4,5,6,7,8|...]]\n"
+ "** Postponed = [{internal,[1,2,3,4,5,6|...]}]\n"
+ "** Stacktrace =\n"
+ "** [{m,f,1,[1,2,3,4|...]}]\n"
+ "** Time-outs: {1,[{timeout,message}]}\n"
+ "** Log =\n"
+ "** [[1,2,3,4,5,6,7,8|...]]\n"
+ "** Client "++NameStr ++ " stacktrace\n"
+ "** []\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ Expected2 = Str2,
+
+ FormatOpts3 = #{chars_limit=>300},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "Stack: [{m,f,1,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]}]. "
+ "Last event: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Log: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "Client " ++ NameStr ++ " stacktrace: [].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ Expected4 = Str4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "Stack: [{m,f,1,[1,2,3,4|...]}]. "
+ "Last event: [1,2,3,4,5,6,7,8,9|...]. "
+ "State: [1,2,3,4,5,6,7,8,9|...]. "
+ "Log: [[1,2,3,4,5,6,7,8|...]]. "
+ "Client " ++ NameStr ++ " stacktrace: [].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ Expected5 = Str5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>300},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+simple_report(Name, Term, Reason) ->
+ #{label=>{gen_statem,terminate},
+ name=>Name,
+ queue=>[],
+ postponed=>[],
+ callback_mode=>state_functions,
+ state_enter=>false,
+ state=>Term,
+ timeouts=>{0,[]},
+ log=>[],
+ reason=>{error,Reason,[]},
+ client_info=>undefined}.
+
+elaborate_report(Name, Term, Reason) ->
+ #{label=>{gen_statem,terminate},
+ name=>Name,
+ queue=>[Term,Term],
+ postponed=>[{internal,Term}],
+ callback_mode=>state_functions,
+ state_enter=>true,
+ state=>Term,
+ timeouts=>{1,[{timeout,message}]},
+ log=>[Term],
+ reason=>{error,Reason,stacktrace()},
+ client_info=>{self(),{self(),[]}}}.
+
+stacktrace() ->
+ [{m,f,1,lists:seq(1, 15)}].
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_statem:format_log(Report, Format)).
+
%%
%% Functionality check
%%
@@ -1862,7 +2217,17 @@ do_func_test(STM) ->
wfor(yes),
ok = do_disconnect(STM),
ok = gen_statem:cast(STM, {'alive?',self()}),
+ P0 = gen_statem:send_request(STM, 'alive?'),
+ timeout = gen_statem:wait_response(P0, 0),
wfor(yes),
+ {reply, yes} = gen_statem:wait_response(P0, infinity),
+ _ = flush(),
+ P1 = gen_statem:send_request(STM, 'alive?'),
+ receive Msg ->
+ no_reply = gen_statem:check_response(Msg, P0),
+ {reply, yes} = gen_statem:check_response(Msg, P1)
+ after 1000 -> exit(timeout)
+ end,
ok.
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index e497b2fb5d..df6958cfa9 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1568,10 +1568,6 @@ request({put_chars, Encoding, Chars}, State) ->
request({put_chars, Encoding, Module, Function, Args}, State) ->
{ok, ok, State#state{q=[{put_chars, Encoding, Module, Function, Args} |
State#state.q ]}};
-request({put_chars,Chars}, State) ->
- {ok, ok, State#state{q=[{put_chars, Chars} | State#state.q ]}};
-request({put_chars,M,F,As}, State) ->
- {ok, ok, State#state{q=[{put_chars, M,F,As} | State#state.q ]}};
request({get_until, Encoding, Prompt, M, F, As}, State) ->
{ok, convert(State#state.nxt, Encoding, State#state.mode), State#state{nxt = eof, q = [{get_until, Encoding, Prompt, M, F, As} | State#state.q]}};
request({get_chars, Encoding, Prompt, N}, State) ->
@@ -1583,20 +1579,6 @@ request({get_line, Encoding, Prompt}, State) ->
State#state{nxt = eof,
q = [{get_line, Encoding, Prompt} |
State#state.q]}};
-request({get_until, Prompt, M, F, As}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_until, Prompt, M, F, As} | State#state.q]}};
-request({get_chars, Prompt, N}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_chars, Prompt, N} |
- State#state.q]}};
-request({get_line, Prompt}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_line, Prompt} |
- State#state.q]}};
request({get_geomentry,_}, State) ->
{error, {error,enotsup}, State};
request({setopts, Opts}, State) when Opts =:= [{binary, false}]; Opts =:= [list] ->
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 6f3cd8bf1b..0ad6989cbb 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -30,7 +30,7 @@
-export([t_update_with_3/1, t_update_with_4/1,
t_get_3/1, t_filter_2/1,
t_fold_3/1,t_map_2/1,t_size_1/1,
- t_iterator_1/1,
+ t_iterator_1/1, t_put_opt/1, t_merge_opt/1,
t_with_2/1,t_without_2/1]).
%%-define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}).
@@ -48,7 +48,7 @@ all() ->
[t_update_with_3,t_update_with_4,
t_get_3,t_filter_2,
t_fold_3,t_map_2,t_size_1,
- t_iterator_1,
+ t_iterator_1,t_put_opt,t_merge_opt,
t_with_2,t_without_2].
t_update_with_3(Config) when is_list(Config) ->
@@ -204,6 +204,28 @@ iter_kv(I) ->
[{K,V} | iter_kv(NI)]
end.
+t_put_opt(Config) when is_list(Config) ->
+ Value = id(#{complex => map}),
+ Map = id(#{a => Value}),
+ true = erts_debug:same(maps:put(a, Value, Map), Map),
+ ok.
+
+t_merge_opt(Config) when is_list(Config) ->
+ Small = id(#{a => 1}),
+ true = erts_debug:same(maps:merge(#{}, Small), Small),
+ true = erts_debug:same(maps:merge(Small, #{}), Small),
+ true = erts_debug:same(maps:merge(Small, Small), Small),
+
+ Large = maps:from_list([{I,I}||I<-lists:seq(1,200)]),
+ true = erts_debug:same(maps:merge(#{}, Large), Large),
+ true = erts_debug:same(maps:merge(Large, #{}), Large),
+ true = erts_debug:same(maps:merge(Large, Large), Large),
+
+ List = id([a|b]),
+ ?badmap([a|b],merge,[[a|b],[a|b]]) = (catch maps:merge(List, List)),
+
+ ok.
+
t_size_1(Config) when is_list(Config) ->
0 = maps:size(#{}),
10 = maps:size(maps:from_list([{{"k",I},I}||I<-lists:seq(1,10)])),
@@ -215,8 +237,11 @@ t_size_1(Config) when is_list(Config) ->
600 = maps:size(maps:from_list([{{"k",I},I}||I<-lists:seq(1,600)])),
%% error case
- ?badmap(a,size,[a]) = (catch maps:size(id(a))),
- ?badmap(<<>>,size,[<<>>]) = (catch maps:size(id(<<>>))),
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:size/2 to map_size.
+ {'EXIT', {{badmap,a}, _}} = (catch maps:size(id(a))),
+ {'EXIT', {{badmap,<<>>}, _}} = (catch maps:size(id(<<>>))),
ok.
id(I) -> I.
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 127b1317e4..b3673efb5a 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,8 +27,12 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
crash/1, stacktrace/1, sync_start_nolink/1, sync_start_link/1,
- spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, '\x{447}'/0,
- hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
+ sync_start_monitor/1, sync_start_monitor_link/1,
+ sync_start_timeout/1, sync_start_link_timeout/1,
+ sync_start_monitor_link_timeout/1,
+ spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, sp6/1, sp7/1,
+ sp8/1, sp9/1, sp10/1,
+ '\x{447}'/0, hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -39,6 +43,8 @@
-export([otp_6345_init/1, init_dont_hang_init/1]).
+-export([report_cb/1, report_cb_chars_limit/1, log/2, rcb_tester/0]).
+
-export([system_terminate/4]).
-ifdef(STANDALONE).
@@ -51,11 +57,14 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[crash, stacktrace, {group, sync_start}, spawn_opt, hibernate,
- {group, tickets}, stop, t_format, t_format_arbitrary].
+ {group, tickets}, stop, t_format, t_format_arbitrary, report_cb].
groups() ->
[{tickets, [], [otp_6345, init_dont_hang]},
- {sync_start, [], [sync_start_nolink, sync_start_link]}].
+ {sync_start, [], [sync_start_nolink, sync_start_link,
+ sync_start_monitor, sync_start_monitor_link,
+ sync_start_timeout, sync_start_link_timeout,
+ sync_start_monitor_link_timeout]}].
init_per_suite(Config) ->
Config.
@@ -275,6 +284,84 @@ sync_start_link(Config) when is_list(Config) ->
end,
ok.
+sync_start_monitor(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp6, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 2000 -> ct:fail(no_down)
+ end,
+ ok.
+
+sync_start_monitor_link(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp7, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 1000 -> ct:fail(no_down)
+ end,
+ receive received_exit -> ok
+ after 1000 -> ct:fail(no_exit)
+ end,
+ ok.
+
+sync_start_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp8, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp9, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_monitor_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp10, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive
+ {received_down, R} ->
+ killed = R,
+ ok
+ after 0 -> ct:fail(no_down)
+ end,
+ ok.
+
+
spawn_opt(Config) when is_list(Config) ->
F = fun sp1/0,
{name,Fname} = erlang:fun_info(F, name),
@@ -313,6 +400,89 @@ sp5(Tester) ->
Pid = proc_lib:start(?MODULE, sp4, [self(), Tester]),
Tester ! {sync_started, Pid}.
+sp6(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ exit(received_exit)
+ after 1000 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp7(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], infinity, [link]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ Tester ! received_exit
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp8(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon2, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+sp9(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start_link(?MODULE, sp4, [self(), Tester], 500),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+
+sp10(Tester) ->
+ process_flag(trap_exit, true),
+ {{error,timeout}, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
sp4(Parent, Tester) ->
Tester ! {self(), init},
receive
@@ -421,10 +591,12 @@ hib_receive_messages(N) ->
%% 'monitor' spawn_opt option.
otp_6345(Config) when is_list(Config) ->
Opts = [link,monitor],
- {'EXIT', {badarg,[{proc_lib,check_for_monitor,_,_}|_Stack]}} =
- (catch proc_lib:start(?MODULE, otp_6345_init, [self()],
- 1000, Opts)),
- ok.
+ try
+ blupp = proc_lib:start(?MODULE, otp_6345_init, [self()],
+ 1000, Opts)
+ catch
+ error:badarg -> ok
+ end.
otp_6345_init(Parent) ->
proc_lib:init_ack(Parent, {ok, self()}),
@@ -628,6 +800,180 @@ do_test_format(Report, Encoding, Depth) ->
'\x{aaa}t_format_looper'()
end.
+%% Test report callback for any Logger handler
+report_cb(_Config) ->
+ ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}),
+ Pid = proc_lib:spawn(?MODULE, sp2, []),
+ ct:sleep(100),
+ {links,[NPid]} = process_info(Pid,links),
+ NPidStr = pid_to_list(NPid),
+ Pid ! die,
+ Report =
+ receive
+ {report,R} ->
+ R
+ after 5000 ->
+ ct:fail(no_report_received)
+ end,
+
+ Str1 = flatten_report_cb(Report,#{}),
+ L1 = length(Str1),
+ Expected1 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str1: ~p",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ FormatOpts1 = #{},
+ Str1 = flatten_report_cb(Report,FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str1: ~p",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_report_cb(Report,FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str2: ~p",[Str2]),
+ ct:log("length(Str2): ~p",[L2]),
+ true = lists:prefix(Expected2,Str2),
+ true = L2<L1,
+
+ FormatOpts3 = #{chars_limit=>500},
+ Str3 = flatten_report_cb(Report,FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str3: ~p",[Str3]),
+ ct:log("length(Str3): ~p",[L3]),
+ true = lists:prefix(Expected3,Str3),
+ true = L3<L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_report_cb(Report,FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str4: ~p",[Str4]),
+ ct:log("length(Str4): ~p",[L4]),
+ true = lists:prefix(Expected4,Str4),
+ true = L4<L1,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_report_cb(Report,FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str5: ~p",[Str5]),
+ ct:log("length(Str5): ~p",[L5]),
+ true = lists:prefix(Expected5,Str5),
+ true = L5<L4,
+ %% Check that neighbour information is printed
+ SplitFun = fun($;) -> false; (_) -> true end,
+ ExpectedNeighbours5 = "; neighbours: neighbour: pid: "++NPidStr++
+ ", registered_name: []",
+ true = lists:prefix(ExpectedNeighbours5,lists:dropwhile(SplitFun, Str5)),
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>500},
+ Str6 = flatten_report_cb(Report,FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str6: ~p",[Str6]),
+ ct:log("length(Str6): ~p",[L6]),
+ true = lists:prefix(Expected6,Str6),
+ true = L6<L4,
+ %% Check that only pid is printed for neighbour, due to chars_limit
+ ExpectedNeighbours6 = "; neighbours: ["++NPidStr++"]",
+ ExpectedNeighbours6 = lists:dropwhile(SplitFun, Str6),
+
+ ok = logger:remove_handler(?MODULE),
+ ok.
+
+report_cb_chars_limit(_Config) ->
+ %% This test does not really test anything, it just formats the
+ %% crash reports with different settings and prints the result. It
+ %% could be used as an example if report_cb was to be modified
+ %% for better utilization of the available number of characters
+ %% according to the chars_limit setting.
+ %%
+ %% Currently, multi-line formatting with chars_limit=1024 gives
+ %% a final report of 1696 character. The excess is due to the fact
+ %% that io_lib_pretty counts non-white characters--the indentation
+ %% of the formatted exception is not counted.
+ %%
+ %% Single-line formatting with chars_limit=1024 gives a final
+ %% report of 1104 characters.
+ %%
+ %% Single-line formatting a fake report with chars_limit=1024 gives
+ %% a final report of 1024 characters.
+
+ ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}),
+ Pid = proc_lib:spawn(?MODULE, rcb_tester, []),
+ ct:sleep(500),
+ Pid ! die,
+ Report =
+ receive
+ {report,R} ->
+ R
+ after 5000 ->
+ ct:fail(no_report_received)
+ end,
+
+ ct:sleep(500), % To separate debug calls to erlang:display(), if any.
+ Str1 = flatten_report_cb(Report,#{}),
+ L1 = length(Str1),
+ ct:log("Multi-line, no size limit:~n~s",[Str1]),
+ ct:log("Length, multi-line, no size limit: ~p",[L1]),
+
+ ct:sleep(500),
+ FormatOpts2 = #{chars_limit=>1024},
+ Str2 = flatten_report_cb(Report,FormatOpts2),
+ L2 = length(Str2),
+ ct:log("Multi-line, chars_limit=1024:~n~s",[Str2]),
+ ct:log("Length, multi-line, chars_limit=1024: ~p",[L2]),
+
+ ct:sleep(500),
+ FormatOpts3 = #{single_line=>true, chars_limit=>1024},
+ Str3 = flatten_report_cb(Report,FormatOpts3),
+ L3 = length(Str3),
+ ct:log("Single-line, chars_limit=1024:~n~s",[Str3]),
+ ct:log("Length, single-line, chars_limit=1024: ~p",[L3]),
+
+ ct:sleep(500),
+ Seq = lists:seq(1, 1000),
+ FakeReport = [[{fake_tag,Seq}],Seq],
+ FReport = #{label=>{proc_lib,crash}, report=>FakeReport},
+ Str4 = flatten_report_cb(FReport,FormatOpts3),
+ L4 = length(Str4),
+ ct:log("Fake: Single-line, chars_limit=1024:~n~s",[Str4]),
+ ct:log("Fake: Length, single-line, chars_limit=1024: ~p",[L4]),
+
+ ok = logger:remove_handler(?MODULE),
+ ok.
+
+rcb_tester() ->
+ L = lists:seq(1,255),
+ Term = [{some_data,#{pids=>processes(),
+ info=>process_info(self())}},
+ {tabs,lists:sort(ets:all())},
+ {bin,list_to_binary(L)},
+ {list,L}],
+
+ %% Put something in process dictionary
+ [put(K,V) ||{K,V} <- Term],
+
+ %% Add some messages
+ [self() ! {some_message,T} || T <- Term],
+
+ %% Create some neighbours
+ [_ = proc_lib:spawn_link(?MODULE,sp1,[]) || _ <- lists:seq(1,5)],
+
+ receive
+ die -> error({badmatch,Term})
+ end.
+
+flatten_report_cb(Report, Format) ->
+ lists:flatten(proc_lib:report_cb(Report, Format)).
+
%%-----------------------------------------------------------------
%% The error_logger handler used.
%%-----------------------------------------------------------------
@@ -648,3 +994,11 @@ handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
terminate(_Reason, State) ->
State.
+
+%%-----------------------------------------------------------------
+%% The Logger handler used.
+%%-----------------------------------------------------------------
+log(#{msg:={report,Report}},#{config:=Pid}) ->
+ Pid ! {report,Report};
+log(_,_) ->
+ ok.
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index cdb6031b07..0f42e4632f 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -601,7 +601,7 @@ otp_5327(Config) when is_list(Config) ->
comm_err(<<"<<103133:64/binary>> = <<103133:64/float>>.">>),
"exception error: interpreted function with arity 1 called with two arguments" =
comm_err(<<"(fun(X) -> X end)(a,b).">>),
- {'EXIT', {{illegal_pattern,_}, _}} =
+ {'EXIT', {{badmatch,<<17:32>>}, _}} =
(catch evaluate("<<A:a>> = <<17:32>>.", [])),
C = <<"
<<A:4,B:4,C:4,D:4,E:4,F:4>> = <<\"hej\">>,
@@ -614,6 +614,9 @@ otp_5327(Config) when is_list(Config) ->
%% unbound_var would be nicer...
{'EXIT',{{illegal_pattern,_},_}} =
(catch evaluate(<<"<<A:B>> = <<17:32>>.">>, [])),
+ %% A badarith exception is turned into badmatch.
+ {'EXIT', {{badmatch,<<1777:32>>}, _}} =
+ (catch evaluate(<<"<<A:(1/0)>> = <<1777:32>>.">>, [])),
%% undefined_bittype is turned into badmatch:
{'EXIT',{{badmatch,<<17:32>>},_}} =
(catch evaluate(<<"<<A/apa>> = <<17:32>>.">>, [])),
@@ -2941,7 +2944,7 @@ otp_14296(Config) when is_list(Config) ->
end(),
fun() ->
- Port = open_port({spawn, "ls"}, [{line,1}]),
+ Port = open_port({spawn, "erl"}, [{line,1}]),
KnownPort = erlang:port_to_list(Port),
S = KnownPort ++ ".",
R = KnownPort ++ ".\n",
@@ -3141,25 +3144,16 @@ io_request({get_geometry,columns}, S) ->
{ok,80,S};
io_request({get_geometry,rows}, S) ->
{ok,24,S};
-io_request({put_chars,Chars}, S) ->
- {ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,latin1,Chars}, S) ->
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,unicode,Chars0}, S) ->
Chars = unicode:characters_to_list(Chars0),
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
-io_request({put_chars,Mod,Func,Args}, S) ->
- case catch apply(Mod, Func, Args) of
- Chars when is_list(Chars) ->
- io_request({put_chars,Chars}, S)
- end;
io_request({put_chars,Enc,Mod,Func,Args}, S) ->
case catch apply(Mod, Func, Args) of
Chars when is_list(Chars) ->
io_request({put_chars,Enc,Chars}, S)
end;
-io_request({get_until,_Prompt,Mod,Func,ExtraArgs}, S) ->
- get_until(Mod, Func, ExtraArgs, S, latin1);
io_request({get_until,Enc,_Prompt,Mod,Func,ExtraArgs}, S) ->
get_until(Mod, Func, ExtraArgs, S, Enc).
diff --git a/lib/stdlib/test/shell_docs_SUITE.erl b/lib/stdlib/test/shell_docs_SUITE.erl
new file mode 100644
index 0000000000..69cca467d0
--- /dev/null
+++ b/lib/stdlib/test/shell_docs_SUITE.erl
@@ -0,0 +1,88 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(shell_docs_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+-export([render/1]).
+
+-include_lib("kernel/include/eep48.hrl").
+
+init_per_testcase(_Case, Config) ->
+ Config.
+
+end_per_testcase(_Case, Config) ->
+ ok.
+
+suite() ->
+ [{timetrap,{minutes,10}}].
+
+all() ->
+ [render].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+render(_Config) ->
+ Avail = code:all_available(),
+ [render_test(Mod) || {Mod,_,_} <- Avail],
+ ok.
+render_test(Mod) when is_list(Mod) ->
+ render_test(list_to_atom(Mod));
+render_test(Mod) ->
+ try
+ case code:get_doc(Mod) of
+ {error,missing} ->
+ ok;
+ {ok, #docs_v1{ docs = Docs } = D} ->
+ shell_docs:render(Mod, D),
+ shell_docs:render_type(Mod, D),
+ [try
+ shell_docs:render(Mod, F, A, D)
+ catch _E:R:ST ->
+ io:format("Failed to render ~p:~p/~p~n~p:~p~n~p~n",
+ [Mod,F,A,R,ST,shell_docs:get_doc(Mod,F,A)]),
+ erlang:raise(error,R,ST)
+ end || {F,A} <- Mod:module_info(exports)],
+ [try
+ shell_docs:render_type(Mod, T, A, D)
+ catch _E:R:ST ->
+ io:format("Failed to render type ~p:~p/~p~n~p:~p~n~p~n",
+ [Mod,T,A,R,ST,shell_docs:get_type_doc(Mod,T,A)]),
+ erlang:raise(error,R,ST)
+ end || {{type,T,A},_,_,_,_} <- Docs]
+ end
+ catch throw:R:ST ->
+ io:format("Failed to render ~p~n~p:~p~n",[Mod,R,ST]),
+ exit(R)
+ end.
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 0b7510c305..5bbd16d1f4 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -27,6 +27,8 @@
init_per_testcase/2, end_per_testcase/2,
app_test/1, appup_test/1, assert_test/1]).
+-compile(r21).
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
@@ -95,7 +97,7 @@ appup_tests(App,{OkVsns0,NokVsns}) ->
create_test_vsns(App) ->
ThisMajor = erlang:system_info(otp_release),
FirstMajor = previous_major(ThisMajor),
- SecondMajor = previous_major(FirstMajor),
+ SecondMajor = previous_major(previous_major(FirstMajor)),
Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index c9aadd7f10..4475d7c06e 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -1111,12 +1111,22 @@ needs_check(_) -> true.
%%%% Timer stuff
time_func(Fun, Mode, Bin, Repeat) ->
- timer:sleep(100), %% Let emulator catch up and clean things before test runs
+ %% Let emulator catch up and clean things before test runs
+ timer:sleep(100),
+
+ %% We're spawning with a minimum heap size of 1k because certain benchmarks
+ %% (e.g. string_lexemes_binary) keep very little heap data alive, making
+ %% them very sensitive to changes in GC behavior.
+ %%
+ %% If we don't do this, something as benign as shrinking a stack frame can
+ %% make things run slower by making it stick to a smaller heap size,
+ %% causing it to GC more often.
Self = self(),
- Pid = spawn_link(fun() ->
- Str = mode(Mode, Bin),
- Self ! {self(),time_func(0,0,0, Fun, Str, undefined, Repeat)}
- end),
+ Pid = spawn_opt(fun() ->
+ Str = mode(Mode, Bin),
+ Res = time_func(0,0,0, Fun, Str, undefined, Repeat),
+ Self ! {self(), Res}
+ end, [link, {min_heap_size, 1 bsl 10}]),
receive {Pid,Msg} -> Msg end.
time_func(N,Sum,SumSq, Fun, Str, _, Repeat) when N < Repeat ->
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 36751c641b..b21cc9c6e0 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -77,7 +77,8 @@
hanging_restart_loop_rest_for_one/1,
hanging_restart_loop_simple/1, code_change/1, code_change_map/1,
code_change_simple/1, code_change_simple_map/1,
- order_of_children/1, scale_start_stop_many_children/1]).
+ order_of_children/1, scale_start_stop_many_children/1,
+ format_log_1/1, format_log_2/1]).
%%-------------------------------------------------------------------------
@@ -104,7 +105,8 @@ all() ->
simple_global_supervisor, hanging_restart_loop,
hanging_restart_loop_rest_for_one, hanging_restart_loop_simple,
code_change, code_change_map, code_change_simple, code_change_simple_map,
- order_of_children, scale_start_stop_many_children].
+ order_of_children, scale_start_stop_many_children,
+ format_log_1, format_log_2].
groups() ->
[{sup_start, [],
@@ -2398,6 +2400,249 @@ scale_start_stop_many_children() ->
ok.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ Error = shutdown_error,
+ Child = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[any,Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child}]},
+ {F1, A1} = supervisor:format_log(Report),
+ FExpected1 = " supervisor: ~tp~n"
+ " errorContext: ~tp~n"
+ " reason: ~tp~n"
+ " offender: ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Supervisor,Error,Term,Child] = A1,
+
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Child}]},
+ {PF1,PA1} = supervisor:format_log(Progress),
+ PFExpected1 = " supervisor: ~tp~n started: ~tp~n",
+ ct:log("PF1: ~ts~nPA1: ~tp", [PF1,PA1]),
+ PFExpected1 = PF1,
+ [Supervisor,Child] = PA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = supervisor:format_log(Report),
+ FExpected2 = " supervisor: ~tP~n"
+ " errorContext: ~tP~n"
+ " reason: ~tP~n"
+ " offender: ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ LimitedChild = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[any,'...']}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+
+ [Supervisor,Depth,Error,Depth,Limited,Depth,LimitedChild,Depth] = A2,
+
+ {PF2,PA2} = supervisor:format_log(Progress),
+ PFExpected2 = " supervisor: ~tP~n started: ~tP~n",
+ ct:log("PF2: ~ts~nPA2: ~tp", [PF2,PA2]),
+ PFExpected2 = PF2,
+ [Supervisor,Depth,LimitedChild,Depth] = PA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Error = shutdown_error,
+ Child = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child}]},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Child}]},
+ PStr1 = flatten_format_log(Progress, FormatOpts1),
+ PL1 = length(PStr1),
+ PExpected1 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("PStr1: ~ts", [PStr1]),
+ ct:log("length(PStr1): ~p", [PL1]),
+ true = PExpected1 =:= PStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9|...]\n"
+ " offender: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,[[...]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ PStr2 = flatten_format_log(Progress, FormatOpts2),
+ PL2 = length(PStr2),
+ PExpected2 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,[[...]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("PStr2: ~ts", [PStr2]),
+ ct:log("length(PStr2): ~p", [PL2]),
+ true = PExpected2 =:= PStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " supervisor: my_supervisor\n"
+ " errorContext: ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ PFormatOpts3 = #{chars_limit=>80},
+ PStr3 = flatten_format_log(Progress, PFormatOpts3),
+ PL3 = length(PStr3),
+ PExpected3 = " supervisor: my_supervisor\n started:",
+ ct:log("PStr3: ~ts", [PStr3]),
+ ct:log("length(PStr3): ~p", [PL3]),
+ true = lists:prefix(PExpected3, PStr3),
+ true = PL3 < PL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+
+ Expected4 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Offender: id=any_id,pid="++NameStr++".",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ PStr4 = flatten_format_log(Progress, FormatOpts4),
+ PL4 = length(PStr4),
+ PExpected4 = "Supervisor: my_supervisor. "
+ "Started: id=any_id,pid="++NameStr++".",
+ ct:log("PStr4: ~ts", [PStr4]),
+ ct:log("length(PStr4): ~p", [PL4]),
+ true = PExpected4 =:= PStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Offender: id=any_id,pid="++NameStr++".",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ PStr5 = flatten_format_log(Progress, FormatOpts5),
+ PL5 = length(PStr5),
+ PExpected5 = "Supervisor: my_supervisor. "
+ "Started: id=any_id,pid="++NameStr++".",
+ ct:log("PStr5: ~ts", [PStr5]),
+ ct:log("length(PStr5): ~p", [PL5]),
+ true = PExpected5 =:= PStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Supervisor: my_supervisor. Context:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ PFormatOpts6 = #{single_line=>true, chars_limit=>60},
+ PStr6 = flatten_format_log(Progress, PFormatOpts6),
+ PL6 = length(PStr6),
+ PExpected6 = "Supervisor: my_supervisor.",
+ ct:log("PStr6: ~ts", [PStr6]),
+ ct:log("length(PStr6): ~p", [PL6]),
+ true = lists:prefix(PExpected6, PStr6),
+ true = PL6 < PL4,
+
+ Child2 = [{nb_children,7},{id,any_id},
+ {mfargs,{mod,func,[Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report2 = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child2}]},
+ Str7 = flatten_format_log(Report2, FormatOpts6),
+ L7 = length(Str7),
+ ct:log("Str7: ~ts", [Str7]),
+ ct:log("length(Str7): ~p", [L7]),
+ true = string:find(Str7, "Offender: id=any_id,nb_children=7.") =/= nomatch,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(supervisor:format_log(Report, Format)).
+
%%-------------------------------------------------------------------------
terminate(Pid, Reason) when Reason =/= supervisor ->
terminate(dummy, Pid, dummy, Reason).
diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl
index 279ae91cdc..60af37dd9a 100644
--- a/lib/stdlib/test/supervisor_bridge_SUITE.erl
+++ b/lib/stdlib/test/supervisor_bridge_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,starting/1,
mini_terminate/1,mini_die/1,badstart/1,
- simple_global_supervisor/1]).
+ simple_global_supervisor/1, format_log_1/1, format_log_2/1]).
-export([client/1,init/1,internal_loop_init/1,terminate/2,server9212/0]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +35,8 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [starting, mini_terminate, mini_die, badstart, simple_global_supervisor].
+ [starting, mini_terminate, mini_die, badstart, simple_global_supervisor,
+ format_log_1, format_log_2].
groups() ->
[].
@@ -227,3 +228,202 @@ simple_global_supervisor(Config) when is_list(Config) ->
server9212() ->
supervisor_bridge:start_link({global,?bridge_name}, ?MODULE, 3).
+
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ Error = error,
+ Offender = [{pid,Name},{mod,a_module}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Offender}]},
+ {F1, A1} = supervisor_bridge:format_log(Report),
+ FExpected1 = " supervisor: ~tp~n"
+ " errorContext: ~tp~n"
+ " reason: ~tp~n"
+ " offender: ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Supervisor,Error,Term,Offender] = A1,
+
+ Started = [{pid,Name},{mfa,{a_module,init,[Term]}}],
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Started}]},
+ {PF1,PA1} = supervisor_bridge:format_log(Progress),
+ PFExpected1 = " supervisor: ~tp~n started: ~tp~n",
+ ct:log("PF1: ~ts~nPA1: ~tp", [PF1,PA1]),
+ PFExpected1 = PF1,
+ [Supervisor,Started] = PA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = supervisor_bridge:format_log(Report),
+ FExpected2 = " supervisor: ~tP~n"
+ " errorContext: ~tP~n"
+ " reason: ~tP~n"
+ " offender: ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ LimitedOffender = Offender,
+ [Supervisor,Depth,Error,Depth,Limited,Depth,LimitedOffender,Depth] = A2,
+
+ LimitedStarted =
+ [{pid,Name},{mfa,{a_module,init,[[1,2,3,4,5,6,7,8,9,'...']]}}],
+ {PF2,PA2} = supervisor_bridge:format_log(Progress),
+ PFExpected2 = " supervisor: ~tP~n started: ~tP~n",
+ ct:log("PF2: ~ts~nPA2: ~tp", [PF2,PA2]),
+ PFExpected2 = PF2,
+ [Supervisor,Depth,LimitedStarted,Depth] = PA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Error = shutdown_error,
+ Offender = [{pid,Name},{mod,a_module}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Offender}]},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Started = [{pid,Name},{mfa,{a_module,init,[Term]}}],
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Started}]},
+ PStr1 = flatten_format_log(Progress, FormatOpts1),
+ PL1 = length(PStr1),
+ PExpected1 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {mfa,{a_module,init,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}}]\n",
+ ct:log("PStr1: ~ts", [PStr1]),
+ ct:log("length(PStr1): ~p", [PL1]),
+ true = PExpected1 =:= PStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9|...]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ PStr2 = flatten_format_log(Progress, FormatOpts2),
+ PL2 = length(PStr2),
+ PExpected2 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},"
+ "{mfa,{a_module,init,[[1|...]]}}]\n",
+ ct:log("PStr2: ~ts", [PStr2]),
+ ct:log("length(PStr2): ~p", [PL2]),
+ true = PExpected2 =:= PStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = Expected3 =:= Str3,
+
+ PFormatOpts3 = #{chars_limit=>80},
+ PStr3 = flatten_format_log(Progress, PFormatOpts3),
+ PL3 = length(PStr3),
+ PExpected3 = " supervisor: my_supervisor\n started:",
+ ct:log("PStr3: ~ts", [PStr3]),
+ ct:log("length(PStr3): ~p", [PL3]),
+ true = lists:prefix(PExpected3, PStr3),
+ true = PL3 < PL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Offender: pid="++NameStr++",mod=a_module.",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ PStr4 = flatten_format_log(Progress, FormatOpts4),
+ PL4 = length(PStr4),
+ PExpected4 = "Supervisor: my_supervisor. "
+ "Started: pid="++NameStr++",mfa={a_module,init,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}.",
+ ct:log("PStr4: ~ts", [PStr4]),
+ ct:log("length(PStr4): ~p", [PL4]),
+ true = PExpected4 =:= PStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Offender: pid="++NameStr++",mod=a_module.",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ PStr5 = flatten_format_log(Progress, FormatOpts5),
+ PL5 = length(PStr5),
+ PExpected5 = "Supervisor: my_supervisor. "
+ "Started: pid="++NameStr++",mfa={a_module,init,[[1,2,3,4,5|...]]}.",
+ ct:log("PStr5: ~ts", [PStr5]),
+ ct:log("length(PStr5): ~p", [PL5]),
+ true = PExpected5 =:= PStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>100},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Supervisor: my_supervisor. Context:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ PFormatOpts6 = #{single_line=>true, chars_limit=>60},
+ PStr6 = flatten_format_log(Progress, PFormatOpts6),
+ PL6 = length(PStr6),
+ PExpected6 = "Supervisor: my_supervisor.",
+ ct:log("PStr6: ~ts", [PStr6]),
+ ct:log("length(PStr6): ~p", [PL6]),
+ true = lists:prefix(PExpected6, PStr6),
+ true = PL6 < PL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(supervisor_bridge:format_log(Report, Format)).
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index fb2b7dc45d..120c689d7f 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -28,7 +28,8 @@
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
memory/1,unicode/1,read_other_implementations/1,
sparse/1, init/1, leading_slash/1, dotdot/1,
- roundtrip_metadata/1, apply_file_info_opts/1]).
+ roundtrip_metadata/1, apply_file_info_opts/1,
+ incompatible_options/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -43,7 +44,7 @@ all() ->
symlinks, open_add_close, cooked_compressed, memory, unicode,
read_other_implementations,
sparse,init,leading_slash,dotdot,roundtrip_metadata,
- apply_file_info_opts].
+ apply_file_info_opts,incompatible_options].
groups() ->
[].
@@ -574,6 +575,29 @@ extract_from_open_file(Config) when is_list(Config) ->
verify_ports(Config).
+%% Make sure incompatible options are rejected when opening archives with file
+%% descriptors.
+incompatible_options(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ Long = filename:join(DataDir, "no_fancy_stuff.tar"),
+
+ {ok, File} = file:open(Long, [read]),
+ Handle = {file, File},
+
+ {error, {Handle, {incompatible_option, compressed}}}
+ = erl_tar:open(Handle, [read, compressed]),
+ {error, {Handle, {incompatible_option, cooked}}}
+ = erl_tar:open(Handle, [read, cooked]),
+
+ {error, {Handle, {incompatible_option, compressed}}}
+ = erl_tar:extract(Handle, [compressed]),
+ {error, {Handle, {incompatible_option, cooked}}}
+ = erl_tar:extract(Handle, [cooked]),
+
+ ok = file:close(File),
+
+ verify_ports(Config).
+
%% Test that archives containing symlinks can be created and extracted.
symlinks(Config) when is_list(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
index 6847953c23..fb4fec9fff 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakTest-11.0.0.txt
-# Date: 2018-03-18, 13:30:33 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -56,8 +56,6 @@
÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0020 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0020 × 0308 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 000D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -92,8 +90,6 @@
÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000D ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000A ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 000D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -128,8 +124,6 @@
÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000A ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000A ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0001 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 000D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -164,8 +158,6 @@
÷ 0001 ÷ 0308 × 200D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0001 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0001 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 034F ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F ÷ 000D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -200,8 +192,6 @@
÷ 034F × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 034F ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 034F ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 034F × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -236,8 +226,6 @@
÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1F1E6 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0600 × 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0600 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -272,8 +260,6 @@
÷ 0600 × 0308 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0600 × 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] <reserved-0378> (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0600 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0600 × 0308 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -308,8 +294,6 @@
÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0903 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0903 × 0308 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -344,8 +328,6 @@
÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1100 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1100 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -380,8 +362,6 @@
÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1160 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1160 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -416,8 +396,6 @@
÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 11A8 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 11A8 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -452,8 +430,6 @@
÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC00 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC00 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -488,8 +464,6 @@
÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC01 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC01 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 231A ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -524,8 +498,6 @@
÷ 231A × 0308 × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 231A ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 231A ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 231A × 0308 ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -560,8 +532,6 @@
÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0300 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0300 × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -596,8 +566,6 @@
÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200D ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 200D × 0308 ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0378 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 ÷ 000D ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -632,44 +600,6 @@
÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0378 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0378 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0378 × 0308 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 0308 × 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 0308 × 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]
@@ -695,6 +625,6 @@
÷ 2701 × 200D × 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
#
-# Lines: 672
+# Lines: 602
#
# EOF
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
index 0e9e678a85..eb056990a0 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
@@ -1,6 +1,6 @@
-# LineBreakTest-11.0.0.txt
-# Date: 2018-05-20, 09:03:09 GMT
-# © 2018 Unicode®, Inc.
+# LineBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:14 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
index 72a31bcdf1..54f43bdf4d 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
@@ -1,6 +1,6 @@
-# NormalizationTest-11.0.0.txt
-# Date: 2018-02-19, 18:33:08 GMT
-# © 2018 Unicode®, Inc.
+# NormalizationTest-12.1.0.txt
+# Date: 2019-04-01, 09:10:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -2149,6 +2149,7 @@
32FC;32FC;32FC;30F0;30F0; # (㋼; ㋼; ㋼; ヰ; ヰ; ) CIRCLED KATAKANA WI
32FD;32FD;32FD;30F1;30F1; # (㋽; ㋽; ㋽; ヱ; ヱ; ) CIRCLED KATAKANA WE
32FE;32FE;32FE;30F2;30F2; # (㋾; ㋾; ㋾; ヲ; ヲ; ) CIRCLED KATAKANA WO
+32FF;32FF;32FF;4EE4 548C;4EE4 548C; # (㋿; ㋿; ㋿; 令和; 令和; ) SQUARE ERA NAME REIWA
3300;3300;3300;30A2 30D1 30FC 30C8;30A2 30CF 309A 30FC 30C8; # (㌀; ㌀; ㌀; アパート; アハ◌゚ート; ) SQUARE APAATO
3301;3301;3301;30A2 30EB 30D5 30A1;30A2 30EB 30D5 30A1; # (㌁; ㌁; ㌁; アルファ; アルファ; ) SQUARE ARUHUA
3302;3302;3302;30A2 30F3 30DA 30A2;30A2 30F3 30D8 309A 30A2; # (㌂; ㌂; ㌂; アンペア; アンヘ◌゚ア; ) SQUARE ANPEA
@@ -16363,6 +16364,7 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
1F14F;1F14F;1F14F;0057 0043;0057 0043; # (🅏; 🅏; 🅏; WC; WC; ) SQUARED WC
1F16A;1F16A;1F16A;004D 0043;004D 0043; # (🅪; 🅪; 🅪; MC; MC; ) RAISED MC SIGN
1F16B;1F16B;1F16B;004D 0044;004D 0044; # (🅫; 🅫; 🅫; MD; MD; ) RAISED MD SIGN
+1F16C;1F16C;1F16C;004D 0052;004D 0052; # (🅬; 🅬; 🅬; MR; MR; ) RAISED MR SIGN
1F190;1F190;1F190;0044 004A;0044 004A; # (🆐; 🆐; 🆐; DJ; DJ; ) SQUARE DJ
1F200;1F200;1F200;307B 304B;307B 304B; # (🈀; 🈀; 🈀; ほか; ほか; ) SQUARE HIRAGANA HOKA
1F201;1F201;1F201;30B3 30B3;30B3 30B3; # (🈁; 🈁; 🈁; ココ; ココ; ) SQUARED KATAKANA KOKO
@@ -17685,6 +17687,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 0EB8 0EC8 0EB8 0E48 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌ຸ◌່◌ຸ◌่b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
0061 0EC8 0EB8 0E48 0EB9 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062; # (a◌່◌ຸ◌่◌ູb; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN UU, LATIN SMALL LETTER B
0061 0EB9 0EC8 0EB8 0E48 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062; # (a◌ູ◌່◌ຸ◌่b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN UU, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0EBA 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062; # (a◌ְ◌्◌゙◌຺b; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LAO SIGN PALI VIRAMA, LATIN SMALL LETTER B
+0061 0EBA 05B0 094D 3099 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062; # (a◌຺◌ְ◌्◌゙b; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; ) LATIN SMALL LETTER A, LAO SIGN PALI VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌ཱ◌່◌ຸ◌່b; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI EK, LATIN SMALL LETTER B
0061 0EC8 0F71 0EC8 0EB8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌່◌ཱ◌່◌ຸb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC9 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062; # (a◌ཱ◌່◌ຸ◌້b; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI THO, LATIN SMALL LETTER B
@@ -18453,6 +18457,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 11839 05B0 094D 3099 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062; # (a◌𑠹◌ְ◌्◌゙b; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; ) LATIN SMALL LETTER A, DOGRA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 3099 093C 0334 1183A 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062; # (a◌゙◌़◌̴◌𑠺b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, DOGRA SIGN NUKTA, LATIN SMALL LETTER B
0061 1183A 3099 093C 0334 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062; # (a◌𑠺◌゙◌़◌̴b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; ) LATIN SMALL LETTER A, DOGRA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 119E0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062; # (a◌ְ◌्◌゙◌𑧠b; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, NANDINAGARI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 119E0 05B0 094D 3099 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062; # (a◌𑧠◌ְ◌्◌゙b; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; ) LATIN SMALL LETTER A, NANDINAGARI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A34 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062; # (a◌ְ◌्◌゙◌𑨴b; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SIGN VIRAMA, LATIN SMALL LETTER B
0061 11A34 05B0 094D 3099 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062; # (a◌𑨴◌ְ◌्◌゙b; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; ) LATIN SMALL LETTER A, ZANABAZAR SQUARE SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A47 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062; # (a◌ְ◌्◌゙◌𑩇b; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SUBJOINER, LATIN SMALL LETTER B
@@ -18637,6 +18643,28 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 1E029 0315 0300 05AE 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062; # (a◌𞀩◌̕◌̀◌֮b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER IOTATED BIG YUS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 0315 0300 05AE 1E02A 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062; # (a◌̕◌̀◌֮◌𞀪b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GLAGOLITIC LETTER FITA, LATIN SMALL LETTER B
0061 1E02A 0315 0300 05AE 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062; # (a◌𞀪◌̕◌̀◌֮b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER FITA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E130 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062; # (a◌̕◌̀◌֮◌𞄰b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-B, LATIN SMALL LETTER B
+0061 1E130 0315 0300 05AE 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062; # (a◌𞄰◌̕◌̀◌֮b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-B, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E131 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062; # (a◌̕◌̀◌֮◌𞄱b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-M, LATIN SMALL LETTER B
+0061 1E131 0315 0300 05AE 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062; # (a◌𞄱◌̕◌̀◌֮b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-M, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E132 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062; # (a◌̕◌̀◌֮◌𞄲b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-J, LATIN SMALL LETTER B
+0061 1E132 0315 0300 05AE 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062; # (a◌𞄲◌̕◌̀◌֮b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-J, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E133 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062; # (a◌̕◌̀◌֮◌𞄳b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-V, LATIN SMALL LETTER B
+0061 1E133 0315 0300 05AE 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062; # (a◌𞄳◌̕◌̀◌֮b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-V, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E134 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062; # (a◌̕◌̀◌֮◌𞄴b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-S, LATIN SMALL LETTER B
+0061 1E134 0315 0300 05AE 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062; # (a◌𞄴◌̕◌̀◌֮b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-S, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E135 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062; # (a◌̕◌̀◌֮◌𞄵b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-G, LATIN SMALL LETTER B
+0061 1E135 0315 0300 05AE 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062; # (a◌𞄵◌̕◌̀◌֮b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-G, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E136 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062; # (a◌̕◌̀◌֮◌𞄶b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-D, LATIN SMALL LETTER B
+0061 1E136 0315 0300 05AE 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062; # (a◌𞄶◌̕◌̀◌֮b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-D, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EC 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062; # (a◌̕◌̀◌֮◌𞋬b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUP, LATIN SMALL LETTER B
+0061 1E2EC 0315 0300 05AE 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062; # (a◌𞋬◌̕◌̀◌֮b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2ED 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062; # (a◌̕◌̀◌֮◌𞋭b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUPNI, LATIN SMALL LETTER B
+0061 1E2ED 0315 0300 05AE 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062; # (a◌𞋭◌̕◌̀◌֮b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUPNI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EE 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062; # (a◌̕◌̀◌֮◌𞋮b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOI, LATIN SMALL LETTER B
+0061 1E2EE 0315 0300 05AE 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062; # (a◌𞋮◌̕◌̀◌֮b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EF 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062; # (a◌̕◌̀◌֮◌𞋯b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOINI, LATIN SMALL LETTER B
+0061 1E2EF 0315 0300 05AE 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062; # (a◌𞋯◌̕◌̀◌֮b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOINI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D0 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062; # (a◌֚◌̖◌〪◌𞣐b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TEENS, LATIN SMALL LETTER B
0061 1E8D0 059A 0316 302A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062; # (a◌𞣐◌֚◌̖◌〪b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; ) LATIN SMALL LETTER A, MENDE KIKAKUI COMBINING NUMBER TEENS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D1 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062; # (a◌֚◌̖◌〪◌𞣑b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TENS, LATIN SMALL LETTER B
diff --git a/lib/stdlib/test/win32reg_SUITE.erl b/lib/stdlib/test/win32reg_SUITE.erl
index 5e44e16ddc..f7d3d8da97 100644
--- a/lib/stdlib/test/win32reg_SUITE.erl
+++ b/lib/stdlib/test/win32reg_SUITE.erl
@@ -59,23 +59,27 @@ long(Config) when is_list(Config) ->
{ok,Read} = win32reg:open([read]),
ok = win32reg:change_key(Read, "\\hklm"),
- ok = win32reg:change_key(Read, LongKey),
- {ok,ErlangKey} = win32reg:current_key(Read),
- io:format("Erlang key: ~s~n", [ErlangKey]),
- ok = win32reg:close(Read),
-
- {ok,Reg} = win32reg:open([read, write]),
- %% Write a long value and read it back.
- TestKey = "test_key",
- LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
- ok = win32reg:set_value(Reg, TestKey, LongValue),
- {ok,LongValue} = win32reg:value(Reg, TestKey),
-
- io:format("Where ~p Key ~s Value ~s ~n", [win32reg:current_key(Reg), TestKey, LongValue]),
- %% Done.
-
- ok = win32reg:close(Reg),
- ok.
+ case os:getenv("WSLENV") of
+ false ->
+ ok = win32reg:change_key(Read, LongKey),
+ {ok,ErlangKey} = win32reg:current_key(Read),
+ io:format("Erlang key: ~s~n", [ErlangKey]),
+ ok = win32reg:close(Read),
+
+ {ok,Reg} = win32reg:open([read, write]),
+ %% Write a long value and read it back.
+ TestKey = "test_key",
+ LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
+ ok = win32reg:set_value(Reg, TestKey, LongValue),
+ {ok,LongValue} = win32reg:value(Reg, TestKey),
+
+ io:format("Where ~p Key ~s Value ~s ~n", [win32reg:current_key(Reg), TestKey, LongValue]),
+ %% Done.
+ ok = win32reg:close(Reg);
+ _ ->
+ %% We have installed erlang when testing on win10 and newer
+ ok
+ end.
evil_write(Config) when is_list(Config) ->
Key = "Software\\Ericsson\\Erlang",
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 081bffa7cb..c1be2786da 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1,
unzip_traversal_exploit/1,
compress_control/1,
- foldl/1,fd_leak/1]).
+ foldl/1,fd_leak/1,unicode/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -40,7 +40,7 @@ all() ->
unzip_to_binary, zip_to_binary, unzip_options,
zip_options, list_dir_options, aliases, openzip_api,
zip_api, open_leak, unzip_jar, compress_control, foldl,
- unzip_traversal_exploit,fd_leak].
+ unzip_traversal_exploit,fd_leak,unicode].
groups() ->
[].
@@ -105,49 +105,32 @@ borderline_test(Size, TempDir) ->
%% Verify that Unix zip can read it. (if we have a unix zip that is!)
- unzip_list(Archive, Name),
+ zipinfo_match(Archive, Name),
ok.
-unzip_list(Archive, Name) ->
- case unix_unzip_exists() of
- true ->
- unzip_list1(Archive, Name);
+zipinfo_match(Archive, Name) ->
+ case check_zipinfo_exists() of
+ true ->
+ Encoding = file:native_name_encoding(),
+ Expect = unicode:characters_to_binary(Name ++ "\n",
+ Encoding, Encoding),
+ cmd_expect("zipinfo -1 " ++ Archive, Expect);
_ ->
ok
end.
-%% Used to do os:find_executable() to check if unzip exists, but on
-%% some hosts that would give an unzip program which did not take the
-%% "-Z" option.
-%% Here we check that "unzip -Z" (which should display usage) and
-%% check that it exists with status 0.
-unix_unzip_exists() ->
- case os:type() of
- {unix,_} ->
- Port = open_port({spawn,"unzip -Z > /dev/null"}, [exit_status]),
- receive
- {Port,{exit_status,0}} ->
- true;
- {Port,{exit_status,_Fail}} ->
- false
- end;
- _ ->
- false
- end.
-
-unzip_list1(Archive, Name) ->
- Expect = Name ++ "\n",
- cmd_expect("unzip -Z -1 " ++ Archive, Expect).
+check_zipinfo_exists() ->
+ is_list(os:find_executable("zipinfo")).
cmd_expect(Cmd, Expect) ->
- Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, eof]),
- get_data(Port, Expect).
+ Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, binary, eof]),
+ get_data(Port, Expect, <<>>).
-get_data(Port, Expect) ->
+get_data(Port, Expect, Acc) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, match_output(Bytes, Expect, Port));
+ get_data(Port, Expect, <<Acc/binary, Bytes/binary>>);
{Port, eof} ->
Port ! {self(), close},
receive
@@ -160,21 +143,17 @@ get_data(Port, Expect) ->
after 1 -> % force context switch
ok
end,
- match_output(eof, Expect, Port)
+ match_output(Acc, Expect, Port)
end.
-match_output([C|Output], [C|Expect], Port) ->
+match_output(<<C, Output/bits>>, <<C,Expect/bits>>, Port) ->
match_output(Output, Expect, Port);
-match_output([_|_], [_|_], Port) ->
+match_output(<<_, _/bits>>, <<_, _/bits>>, Port) ->
kill_port_and_fail(Port, badmatch);
-match_output([X|Output], [], Port) ->
- kill_port_and_fail(Port, {too_much_data, [X|Output]});
-match_output([], Expect, _Port) ->
- Expect;
-match_output(eof, [], _Port) ->
- [];
-match_output(eof, Expect, Port) ->
- kill_port_and_fail(Port, {unexpected_end_of_input, Expect}).
+match_output(<<_, _/bits>>=Rest, <<>>, Port) ->
+ kill_port_and_fail(Port, {too_much_data, Rest});
+match_output(<<>>, <<>>, _Port) ->
+ ok.
kill_port_and_fail(Port, Reason) ->
unlink(Port),
@@ -913,3 +892,137 @@ do_fd_leak(Bad, N) ->
io:format("Bad error after ~p attempts\n", [N]),
erlang:raise(C, R, Stk)
end.
+
+unicode(Config) ->
+ case file:native_name_encoding() of
+ latin1 ->
+ {comment, "Native name encoding is Latin-1; skipping all tests"};
+ utf8 ->
+ DataDir = proplists:get_value(data_dir, Config),
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config)),
+ test_file_comment(DataDir),
+ test_archive_comment(DataDir),
+ test_bad_comment(DataDir),
+ test_latin1_archive(DataDir),
+ case has_zip() of
+ false ->
+ {comment, "No zip program found; skipping some tests"};
+ true ->
+ case zip_is_unicode_aware() of
+ true ->
+ test_filename_compatibility(),
+ ok;
+ false ->
+ {comment, "Old zip program; skipping some tests"}
+ end
+ end
+ end.
+
+test_filename_compatibility() ->
+ FancyName = "üñíĉòdë한",
+ Archive = "test.zip",
+
+ {ok, Archive} = zip:zip(Archive, [{FancyName, <<"test">>}]),
+ zipinfo_match(Archive, FancyName).
+
+test_file_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_file_comment.zip"),
+ Comments = ["a", [246], [1024]],
+ FileNames = [[C] ++ ".txt" || C <- [$a, 246, 1024]],
+ [begin
+ test_zip_file(FileName, Comment, Archive),
+ test_file_comment(FileName, Comment, Archive)
+ end ||
+ Comment <- Comments, FileName <- FileNames],
+ ok.
+
+test_zip_file(FileName, Comment, Archive) ->
+ _ = file:delete(Archive),
+ io:format("*** zip:zip(). Testing FileName ~ts, Comment ~ts\n",
+ [FileName, Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+ {ok, Archive} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]),
+ zip_check(Archive, Comment, FileName, "").
+
+test_file_comment(FileName, Comment, Archive) ->
+ case test_zip1() of
+ false ->
+ ok;
+ true ->
+ _ = file:delete(Archive),
+ io:format("*** zip(1). Testing FileName ~ts, Comment ~ts\n",
+ [FileName, Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+ R = os:cmd("echo " ++ Comment ++ "| zip -c " ++
+ Archive ++ " " ++ FileName),
+ io:format("os:cmd/1 returns ~lp\n", [R]),
+ zip_check(Archive, "", FileName, Comment)
+ end.
+
+test_archive_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_archive_comment.zip"),
+ Chars = [$a, 246, 1024],
+ [test_archive_comment(Char, Archive) || Char <- Chars],
+ ok.
+
+test_archive_comment(Char, Archive) ->
+ case test_zip1() of
+ false ->
+ ok;
+ true ->
+ _ = file:delete(Archive),
+ FileName = "a.txt",
+ Comment = [Char],
+ io:format("*** Testing archive Comment ~ts\n", [Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+
+ {ok, _} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]),
+ Res = os:cmd("zip -z " ++ Archive),
+ io:format("os:cmd/1 returns ~lp\n", [Res]),
+ true = lists:member(Char, Res),
+
+ os:cmd("echo " ++ Comment ++ "| zip -z "++
+ Archive ++ " " ++ FileName),
+ zip_check(Archive, Comment, FileName, "")
+ end.
+
+test_zip1() ->
+ has_zip() andalso zip_is_unicode_aware().
+
+has_zip() ->
+ os:find_executable("zip") =/= false.
+
+zip_is_unicode_aware() ->
+ S = os:cmd("zip -v | grep 'UNICODE_SUPPORT'"),
+ string:find(S, "UNICODE_SUPPORT") =/= nomatch.
+
+zip_check(Archive, ArchiveComment, FileName, FileNameComment) ->
+ {ok, CommentAndFiles} = zip:table(Archive),
+ io:format("zip:table/1 returns\n ~lp\n", [CommentAndFiles]),
+ io:format("checking archive comment ~lp\n", [ArchiveComment]),
+ [_] = [C || #zip_comment{comment = C} <- CommentAndFiles,
+ C =:= ArchiveComment],
+ io:format("checking filename ~lp\n", [FileName]),
+ io:format("and filename comment ~lp\n", [FileNameComment]),
+ [_] = [F || #zip_file{name = F, comment = C} <- CommentAndFiles,
+ F =:= FileName, C =:= FileNameComment],
+ {ok, FileList} = zip:unzip(Archive, [verbose]),
+ io:format("zip:unzip/2 returns\n ~lp\n", [FileList]),
+ true = lists:member(FileName, FileList),
+ ok.
+
+test_bad_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_bad_comment.zip"),
+ FileName = "a.txt",
+ file:write_file(FileName, ["something"]),
+ Comment = [9999999],
+ {error,{bad_unicode,Comment}} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]).
+
+test_latin1_archive(DataDir) ->
+ Archive = filename:join(DataDir, "zip-latin1.zip"),
+ FileName = [246] ++ ".txt",
+ ArchiveComment = [246],
+ zip_check(Archive, ArchiveComment, FileName, "").
diff --git a/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip b/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip
new file mode 100644
index 0000000000..d54c783653
--- /dev/null
+++ b/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip
Binary files differ
diff --git a/lib/stdlib/uc_spec/CaseFolding.txt b/lib/stdlib/uc_spec/CaseFolding.txt
index cce350f49c..7eeb915abf 100644
--- a/lib/stdlib/uc_spec/CaseFolding.txt
+++ b/lib/stdlib/uc_spec/CaseFolding.txt
@@ -1,6 +1,6 @@
-# CaseFolding-11.0.0.txt
-# Date: 2018-01-31, 08:20:09 GMT
-# © 2018 Unicode®, Inc.
+# CaseFolding-12.1.0.txt
+# Date: 2019-03-10, 10:53:00 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -1227,6 +1227,13 @@ A7B3; C; AB53; # LATIN CAPITAL LETTER CHI
A7B4; C; A7B5; # LATIN CAPITAL LETTER BETA
A7B6; C; A7B7; # LATIN CAPITAL LETTER OMEGA
A7B8; C; A7B9; # LATIN CAPITAL LETTER U WITH STROKE
+A7BA; C; A7BB; # LATIN CAPITAL LETTER GLOTTAL A
+A7BC; C; A7BD; # LATIN CAPITAL LETTER GLOTTAL I
+A7BE; C; A7BF; # LATIN CAPITAL LETTER GLOTTAL U
+A7C2; C; A7C3; # LATIN CAPITAL LETTER ANGLICANA W
+A7C4; C; A794; # LATIN CAPITAL LETTER C WITH PALATAL HOOK
+A7C5; C; 0282; # LATIN CAPITAL LETTER S WITH HOOK
+A7C6; C; 1D8E; # LATIN CAPITAL LETTER Z WITH PALATAL HOOK
AB70; C; 13A0; # CHEROKEE SMALL LETTER A
AB71; C; 13A1; # CHEROKEE SMALL LETTER E
AB72; C; 13A2; # CHEROKEE SMALL LETTER I
diff --git a/lib/stdlib/uc_spec/CompositionExclusions.txt b/lib/stdlib/uc_spec/CompositionExclusions.txt
index ea63595bd3..aa654974be 100644
--- a/lib/stdlib/uc_spec/CompositionExclusions.txt
+++ b/lib/stdlib/uc_spec/CompositionExclusions.txt
@@ -1,6 +1,6 @@
-# CompositionExclusions-11.0.0.txt
-# Date: 2017-12-06, 00:00:00 GMT [KW, LI]
-# © 2017 Unicode®, Inc.
+# CompositionExclusions-12.1.0.txt
+# Date: 2019-03-08, 23:59:00 GMT [KW, LI]
+# © 2019 Unicode®, Inc.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
diff --git a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
index 52052e6e33..b75b201f97 100644
--- a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
+++ b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakProperty-11.0.0.txt
-# Date: 2018-03-16, 20:34:02 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakProperty-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -27,10 +27,10 @@
110CD ; Prepend # Cf KAITHI NUMBER SIGN ABOVE
111C2..111C3 ; Prepend # Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA
11A3A ; Prepend # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
-11A86..11A89 ; Prepend # Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA
+11A84..11A89 ; Prepend # Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
11D46 ; Prepend # Lo MASARAM GONDI REPHA
-# Total code points: 20
+# Total code points: 22
# ================================================
@@ -61,10 +61,10 @@
2060..2064 ; Control # Cf [5] WORD JOINER..INVISIBLE PLUS
2065 ; Control # Cn <reserved-2065>
2066..206F ; Control # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
-D800..DFFF ; Control # Cs [2048] <surrogate-D800>..<surrogate-DFFF>
FEFF ; Control # Cf ZERO WIDTH NO-BREAK SPACE
FFF0..FFF8 ; Control # Cn [9] <reserved-FFF0>..<reserved-FFF8>
FFF9..FFFB ; Control # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+13430..13438 ; Control # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
1BCA0..1BCA3 ; Control # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
1D173..1D17A ; Control # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
E0000 ; Control # Cn <reserved-E0000>
@@ -73,7 +73,7 @@ E0002..E001F ; Control # Cn [30] <reserved-E0002>..<reserved-E001F>
E0080..E00FF ; Control # Cn [128] <reserved-E0080>..<reserved-E00FF>
E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
-# Total code points: 5925
+# Total code points: 3886
# ================================================
@@ -178,8 +178,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
0E34..0E3A ; Extend # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
0E47..0E4E ; Extend # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
0EB1 ; Extend # Mn LAO VOWEL SIGN MAI KAN
-0EB4..0EB9 ; Extend # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
-0EBB..0EBC ; Extend # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EB4..0EBC ; Extend # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
0EC8..0ECD ; Extend # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
0F18..0F19 ; Extend # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Extend # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -232,6 +231,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY
1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
1B34 ; Extend # Mn BALINESE SIGN REREKAN
+1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG
1B36..1B3A ; Extend # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
1B3C ; Extend # Mn BALINESE VOWEL SIGN LA LENGA
1B42 ; Extend # Mn BALINESE VOWEL SIGN PEPET
@@ -283,7 +283,7 @@ A947..A951 ; Extend # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A980..A982 ; Extend # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
A9B3 ; Extend # Mn JAVANESE SIGN CECAK TELU
A9B6..A9B9 ; Extend # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
-A9BC ; Extend # Mn JAVANESE VOWEL SIGN PEPET
+A9BC..A9BD ; Extend # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
A9E5 ; Extend # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Extend # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA31..AA32 ; Extend # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -368,6 +368,9 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11727..1172B ; Extend # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
1182F..11837 ; Extend # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11839..1183A ; Extend # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119D4..119D7 ; Extend # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Extend # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119E0 ; Extend # Mn NANDINAGARI SIGN VIRAMA
11A01..11A0A ; Extend # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A33..11A38 ; Extend # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
11A3B..11A3E ; Extend # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
@@ -394,6 +397,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11EF3..11EF4 ; Extend # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
16AF0..16AF4 ; Extend # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
16B30..16B36 ; Extend # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16F4F ; Extend # Mn MIAO SIGN CONSONANT MODIFIER BAR
16F8F..16F92 ; Extend # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9D..1BC9E ; Extend # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
1D165 ; Extend # Mc MUSICAL SYMBOL COMBINING STEM
@@ -414,13 +418,15 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
1E01B..1E021 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
1E023..1E024 ; Extend # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
1E026..1E02A ; Extend # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+1E130..1E136 ; Extend # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG
E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
-# Total code points: 1948
+# Total code points: 1970
# ================================================
@@ -489,7 +495,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1A57 ; SpacingMark # Mc TAI THAM CONSONANT SIGN LA TANG LAI
1A6D..1A72 ; SpacingMark # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
1B04 ; SpacingMark # Mc BALINESE SIGN BISAH
-1B35 ; SpacingMark # Mc BALINESE VOWEL SIGN TEDUNG
1B3B ; SpacingMark # Mc BALINESE VOWEL SIGN RA REPA TEDUNG
1B3D..1B41 ; SpacingMark # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
1B43..1B44 ; SpacingMark # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
@@ -504,7 +509,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1C24..1C2B ; SpacingMark # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C34..1C35 ; SpacingMark # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
1CE1 ; SpacingMark # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
-1CF2..1CF3 ; SpacingMark # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
1CF7 ; SpacingMark # Mc VEDIC SIGN ATIKRAMA
A823..A824 ; SpacingMark # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A827 ; SpacingMark # Mc SYLOTI NAGRI VOWEL SIGN OO
@@ -514,7 +518,7 @@ A952..A953 ; SpacingMark # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
A983 ; SpacingMark # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9BA..A9BB ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BD..A9C0 ; SpacingMark # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON
+A9BE..A9C0 ; SpacingMark # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
AA2F..AA30 ; SpacingMark # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA33..AA34 ; SpacingMark # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
AA4D ; SpacingMark # Mc CHAM CONSONANT SIGN FINAL H
@@ -566,6 +570,9 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11726 ; SpacingMark # Mc AHOM VOWEL SIGN E
1182C..1182E ; SpacingMark # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
11838 ; SpacingMark # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; SpacingMark # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119DC..119DF ; SpacingMark # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; SpacingMark # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A39 ; SpacingMark # Mc ZANABAZAR SQUARE SIGN VISARGA
11A57..11A58 ; SpacingMark # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
11A97 ; SpacingMark # Mc SOYOMBO SIGN VISARGA
@@ -578,11 +585,11 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11D93..11D94 ; SpacingMark # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
11D96 ; SpacingMark # Mc GUNJALA GONDI SIGN VISARGA
11EF5..11EF6 ; SpacingMark # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16F51..16F7E ; SpacingMark # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F51..16F87 ; SpacingMark # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
1D166 ; SpacingMark # Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
1D16D ; SpacingMark # Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT
-# Total code points: 362
+# Total code points: 375
# ================================================
diff --git a/lib/stdlib/uc_spec/PropList.txt b/lib/stdlib/uc_spec/PropList.txt
index ef86795abe..4394602fea 100644
--- a/lib/stdlib/uc_spec/PropList.txt
+++ b/lib/stdlib/uc_spec/PropList.txt
@@ -1,6 +1,6 @@
-# PropList-11.0.0.txt
-# Date: 2018-03-15, 04:28:35 GMT
-# © 2018 Unicode®, Inc.
+# PropList-12.1.0.txt
+# Date: 2019-03-10, 10:53:16 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -138,7 +138,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
0F0D..0F12 ; Terminal_Punctuation # Po [6] TIBETAN MARK SHAD..TIBETAN MARK RGYA GRAM SHAD
104A..104B ; Terminal_Punctuation # Po [2] MYANMAR SIGN LITTLE SECTION..MYANMAR SIGN SECTION
1361..1368 ; Terminal_Punctuation # Po [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
-166D..166E ; Terminal_Punctuation # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166E ; Terminal_Punctuation # Po CANADIAN SYLLABICS FULL STOP
16EB..16ED ; Terminal_Punctuation # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
1735..1736 ; Terminal_Punctuation # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
17D4..17D6 ; Terminal_Punctuation # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
@@ -157,7 +157,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
2E3C ; Terminal_Punctuation # Po STENOGRAPHIC FULL STOP
2E41 ; Terminal_Punctuation # Po REVERSED COMMA
2E4C ; Terminal_Punctuation # Po MEDIEVAL COMMA
-2E4E ; Terminal_Punctuation # Po PUNCTUS ELEVATUS MARK
+2E4E..2E4F ; Terminal_Punctuation # Po [2] PUNCTUS ELEVATUS MARK..CORNISH VERSE DIVIDER
3001..3002 ; Terminal_Punctuation # Po [2] IDEOGRAPHIC COMMA..IDEOGRAPHIC FULL STOP
A4FE..A4FF ; Terminal_Punctuation # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP
A60D..A60F ; Terminal_Punctuation # Po [3] VAI COMMA..VAI QUESTION MARK
@@ -553,15 +553,17 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1056..1057 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
1058..1059 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
105E..1060 ; Other_Alphabetic # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
-1062 ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN SGAW KAREN EU
-1067..1068 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR VOWEL SIGN WESTERN PWO KAREN UE
+1062..1064 ; Other_Alphabetic # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1067..106D ; Other_Alphabetic # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1071..1074 ; Other_Alphabetic # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
1082 ; Other_Alphabetic # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
1083..1084 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
1085..1086 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
-109C ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN AITON A
+1087..108C ; Other_Alphabetic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108D ; Other_Alphabetic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+108F ; Other_Alphabetic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
+109A..109C ; Other_Alphabetic # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A
109D ; Other_Alphabetic # Mn MYANMAR VOWEL SIGN AITON AI
-135F ; Other_Alphabetic # Mn ETHIOPIC COMBINING GEMINATION MARK
1712..1713 ; Other_Alphabetic # Mn [2] TAGALOG VOWEL SIGN I..TAGALOG VOWEL SIGN U
1732..1733 ; Other_Alphabetic # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
1752..1753 ; Other_Alphabetic # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
@@ -618,18 +620,21 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1C24..1C2B ; Other_Alphabetic # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C2C..1C33 ; Other_Alphabetic # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
1C34..1C35 ; Other_Alphabetic # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
-1CF2..1CF3 ; Other_Alphabetic # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
+1C36 ; Other_Alphabetic # Mn LEPCHA SIGN RAN
1DE7..1DF4 ; Other_Alphabetic # Mn [14] COMBINING LATIN SMALL LETTER ALPHA..COMBINING LATIN SMALL LETTER U WITH DIAERESIS
24B6..24E9 ; Other_Alphabetic # So [52] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN SMALL LETTER Z
2DE0..2DFF ; Other_Alphabetic # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
A674..A67B ; Other_Alphabetic # Mn [8] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC LETTER OMEGA
A69E..A69F ; Other_Alphabetic # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+A802 ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN DVISVARA
+A80B ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN ANUSVARA
A823..A824 ; Other_Alphabetic # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A825..A826 ; Other_Alphabetic # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
A827 ; Other_Alphabetic # Mc SYLOTI NAGRI VOWEL SIGN OO
A880..A881 ; Other_Alphabetic # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
A8B4..A8C3 ; Other_Alphabetic # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
A8C5 ; Other_Alphabetic # Mn SAURASHTRA SIGN CANDRABINDU
+A8FF ; Other_Alphabetic # Mn DEVANAGARI VOWEL SIGN AY
A926..A92A ; Other_Alphabetic # Mn [5] KAYAH LI VOWEL UE..KAYAH LI VOWEL O
A947..A951 ; Other_Alphabetic # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A952 ; Other_Alphabetic # Mc REJANG CONSONANT SIGN H
@@ -638,8 +643,9 @@ A983 ; Other_Alphabetic # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9B6..A9B9 ; Other_Alphabetic # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
A9BA..A9BB ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BC ; Other_Alphabetic # Mn JAVANESE VOWEL SIGN PEPET
-A9BD..A9BF ; Other_Alphabetic # Mc [3] JAVANESE CONSONANT SIGN KERET..JAVANESE CONSONANT SIGN CAKRA
+A9BC..A9BD ; Other_Alphabetic # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+A9BE..A9BF ; Other_Alphabetic # Mc [2] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE CONSONANT SIGN CAKRA
+A9E5 ; Other_Alphabetic # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Other_Alphabetic # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA2F..AA30 ; Other_Alphabetic # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA31..AA32 ; Other_Alphabetic # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -648,6 +654,9 @@ AA35..AA36 ; Other_Alphabetic # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONA
AA43 ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL NG
AA4C ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL M
AA4D ; Other_Alphabetic # Mc CHAM CONSONANT SIGN FINAL H
+AA7B ; Other_Alphabetic # Mc MYANMAR SIGN PAO KAREN TONE
+AA7C ; Other_Alphabetic # Mn MYANMAR SIGN TAI LAING TONE-2
+AA7D ; Other_Alphabetic # Mc MYANMAR SIGN TAI LAING TONE-5
AAB0 ; Other_Alphabetic # Mn TAI VIET MAI KANG
AAB2..AAB4 ; Other_Alphabetic # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
AAB7..AAB8 ; Other_Alphabetic # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
@@ -740,6 +749,11 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1182C..1182E ; Other_Alphabetic # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
1182F..11837 ; Other_Alphabetic # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11838 ; Other_Alphabetic # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; Other_Alphabetic # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119D4..119D7 ; Other_Alphabetic # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Other_Alphabetic # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119DC..119DF ; Other_Alphabetic # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; Other_Alphabetic # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A01..11A0A ; Other_Alphabetic # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A35..11A38 ; Other_Alphabetic # Mn [4] ZANABAZAR SQUARE SIGN CANDRABINDU..ZANABAZAR SQUARE SIGN ANUSVARA
11A39 ; Other_Alphabetic # Mc ZANABAZAR SQUARE SIGN VISARGA
@@ -773,8 +787,9 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
11D96 ; Other_Alphabetic # Mc GUNJALA GONDI SIGN VISARGA
11EF3..11EF4 ; Other_Alphabetic # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
11EF5..11EF6 ; Other_Alphabetic # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16B30..16B36 ; Other_Alphabetic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
-16F51..16F7E ; Other_Alphabetic # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F4F ; Other_Alphabetic # Mn MIAO SIGN CONSONANT MODIFIER BAR
+16F51..16F87 ; Other_Alphabetic # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+16F8F..16F92 ; Other_Alphabetic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9E ; Other_Alphabetic # Mn DUPLOYAN DOUBLE MARK
1E000..1E006 ; Other_Alphabetic # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
1E008..1E018 ; Other_Alphabetic # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
@@ -786,7 +801,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1F150..1F169 ; Other_Alphabetic # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
1F170..1F189 ; Other_Alphabetic # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
-# Total code points: 1334
+# Total code points: 1377
# ================================================
@@ -798,7 +813,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
4E00..9FEF ; Ideographic # Lo [20976] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FEF
F900..FA6D ; Ideographic # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D
FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
-17000..187F1 ; Ideographic # Lo [6130] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F1
+17000..187F7 ; Ideographic # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
18800..18AF2 ; Ideographic # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755
1B170..1B2FB ; Ideographic # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
20000..2A6D6 ; Ideographic # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
@@ -808,7 +823,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
2CEB0..2EBE0 ; Ideographic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
2F800..2FA1D ; Ideographic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
-# Total code points: 96184
+# Total code points: 96190
# ================================================
@@ -876,6 +891,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0DCA ; Diacritic # Mn SINHALA SIGN AL-LAKUNA
0E47..0E4C ; Diacritic # Mn [6] THAI CHARACTER MAITAIKHU..THAI CHARACTER THANTHAKHAT
0E4E ; Diacritic # Mn THAI CHARACTER YAMAKKAN
+0EBA ; Diacritic # Mn LAO SIGN PALI VIRAMA
0EC8..0ECC ; Diacritic # Mn [5] LAO TONE MAI EK..LAO CANCELLATION MARK
0F18..0F19 ; Diacritic # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Diacritic # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -887,10 +903,13 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0FC6 ; Diacritic # Mn TIBETAN SYMBOL PADMA GDAN
1037 ; Diacritic # Mn MYANMAR SIGN DOT BELOW
1039..103A ; Diacritic # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+1063..1064 ; Diacritic # Mc [2] MYANMAR TONE MARK SGAW KAREN HATHI..MYANMAR TONE MARK SGAW KAREN KE PHO
+1069..106D ; Diacritic # Mc [5] MYANMAR SIGN WESTERN PWO KAREN TONE-1..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1087..108C ; Diacritic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
108D ; Diacritic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
108F ; Diacritic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
109A..109B ; Diacritic # Mc [2] MYANMAR SIGN KHAMTI TONE-1..MYANMAR SIGN KHAMTI TONE-3
+135D..135F ; Diacritic # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
17C9..17D3 ; Diacritic # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
17DD ; Diacritic # Mn KHMER SIGN ATTHACAN
1939..193B ; Diacritic # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
@@ -935,9 +954,11 @@ A67C..A67D ; Diacritic # Mn [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILL
A67F ; Diacritic # Lm CYRILLIC PAYEROK
A69C..A69D ; Diacritic # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN
A6F0..A6F1 ; Diacritic # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+A700..A716 ; Diacritic # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
A717..A71F ; Diacritic # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
A720..A721 ; Diacritic # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
A788 ; Diacritic # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+A789..A78A ; Diacritic # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A7F8..A7F9 ; Diacritic # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
A8C4 ; Diacritic # Mn SAURASHTRA SIGN VIRAMA
A8E0..A8F1 ; Diacritic # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
@@ -992,6 +1013,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
116B7 ; Diacritic # Mn TAKRI SIGN NUKTA
1172B ; Diacritic # Mn AHOM SIGN KILLER
11839..1183A ; Diacritic # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119E0 ; Diacritic # Mn NANDINAGARI SIGN VIRAMA
11A34 ; Diacritic # Mn ZANABAZAR SQUARE SIGN VIRAMA
11A47 ; Diacritic # Mn ZANABAZAR SQUARE SUBJOINER
11A99 ; Diacritic # Mn SOYOMBO SUBJOINER
@@ -1000,6 +1022,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
11D44..11D45 ; Diacritic # Mn [2] MASARAM GONDI SIGN HALANTA..MASARAM GONDI VIRAMA
11D97 ; Diacritic # Mn GUNJALA GONDI VIRAMA
16AF0..16AF4 ; Diacritic # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+16B30..16B36 ; Diacritic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
16F8F..16F92 ; Diacritic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
16F93..16F9F ; Diacritic # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
1D167..1D169 ; Diacritic # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
@@ -1007,11 +1030,13 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
1D17B..1D182 ; Diacritic # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
1D185..1D18B ; Diacritic # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
1D1AA..1D1AD ; Diacritic # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1E130..1E136 ; Diacritic # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Diacritic # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Diacritic # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E946 ; Diacritic # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
1E948..1E94A ; Diacritic # Mn [3] ADLAM CONSONANT MODIFIER..ADLAM NUKTA
-# Total code points: 818
+# Total code points: 873
# ================================================
@@ -1043,9 +1068,11 @@ FF70 ; Extender # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND
11A98 ; Extender # Mn SOYOMBO GEMINATION MARK
16B42..16B43 ; Extender # Lm [2] PAHAWH HMONG SIGN VOS NRUA..PAHAWH HMONG SIGN IB YAM
16FE0..16FE1 ; Extender # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
+16FE3 ; Extender # Lm OLD CHINESE ITERATION MARK
+1E13C..1E13D ; Extender # Lm [2] NYIAKENG PUACHUE HMONG SIGN XW XW..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
1E944..1E946 ; Extender # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
-# Total code points: 44
+# Total code points: 47
# ================================================
@@ -1119,6 +1146,7 @@ FFFFE..FFFFF ; Noncharacter_Code_Point # Cn [2] <noncharacter-FFFFE>..<noncha
0D57 ; Other_Grapheme_Extend # Mc MALAYALAM AU LENGTH MARK
0DCF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN AELA-PILLA
0DDF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN GAYANUKITTA
+1B35 ; Other_Grapheme_Extend # Mc BALINESE VOWEL SIGN TEDUNG
200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
302E..302F ; Other_Grapheme_Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
@@ -1131,7 +1159,7 @@ FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND
1D16E..1D172 ; Other_Grapheme_Extend # Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
-# Total code points: 125
+# Total code points: 126
# ================================================
@@ -1547,10 +1575,7 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2B74..2B75 ; Pattern_Syntax # Cn [2] <reserved-2B74>..<reserved-2B75>
2B76..2B95 ; Pattern_Syntax # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
2B96..2B97 ; Pattern_Syntax # Cn [2] <reserved-2B96>..<reserved-2B97>
-2B98..2BC8 ; Pattern_Syntax # So [49] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED
-2BC9 ; Pattern_Syntax # Cn <reserved-2BC9>
-2BCA..2BFE ; Pattern_Syntax # So [53] TOP HALF BLACK CIRCLE..REVERSED RIGHT ANGLE
-2BFF ; Pattern_Syntax # Cn <reserved-2BFF>
+2B98..2BFF ; Pattern_Syntax # So [104] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..HELLSCHREIBER PAUSE SYMBOL
2E00..2E01 ; Pattern_Syntax # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
2E02 ; Pattern_Syntax # Pi LEFT SUBSTITUTION BRACKET
2E03 ; Pattern_Syntax # Pf RIGHT SUBSTITUTION BRACKET
@@ -1588,8 +1613,8 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2E40 ; Pattern_Syntax # Pd DOUBLE HYPHEN
2E41 ; Pattern_Syntax # Po REVERSED COMMA
2E42 ; Pattern_Syntax # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
-2E43..2E4E ; Pattern_Syntax # Po [12] DASH WITH LEFT UPTURN..PUNCTUS ELEVATUS MARK
-2E4F..2E7F ; Pattern_Syntax # Cn [49] <reserved-2E4F>..<reserved-2E7F>
+2E43..2E4F ; Pattern_Syntax # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
+2E50..2E7F ; Pattern_Syntax # Cn [48] <reserved-2E50>..<reserved-2E7F>
3001..3003 ; Pattern_Syntax # Po [3] IDEOGRAPHIC COMMA..DITTO MARK
3008 ; Pattern_Syntax # Ps LEFT ANGLE BRACKET
3009 ; Pattern_Syntax # Pe RIGHT ANGLE BRACKET
diff --git a/lib/stdlib/uc_spec/README-UPDATE.txt b/lib/stdlib/uc_spec/README-UPDATE.txt
index e1f5c8fcd0..73274e512a 100644
--- a/lib/stdlib/uc_spec/README-UPDATE.txt
+++ b/lib/stdlib/uc_spec/README-UPDATE.txt
@@ -2,12 +2,10 @@ When updating the unicode version copy the necessary files to this
directory.
And update the test files in stdlib/test/unicode_util_SUITE_data/*
-Unicode 11 was updated from:
-https://www.unicode.org/Public/11.0.0/ucd/
-https://www.unicode.org/Public/11.0.0/ucd/auxiliary/
-https://www.unicode.org/Public/emoji/11.0/
+Unicode 12.1 was updated from:
+https://www.unicode.org/Public/12.1.0/ucd/
+https://www.unicode.org/Public/12.1.0/ucd/auxiliary/
+https://www.unicode.org/Public/emoji/12.0/
Update the spec_version(..) function in the generator,
gen_unicode_mod.escript
-
-
diff --git a/lib/stdlib/uc_spec/SpecialCasing.txt b/lib/stdlib/uc_spec/SpecialCasing.txt
index c90d09acb3..1c04aacf97 100644
--- a/lib/stdlib/uc_spec/SpecialCasing.txt
+++ b/lib/stdlib/uc_spec/SpecialCasing.txt
@@ -1,6 +1,6 @@
-# SpecialCasing-11.0.0.txt
-# Date: 2018-02-22, 06:16:47 GMT
-# © 2018 Unicode®, Inc.
+# SpecialCasing-12.1.0.txt
+# Date: 2019-03-10, 10:53:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/uc_spec/UnicodeData.txt b/lib/stdlib/uc_spec/UnicodeData.txt
index ec32fafbce..e65aec52f7 100644
--- a/lib/stdlib/uc_spec/UnicodeData.txt
+++ b/lib/stdlib/uc_spec/UnicodeData.txt
@@ -640,7 +640,7 @@
027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6
0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
-0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;A7C5;;A7C5
0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
@@ -2809,6 +2809,7 @@
0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C77;TELUGU SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;;
0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
@@ -3203,14 +3204,24 @@
0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E86;LAO LETTER PALI GHA;Lo;0;L;;;;;N;;;;;
0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E89;LAO LETTER PALI CHA;Lo;0;L;;;;;N;;;;;
0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8C;LAO LETTER PALI JHA;Lo;0;L;;;;;N;;;;;
0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E8E;LAO LETTER PALI NYA;Lo;0;L;;;;;N;;;;;
+0E8F;LAO LETTER PALI TTA;Lo;0;L;;;;;N;;;;;
+0E90;LAO LETTER PALI TTHA;Lo;0;L;;;;;N;;;;;
+0E91;LAO LETTER PALI DDA;Lo;0;L;;;;;N;;;;;
+0E92;LAO LETTER PALI DDHA;Lo;0;L;;;;;N;;;;;
+0E93;LAO LETTER PALI NNA;Lo;0;L;;;;;N;;;;;
0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E98;LAO LETTER PALI DHA;Lo;0;L;;;;;N;;;;;
0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
@@ -3218,13 +3229,17 @@
0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA0;LAO LETTER PALI BHA;Lo;0;L;;;;;N;;;;;
0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EA8;LAO LETTER SANSKRIT SHA;Lo;0;L;;;;;N;;;;;
+0EA9;LAO LETTER SANSKRIT SSA;Lo;0;L;;;;;N;;;;;
0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAC;LAO LETTER PALI LLA;Lo;0;L;;;;;N;;;;;
0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
@@ -3238,6 +3253,7 @@
0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBA;LAO SIGN PALI VIRAMA;Mn;9;NSM;;;;;N;;;;;
0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
@@ -5079,7 +5095,7 @@
166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
-166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;So;0;L;;;;;N;;;;;
166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
@@ -6488,14 +6504,15 @@
1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
-1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
-1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF2;VEDIC SIGN ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
1CF7;VEDIC SIGN ATIKRAMA;Mc;0;L;;;;;N;;;;;
1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CFA;VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
@@ -6638,7 +6655,7 @@
1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
-1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C6;;A7C6
1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;;
@@ -10165,6 +10182,7 @@
2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC9;NEPTUNE FORM TWO;So;0;ON;;;;;N;;;;;
2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
@@ -10218,6 +10236,7 @@
2BFC;DOUBLED SYMBOL;So;0;ON;;;;;N;;;;;
2BFD;PASSED SYMBOL;So;0;ON;;;;;N;;;;;
2BFE;REVERSED RIGHT ANGLE;So;0;ON;;;;;Y;;;;;
+2BFF;HELLSCHREIBER PAUSE SYMBOL;So;0;ON;;;;;N;;;;;
2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30;
2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31;
2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32;
@@ -10756,6 +10775,7 @@
2E4C;MEDIEVAL COMMA;Po;0;ON;;;;;N;;;;;
2E4D;PARAGRAPHUS MARK;Po;0;ON;;;;;N;;;;;
2E4E;PUNCTUS ELEVATUS MARK;Po;0;ON;;;;;N;;;;;
+2E4F;CORNISH VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -11836,6 +11856,7 @@
32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+32FF;SQUARE ERA NAME REIWA;So;0;L;<square> 4EE4 548C;;;;N;;;;;
3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
@@ -14060,7 +14081,7 @@ A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
-A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C4;;A7C4
A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797;
A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796
@@ -14098,6 +14119,17 @@ A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7;
A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6
A7B8;LATIN CAPITAL LETTER U WITH STROKE;Lu;0;L;;;;;N;;;;A7B9;
A7B9;LATIN SMALL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;A7B8;;A7B8
+A7BA;LATIN CAPITAL LETTER GLOTTAL A;Lu;0;L;;;;;N;;;;A7BB;
+A7BB;LATIN SMALL LETTER GLOTTAL A;Ll;0;L;;;;;N;;;A7BA;;A7BA
+A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
+A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
+A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
+A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
+A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
+A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
+A7C5;LATIN CAPITAL LETTER S WITH HOOK;Lu;0;L;;;;;N;;;;0282;
+A7C6;LATIN CAPITAL LETTER Z WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;1D8E;
A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
@@ -14506,7 +14538,7 @@ A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;;
A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;;
A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
-A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;;
+A9BD;JAVANESE CONSONANT SIGN KERET;Mn;0;NSM;;;;;N;;;;;
A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;;
A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;;
A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;;
@@ -14863,6 +14895,8 @@ AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;;
AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;;
AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;;
AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;;
+AB66;LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB67;LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0
AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1
AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2
@@ -19105,6 +19139,29 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10FE0;ELYMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10FE1;ELYMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10FE2;ELYMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10FE3;ELYMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10FE4;ELYMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10FE5;ELYMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10FE6;ELYMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10FE7;ELYMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10FE8;ELYMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10FE9;ELYMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+10FEA;ELYMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10FEB;ELYMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10FEC;ELYMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+10FED;ELYMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+10FEE;ELYMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10FEF;ELYMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10FF0;ELYMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10FF1;ELYMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10FF2;ELYMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10FF3;ELYMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10FF4;ELYMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10FF5;ELYMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10FF6;ELYMAIC LIGATURE ZAYIN-YODH;Lo;0;R;;;;;N;;;;;
11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
@@ -19887,6 +19944,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;;
1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;;
1145E;NEWA SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+1145F;NEWA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;;
11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;;
11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;;
@@ -20209,6 +20267,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -20421,6 +20480,71 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;;
118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;;
+119A0;NANDINAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+119A1;NANDINAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+119A2;NANDINAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+119A3;NANDINAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+119A4;NANDINAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+119A5;NANDINAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+119A6;NANDINAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+119A7;NANDINAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+119AA;NANDINAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+119AB;NANDINAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+119AC;NANDINAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+119AD;NANDINAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+119AE;NANDINAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+119AF;NANDINAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+119B0;NANDINAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+119B1;NANDINAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+119B2;NANDINAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+119B3;NANDINAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+119B4;NANDINAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+119B5;NANDINAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+119B6;NANDINAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+119B7;NANDINAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+119B8;NANDINAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+119B9;NANDINAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+119BA;NANDINAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+119BB;NANDINAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+119BC;NANDINAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+119BD;NANDINAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+119BE;NANDINAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+119BF;NANDINAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+119C0;NANDINAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+119C1;NANDINAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+119C2;NANDINAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+119C3;NANDINAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+119C4;NANDINAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+119C5;NANDINAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+119C6;NANDINAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+119C7;NANDINAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+119C8;NANDINAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+119C9;NANDINAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+119CA;NANDINAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+119CB;NANDINAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+119CC;NANDINAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+119CD;NANDINAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+119CE;NANDINAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+119CF;NANDINAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+119D0;NANDINAGARI LETTER RRA;Lo;0;L;;;;;N;;;;;
+119D1;NANDINAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+119D2;NANDINAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+119D3;NANDINAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+119D4;NANDINAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+119D5;NANDINAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+119D6;NANDINAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+119D7;NANDINAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+119DA;NANDINAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+119DB;NANDINAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+119DC;NANDINAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+119DD;NANDINAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+119DE;NANDINAGARI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+119DF;NANDINAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+119E0;NANDINAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+119E1;NANDINAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+119E2;NANDINAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+119E3;NANDINAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+119E4;NANDINAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
11A00;ZANABAZAR SQUARE LETTER A;Lo;0;L;;;;;N;;;;;
11A01;ZANABAZAR SQUARE VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
11A02;ZANABAZAR SQUARE VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
@@ -20545,6 +20669,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11A81;SOYOMBO LETTER SA;Lo;0;L;;;;;N;;;;;
11A82;SOYOMBO LETTER HA;Lo;0;L;;;;;N;;;;;
11A83;SOYOMBO LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A84;SOYOMBO SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11A85;SOYOMBO SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
11A86;SOYOMBO CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
11A87;SOYOMBO CLUSTER-INITIAL LETTER LA;Lo;0;L;;;;;N;;;;;
11A88;SOYOMBO CLUSTER-INITIAL LETTER SHA;Lo;0;L;;;;;N;;;;;
@@ -20959,6 +21085,57 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
+11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+11FC2;TAMIL FRACTION ONE EIGHTIETH;No;0;L;;;;1/80;N;;;;;
+11FC3;TAMIL FRACTION ONE SIXTY-FOURTH;No;0;L;;;;1/64;N;;;;;
+11FC4;TAMIL FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+11FC5;TAMIL FRACTION ONE THIRTY-SECOND;No;0;L;;;;1/32;N;;;;;
+11FC6;TAMIL FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+11FC7;TAMIL FRACTION THREE SIXTY-FOURTHS;No;0;L;;;;3/64;N;;;;;
+11FC8;TAMIL FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+11FC9;TAMIL FRACTION ONE SIXTEENTH-1;No;0;L;;;;1/16;N;;;;;
+11FCA;TAMIL FRACTION ONE SIXTEENTH-2;No;0;L;;;;1/16;N;;;;;
+11FCB;TAMIL FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+11FCC;TAMIL FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+11FCD;TAMIL FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+11FCE;TAMIL FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+11FCF;TAMIL FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+11FD0;TAMIL FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+11FD1;TAMIL FRACTION ONE HALF-1;No;0;L;;;;1/2;N;;;;;
+11FD2;TAMIL FRACTION ONE HALF-2;No;0;L;;;;1/2;N;;;;;
+11FD3;TAMIL FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+11FD4;TAMIL FRACTION DOWNSCALING FACTOR KIIZH;No;0;L;;;;1/320;N;;;;;
+11FD5;TAMIL SIGN NEL;So;0;ON;;;;;N;;;;;
+11FD6;TAMIL SIGN CEVITU;So;0;ON;;;;;N;;;;;
+11FD7;TAMIL SIGN AAZHAAKKU;So;0;ON;;;;;N;;;;;
+11FD8;TAMIL SIGN UZHAKKU;So;0;ON;;;;;N;;;;;
+11FD9;TAMIL SIGN MUUVUZHAKKU;So;0;ON;;;;;N;;;;;
+11FDA;TAMIL SIGN KURUNI;So;0;ON;;;;;N;;;;;
+11FDB;TAMIL SIGN PATHAKKU;So;0;ON;;;;;N;;;;;
+11FDC;TAMIL SIGN MUKKURUNI;So;0;ON;;;;;N;;;;;
+11FDD;TAMIL SIGN KAACU;Sc;0;ET;;;;;N;;;;;
+11FDE;TAMIL SIGN PANAM;Sc;0;ET;;;;;N;;;;;
+11FDF;TAMIL SIGN PON;Sc;0;ET;;;;;N;;;;;
+11FE0;TAMIL SIGN VARAAKAN;Sc;0;ET;;;;;N;;;;;
+11FE1;TAMIL SIGN PAARAM;So;0;ON;;;;;N;;;;;
+11FE2;TAMIL SIGN KUZHI;So;0;ON;;;;;N;;;;;
+11FE3;TAMIL SIGN VELI;So;0;ON;;;;;N;;;;;
+11FE4;TAMIL WET CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE5;TAMIL DRY CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE6;TAMIL LAND SIGN;So;0;ON;;;;;N;;;;;
+11FE7;TAMIL SALT PAN SIGN;So;0;ON;;;;;N;;;;;
+11FE8;TAMIL TRADITIONAL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+11FE9;TAMIL TRADITIONAL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+11FEA;TAMIL CURRENT SIGN;So;0;ON;;;;;N;;;;;
+11FEB;TAMIL AND ODD SIGN;So;0;ON;;;;;N;;;;;
+11FEC;TAMIL SPENT SIGN;So;0;ON;;;;;N;;;;;
+11FED;TAMIL TOTAL SIGN;So;0;ON;;;;;N;;;;;
+11FEE;TAMIL IN POSSESSION SIGN;So;0;ON;;;;;N;;;;;
+11FEF;TAMIL STARTING FROM SIGN;So;0;ON;;;;;N;;;;;
+11FF0;TAMIL SIGN MUTHALIYA;So;0;ON;;;;;N;;;;;
+11FF1;TAMIL SIGN VAKAIYARAA;So;0;ON;;;;;N;;;;;
+11FFF;TAMIL PUNCTUATION END OF TEXT;Po;0;L;;;;;N;;;;;
12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
@@ -23264,6 +23441,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
+13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
+13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
+13433;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM START;Cf;0;L;;;;;N;;;;;
+13434;EGYPTIAN HIEROGLYPH INSERT AT TOP END;Cf;0;L;;;;;N;;;;;
+13435;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM END;Cf;0;L;;;;;N;;;;;
+13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
+13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
+13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -24782,6 +24968,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F45;MIAO LETTER BRI;Lo;0;L;;;;;N;;;;;
+16F46;MIAO LETTER SYI;Lo;0;L;;;;;N;;;;;
+16F47;MIAO LETTER DZYI;Lo;0;L;;;;;N;;;;;
+16F48;MIAO LETTER TE;Lo;0;L;;;;;N;;;;;
+16F49;MIAO LETTER TSE;Lo;0;L;;;;;N;;;;;
+16F4A;MIAO LETTER RTE;Lo;0;L;;;;;N;;;;;
+16F4F;MIAO SIGN CONSONANT MODIFIER BAR;Mn;0;NSM;;;;;N;;;;;
16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
@@ -24829,6 +25022,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F7F;MIAO VOWEL SIGN UOG;Mc;0;L;;;;;N;;;;;
+16F80;MIAO VOWEL SIGN YUI;Mc;0;L;;;;;N;;;;;
+16F81;MIAO VOWEL SIGN OG;Mc;0;L;;;;;N;;;;;
+16F82;MIAO VOWEL SIGN OER;Mc;0;L;;;;;N;;;;;
+16F83;MIAO VOWEL SIGN VW;Mc;0;L;;;;;N;;;;;
+16F84;MIAO VOWEL SIGN IG;Mc;0;L;;;;;N;;;;;
+16F85;MIAO VOWEL SIGN EA;Mc;0;L;;;;;N;;;;;
+16F86;MIAO VOWEL SIGN IONG;Mc;0;L;;;;;N;;;;;
+16F87;MIAO VOWEL SIGN UI;Mc;0;L;;;;;N;;;;;
16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
@@ -24848,8 +25050,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;;
16FE1;NUSHU ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE2;OLD CHINESE HOOK MARK;Po;0;ON;;;;;N;;;;;
+16FE3;OLD CHINESE ITERATION MARK;Lm;0;L;;;;;N;;;;;
17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
-187F1;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
@@ -25892,6 +26096,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B167;KATAKANA LETTER SMALL N;Lo;0;L;;;;;N;;;;;
1B170;NUSHU CHARACTER-1B170;Lo;0;L;;;;;N;;;;;
1B171;NUSHU CHARACTER-1B171;Lo;0;L;;;;;N;;;;;
1B172;NUSHU CHARACTER-1B172;Lo;0;L;;;;;N;;;;;
@@ -28820,6 +29031,136 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
+1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
+1E103;NYIAKENG PUACHUE HMONG LETTER TA;Lo;0;L;;;;;N;;;;;
+1E104;NYIAKENG PUACHUE HMONG LETTER HA;Lo;0;L;;;;;N;;;;;
+1E105;NYIAKENG PUACHUE HMONG LETTER NA;Lo;0;L;;;;;N;;;;;
+1E106;NYIAKENG PUACHUE HMONG LETTER XA;Lo;0;L;;;;;N;;;;;
+1E107;NYIAKENG PUACHUE HMONG LETTER NKA;Lo;0;L;;;;;N;;;;;
+1E108;NYIAKENG PUACHUE HMONG LETTER CA;Lo;0;L;;;;;N;;;;;
+1E109;NYIAKENG PUACHUE HMONG LETTER LA;Lo;0;L;;;;;N;;;;;
+1E10A;NYIAKENG PUACHUE HMONG LETTER SA;Lo;0;L;;;;;N;;;;;
+1E10B;NYIAKENG PUACHUE HMONG LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E10C;NYIAKENG PUACHUE HMONG LETTER NCA;Lo;0;L;;;;;N;;;;;
+1E10D;NYIAKENG PUACHUE HMONG LETTER NTSA;Lo;0;L;;;;;N;;;;;
+1E10E;NYIAKENG PUACHUE HMONG LETTER KA;Lo;0;L;;;;;N;;;;;
+1E10F;NYIAKENG PUACHUE HMONG LETTER DA;Lo;0;L;;;;;N;;;;;
+1E110;NYIAKENG PUACHUE HMONG LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E111;NYIAKENG PUACHUE HMONG LETTER NRA;Lo;0;L;;;;;N;;;;;
+1E112;NYIAKENG PUACHUE HMONG LETTER VA;Lo;0;L;;;;;N;;;;;
+1E113;NYIAKENG PUACHUE HMONG LETTER NTXA;Lo;0;L;;;;;N;;;;;
+1E114;NYIAKENG PUACHUE HMONG LETTER TXA;Lo;0;L;;;;;N;;;;;
+1E115;NYIAKENG PUACHUE HMONG LETTER FA;Lo;0;L;;;;;N;;;;;
+1E116;NYIAKENG PUACHUE HMONG LETTER RA;Lo;0;L;;;;;N;;;;;
+1E117;NYIAKENG PUACHUE HMONG LETTER QA;Lo;0;L;;;;;N;;;;;
+1E118;NYIAKENG PUACHUE HMONG LETTER YA;Lo;0;L;;;;;N;;;;;
+1E119;NYIAKENG PUACHUE HMONG LETTER NQA;Lo;0;L;;;;;N;;;;;
+1E11A;NYIAKENG PUACHUE HMONG LETTER PA;Lo;0;L;;;;;N;;;;;
+1E11B;NYIAKENG PUACHUE HMONG LETTER XYA;Lo;0;L;;;;;N;;;;;
+1E11C;NYIAKENG PUACHUE HMONG LETTER NPA;Lo;0;L;;;;;N;;;;;
+1E11D;NYIAKENG PUACHUE HMONG LETTER DLA;Lo;0;L;;;;;N;;;;;
+1E11E;NYIAKENG PUACHUE HMONG LETTER NPLA;Lo;0;L;;;;;N;;;;;
+1E11F;NYIAKENG PUACHUE HMONG LETTER HAH;Lo;0;L;;;;;N;;;;;
+1E120;NYIAKENG PUACHUE HMONG LETTER MLA;Lo;0;L;;;;;N;;;;;
+1E121;NYIAKENG PUACHUE HMONG LETTER PLA;Lo;0;L;;;;;N;;;;;
+1E122;NYIAKENG PUACHUE HMONG LETTER GA;Lo;0;L;;;;;N;;;;;
+1E123;NYIAKENG PUACHUE HMONG LETTER RRA;Lo;0;L;;;;;N;;;;;
+1E124;NYIAKENG PUACHUE HMONG LETTER A;Lo;0;L;;;;;N;;;;;
+1E125;NYIAKENG PUACHUE HMONG LETTER AA;Lo;0;L;;;;;N;;;;;
+1E126;NYIAKENG PUACHUE HMONG LETTER I;Lo;0;L;;;;;N;;;;;
+1E127;NYIAKENG PUACHUE HMONG LETTER U;Lo;0;L;;;;;N;;;;;
+1E128;NYIAKENG PUACHUE HMONG LETTER O;Lo;0;L;;;;;N;;;;;
+1E129;NYIAKENG PUACHUE HMONG LETTER OO;Lo;0;L;;;;;N;;;;;
+1E12A;NYIAKENG PUACHUE HMONG LETTER E;Lo;0;L;;;;;N;;;;;
+1E12B;NYIAKENG PUACHUE HMONG LETTER EE;Lo;0;L;;;;;N;;;;;
+1E12C;NYIAKENG PUACHUE HMONG LETTER W;Lo;0;L;;;;;N;;;;;
+1E130;NYIAKENG PUACHUE HMONG TONE-B;Mn;230;NSM;;;;;N;;;;;
+1E131;NYIAKENG PUACHUE HMONG TONE-M;Mn;230;NSM;;;;;N;;;;;
+1E132;NYIAKENG PUACHUE HMONG TONE-J;Mn;230;NSM;;;;;N;;;;;
+1E133;NYIAKENG PUACHUE HMONG TONE-V;Mn;230;NSM;;;;;N;;;;;
+1E134;NYIAKENG PUACHUE HMONG TONE-S;Mn;230;NSM;;;;;N;;;;;
+1E135;NYIAKENG PUACHUE HMONG TONE-G;Mn;230;NSM;;;;;N;;;;;
+1E136;NYIAKENG PUACHUE HMONG TONE-D;Mn;230;NSM;;;;;N;;;;;
+1E137;NYIAKENG PUACHUE HMONG SIGN FOR PERSON;Lm;0;L;;;;;N;;;;;
+1E138;NYIAKENG PUACHUE HMONG SIGN FOR THING;Lm;0;L;;;;;N;;;;;
+1E139;NYIAKENG PUACHUE HMONG SIGN FOR LOCATION;Lm;0;L;;;;;N;;;;;
+1E13A;NYIAKENG PUACHUE HMONG SIGN FOR ANIMAL;Lm;0;L;;;;;N;;;;;
+1E13B;NYIAKENG PUACHUE HMONG SIGN FOR INVERTEBRATE;Lm;0;L;;;;;N;;;;;
+1E13C;NYIAKENG PUACHUE HMONG SIGN XW XW;Lm;0;L;;;;;N;;;;;
+1E13D;NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+1E140;NYIAKENG PUACHUE HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E141;NYIAKENG PUACHUE HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E142;NYIAKENG PUACHUE HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E143;NYIAKENG PUACHUE HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E144;NYIAKENG PUACHUE HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E145;NYIAKENG PUACHUE HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E146;NYIAKENG PUACHUE HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E147;NYIAKENG PUACHUE HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E148;NYIAKENG PUACHUE HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
+1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
+1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E2C3;WANCHO LETTER CA;Lo;0;L;;;;;N;;;;;
+1E2C4;WANCHO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E2C5;WANCHO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E2C6;WANCHO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E2C7;WANCHO LETTER PHA;Lo;0;L;;;;;N;;;;;
+1E2C8;WANCHO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2C9;WANCHO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E2CA;WANCHO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E2CB;WANCHO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E2CC;WANCHO LETTER THA;Lo;0;L;;;;;N;;;;;
+1E2CD;WANCHO LETTER FA;Lo;0;L;;;;;N;;;;;
+1E2CE;WANCHO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E2CF;WANCHO LETTER SHA;Lo;0;L;;;;;N;;;;;
+1E2D0;WANCHO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E2D1;WANCHO LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E2D2;WANCHO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E2D3;WANCHO LETTER VA;Lo;0;L;;;;;N;;;;;
+1E2D4;WANCHO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E2D5;WANCHO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2D6;WANCHO LETTER AU;Lo;0;L;;;;;N;;;;;
+1E2D7;WANCHO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2D8;WANCHO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E2D9;WANCHO LETTER KHA;Lo;0;L;;;;;N;;;;;
+1E2DA;WANCHO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E2DB;WANCHO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2DC;WANCHO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2DD;WANCHO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E2DE;WANCHO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2DF;WANCHO LETTER LLHA;Lo;0;L;;;;;N;;;;;
+1E2E0;WANCHO LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E2E1;WANCHO LETTER TRA;Lo;0;L;;;;;N;;;;;
+1E2E2;WANCHO LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E2E3;WANCHO LETTER AANG;Lo;0;L;;;;;N;;;;;
+1E2E4;WANCHO LETTER ANG;Lo;0;L;;;;;N;;;;;
+1E2E5;WANCHO LETTER ING;Lo;0;L;;;;;N;;;;;
+1E2E6;WANCHO LETTER ON;Lo;0;L;;;;;N;;;;;
+1E2E7;WANCHO LETTER EN;Lo;0;L;;;;;N;;;;;
+1E2E8;WANCHO LETTER AAN;Lo;0;L;;;;;N;;;;;
+1E2E9;WANCHO LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E2EA;WANCHO LETTER UEN;Lo;0;L;;;;;N;;;;;
+1E2EB;WANCHO LETTER YIH;Lo;0;L;;;;;N;;;;;
+1E2EC;WANCHO TONE TUP;Mn;230;NSM;;;;;N;;;;;
+1E2ED;WANCHO TONE TUPNI;Mn;230;NSM;;;;;N;;;;;
+1E2EE;WANCHO TONE KOI;Mn;230;NSM;;;;;N;;;;;
+1E2EF;WANCHO TONE KOINI;Mn;230;NSM;;;;;N;;;;;
+1E2F0;WANCHO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E2F1;WANCHO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E2F2;WANCHO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E2F3;WANCHO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E2F4;WANCHO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E2F5;WANCHO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E2F6;WANCHO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E2F7;WANCHO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
@@ -29108,6 +29449,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;;
+1E94B;ADLAM NASALIZATION MARK;Lm;0;R;;;;;N;;;;;
1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
@@ -29188,6 +29530,67 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1ECB2;INDIC SIYAQ NUMBER ALTERNATE TWO;No;0;AL;;;;2;N;;;;;
1ECB3;INDIC SIYAQ NUMBER ALTERNATE TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
1ECB4;INDIC SIYAQ ALTERNATE LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ED01;OTTOMAN SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1ED02;OTTOMAN SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED03;OTTOMAN SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED04;OTTOMAN SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED05;OTTOMAN SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED06;OTTOMAN SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED07;OTTOMAN SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED08;OTTOMAN SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED09;OTTOMAN SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED0A;OTTOMAN SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED0B;OTTOMAN SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1ED0C;OTTOMAN SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1ED0D;OTTOMAN SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1ED0E;OTTOMAN SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1ED0F;OTTOMAN SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1ED10;OTTOMAN SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1ED11;OTTOMAN SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1ED12;OTTOMAN SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1ED13;OTTOMAN SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1ED14;OTTOMAN SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1ED15;OTTOMAN SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1ED16;OTTOMAN SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED17;OTTOMAN SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1ED18;OTTOMAN SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED19;OTTOMAN SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1ED1A;OTTOMAN SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1ED1B;OTTOMAN SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1ED1C;OTTOMAN SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1ED1D;OTTOMAN SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED1E;OTTOMAN SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1ED1F;OTTOMAN SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1ED20;OTTOMAN SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1ED21;OTTOMAN SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1ED22;OTTOMAN SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1ED23;OTTOMAN SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1ED24;OTTOMAN SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1ED25;OTTOMAN SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED26;OTTOMAN SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1ED27;OTTOMAN SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1ED28;OTTOMAN SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1ED29;OTTOMAN SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1ED2A;OTTOMAN SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1ED2B;OTTOMAN SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1ED2C;OTTOMAN SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1ED2D;OTTOMAN SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1ED2E;OTTOMAN SIYAQ MARRATAN;So;0;AL;;;;;N;;;;;
+1ED2F;OTTOMAN SIYAQ ALTERNATE NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED30;OTTOMAN SIYAQ ALTERNATE NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED31;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED32;OTTOMAN SIYAQ ALTERNATE NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED33;OTTOMAN SIYAQ ALTERNATE NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED34;OTTOMAN SIYAQ ALTERNATE NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED35;OTTOMAN SIYAQ ALTERNATE NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED36;OTTOMAN SIYAQ ALTERNATE NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED37;OTTOMAN SIYAQ ALTERNATE NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED38;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED39;OTTOMAN SIYAQ ALTERNATE NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED3A;OTTOMAN SIYAQ ALTERNATE NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED3B;OTTOMAN SIYAQ ALTERNATE NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED3C;OTTOMAN SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ED3D;OTTOMAN SIYAQ FRACTION ONE SIXTH;No;0;AL;;;;1/6;N;;;;;
1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
@@ -29662,6 +30065,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
+1F16C;RAISED MR SIGN;So;0;ON;<super> 004D 0052;;;;N;;;;;
1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
@@ -30794,6 +31198,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;;
1F6D3;STUPA;So;0;ON;;;;;N;;;;;
1F6D4;PAGODA;So;0;ON;;;;;N;;;;;
+1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
@@ -30817,6 +31222,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6F7;SLED;So;0;ON;;;;;N;;;;;
1F6F8;FLYING SAUCER;So;0;ON;;;;;N;;;;;
1F6F9;SKATEBOARD;So;0;ON;;;;;N;;;;;
+1F6FA;AUTO RICKSHAW;So;0;ON;;;;;N;;;;;
1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;;
1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;;
1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;;
@@ -31022,6 +31428,18 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E3;LARGE PURPLE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E4;LARGE BROWN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E5;LARGE RED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E6;LARGE BLUE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E7;LARGE ORANGE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E8;LARGE YELLOW SQUARE;So;0;ON;;;;;N;;;;;
+1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
+1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
+1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
@@ -31182,6 +31600,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F909;DOWNWARD FACING NOTCHED HOOK;So;0;ON;;;;;N;;;;;
1F90A;DOWNWARD FACING HOOK WITH DOT;So;0;ON;;;;;N;;;;;
1F90B;DOWNWARD FACING NOTCHED HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90D;WHITE HEART;So;0;ON;;;;;N;;;;;
+1F90E;BROWN HEART;So;0;ON;;;;;N;;;;;
+1F90F;PINCHING HAND;So;0;ON;;;;;N;;;;;
1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;;
@@ -31229,6 +31650,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F93C;WRESTLERS;So;0;ON;;;;;N;;;;;
1F93D;WATER POLO;So;0;ON;;;;;N;;;;;
1F93E;HANDBALL;So;0;ON;;;;;N;;;;;
+1F93F;DIVING MASK;So;0;ON;;;;;N;;;;;
1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;;
1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;;
1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;;
@@ -31278,11 +31700,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F96E;MOON CAKE;So;0;ON;;;;;N;;;;;
1F96F;BAGEL;So;0;ON;;;;;N;;;;;
1F970;SMILING FACE WITH SMILING EYES AND THREE HEARTS;So;0;ON;;;;;N;;;;;
+1F971;YAWNING FACE;So;0;ON;;;;;N;;;;;
1F973;FACE WITH PARTY HORN AND PARTY HAT;So;0;ON;;;;;N;;;;;
1F974;FACE WITH UNEVEN EYES AND WAVY MOUTH;So;0;ON;;;;;N;;;;;
1F975;OVERHEATED FACE;So;0;ON;;;;;N;;;;;
1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
+1F97B;SARI;So;0;ON;;;;;N;;;;;
1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
1F97D;GOGGLES;So;0;ON;;;;;N;;;;;
1F97E;HIKING BOOT;So;0;ON;;;;;N;;;;;
@@ -31322,6 +31746,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9A0;MICROBE;So;0;ON;;;;;N;;;;;
1F9A1;BADGER;So;0;ON;;;;;N;;;;;
1F9A2;SWAN;So;0;ON;;;;;N;;;;;
+1F9A5;SLOTH;So;0;ON;;;;;N;;;;;
+1F9A6;OTTER;So;0;ON;;;;;N;;;;;
+1F9A7;ORANGUTAN;So;0;ON;;;;;N;;;;;
+1F9A8;SKUNK;So;0;ON;;;;;N;;;;;
+1F9A9;FLAMINGO;So;0;ON;;;;;N;;;;;
+1F9AA;OYSTER;So;0;ON;;;;;N;;;;;
+1F9AE;GUIDE DOG;So;0;ON;;;;;N;;;;;
+1F9AF;PROBING CANE;So;0;ON;;;;;N;;;;;
1F9B0;EMOJI COMPONENT RED HAIR;So;0;ON;;;;;N;;;;;
1F9B1;EMOJI COMPONENT CURLY HAIR;So;0;ON;;;;;N;;;;;
1F9B2;EMOJI COMPONENT BALD;So;0;ON;;;;;N;;;;;
@@ -31332,9 +31764,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9B7;TOOTH;So;0;ON;;;;;N;;;;;
1F9B8;SUPERHERO;So;0;ON;;;;;N;;;;;
1F9B9;SUPERVILLAIN;So;0;ON;;;;;N;;;;;
+1F9BA;SAFETY VEST;So;0;ON;;;;;N;;;;;
+1F9BB;EAR WITH HEARING AID;So;0;ON;;;;;N;;;;;
+1F9BC;MOTORIZED WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BD;MANUAL WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BE;MECHANICAL ARM;So;0;ON;;;;;N;;;;;
+1F9BF;MECHANICAL LEG;So;0;ON;;;;;N;;;;;
1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;;
1F9C1;CUPCAKE;So;0;ON;;;;;N;;;;;
1F9C2;SALT SHAKER;So;0;ON;;;;;N;;;;;
+1F9C3;BEVERAGE BOX;So;0;ON;;;;;N;;;;;
+1F9C4;GARLIC;So;0;ON;;;;;N;;;;;
+1F9C5;ONION;So;0;ON;;;;;N;;;;;
+1F9C6;FALAFEL;So;0;ON;;;;;N;;;;;
+1F9C7;WAFFLE;So;0;ON;;;;;N;;;;;
+1F9C8;BUTTER;So;0;ON;;;;;N;;;;;
+1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
+1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
+1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
+1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
+1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
1F9D0;FACE WITH MONOCLE;So;0;ON;;;;;N;;;;;
1F9D1;ADULT;So;0;ON;;;;;N;;;;;
1F9D2;CHILD;So;0;ON;;;;;N;;;;;
@@ -31383,6 +31832,90 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9FD;SPONGE;So;0;ON;;;;;N;;;;;
1F9FE;RECEIPT;So;0;ON;;;;;N;;;;;
1F9FF;NAZAR AMULET;So;0;ON;;;;;N;;;;;
+1FA00;NEUTRAL CHESS KING;So;0;ON;;;;;N;;;;;
+1FA01;NEUTRAL CHESS QUEEN;So;0;ON;;;;;N;;;;;
+1FA02;NEUTRAL CHESS ROOK;So;0;ON;;;;;N;;;;;
+1FA03;NEUTRAL CHESS BISHOP;So;0;ON;;;;;N;;;;;
+1FA04;NEUTRAL CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+1FA05;NEUTRAL CHESS PAWN;So;0;ON;;;;;N;;;;;
+1FA06;WHITE CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA07;BLACK CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA08;NEUTRAL CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA09;WHITE CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0A;WHITE CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0B;WHITE CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0C;WHITE CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0D;WHITE CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0E;WHITE CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0F;BLACK CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA10;BLACK CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA11;BLACK CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA12;BLACK CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA13;BLACK CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA14;BLACK CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA15;NEUTRAL CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA16;NEUTRAL CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA17;NEUTRAL CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA18;NEUTRAL CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA19;NEUTRAL CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1A;NEUTRAL CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1B;WHITE CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1C;BLACK CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1D;NEUTRAL CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1E;WHITE CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA1F;WHITE CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA20;WHITE CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA21;WHITE CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA22;WHITE CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA23;WHITE CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA24;BLACK CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA25;BLACK CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA26;BLACK CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA27;BLACK CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA28;BLACK CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA29;BLACK CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA2A;NEUTRAL CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA2B;NEUTRAL CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA2C;NEUTRAL CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA2D;NEUTRAL CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA2E;NEUTRAL CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA2F;NEUTRAL CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA30;WHITE CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA31;BLACK CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA32;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA33;WHITE CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA34;WHITE CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA35;WHITE CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA36;WHITE CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA37;WHITE CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA38;WHITE CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA39;BLACK CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3A;BLACK CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3B;BLACK CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3C;BLACK CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3D;BLACK CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3E;BLACK CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3F;NEUTRAL CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA40;NEUTRAL CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA41;NEUTRAL CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA42;NEUTRAL CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA43;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA44;NEUTRAL CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA45;WHITE CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA46;BLACK CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA47;NEUTRAL CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA48;WHITE CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA49;BLACK CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4A;NEUTRAL CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4B;WHITE CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4C;BLACK CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4D;NEUTRAL CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4E;WHITE CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA4F;WHITE CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA50;WHITE CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
@@ -31397,6 +31930,22 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA6B;XIANGQI BLACK CHARIOT;So;0;ON;;;;;N;;;;;
1FA6C;XIANGQI BLACK CANNON;So;0;ON;;;;;N;;;;;
1FA6D;XIANGQI BLACK SOLDIER;So;0;ON;;;;;N;;;;;
+1FA70;BALLET SHOES;So;0;ON;;;;;N;;;;;
+1FA71;ONE-PIECE SWIMSUIT;So;0;ON;;;;;N;;;;;
+1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
+1FA73;SHORTS;So;0;ON;;;;;N;;;;;
+1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
+1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
+1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA80;YO-YO;So;0;ON;;;;;N;;;;;
+1FA81;KITE;So;0;ON;;;;;N;;;;;
+1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
+1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
+1FA91;CHAIR;So;0;ON;;;;;N;;;;;
+1FA92;RAZOR;So;0;ON;;;;;N;;;;;
+1FA93;AXE;So;0;ON;;;;;N;;;;;
+1FA94;DIYA LAMP;So;0;ON;;;;;N;;;;;
+1FA95;BANJO;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
diff --git a/lib/stdlib/uc_spec/emoji-data.txt b/lib/stdlib/uc_spec/emoji-data.txt
index 6e66455252..2fb5c3ff68 100644
--- a/lib/stdlib/uc_spec/emoji-data.txt
+++ b/lib/stdlib/uc_spec/emoji-data.txt
@@ -1,11 +1,11 @@
# emoji-data.txt
-# Date: 2018-02-07, 07:55:18 GMT
-# © 2018 Unicode®, Inc.
+# Date: 2019-01-15, 12:10:05 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
-# Version: 11.0
+# Version: 12.0
#
# For documentation and usage, see http://www.unicode.org/reports/tr51
#
@@ -45,7 +45,7 @@
25FB..25FE ; Emoji # 3.2 [4] (◻️..◾) white medium square..black medium-small square
2600..2604 ; Emoji # 1.1 [5] (☀️..☄️) sun..comet
260E ; Emoji # 1.1 [1] (☎️) telephone
-2611 ; Emoji # 1.1 [1] (☑️) ballot box with check
+2611 ; Emoji # 1.1 [1] (☑️) check box with check
2614..2615 ; Emoji # 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
2618 ; Emoji # 4.1 [1] (☘️) shamrock
261D ; Emoji # 1.1 [1] (☝️) index pointing up
@@ -82,14 +82,14 @@
26F7..26FA ; Emoji # 5.2 [4] (⛷️..⛺) skier..tent
26FD ; Emoji # 5.2 [1] (⛽) fuel pump
2702 ; Emoji # 1.1 [1] (✂️) scissors
-2705 ; Emoji # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji # 6.0 [1] (✅) check mark button
2708..2709 ; Emoji # 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Emoji # 6.0 [2] (✊..✋) raised fist..raised hand
270C..270D ; Emoji # 1.1 [2] (✌️..✍️) victory hand..writing hand
270F ; Emoji # 1.1 [1] (✏️) pencil
2712 ; Emoji # 1.1 [1] (✒️) black nib
-2714 ; Emoji # 1.1 [1] (✔️) heavy check mark
-2716 ; Emoji # 1.1 [1] (✖️) heavy multiplication x
+2714 ; Emoji # 1.1 [1] (✔️) check mark
+2716 ; Emoji # 1.1 [1] (✖️) multiplication sign
271D ; Emoji # 1.1 [1] (✝️) latin cross
2721 ; Emoji # 1.1 [1] (✡️) star of David
2728 ; Emoji # 6.0 [1] (✨) sparkles
@@ -100,8 +100,8 @@
274E ; Emoji # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji # 5.2 [1] (❗) exclamation mark
-2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heavy heart exclamation..red heart
-2795..2797 ; Emoji # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heart exclamation..red heart
+2795..2797 ; Emoji # 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Emoji # 1.1 [1] (➡️) right arrow
27B0 ; Emoji # 6.0 [1] (➰) curly loop
27BF ; Emoji # 6.0 [1] (➿) double curly loop
@@ -109,7 +109,7 @@
2B05..2B07 ; Emoji # 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Emoji # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji # 5.1 [1] (⭐) star
-2B55 ; Emoji # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji # 5.2 [1] (⭕) hollow red circle
3030 ; Emoji # 1.1 [1] (〰️) wavy dash
303D ; Emoji # 3.2 [1] (〽️) part alternation mark
3297 ; Emoji # 1.1 [1] (㊗️) Japanese “congratulations” button
@@ -206,7 +206,7 @@
1F62E..1F62F ; Emoji # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -214,6 +214,7 @@
1F6CB..1F6CF ; Emoji # 7.0 [5] (🛋️..🛏️) couch and lamp..bed
1F6D0 ; Emoji # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji # 12.0 [1] (🛕) hindu temple
1F6E0..1F6E5 ; Emoji # 7.0 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E9 ; Emoji # 7.0 [1] (🛩️) small airplane
1F6EB..1F6EC ; Emoji # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
@@ -222,6 +223,9 @@
1F6F4..1F6F6 ; Emoji # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji # 10.0 [1] (🤟) love-you gesture
@@ -231,27 +235,39 @@
1F931..1F932 ; Emoji # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1250
+# Total elements: 1311
# ================================================
@@ -278,19 +294,19 @@
26F5 ; Emoji_Presentation # 5.2 [1] (⛵) sailboat
26FA ; Emoji_Presentation # 5.2 [1] (⛺) tent
26FD ; Emoji_Presentation # 5.2 [1] (⛽) fuel pump
-2705 ; Emoji_Presentation # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji_Presentation # 6.0 [1] (✅) check mark button
270A..270B ; Emoji_Presentation # 6.0 [2] (✊..✋) raised fist..raised hand
2728 ; Emoji_Presentation # 6.0 [1] (✨) sparkles
274C ; Emoji_Presentation # 6.0 [1] (❌) cross mark
274E ; Emoji_Presentation # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji_Presentation # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji_Presentation # 5.2 [1] (❗) exclamation mark
-2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) plus sign..division sign
27B0 ; Emoji_Presentation # 6.0 [1] (➰) curly loop
27BF ; Emoji_Presentation # 6.0 [1] (➿) double curly loop
2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji_Presentation # 5.1 [1] (⭐) star
-2B55 ; Emoji_Presentation # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji_Presentation # 5.2 [1] (⭕) hollow red circle
1F004 ; Emoji_Presentation # 5.1 [1] (🀄) mahjong red dragon
1F0CF ; Emoji_Presentation # 6.0 [1] (🃏) joker
1F18E ; Emoji_Presentation # 6.0 [1] (🆎) AB button (blood type)
@@ -349,7 +365,7 @@
1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji_Presentation # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji_Presentation # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji_Presentation # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji_Presentation # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji_Presentation # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -357,10 +373,14 @@
1F6CC ; Emoji_Presentation # 7.0 [1] (🛌) person in bed
1F6D0 ; Emoji_Presentation # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji_Presentation # 12.0 [1] (🛕) hindu temple
1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji_Presentation # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji_Presentation # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji_Presentation # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji_Presentation # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji_Presentation # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji_Presentation # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji_Presentation # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Presentation # 10.0 [1] (🤟) love-you gesture
@@ -370,27 +390,39 @@
1F931..1F932 ; Emoji_Presentation # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji_Presentation # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji_Presentation # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji_Presentation # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji_Presentation # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji_Presentation # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji_Presentation # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji_Presentation # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji_Presentation # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji_Presentation # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji_Presentation # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji_Presentation # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji_Presentation # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji_Presentation # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji_Presentation # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji_Presentation # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji_Presentation # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji_Presentation # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji_Presentation # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji_Presentation # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji_Presentation # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji_Presentation # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji_Presentation # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji_Presentation # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji_Presentation # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji_Presentation # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji_Presentation # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji_Presentation # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji_Presentation # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1032
+# Total elements: 1093
# ================================================
@@ -417,12 +449,12 @@
1F3CB..1F3CC ; Emoji_Modifier_Base # 7.0 [2] (🏋️..🏌️) person lifting weights..person golfing
1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (👂..👃) ear..nose
1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (👆..👐) backhand index pointing up..open hands
-1F466..1F469 ; Emoji_Modifier_Base # 6.0 [4] (👦..👩) boy..woman
-1F46E ; Emoji_Modifier_Base # 6.0 [1] (👮) police officer
-1F470..1F478 ; Emoji_Modifier_Base # 6.0 [9] (👰..👸) bride with veil..princess
+1F466..1F478 ; Emoji_Modifier_Base # 6.0 [19] (👦..👸) boy..princess
1F47C ; Emoji_Modifier_Base # 6.0 [1] (👼) baby angel
1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (💁..💃) person tipping hand..woman dancing
1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (💅..💇) nail polish..person getting haircut
+1F48F ; Emoji_Modifier_Base # 6.0 [1] (💏) kiss
+1F491 ; Emoji_Modifier_Base # 6.0 [1] (💑) couple with heart
1F4AA ; Emoji_Modifier_Base # 6.0 [1] (💪) flexed biceps
1F574..1F575 ; Emoji_Modifier_Base # 7.0 [2] (🕴️..🕵️) man in suit levitating..detective
1F57A ; Emoji_Modifier_Base # 9.0 [1] (🕺) man dancing
@@ -434,20 +466,22 @@
1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (🚴..🚶) person biking..person walking
1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (🛀) person taking bath
1F6CC ; Emoji_Modifier_Base # 7.0 [1] (🛌) person in bed
+1F90F ; Emoji_Modifier_Base # 12.0 [1] (🤏) pinching hand
1F918 ; Emoji_Modifier_Base # 8.0 [1] (🤘) sign of the horns
-1F919..1F91C ; Emoji_Modifier_Base # 9.0 [4] (🤙..🤜) call me hand..right-facing fist
-1F91E ; Emoji_Modifier_Base # 9.0 [1] (🤞) crossed fingers
+1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Modifier_Base # 10.0 [1] (🤟) love-you gesture
1F926 ; Emoji_Modifier_Base # 9.0 [1] (🤦) person facepalming
1F930 ; Emoji_Modifier_Base # 9.0 [1] (🤰) pregnant woman
1F931..1F932 ; Emoji_Modifier_Base # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (🤳..🤹) selfie..person juggling
-1F93D..1F93E ; Emoji_Modifier_Base # 9.0 [2] (🤽..🤾) person playing water polo..person playing handball
+1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
1F9B5..1F9B6 ; Emoji_Modifier_Base # 11.0 [2] (🦵..🦶) leg..foot
1F9B8..1F9B9 ; Emoji_Modifier_Base # 11.0 [2] (🦸..🦹) superhero..supervillain
-1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) adult..elf
+1F9BB ; Emoji_Modifier_Base # 12.0 [1] (🦻) ear with hearing aid
+1F9CD..1F9CF ; Emoji_Modifier_Base # 12.0 [3] (🧍..🧏) person standing..deaf person
+1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) person..elf
-# Total elements: 106
+# Total elements: 120
# ================================================
@@ -462,7 +496,7 @@
FE0F ; Emoji_Component # 3.2 [1] () VARIATION SELECTOR-16
1F1E6..1F1FF ; Emoji_Component # 6.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
1F3FB..1F3FF ; Emoji_Component # 8.0 [5] (🏻..🏿) light skin tone..dark skin tone
-1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red-haired..white-haired
+1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red hair..white hair
E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..cancel tag
# Total elements: 146
@@ -482,7 +516,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
21A9..21AA ; Extended_Pictographic# 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right
231A..231B ; Extended_Pictographic# 1.1 [2] (⌚..⌛) watch..hourglass done
2328 ; Extended_Pictographic# 1.1 [1] (⌨️) keyboard
-2388 ; Extended_Pictographic# 3.0 [1] (⎈️) HELM SYMBOL
+2388 ; Extended_Pictographic# 3.0 [1] (⎈) HELM SYMBOL
23CF ; Extended_Pictographic# 4.0 [1] (⏏️) eject button
23E9..23F3 ; Extended_Pictographic# 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done
23F8..23FA ; Extended_Pictographic# 7.0 [3] (⏸️..⏺️) pause button..record button
@@ -491,42 +525,42 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
25B6 ; Extended_Pictographic# 1.1 [1] (▶️) play button
25C0 ; Extended_Pictographic# 1.1 [1] (◀️) reverse button
25FB..25FE ; Extended_Pictographic# 3.2 [4] (◻️..◾) white medium square..black medium-small square
-2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★️) sun..BLACK STAR
-2607..2612 ; Extended_Pictographic# 1.1 [12] (☇️..☒️) LIGHTNING..BALLOT BOX WITH X
+2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★) sun..BLACK STAR
+2607..2612 ; Extended_Pictographic# 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X
2614..2615 ; Extended_Pictographic# 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
-2616..2617 ; Extended_Pictographic# 3.2 [2] (☖️..☗️) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+2616..2617 ; Extended_Pictographic# 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
2618 ; Extended_Pictographic# 4.1 [1] (☘️) shamrock
-2619 ; Extended_Pictographic# 3.0 [1] (☙️) REVERSED ROTATED FLORAL HEART BULLET
-261A..266F ; Extended_Pictographic# 1.1 [86] (☚️..♯️) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
-2670..2671 ; Extended_Pictographic# 3.0 [2] (♰️..♱️) WEST SYRIAC CROSS..EAST SYRIAC CROSS
-2672..267D ; Extended_Pictographic# 3.2 [12] (♲️..♽️) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+2619 ; Extended_Pictographic# 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET
+261A..266F ; Extended_Pictographic# 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+2670..2671 ; Extended_Pictographic# 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+2672..267D ; Extended_Pictographic# 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
267E..267F ; Extended_Pictographic# 4.1 [2] (♾️..♿) infinity..wheelchair symbol
-2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀️..⚅️) DIE FACE-1..DIE FACE-6
-2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐️..⚑️) WHITE FLAG..BLACK FLAG
+2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
2692..269C ; Extended_Pictographic# 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis
-269D ; Extended_Pictographic# 5.1 [1] (⚝️) OUTLINED WHITE STAR
-269E..269F ; Extended_Pictographic# 5.2 [2] (⚞️..⚟️) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+269D ; Extended_Pictographic# 5.1 [1] (⚝) OUTLINED WHITE STAR
+269E..269F ; Extended_Pictographic# 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
26A0..26A1 ; Extended_Pictographic# 4.0 [2] (⚠️..⚡) warning..high voltage
-26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢️..⚱️) DOUBLED FEMALE SIGN..funeral urn
-26B2 ; Extended_Pictographic# 5.0 [1] (⚲️) NEUTER
-26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳️..⚼️) CERES..SESQUIQUADRATE
-26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿️) soccer ball..SQUARED KEY
-26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀️..⛃️) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
-26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍️) snowman without snow..DISABLED CAR
+26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn
+26B2 ; Extended_Pictographic# 5.0 [1] (⚲) NEUTER
+26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
+26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
+26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR
26CE ; Extended_Pictographic# 6.0 [1] (⛎) Ophiuchus
-26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡️) pick..RESTRICTED LEFT ENTRY-2
-26E2 ; Extended_Pictographic# 6.0 [1] (⛢️) ASTRONOMICAL SYMBOL FOR URANUS
-26E3 ; Extended_Pictographic# 5.2 [1] (⛣️) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
-26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤️..⛧️) PENTAGRAM..INVERTED PENTAGRAM
-26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨️..⛿️) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
-2700 ; Extended_Pictographic# 7.0 [1] (✀️) BLACK SAFETY SCISSORS
-2701..2704 ; Extended_Pictographic# 1.1 [4] (✁️..✄️) UPPER BLADE SCISSORS..WHITE SCISSORS
-2705 ; Extended_Pictographic# 6.0 [1] (✅) white heavy check mark
+26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2
+26E2 ; Extended_Pictographic# 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS
+26E3 ; Extended_Pictographic# 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM
+26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+2700 ; Extended_Pictographic# 7.0 [1] (✀) BLACK SAFETY SCISSORS
+2701..2704 ; Extended_Pictographic# 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS
+2705 ; Extended_Pictographic# 6.0 [1] (✅) check mark button
2708..2709 ; Extended_Pictographic# 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Extended_Pictographic# 6.0 [2] (✊..✋) raised fist..raised hand
270C..2712 ; Extended_Pictographic# 1.1 [7] (✌️..✒️) victory hand..black nib
-2714 ; Extended_Pictographic# 1.1 [1] (✔️) heavy check mark
-2716 ; Extended_Pictographic# 1.1 [1] (✖️) heavy multiplication x
+2714 ; Extended_Pictographic# 1.1 [1] (✔️) check mark
+2716 ; Extended_Pictographic# 1.1 [1] (✖️) multiplication sign
271D ; Extended_Pictographic# 1.1 [1] (✝️) latin cross
2721 ; Extended_Pictographic# 1.1 [1] (✡️) star of David
2728 ; Extended_Pictographic# 6.0 [1] (✨) sparkles
@@ -537,8 +571,8 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
274E ; Extended_Pictographic# 6.0 [1] (❎) cross mark button
2753..2755 ; Extended_Pictographic# 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Extended_Pictographic# 5.2 [1] (❗) exclamation mark
-2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧️) heavy heart exclamation..ROTATED FLORAL HEART BULLET
-2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET
+2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Extended_Pictographic# 1.1 [1] (➡️) right arrow
27B0 ; Extended_Pictographic# 6.0 [1] (➰) curly loop
27BF ; Extended_Pictographic# 6.0 [1] (➿) double curly loop
@@ -546,45 +580,46 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
2B05..2B07 ; Extended_Pictographic# 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Extended_Pictographic# 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Extended_Pictographic# 5.1 [1] (⭐) star
-2B55 ; Extended_Pictographic# 5.2 [1] (⭕) heavy large circle
+2B55 ; Extended_Pictographic# 5.2 [1] (⭕) hollow red circle
3030 ; Extended_Pictographic# 1.1 [1] (〰️) wavy dash
303D ; Extended_Pictographic# 3.2 [1] (〽️) part alternation mark
3297 ; Extended_Pictographic# 1.1 [1] (㊗️) Japanese “congratulations” button
3299 ; Extended_Pictographic# 1.1 [1] (㊙️) Japanese “secret” button
-1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀️..🀫️) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
-1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬️..🀯️) <reserved-1F02C>..<reserved-1F02F>
-1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰️..🂓️) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
-1F094..1F09F ; Extended_Pictographic# NA [12] (🂔️..🂟️) <reserved-1F094>..<reserved-1F09F>
-1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠️..🂮️) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
-1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯️..🂰️) <reserved-1F0AF>..<reserved-1F0B0>
-1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱️..🂾️) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
-1F0BF ; Extended_Pictographic# 7.0 [1] (🂿️) PLAYING CARD RED JOKER
-1F0C0 ; Extended_Pictographic# NA [1] (🃀️) <reserved-1F0C0>
-1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁️..🃏) PLAYING CARD ACE OF DIAMONDS..joker
-1F0D0 ; Extended_Pictographic# NA [1] (🃐️) <reserved-1F0D0>
-1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑️..🃟️) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
-1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠️..🃵️) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
-1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶️..🃿️) <reserved-1F0F6>..<reserved-1F0FF>
-1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍️..🄏️) <reserved-1F10D>..<reserved-1F10F>
-1F12F ; Extended_Pictographic# 11.0 [1] (🄯️) COPYLEFT SYMBOL
-1F16C..1F16F ; Extended_Pictographic# NA [4] (🅬️..🅯️) <reserved-1F16C>..<reserved-1F16F>
+1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬..🀯) <reserved-1F02C>..<reserved-1F02F>
+1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+1F094..1F09F ; Extended_Pictographic# NA [12] (🂔..🂟) <reserved-1F094>..<reserved-1F09F>
+1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯..🂰) <reserved-1F0AF>..<reserved-1F0B0>
+1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+1F0BF ; Extended_Pictographic# 7.0 [1] (🂿) PLAYING CARD RED JOKER
+1F0C0 ; Extended_Pictographic# NA [1] (🃀) <reserved-1F0C0>
+1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker
+1F0D0 ; Extended_Pictographic# NA [1] (🃐) <reserved-1F0D0>
+1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶..🃿) <reserved-1F0F6>..<reserved-1F0FF>
+1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍..🄏) <reserved-1F10D>..<reserved-1F10F>
+1F12F ; Extended_Pictographic# 11.0 [1] (🄯) COPYLEFT SYMBOL
+1F16C ; Extended_Pictographic# 12.0 [1] (🅬) RAISED MR SIGN
+1F16D..1F16F ; Extended_Pictographic# NA [3] (🅭..🅯) <reserved-1F16D>..<reserved-1F16F>
1F170..1F171 ; Extended_Pictographic# 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
1F17E ; Extended_Pictographic# 6.0 [1] (🅾️) O button (blood type)
1F17F ; Extended_Pictographic# 5.2 [1] (🅿️) P button
1F18E ; Extended_Pictographic# 6.0 [1] (🆎) AB button (blood type)
1F191..1F19A ; Extended_Pictographic# 6.0 [10] (🆑..🆚) CL button..VS button
-1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭️..🇥️) <reserved-1F1AD>..<reserved-1F1E5>
+1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭..🇥) <reserved-1F1AD>..<reserved-1F1E5>
1F201..1F202 ; Extended_Pictographic# 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
-1F203..1F20F ; Extended_Pictographic# NA [13] (🈃️..🈏️) <reserved-1F203>..<reserved-1F20F>
+1F203..1F20F ; Extended_Pictographic# NA [13] (🈃..🈏) <reserved-1F203>..<reserved-1F20F>
1F21A ; Extended_Pictographic# 5.2 [1] (🈚) Japanese “free of charge” button
1F22F ; Extended_Pictographic# 5.2 [1] (🈯) Japanese “reserved” button
1F232..1F23A ; Extended_Pictographic# 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
-1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼️..🈿️) <reserved-1F23C>..<reserved-1F23F>
-1F249..1F24F ; Extended_Pictographic# NA [7] (🉉️..🉏️) <reserved-1F249>..<reserved-1F24F>
+1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼..🈿) <reserved-1F23C>..<reserved-1F23F>
+1F249..1F24F ; Extended_Pictographic# NA [7] (🉉..🉏) <reserved-1F249>..<reserved-1F24F>
1F250..1F251 ; Extended_Pictographic# 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
-1F252..1F25F ; Extended_Pictographic# NA [14] (🉒️..🉟️) <reserved-1F252>..<reserved-1F25F>
-1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠️..🉥️) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
-1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦️..🋿️) <reserved-1F266>..<reserved-1F2FF>
+1F252..1F25F ; Extended_Pictographic# NA [14] (🉒..🉟) <reserved-1F252>..<reserved-1F25F>
+1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦..🋿) <reserved-1F266>..<reserved-1F2FF>
1F300..1F320 ; Extended_Pictographic# 6.0 [33] (🌀..🌠) cyclone..shooting star
1F321..1F32C ; Extended_Pictographic# 7.0 [12] (🌡️..🌬️) thermometer..wind face
1F32D..1F32F ; Extended_Pictographic# 8.0 [3] (🌭..🌯) hot dog..burrito
@@ -594,7 +629,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F37D ; Extended_Pictographic# 7.0 [1] (🍽️) fork and knife with plate
1F37E..1F37F ; Extended_Pictographic# 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn
1F380..1F393 ; Extended_Pictographic# 6.0 [20] (🎀..🎓) ribbon..graduation cap
-1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔️..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
+1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
1F3A0..1F3C4 ; Extended_Pictographic# 6.0 [37] (🎠..🏄) carousel horse..person surfing
1F3C5 ; Extended_Pictographic# 7.0 [1] (🏅) sports medal
1F3C6..1F3CA ; Extended_Pictographic# 6.0 [5] (🏆..🏊) trophy..person swimming
@@ -602,7 +637,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F3CF..1F3D3 ; Extended_Pictographic# 8.0 [5] (🏏..🏓) cricket game..ping pong
1F3D4..1F3DF ; Extended_Pictographic# 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium
1F3E0..1F3F0 ; Extended_Pictographic# 6.0 [17] (🏠..🏰) house..castle
-1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱️..🏷️) WHITE PENNANT..label
+1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱..🏷️) WHITE PENNANT..label
1F3F8..1F3FA ; Extended_Pictographic# 8.0 [3] (🏸..🏺) badminton..amphora
1F400..1F43E ; Extended_Pictographic# 6.0 [63] (🐀..🐾) rat..paw prints
1F43F ; Extended_Pictographic# 7.0 [1] (🐿️) chipmunk
@@ -611,15 +646,15 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F442..1F4F7 ; Extended_Pictographic# 6.0[182] (👂..📷) ear..camera
1F4F8 ; Extended_Pictographic# 7.0 [1] (📸) camera with flash
1F4F9..1F4FC ; Extended_Pictographic# 6.0 [4] (📹..📼) video camera..videocassette
-1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾️) film projector..PORTABLE STEREO
+1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO
1F4FF ; Extended_Pictographic# 8.0 [1] (📿) prayer beads
1F500..1F53D ; Extended_Pictographic# 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button
-1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆️..🕊️) WHITE LATIN CROSS..dove
-1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏️) kaaba..BOWL OF HYGIEIA
+1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
+1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
1F550..1F567 ; Extended_Pictographic# 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty
-1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨️..🕹️) RIGHT SPEAKER..joystick
+1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
1F57A ; Extended_Pictographic# 9.0 [1] (🕺) man dancing
-1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻️..🖣️) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
1F5A4 ; Extended_Pictographic# 9.0 [1] (🖤) black heart
1F5A5..1F5FA ; Extended_Pictographic# 7.0 [86] (🖥️..🗺️) desktop computer..world map
1F5FB..1F5FF ; Extended_Pictographic# 6.0 [5] (🗻..🗿) mount fuji..moai
@@ -644,32 +679,37 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F62E..1F62F ; Extended_Pictographic# 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Extended_Pictographic# 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Extended_Pictographic# 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Extended_Pictographic# 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Extended_Pictographic# 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Extended_Pictographic# 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
1F680..1F6C5 ; Extended_Pictographic# 6.0 [70] (🚀..🛅) rocket..left luggage
-1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆️..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
+1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
1F6D0 ; Extended_Pictographic# 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Extended_Pictographic# 9.0 [2] (🛑..🛒) stop sign..shopping cart
-1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓️..🛔️) STUPA..PAGODA
-1F6D5..1F6DF ; Extended_Pictographic# NA [11] (🛕️..🛟️) <reserved-1F6D5>..<reserved-1F6DF>
+1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓..🛔) STUPA..PAGODA
+1F6D5 ; Extended_Pictographic# 12.0 [1] (🛕) hindu temple
+1F6D6..1F6DF ; Extended_Pictographic# NA [10] (🛖..🛟) <reserved-1F6D6>..<reserved-1F6DF>
1F6E0..1F6EC ; Extended_Pictographic# 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival
-1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭️..🛯️) <reserved-1F6ED>..<reserved-1F6EF>
+1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭..🛯) <reserved-1F6ED>..<reserved-1F6EF>
1F6F0..1F6F3 ; Extended_Pictographic# 7.0 [4] (🛰️..🛳️) satellite..passenger ship
1F6F4..1F6F6 ; Extended_Pictographic# 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Extended_Pictographic# 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Extended_Pictographic# 11.0 [1] (🛹) skateboard
-1F6FA..1F6FF ; Extended_Pictographic# NA [6] (🛺️..🛿️) <reserved-1F6FA>..<reserved-1F6FF>
-1F774..1F77F ; Extended_Pictographic# NA [12] (🝴️..🝿️) <reserved-1F774>..<reserved-1F77F>
-1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕️..🟘️) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
-1F7D9..1F7FF ; Extended_Pictographic# NA [39] (🟙️..🟿️) <reserved-1F7D9>..<reserved-1F7FF>
-1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌️..🠏️) <reserved-1F80C>..<reserved-1F80F>
-1F848..1F84F ; Extended_Pictographic# NA [8] (🡈️..🡏️) <reserved-1F848>..<reserved-1F84F>
-1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚️..🡟️) <reserved-1F85A>..<reserved-1F85F>
-1F888..1F88F ; Extended_Pictographic# NA [8] (🢈️..🢏️) <reserved-1F888>..<reserved-1F88F>
-1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮️..🣿️) <reserved-1F8AE>..<reserved-1F8FF>
-1F90C..1F90F ; Extended_Pictographic# NA [4] (🤌️..🤏️) <reserved-1F90C>..<reserved-1F90F>
+1F6FA ; Extended_Pictographic# 12.0 [1] (🛺) auto rickshaw
+1F6FB..1F6FF ; Extended_Pictographic# NA [5] (🛻..🛿) <reserved-1F6FB>..<reserved-1F6FF>
+1F774..1F77F ; Extended_Pictographic# NA [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
+1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7D9..1F7DF ; Extended_Pictographic# NA [7] (🟙..🟟) <reserved-1F7D9>..<reserved-1F7DF>
+1F7E0..1F7EB ; Extended_Pictographic# 12.0 [12] (🟠..🟫) orange circle..brown square
+1F7EC..1F7FF ; Extended_Pictographic# NA [20] (🟬..🟿) <reserved-1F7EC>..<reserved-1F7FF>
+1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌..🠏) <reserved-1F80C>..<reserved-1F80F>
+1F848..1F84F ; Extended_Pictographic# NA [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
+1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
+1F888..1F88F ; Extended_Pictographic# NA [8] (🢈..🢏) <reserved-1F888>..<reserved-1F88F>
+1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮..🣿) <reserved-1F8AE>..<reserved-1F8FF>
+1F90C ; Extended_Pictographic# NA [1] (🤌) <reserved-1F90C>
+1F90D..1F90F ; Extended_Pictographic# 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Extended_Pictographic# 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Extended_Pictographic# 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Extended_Pictographic# 10.0 [1] (🤟) love-you gesture
@@ -679,35 +719,50 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F931..1F932 ; Extended_Pictographic# 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Extended_Pictographic# 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Extended_Pictographic# 9.0 [3] (🤼..🤾) people wrestling..person playing handball
-1F93F ; Extended_Pictographic# NA [1] (🤿️) <reserved-1F93F>
+1F93F ; Extended_Pictographic# 12.0 [1] (🤿) diving mask
1F940..1F945 ; Extended_Pictographic# 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Extended_Pictographic# 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Extended_Pictographic# 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Extended_Pictographic# 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Extended_Pictographic# 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Extended_Pictographic# 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
-1F971..1F972 ; Extended_Pictographic# NA [2] (🥱️..🥲️) <reserved-1F971>..<reserved-1F972>
+1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Extended_Pictographic# 12.0 [1] (🥱) yawning face
+1F972 ; Extended_Pictographic# NA [1] (🥲) <reserved-1F972>
1F973..1F976 ; Extended_Pictographic# 11.0 [4] (🥳..🥶) partying face..cold face
-1F977..1F979 ; Extended_Pictographic# NA [3] (🥷️..🥹️) <reserved-1F977>..<reserved-1F979>
+1F977..1F979 ; Extended_Pictographic# NA [3] (🥷..🥹) <reserved-1F977>..<reserved-1F979>
1F97A ; Extended_Pictographic# 11.0 [1] (🥺) pleading face
-1F97B ; Extended_Pictographic# NA [1] (🥻️) <reserved-1F97B>
-1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Extended_Pictographic# 12.0 [1] (🥻) sari
+1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Extended_Pictographic# 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Extended_Pictographic# 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Extended_Pictographic# 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9A3..1F9AF ; Extended_Pictographic# NA [13] (🦣️..🦯️) <reserved-1F9A3>..<reserved-1F9AF>
-1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red-haired..supervillain
-1F9BA..1F9BF ; Extended_Pictographic# NA [6] (🦺️..🦿️) <reserved-1F9BA>..<reserved-1F9BF>
+1F9A3..1F9A4 ; Extended_Pictographic# NA [2] (🦣..🦤) <reserved-1F9A3>..<reserved-1F9A4>
+1F9A5..1F9AA ; Extended_Pictographic# 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AB..1F9AD ; Extended_Pictographic# NA [3] (🦫..🦭) <reserved-1F9AB>..<reserved-1F9AD>
+1F9AE..1F9AF ; Extended_Pictographic# 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Extended_Pictographic# 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Extended_Pictographic# 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Extended_Pictographic# 11.0 [2] (🧁..🧂) cupcake..salt
-1F9C3..1F9CF ; Extended_Pictographic# NA [13] (🧃️..🧏️) <reserved-1F9C3>..<reserved-1F9CF>
+1F9C3..1F9CA ; Extended_Pictographic# 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CB..1F9CC ; Extended_Pictographic# NA [2] (🧋..🧌) <reserved-1F9CB>..<reserved-1F9CC>
+1F9CD..1F9CF ; Extended_Pictographic# 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# 11.0 [25] (🧧..🧿) red envelope..nazar amulet
-1FA00..1FA5F ; Extended_Pictographic# NA [96] (🨀️..🩟️) <reserved-1FA00>..<reserved-1FA5F>
-1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠️..🩭️) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
-1FA6E..1FFFD ; Extended_Pictographic# NA[1424] (🩮️..🿽️) <reserved-1FA6E>..<reserved-1FFFD>
+1FA00..1FA53 ; Extended_Pictographic# 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA54..1FA5F ; Extended_Pictographic# NA [12] (🩔..🩟) <reserved-1FA54>..<reserved-1FA5F>
+1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA6E..1FA6F ; Extended_Pictographic# NA [2] (🩮..🩯) <reserved-1FA6E>..<reserved-1FA6F>
+1FA70..1FA73 ; Extended_Pictographic# 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA74..1FA77 ; Extended_Pictographic# NA [4] (🩴..🩷) <reserved-1FA74>..<reserved-1FA77>
+1FA78..1FA7A ; Extended_Pictographic# 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7F ; Extended_Pictographic# NA [5] (🩻..🩿) <reserved-1FA7B>..<reserved-1FA7F>
+1FA80..1FA82 ; Extended_Pictographic# 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA83..1FA8F ; Extended_Pictographic# NA [13] (🪃..🪏) <reserved-1FA83>..<reserved-1FA8F>
+1FA90..1FA95 ; Extended_Pictographic# 12.0 [6] (🪐..🪕) ringed planet..banjo
+1FA96..1FFFD ; Extended_Pictographic# NA[1384] (🪖..🿽) <reserved-1FA96>..<reserved-1FFFD>
# Total elements: 3793
diff --git a/lib/stdlib/uc_spec/gen_unicode_mod.escript b/lib/stdlib/uc_spec/gen_unicode_mod.escript
index de67b18afc..d820b9ed8e 100644
--- a/lib/stdlib/uc_spec/gen_unicode_mod.escript
+++ b/lib/stdlib/uc_spec/gen_unicode_mod.escript
@@ -191,7 +191,7 @@ gen_static(Fd) ->
" {U,L} -> #{upper=>U,lower=>L,title=>U,fold=>L};\n"
" {U,L,T,F} -> #{upper=>U,lower=>L,title=>T,fold=>F}\n"
" end.\n\n"),
- io:put_chars(Fd, "spec_version() -> {11,0}.\n\n\n"),
+ io:put_chars(Fd, "spec_version() -> {12,1}.\n\n\n"),
io:put_chars(Fd, "class(Codepoint) -> {CCC,_,_} = unicode_table(Codepoint),\n CCC.\n\n"),
io:put_chars(Fd, "-spec uppercase(unicode:chardata()) -> "
"maybe_improper_list(gc(),unicode:chardata()).\n"),
diff --git a/lib/syntax_tools/Makefile b/lib/syntax_tools/Makefile
index 14ae6d4f97..d3e2aa9b2c 100644
--- a/lib/syntax_tools/Makefile
+++ b/lib/syntax_tools/Makefile
@@ -91,3 +91,5 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index b799c76177..3ff5b92cd7 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -28,21 +28,10 @@ VSN=$(SYNTAX_TOOLS_VSN)
APPLICATION=syntax_tools
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Man page source directory (with .erl files)
-# ----------------------------------------------------
-SRC_DIR = $(ERL_TOP)/lib/syntax_tools/src
-INC_DIR = $(ERL_TOP)/lib/syntax_tools/include
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = \
+EDOC_REF3_FILES = \
epp_dodger.xml \
erl_comment_scan.xml \
erl_prettypr.xml \
@@ -56,100 +45,20 @@ XML_REF3_FILES = \
prettypr.xml
XML_PART_FILES = part.xml
-XML_CHAPTER_FILES = chapter.xml
+EDOC_CHAPTER_FILE = chapter.xml
XML_NOTES_FILES = notes.xml
BOOK_FILES = book.xml
-
XML_FILES=\
$(BOOK_FILES) $(XML_PART_FILES) $(XML_APPLICATION_FILES) \
$(XML_NOTES_FILES)
-XML_GEN_FILES = $(XML_REF3_FILES:%=$(XMLDIR)/%) $(XML_CHAPTER_FILES:%=$(XMLDIR)/%)
-
-# ----------------------------------------------------
-INFO_FILE = ../../info
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_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
+HTML_EXTRA_FILES = ../../examples/demo.erl
-EXAMPLES_DIR = ../../examples
-EXAMPLES = $(EXAMPLES_DIR)/demo.erl
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+XML_GEN_FILES = $(XML_CHAPTER_FILES:%=$(XMLDIR)/%)
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-SPECS_FLAGS = -I../../include
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: man pdf html
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES)
-
-$(XML_REF3_FILES:%=$(XMLDIR)/%):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
- -dir $(XMLDIR) $(SRC_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
-
-$(XML_CHAPTER_FILES:%=$(XMLDIR)/%):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) \
- -chapter -dir $(XMLDIR) ../overview.edoc
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELSYSDIR)/examples"
- $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)/doc/html"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index 7e741cc649..da22a91de0 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -598,8 +598,6 @@ skip_macro_args([{'receive',_}=T | Ts], Es, As) ->
skip_macro_args(Ts, ['end' | Es], [T | As]);
skip_macro_args([{'try',_}=T | Ts], Es, As) ->
skip_macro_args(Ts, ['end' | Es], [T | As]);
-skip_macro_args([{'cond',_}=T | Ts], Es, As) ->
- skip_macro_args(Ts, ['end' | Es], [T | As]);
skip_macro_args([{E,_}=T | Ts], [E], As) -> %final close
{lists:reverse([T | As]), Ts};
skip_macro_args([{E,_}=T | Ts], [E | Es], As) -> %matching close
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 6ad9bec2e6..75a5cd56bf 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -53,7 +53,7 @@
-type hook() :: 'none'
| fun((erl_syntax:syntaxTree(), _, _) -> prettypr:document()).
--type clause_t() :: 'case_expr' | 'cond_expr' | 'fun_expr'
+-type clause_t() :: 'case_expr' | 'fun_expr'
| 'if_expr' | 'receive_expr' | 'try_expr'
| {'function', prettypr:document()}
| 'spec'.
@@ -66,7 +66,8 @@
paper = ?PAPER :: integer(),
ribbon = ?RIBBON :: integer(),
user = ?NOUSER :: term(),
- encoding = epp:default_encoding() :: epp:source_encoding()}).
+ encoding = epp:default_encoding() :: epp:source_encoding(),
+ empty_lines = sets:new() :: sets:set(integer())}).
-type context() :: #ctxt{}.
@@ -358,7 +359,8 @@ layout(Node, Options) ->
ribbon = proplists:get_value(ribbon, Options, ?RIBBON),
user = proplists:get_value(user, Options),
encoding = proplists:get_value(encoding, Options,
- epp:default_encoding())}).
+ epp:default_encoding()),
+ empty_lines = proplists:get_value(empty_lines, Options, sets:new())}).
lay(Node, Ctxt) ->
case erl_syntax:get_ann(Node) of
@@ -474,13 +476,13 @@ lay_2(Node, Ctxt) ->
floating(text(",")), reset_prec(Ctxt),
fun lay/2),
beside(floating(text("{")),
- beside(par(Es),
+ beside(sep(Es),
floating(text("}"))));
list ->
Ctxt1 = reset_prec(Ctxt),
Node1 = erl_syntax:compact_list(Node),
- D1 = par(seq(erl_syntax:list_prefix(Node1),
+ D1 = sep(seq(erl_syntax:list_prefix(Node1),
floating(text(",")), Ctxt1,
fun lay/2)),
D = case erl_syntax:list_suffix(Node1) of
@@ -513,7 +515,7 @@ lay_2(Node, Ctxt) ->
D2 = lay(Operator, reset_prec(Ctxt)),
D3 = lay(erl_syntax:infix_expr_right(Node),
set_prec(Ctxt, PrecR)),
- D4 = par([D1, D2, D3], Ctxt#ctxt.sub_indent),
+ D4 = par([D1, D2, D3], Ctxt#ctxt.break_indent),
maybe_parentheses(D4, Prec, Ctxt);
prefix_expr ->
@@ -535,7 +537,7 @@ lay_2(Node, Ctxt) ->
'-' ->
beside(D1, D2);
_ ->
- par([D1, D2], Ctxt#ctxt.sub_indent)
+ par([D1, D2], Ctxt#ctxt.break_indent)
end,
maybe_parentheses(D3, Prec, Ctxt);
@@ -547,7 +549,7 @@ lay_2(Node, Ctxt) ->
floating(text(",")), reset_prec(Ctxt),
fun lay/2),
D1 = beside(D, beside(text("("),
- beside(par(As),
+ beside(sep(As),
floating(text(")"))))),
maybe_parentheses(D1, Prec, Ctxt);
@@ -576,9 +578,7 @@ lay_2(Node, Ctxt) ->
G ->
lay(G, Ctxt1)
end,
- D3 = sep(seq(erl_syntax:clause_body(Node),
- floating(text(",")), Ctxt1,
- fun lay/2)),
+ D3 = lay_clause_expressions(erl_syntax:clause_body(Node), Ctxt1),
case Ctxt#ctxt.clause of
fun_expr ->
make_fun_clause(D1, D2, D3, Ctxt);
@@ -586,8 +586,6 @@ lay_2(Node, Ctxt) ->
make_fun_clause(N, D1, D2, D3, Ctxt);
if_expr ->
make_if_clause(D1, D2, D3, Ctxt);
- cond_expr ->
- make_if_clause(D1, D2, D3, Ctxt);
case_expr ->
make_case_clause(D1, D2, D3, Ctxt);
receive_expr ->
@@ -614,32 +612,24 @@ lay_2(Node, Ctxt) ->
D1 = lay(erl_syntax:case_expr_argument(Node), Ctxt1),
D2 = lay_clauses(erl_syntax:case_expr_clauses(Node),
case_expr, Ctxt1),
- sep([par([follow(text("case"), D1, Ctxt1#ctxt.sub_indent),
+ sep([par([follow(text("case"), D1, Ctxt1#ctxt.break_indent),
text("of")],
Ctxt1#ctxt.break_indent),
- nest(Ctxt1#ctxt.sub_indent, D2),
+ nest(Ctxt1#ctxt.break_indent, D2),
text("end")]);
if_expr ->
Ctxt1 = reset_prec(Ctxt),
D = lay_clauses(erl_syntax:if_expr_clauses(Node),
if_expr, Ctxt1),
- sep([follow(text("if"), D, Ctxt1#ctxt.sub_indent),
- text("end")]);
-
- cond_expr ->
- Ctxt1 = reset_prec(Ctxt),
- D = lay_clauses(erl_syntax:cond_expr_clauses(Node),
- cond_expr, Ctxt1),
- sep([text("cond"),
- nest(Ctxt1#ctxt.sub_indent, D),
+ sep([follow(text("if"), D, Ctxt1#ctxt.break_indent),
text("end")]);
fun_expr ->
Ctxt1 = reset_prec(Ctxt),
D = lay_clauses(erl_syntax:fun_expr_clauses(Node),
fun_expr, Ctxt1),
- sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
+ sep([follow(text("fun"), D, Ctxt1#ctxt.break_indent),
text("end")]);
named_fun_expr ->
@@ -647,7 +637,7 @@ lay_2(Node, Ctxt) ->
D1 = lay(erl_syntax:named_fun_expr_name(Node), Ctxt1),
D = lay_clauses(erl_syntax:named_fun_expr_clauses(Node),
{function,D1}, Ctxt1),
- sep([follow(text("fun"), D, Ctxt1#ctxt.sub_indent),
+ sep([follow(text("fun"), D, Ctxt1#ctxt.break_indent),
text("end")]);
module_qualifier ->
@@ -734,7 +724,7 @@ lay_2(Node, Ctxt) ->
_ when Args =:= none ->
lay(N, Ctxt1);
_ ->
- D1 = par(seq(Args, floating(text(",")), Ctxt1,
+ D1 = sep(seq(Args, text(","), Ctxt1,
fun lay/2)),
beside(lay(N, Ctxt1),
beside(text("("),
@@ -766,14 +756,14 @@ lay_2(Node, Ctxt) ->
Es = seq(erl_syntax:block_expr_body(Node),
floating(text(",")), Ctxt1, fun lay/2),
sep([text("begin"),
- nest(Ctxt1#ctxt.sub_indent, sep(Es)),
+ nest(Ctxt1#ctxt.break_indent, sep(Es)),
text("end")]);
catch_expr ->
{Prec, PrecR} = preop_prec('catch'),
D = lay(erl_syntax:catch_expr_body(Node),
set_prec(Ctxt, PrecR)),
- D1 = follow(text("catch"), D, Ctxt#ctxt.sub_indent),
+ D1 = follow(text("catch"), D, Ctxt#ctxt.break_indent),
maybe_parentheses(D1, Prec, Ctxt);
class_qualifier ->
@@ -903,10 +893,10 @@ lay_2(Node, Ctxt) ->
follow(floating(text("after")),
append_clause_body(D4, D3,
Ctxt1),
- Ctxt1#ctxt.sub_indent)])
+ Ctxt1#ctxt.break_indent)])
end,
sep([text("receive"),
- nest(Ctxt1#ctxt.sub_indent, D2),
+ nest(Ctxt1#ctxt.break_indent, D2),
text("end")]);
record_access ->
@@ -1003,7 +993,7 @@ lay_2(Node, Ctxt) ->
D1 = lay(erl_syntax:typed_record_field_body(Node), Ctxt1),
D2 = lay(erl_syntax:typed_record_field_type(Node),
set_prec(Ctxt, Prec)),
- D3 = par([D1, floating(text(" ::")), D2],
+ D3 = par([D1, floating(text("::")), D2],
Ctxt1#ctxt.break_indent),
maybe_parentheses(D3, Prec, Ctxt);
@@ -1018,7 +1008,7 @@ lay_2(Node, Ctxt) ->
D2 = sep(seq(As, floating(text(",")), Ctxt1,
fun lay/2)),
[text("after"),
- nest(Ctxt1#ctxt.sub_indent, D2)
+ nest(Ctxt1#ctxt.break_indent, D2)
| Es0]
end,
Es2 = case erl_syntax:try_expr_handlers(Node) of
@@ -1026,7 +1016,7 @@ lay_2(Node, Ctxt) ->
Hs ->
D3 = lay_clauses(Hs, try_expr, Ctxt1),
[text("catch"),
- nest(Ctxt1#ctxt.sub_indent, D3)
+ nest(Ctxt1#ctxt.break_indent, D3)
| Es1]
end,
Es3 = case erl_syntax:try_expr_clauses(Node) of
@@ -1034,10 +1024,10 @@ lay_2(Node, Ctxt) ->
Cs ->
D4 = lay_clauses(Cs, try_expr, Ctxt1),
[text("of"),
- nest(Ctxt1#ctxt.sub_indent, D4)
+ nest(Ctxt1#ctxt.break_indent, D4)
| Es2]
end,
- sep([par([follow(text("try"), D1, Ctxt1#ctxt.sub_indent),
+ sep([par([follow(text("try"), D1, Ctxt1#ctxt.break_indent),
hd(Es3)])
| tl(Es3)]);
@@ -1213,12 +1203,12 @@ lay_2(Node, Ctxt) ->
floating(text(",")), reset_prec(Ctxt),
fun lay/2),
beside(floating(text("{")),
- beside(par(Es), floating(text("}"))))
+ beside(sep(Es), floating(text("}"))))
end;
type_union ->
{_, Prec, PrecR} = type_inop_prec('|'),
- Es = par(seq(erl_syntax:type_union_types(Node),
+ Es = sep(seq(erl_syntax:type_union_types(Node),
floating(text(" |")), set_prec(Ctxt, PrecR),
fun lay/2)),
maybe_parentheses(Es, Prec, Ctxt);
@@ -1504,5 +1494,27 @@ tidy_float_2([$e | Cs]) -> tidy_float_2([$e, $+ | Cs]);
tidy_float_2([_C | Cs]) -> tidy_float_2(Cs);
tidy_float_2([]) -> [].
+lay_clause_expressions([H], Ctxt) ->
+ lay(H, Ctxt);
+lay_clause_expressions([H | T], Ctxt) ->
+ Clause = beside(lay(H, Ctxt), floating(text(","))),
+ Next = lay_clause_expressions(T, Ctxt),
+ case is_last_and_before_empty_line(H, T, Ctxt) of
+ true ->
+ above(above(Clause, text("")), Next);
+ false ->
+ above(Clause, Next)
+ end;
+lay_clause_expressions([], _) ->
+ empty().
+
+is_last_and_before_empty_line(H, [], #ctxt{empty_lines = EmptyLines}) ->
+ try sets:is_element(erl_syntax:get_pos(H) + 1, EmptyLines)
+ catch error:badarith -> false
+ end;
+is_last_and_before_empty_line(H, [H2 | _], #ctxt{empty_lines = EmptyLines}) ->
+ try ((erl_syntax:get_pos(H2) - erl_syntax:get_pos(H)) >= 2) and sets:is_element(erl_syntax:get_pos(H) + 1, EmptyLines)
+ catch error:badarith -> false
+ end.
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 09ef0bf7a5..ed94bd383c 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -183,8 +183,6 @@
comment/2,
comment_padding/1,
comment_text/1,
- cond_expr/1,
- cond_expr_clauses/1,
conjunction/1,
conjunction_body/1,
constrained_function_type/2,
@@ -431,6 +429,7 @@
-record(tree, {type :: atom(),
attr = #attr{} :: #attr{},
data :: term()}).
+-type tree() :: #tree{}.
%% `wrapper' records are used for attaching new-form node information to
%% `erl_parse' trees.
@@ -446,18 +445,20 @@
-record(wrapper, {type :: atom(),
attr = #attr{} :: #attr{},
tree :: erl_parse()}).
+-type wrapper() :: #wrapper{}.
%% =====================================================================
--type syntaxTree() :: #tree{} | #wrapper{} | erl_parse().
+-type syntaxTree() :: tree() | wrapper() | erl_parse().
-type erl_parse() :: erl_parse:abstract_clause()
| erl_parse:abstract_expr()
| erl_parse:abstract_form()
| erl_parse:abstract_type()
| erl_parse:form_info()
- %% To shut up Dialyzer:
- | {bin_element, _, _, _, _}.
+ | erl_parse:af_binelement(term())
+ | erl_parse:af_generator()
+ | erl_parse:af_remote_function().
%% The representation built by the Erlang standard library parser
%% `erl_parse'. This is a subset of the {@link syntaxTree()} type.
@@ -494,39 +495,38 @@
%% <td>class_qualifier</td>
%% <td>clause</td>
%% <td>comment</td>
-%% <td>cond_expr</td>
-%% </tr><tr>
%% <td>conjunction</td>
+%% </tr><tr>
%% <td>constrained_function_type</td>
%% <td>constraint</td>
%% <td>disjunction</td>
-%% </tr><tr>
%% <td>eof_marker</td>
+%% </tr><tr>
%% <td>error_marker</td>
%% <td>float</td>
%% <td>form_list</td>
-%% </tr><tr>
%% <td>fun_expr</td>
+%% </tr><tr>
%% <td>fun_type</td>
%% <td>function</td>
%% <td>function_type</td>
-%% </tr><tr>
%% <td>generator</td>
+%% </tr><tr>
%% <td>if_expr</td>
%% <td>implicit_fun</td>
%% <td>infix_expr</td>
-%% </tr><tr>
%% <td>integer</td>
+%% </tr><tr>
%% <td>integer_range_type</td>
%% <td>list</td>
%% <td>list_comp</td>
-%% </tr><tr>
%% <td>macro</td>
+%% </tr><tr>
%% <td>map_expr</td>
%% <td>map_field_assoc</td>
%% <td>map_field_exact</td>
-%% </tr><tr>
%% <td>map_type</td>
+%% </tr><tr>
%% <td>map_type_assoc</td>
%% <td>map_type_exact</td>
%% <td>match_expr</td>
@@ -556,6 +556,7 @@
%% <td>tuple_type</td>
%% <td>typed_record_field</td>
%% <td>type_application</td>
+%% </tr><tr>
%% <td>type_union</td>
%% <td>underscore</td>
%% <td>user_type_application</td>
@@ -587,7 +588,6 @@
%% @see class_qualifier/2
%% @see clause/3
%% @see comment/2
-%% @see cond_expr/1
%% @see conjunction/1
%% @see constrained_function_type/2
%% @see constraint/2
@@ -673,7 +673,6 @@ type(Node) ->
%% Composite types
{'case', _, _, _} -> case_expr;
{'catch', _, _} -> catch_expr;
- {'cond', _, _} -> cond_expr;
{'fun', _, {clauses, _}} -> fun_expr;
{named_fun, _, _, _} -> named_fun_expr;
{'fun', _, {function, _, _}} -> implicit_fun;
@@ -6290,7 +6289,6 @@ if_expr_clauses(Node) ->
%% @see case_expr_argument/1
%% @see clause/3
%% @see if_expr/1
-%% @see cond_expr/1
-record(case_expr, {argument :: syntaxTree(), clauses :: [syntaxTree()]}).
@@ -6357,60 +6355,6 @@ case_expr_clauses(Node) ->
%% =====================================================================
-%% @doc Creates an abstract cond-expression. If `Clauses' is
-%% `[C1, ..., Cn]', the result represents "<code>cond
-%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% `Ci' represents "<code>() <em>Ei</em> ->
-%% <em>Bi</em></code>", then the result represents "<code>cond
-%% <em>E1</em> -> <em>B1</em>; ...; <em>En</em> -> <em>Bn</em>
-%% end</code>".
-%%
-%% @see cond_expr_clauses/1
-%% @see clause/3
-%% @see case_expr/2
-
-%% type(Node) = cond_expr
-%% data(Node) = Clauses
-%%
-%% Clauses = [syntaxTree()]
-%%
-%% `erl_parse' representation:
-%%
-%% {'cond', Pos, Clauses}
-%%
-%% Clauses = [Clause] \ []
-%% Clause = {clause, ...}
-%%
-%% See `clause' for documentation on `erl_parse' clauses.
-
--spec cond_expr([syntaxTree()]) -> syntaxTree().
-
-cond_expr(Clauses) ->
- tree(cond_expr, Clauses).
-
-revert_cond_expr(Node) ->
- Pos = get_pos(Node),
- Clauses = [revert_clause(C) || C <- cond_expr_clauses(Node)],
- {'cond', Pos, Clauses}.
-
-
-%% =====================================================================
-%% @doc Returns the list of clause subtrees of a `cond_expr' node.
-%%
-%% @see cond_expr/1
-
--spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()].
-
-cond_expr_clauses(Node) ->
- case unwrap(Node) of
- {'cond', _, Clauses} ->
- Clauses;
- Node1 ->
- data(Node1)
- end.
-
-
-%% =====================================================================
%% @equiv receive_expr(Clauses, none, [])
-spec receive_expr([syntaxTree()]) -> syntaxTree().
@@ -7534,8 +7478,6 @@ revert_root(Node) ->
revert_char(Node);
clause ->
revert_clause(Node);
- cond_expr ->
- revert_cond_expr(Node);
constrained_function_type ->
revert_constrained_function_type(Node);
constraint ->
@@ -7802,8 +7744,6 @@ subtrees(T) ->
[clause_patterns(T), [G],
clause_body(T)]
end;
- cond_expr ->
- [cond_expr_clauses(T)];
conjunction ->
[conjunction_body(T)];
constrained_function_type ->
@@ -8017,7 +7957,6 @@ make_tree(class_qualifier, [[A], [B]]) -> class_qualifier(A, B);
make_tree(class_qualifier, [[A], [B], [C]]) -> class_qualifier(A, B, C);
make_tree(clause, [P, B]) -> clause(P, none, B);
make_tree(clause, [P, [G], B]) -> clause(P, G, B);
-make_tree(cond_expr, [C]) -> cond_expr(C);
make_tree(conjunction, [E]) -> conjunction(E);
make_tree(constrained_function_type, [[F],C]) ->
constrained_function_type(F, C);
@@ -8239,7 +8178,7 @@ meta_call(F, As) ->
%% =====================================================================
%% @equiv tree(Type, [])
--spec tree(atom()) -> #tree{}.
+-spec tree(atom()) -> tree().
tree(Type) ->
tree(Type, []).
@@ -8274,7 +8213,7 @@ tree(Type) ->
%% @see data/1
%% @see type/1
--spec tree(atom(), term()) -> #tree{}.
+-spec tree(atom(), term()) -> tree().
tree(Type, Data) ->
#tree{type = Type, data = Data}.
@@ -8330,7 +8269,7 @@ data(T) -> erlang:error({badarg, T}).
%% trees. <em>Attaching a wrapper onto another wrapper structure is an
%% error</em>.
--spec wrap(erl_parse()) -> #wrapper{}.
+-spec wrap(erl_parse()) -> wrapper().
wrap(Node) ->
%% We assume that Node is an old-school `erl_parse' tree.
@@ -8344,7 +8283,7 @@ wrap(Node) ->
%% `erl_parse' tree; otherwise it returns `Node'
%% itself.
--spec unwrap(syntaxTree()) -> #tree{} | erl_parse().
+-spec unwrap(syntaxTree()) -> tree() | erl_parse().
unwrap(#wrapper{tree = Node}) -> Node;
unwrap(Node) -> Node. % This could also be a new-form node.
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 352165893f..6185007235 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -528,8 +528,6 @@ vann(Tree, Env) ->
vann_case_expr(Tree, Env);
if_expr ->
vann_if_expr(Tree, Env);
- cond_expr ->
- vann_cond_expr(Tree, Env);
receive_expr ->
vann_receive_expr(Tree, Env);
catch_expr ->
@@ -613,9 +611,6 @@ vann_if_expr(Tree, Env) ->
Tree1 = rewrite(Tree, erl_syntax:if_expr(Cs1)),
{ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
-vann_cond_expr(_Tree, _Env) ->
- erlang:error({not_implemented,cond_expr}).
-
vann_catch_expr(Tree, Env) ->
E = erl_syntax:catch_expr_body(Tree),
{E1, _, Free} = vann(E, Env),
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 5623aa6af3..d97afda0ea 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -319,7 +319,8 @@ file_1(Parent, Name, Opts) ->
file_2(Name, Opts) ->
Opts1 = Opts ++ file__defaults(),
- Forms = read_module(Name, Opts1),
+ {Forms, EmptyLines} = read_module(Name, Opts1),
+ Opts2 = [{empty_lines, EmptyLines} | Opts1],
Comments = erl_comment_scan:file(Name),
Forms1 = erl_recomment:recomment_forms(Forms, Comments),
Tree = module(Forms1, [{file, Name} | Opts1]),
@@ -329,10 +330,10 @@ file_2(Name, Opts) ->
false ->
case proplists:get_bool(stdout, Opts1) of
true ->
- print_module(Tree, Opts1),
+ print_module(Tree, Opts2),
ok;
false ->
- write_module(Tree, Name, Opts1),
+ write_module(Tree, Name, Opts2),
ok
end
end.
@@ -341,31 +342,25 @@ read_module(Name, Opts) ->
verbose("reading module `~ts'.", [filename(Name)], Opts),
case epp_dodger:parse_file(Name, [no_fail]) of
{ok, Forms} ->
- check_forms(Forms, Name),
- Forms;
+ {Forms, empty_lines(Name)};
{error, R} ->
error_read_file(Name),
exit({error, R})
end.
-check_forms(Fs, Name) ->
- Fun = fun (F) ->
- case erl_syntax:type(F) of
- error_marker ->
- S = case erl_syntax:error_marker_info(F) of
- {_, M, D} ->
- M:format_error(D);
- _ ->
- "unknown error"
- end,
- report_error({Name, erl_syntax:get_pos(F),
- "\n ~ts"}, [S]),
- exit(error);
- _ ->
- ok
- end
- end,
- lists:foreach(Fun, Fs).
+empty_lines(Name) ->
+ {ok, Data} = file:read_file(Name),
+ List = binary:split(Data, [<<"\n">>], [global]),
+ {ok, NonEmptyLineRe} = re:compile("\\S"),
+ {Res, _} = lists:foldl(
+ fun(Line, {Set, N}) ->
+ case re:run(Line, NonEmptyLineRe) of
+ {match, _} -> {Set, N + 1};
+ nomatch -> {sets:add_element(N, Set), N + 1}
+ end
+ end,
+ {sets:new(), 1}, List),
+ Res.
%% Create the target directory and make a backup file if necessary,
%% then open the file, output the text and close the file
@@ -1551,18 +1546,6 @@ visit_match_body(Ps, P, B, Tree, Env, St0) ->
false ->
visit_match_expr_final(P, B, Tree, Env, St0)
end;
- cond_expr ->
- Cs = erl_syntax:cond_expr_clauses(B),
- case multival_clauses(Cs, length(Ps), Ps) of
- {true, Cs1} ->
- report_export_vars(Env#env.file,
- erl_syntax:get_pos(B),
- "cond", Env#env.verbosity),
- Tree1 = erl_syntax:cond_expr(Cs1),
- {rewrite(Tree, Tree1), St0};
- false ->
- visit_match_expr_final(P, B, Tree, Env, St0)
- end;
receive_expr ->
%% Handle the timeout case as an extra clause.
As = erl_syntax:receive_expr_action(B),
diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl
index 61a8993b84..1f2dfffbdb 100644
--- a/lib/syntax_tools/src/prettypr.erl
+++ b/lib/syntax_tools/src/prettypr.erl
@@ -569,6 +569,9 @@ format(D, W, R) ->
layout(L) ->
lists:reverse(layout(0, L, [])).
+layout(N, #above{d1 = #text{s = [_ | ""]}, d2 = L}, Cs) ->
+ %% Text for this line is empty. Print newline but no indentation.
+ layout(N, L, [$\n | Cs]);
layout(N, #above{d1 = #text{s = S}, d2 = L}, Cs) ->
layout(N, L, [$\n | flatrev(string_chars(S), indent(N, Cs))]);
layout(N, #nest{n = N1, d = L}, Cs) ->
@@ -578,8 +581,6 @@ layout(N, #text{s = S}, Cs) ->
layout(_N, null, Cs) ->
Cs.
-indent(N, Cs) when N >= 8 ->
- indent(N - 8, [$\t | Cs]);
indent(N, Cs) when N > 0 ->
indent(N - 1, [$\s | Cs]);
indent(_N, Cs) ->
diff --git a/lib/tftp/Makefile b/lib/tftp/Makefile
index a4559fbc2e..d0397beaa0 100644
--- a/lib/tftp/Makefile
+++ b/lib/tftp/Makefile
@@ -43,36 +43,6 @@ include $(ERL_TOP)/make/otp_subdir.mk
.PHONY: info gclean dialyzer dialyzer_plt dclean
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "TFTP_VSN: $(TFTP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
+DIA_PLT_APPS=
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/tftp/doc/src/Makefile b/lib/tftp/doc/src/Makefile
index 5d76799e41..e5ff8010fe 100644
--- a/lib/tftp/doc/src/Makefile
+++ b/lib/tftp/doc/src/Makefile
@@ -26,12 +26,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
include ../../vsn.mk
VSN=$(TFTP_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
+APPLICATION=tftp
# ----------------------------------------------------
# Target Specs
@@ -59,97 +54,6 @@ XML_FILES = \
$(XML_REF3_FILES) \
$(XML_APPLICATION_FILES)
-# GIF_FILES = tftp.gif
-
-
-# ----------------------------------------------------
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-EXTRA_FILES = \
- $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_REF6_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 +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-ldocs: local_docs
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-clean clean_docs: clean_html clean_man clean_pdf
- rm -rf $(XMLDIR)
- rm -f errs core *~
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean_pdf:
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
-
-clean_html:
- rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
-
-clean_man:
- rm -f $(MAN3_FILES)
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-info:
- @echo "GIF_FILES:\n$(GIF_FILES)"
- @echo ""
- @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
- @echo ""
- @echo "HTML_FILES:\n$(HTML_FILES)"
- @echo ""
- @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
- @echo ""
- @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
- @echo ""
- @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
- @echo ""
- @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
- @echo ""
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/tftp/test/Makefile b/lib/tftp/test/Makefile
index 99f36256b0..a68bc4df6f 100644
--- a/lib/tftp/test/Makefile
+++ b/lib/tftp/test/Makefile
@@ -114,7 +114,7 @@ SOURCE = $(ERL_FILES) $(HRL_FILES)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-TFTP_SPECS = tftp.spec tftp_bench.spec
+TFTP_SPECS = tftp.spec
COVER_FILE = tftp.cover
TFTP_FILES = tftp.config $(TFTP_SPECS)
diff --git a/lib/tftp/test/tftp_bench.spec b/lib/tftp/test/tftp_bench.spec
deleted file mode 100644
index 43fa385c85..0000000000
--- a/lib/tftp/test/tftp_bench.spec
+++ /dev/null
@@ -1 +0,0 @@
-{suites,"../tftp_test",[]}.
diff --git a/lib/tools/Makefile b/lib/tools/Makefile
index e17e9cfd1e..d849989a2d 100644
--- a/lib/tools/Makefile
+++ b/lib/tools/Makefile
@@ -36,3 +36,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler runtime_tools
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in
index 289322b6fa..19f1fd746b 100644
--- a/lib/tools/c_src/Makefile.in
+++ b/lib/tools/c_src/Makefile.in
@@ -22,7 +22,6 @@ include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/erts/include/internal/$(TARGET)/ethread.mk
-USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_VC=@MIXED_VC@
CC=@CC@
diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile
index 5ff4fe3113..f9bb2314a8 100644
--- a/lib/tools/doc/src/Makefile
+++ b/lib/tools/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(TOOLS_VSN)
APPLICATION=tools
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -60,89 +55,25 @@ XML_CHAPTER_FILES = \
xref_chapter.xml \
notes.xml
-
BOOK_FILES = book.xml
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-GIF_FILES = \
+IMAGE_FILES = \
venn1.gif \
venn2.gif
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-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 +=
-
-TOOLS_SRC=$(ERL_TOP)/lib/tools/src
-TOOLS_INCLUDE=$(ERL_TOP)/lib/tools/include
-
-SPECS_FLAGS = -I$(TOOLS_SRC) -I$(TOOLS_INCLUDE)
+NO_CHUNKS = erlang_mode.xml
# ----------------------------------------------------
-# 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 -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~
# erlang_mode doesn't have erlang source so we generate a dummy file for it.
$(SPECDIR)/specs_erlang_mode.xml:
- echo '<module name="erlang_mode"/>' > $(SPECDIR)/specs_erlang_mode.xml
+ $(gen_verbose)echo '<module name="erlang_mode"/>' > $(SPECDIR)/specs_erlang_mode.xml
# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml
index b6af8b6d28..791fb06ba1 100644
--- a/lib/tools/doc/src/cprof.xml
+++ b/lib/tools/doc/src/cprof.xml
@@ -44,7 +44,7 @@
</p>
<p>Since breakpoints are used there is no need for special
compilation of any module to be profiled. For now these
- breakpoints can only be set on BEAM code so <term id="BIF"></term>s
+ breakpoints can only be set on BEAM code so BIFs
cannot be call count traced.
</p>
<p>The size of the call counters is the host machine word
diff --git a/lib/tools/doc/src/instrument.xml b/lib/tools/doc/src/instrument.xml
index 7e9cbaebb0..7286e5c1cc 100644
--- a/lib/tools/doc/src/instrument.xml
+++ b/lib/tools/doc/src/instrument.xml
@@ -73,14 +73,12 @@
<desc>
<p><c><anno>AllocatorType</anno></c> is the type of the allocator that
employs this carrier.</p>
- <p><c><anno>TotalSize</anno></c> is the total size of the carrier,
- including its header.</p>
- <p><c><anno>AllocatedSize</anno></c> is the combined size of the
- carrier's allocated blocks, including their headers.</p>
- <p><c><anno>AllocatedCount</anno></c> is the number of allocated
- blocks in the carrier.</p>
<p><c><anno>InPool</anno></c> is whether the carrier is in the
migration pool.</p>
+ <p><c><anno>TotalSize</anno></c> is the total size of the carrier,
+ including its header.</p>
+ <p><c><anno>Allocations</anno></c> is a summary of the allocated blocks
+ in the carrier.</p>
<p><c><anno>FreeBlocks</anno></c> is a histogram of the free block
sizes in the carrier.</p>
<p>If the carrier could not be scanned in full without harming the
diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml
index ab3641a52f..91f152e678 100644
--- a/lib/tools/doc/src/xref.xml
+++ b/lib/tools/doc/src/xref.xml
@@ -46,7 +46,7 @@
<em>Module data</em>,
which are extracted from BEAM files, include local functions,
exported functions, local calls and external calls. By default,
- calls to built-in functions (<term id="BIF"></term>) are ignored, but
+ calls to built-in functions (BIF) are ignored, but
if the option <c>builtins</c>, accepted by some of this
module's functions, is set to <c>true</c>, calls to BIFs
are included as well. It is the analyzing OTP version that
@@ -138,6 +138,9 @@ debug information, which is an abstract
<tag>-deprecated({f,1}).</tag>
<item>The exported function <c>f/1</c> is deprecated. Nothing is
said whether <c>f/1</c> will be removed or not.</item>
+ <tag>-deprecated({f,1,"Use g/1 instead"}).</tag>
+ <item>As above but with a descriptive string. The string is currently
+ unused by <c>xref</c> but other tools can make use of it.</item>
<tag>-deprecated({f,'_'}).</tag>
<item>All exported functions <c>f/0</c>, <c>f/1</c> and so on are
deprecated.</item>
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index b7775d1c8c..c19ad3668c 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -65,7 +65,9 @@ clean:
rm -f $(TARGET_FILES) $(ELC_FILES)
rm -f errs core *~
-docs:
+DOC_TARGETS?=man
+
+docs: $(DOC_TARGETS)
# ----------------------------------------------------
# Release Target
@@ -77,14 +79,8 @@ release_spec: opt
$(INSTALL_DATA) $(EL_FILES) $(README_FILES) \
"$(RELSYSDIR)/emacs"
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec:
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec:
-else
-release_docs_spec: docs
+release_man_spec:
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
-endif
-endif
+
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index dafb9d56ac..d4ac34b05f 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -883,9 +883,12 @@ resulting regexp is surrounded by \\_< and \\_>."
"spawn_link"
"spawn_monitor"
"spawn_opt"
+ "spawn_request"
+ "spawn_request_abandon"
"split_binary"
"statistics"
"term_to_binary"
+ "term_to_iovec"
"time"
"throw"
"tl"
@@ -912,7 +915,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"bump_reductions"
"call_on_load_function"
"cancel_timer"
- "crasher"
"crc32"
"crc32_combine"
"decode_packet"
@@ -994,7 +996,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"set_cookie"
"set_cpu_topology"
"setnode"
- "spawn_opt"
"start_timer"
"subtract"
"suspend_process"
@@ -1237,8 +1238,8 @@ This must be placed in front of `erlang-font-lock-keywords-vars'.")
1 'font-lock-type-face)
;; Don't highlight numerical constants.
(list (if erlang-regexp-modern-p
- "\\_<[0-9]+#\\([0-9a-zA-Z]+\\)"
- "\\<[0-9]+#\\([0-9a-zA-Z]+\\)")
+ "\\_<\\([0-9]+\\(_[0-9]+\\)*#[0-9a-zA-Z]+\\(_[0-9a-zA-Z]+\\)*\\)"
+ "\\<\\([0-9]+\\(_[0-9]+\\)*#[0-9a-zA-Z]+\\(_[0-9a-zA-Z]+\\)*\\)")
1 nil t)
(list (concat "^-record\\s-*(\\s-*" erlang-atom-regexp)
1 'font-lock-type-face))
@@ -1946,7 +1947,6 @@ The format is described in the documentation of `erlang-man-dirs'."
'(("Man Pages"
(("Error! Why?" erlang-man-describe-error)))))))
-
(defcustom erlang-man-download-url "http://erlang.org/download/otp_doc_man_22.1.tar.gz"
"The URL from which the erlang-man-download function will
download Erlang man pages "
diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl
index 86e3d3a8b8..ee72bad234 100644
--- a/lib/tools/src/eprof.erl
+++ b/lib/tools/src/eprof.erl
@@ -28,7 +28,7 @@
stop/0,
dump/0, dump_data/0,
start_profiling/1, start_profiling/2, start_profiling/3,
- profile/1, profile/2, profile/3, profile/4, profile/5,
+ profile/1, profile/2, profile/3, profile/4, profile/5, profile/6,
stop_profiling/0,
analyze/0, analyze/1, analyze/2, analyze/4,
log/1]).
diff --git a/lib/tools/src/instrument.erl b/lib/tools/src/instrument.erl
index 0203fefe13..1d1edcbbea 100644
--- a/lib/tools/src/instrument.erl
+++ b/lib/tools/src/instrument.erl
@@ -94,11 +94,12 @@ alloc_hist_merge_hist(Index, A, B) ->
-type carrier_info_list() ::
{HistogramStart :: non_neg_integer(),
Carriers :: [{AllocatorType :: atom(),
+ InPool :: boolean(),
TotalSize :: non_neg_integer(),
UnscannedSize :: non_neg_integer(),
- AllocatedSize :: non_neg_integer(),
- AllocatedCount :: non_neg_integer(),
- InPool :: boolean(),
+ Allocations :: {Type :: atom(),
+ Count :: non_neg_integer(),
+ Size :: non_neg_integer()},
FreeBlocks :: block_histogram()}]}.
-spec carriers() -> {ok, Result} | {error, Reason} when
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index f0e0fc4bec..f812c0a391 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -43,6 +43,6 @@
]
},
{runtime_dependencies, ["stdlib-3.4","runtime_tools-1.8.14",
- "kernel-5.4","erts-9.1","compiler-5.0"]}
+ "kernel-5.4","erts-9.1","compiler-5.0", "erts-@OTP-16327@"]}
]
}.
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index a28c6ee283..ef348206ca 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -897,15 +897,20 @@ depr_fa(_F, _A, _X, _M, _I) ->
undefined.
%% deprecated_flag(Flag) -> integer() | undefined
-%% Maps symbolic flags for deprecated functions to integers.
+%% Maps symbolic flags for deprecated functions to their category indexes
+%% in the deprecation tuple.
+%%
+%% {DF_1, DF_2, DF_3, DF}
-%deprecated_flag(1) -> 1;
-%deprecated_flag(2) -> 2;
-%deprecated_flag(3) -> 3;
deprecated_flag(next_version) -> 1;
deprecated_flag(next_major_release) -> 2;
deprecated_flag(eventually) -> 3;
-deprecated_flag(_) -> undefined.
+deprecated_flag(String) -> depr_desc(String).
+
+%% Strings fall into the general category, index 4.
+depr_desc([Char | Str]) when is_integer(Char) -> depr_desc(Str);
+depr_desc([]) -> 4;
+depr_desc(_) -> undefined.
%% -> {ok, Module, Bad, State} | throw(Error)
%% Assumes:
diff --git a/lib/tools/test/cprof_SUITE.erl b/lib/tools/test/cprof_SUITE.erl
index 9cbc27fb17..39239a66a9 100644
--- a/lib/tools/test/cprof_SUITE.erl
+++ b/lib/tools/test/cprof_SUITE.erl
@@ -211,16 +211,12 @@ on_load_test(Config) ->
Lr = seq_r(1, M, fun succ/1),
N2 = cprof:pause(),
{Module,0,[]} = cprof:analyse(Module),
- M_1 = M - 1,
M4__4 = M*4 - 4,
M10_7 = M*10 - 7,
{?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
+ {{?MODULE,'-fun.succ/1-',1},M4__4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
- {{?MODULE,'-on_load_test/1-fun-5-',1},M_1},
- {{?MODULE,'-on_load_test/1-fun-4-',1},M_1},
- {{?MODULE,'-on_load_test/1-fun-3-',1},M_1},
- {{?MODULE,'-on_load_test/1-fun-2-',1},M_1},
{{?MODULE,seq_r,3},1}]}
= cprof:analyse(?MODULE),
N2 = cprof:stop(),
@@ -246,18 +242,14 @@ modules_test(Config) ->
Lr = seq_r(1, M, fun succ/1),
N = cprof:pause(),
Lr = lists:reverse(L),
- M_1 = M - 1,
M4_4 = M*4 - 4,
M10_7 = M*10 - 7,
M2__1 = M*2 + 1,
{Tot,ModList} = cprof:analyse(),
{value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
+ {{?MODULE,'-fun.succ/1-',1},M4_4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
- {{?MODULE,'-modules_test/1-fun-3-',1},M_1},
- {{?MODULE,'-modules_test/1-fun-2-',1},M_1},
- {{?MODULE,'-modules_test/1-fun-1-',1},M_1},
- {{?MODULE,'-modules_test/1-fun-0-',1},M_1},
{{?MODULE,seq_r,3},1}]}} =
lists:keysearch(?MODULE, 1, ModList),
{value,{Module,M2__1,[{{Module,seq_r,4},M},
diff --git a/lib/tools/test/instrument_SUITE.erl b/lib/tools/test/instrument_SUITE.erl
index f474669836..c1a5921e04 100644
--- a/lib/tools/test/instrument_SUITE.erl
+++ b/lib/tools/test/instrument_SUITE.erl
@@ -194,23 +194,19 @@ verify_carriers_output(#{ histogram_start := HistStart,
%% Do the carriers look alright?
CarrierSet = ordsets:from_list(AllCarriers),
Verified = [C || {AllocType,
+ InPool,
TotalSize,
UnscannedSize,
- AllocatedSize,
- AllocatedCount,
- InPool,
+ Allocations,
FreeBlockHist} = C <- CarrierSet,
is_atom(AllocType),
+ is_boolean(InPool),
is_integer(TotalSize), TotalSize >= 1,
is_integer(UnscannedSize), UnscannedSize < TotalSize,
UnscannedSize >= 0,
- is_integer(AllocatedSize), AllocatedSize < TotalSize,
- AllocatedSize >= 0,
- is_integer(AllocatedCount), AllocatedCount =< AllocatedSize,
- AllocatedCount >= 0,
- is_boolean(InPool),
+ is_list(Allocations),
tuple_size(FreeBlockHist) =:= HistWidth,
- carrier_block_check(AllocatedCount, FreeBlockHist)],
+ carrier_block_check(Allocations, FreeBlockHist)],
[] = ordsets:subtract(CarrierSet, Verified),
%% Do we have at least as many carriers as we've generated?
@@ -229,8 +225,12 @@ verify_carriers_output(#{ histogram_start := HistStart,
verify_carriers_output(#{}, {error, not_enabled}) ->
ok.
-carrier_block_check(AllocCount, FreeHist) ->
- %% A carrier must contain at least one block, and th. number of free blocks
+carrier_block_check(Allocations, FreeHist) ->
+ AllocCount = lists:foldl(fun({_Type, Count, _Size}, Acc) ->
+ Count + Acc
+ end, 0, Allocations),
+
+ %% A carrier must contain at least one block, and the number of free blocks
%% must not exceed the number of allocated blocks + 1.
FreeCount = hist_sum(FreeHist),
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index da4f56c09b..d258966bc2 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1098,10 +1098,8 @@ read_expected(Version) ->
{POS4+5,{FF,{spm,spf,2}}},
{POS4+6,{FF,{dist,func,2}}},
{POS4+6,{FF,{erlang,spawn_link,4}}},
- {POS4+7,{FF,{erlang,spawn_opt,4}}},
{POS4+7,{FF,{read,bi,0}}},
{POS4+7,{FF,{spm,spf,2}}},
- {POS4+8,{FF,{erlang,spawn_opt,4}}},
{POS4+8,{FF,{read,bi,0}}},
{POS5+1,{FF,{erlang,spawn,1}}},
{POS5+2,{FF,{erlang,spawn,1}}},
@@ -1109,7 +1107,6 @@ read_expected(Version) ->
{POS6+1,{FF,{erlang,spawn,2}}},
{POS6+2,{FF,{erlang,spawn_link,2}}},
{POS7+1,{FF,{erlang,spawn,4}}},
- {POS7+2,{FF,{erlang,spawn_opt,4}}},
{POS8+1,{FF,{hej,san,1}}},
{POS8+4,{FF,{a,b,1}}},
{POS8+6,{FF,{m,f,1}}},
@@ -1150,7 +1147,10 @@ read_expected(Version) ->
{POS3+2, {FF,{erlang,spawn,3}}},
{POS3+3, {FF,{erlang,spawn_link,3}}},
{POS3+4, {FF,{erlang,spawn_link,3}}},
+ {POS4+7, {FF,{erlang,spawn_opt,4}}},
+ {POS4+8, {FF,{erlang,spawn_opt,4}}},
{POS6+4, {FF,{erlang,spawn,3}}},
+ {POS7+2, {FF,{erlang,spawn_opt,4}}},
{POS8+4,{FF,{erlang,apply,2}}},
{POS8+5,{FF,{erlang,apply,2}}},
{POS8+6,{FF,{erlang,apply,3}}},
@@ -1479,12 +1479,13 @@ deprecated(Conf) when is_list(Conf) ->
Test2= <<"-module(depr).
- -export([t/0,f/1,bar/2,f/2,g/3]).
+ -export([t/0,f/1,bar/2,f/2,g/3,string/0]).
-deprecated([{'_','_',eventually}]). % DF_3
-deprecated([{f,'_',next_major_release}]). % DF_2
-deprecated([{g,'_',next_version}]). % DF_1
-deprecated([{bar,2}]). % DF
+ -deprecated([{string,0,\"hello\"}]). % DF
t() ->
g(1,2, 3),
@@ -1499,6 +1500,9 @@ deprecated(Conf) when is_list(Conf) ->
g(F, G, H) ->
?MODULE:bar(F, {G,H}).
+ string() ->
+ ?MODULE:string().
+
bar(_, _) ->
?MODULE:t().
">>,
@@ -1512,7 +1516,8 @@ deprecated(Conf) when is_list(Conf) ->
M = depr,
DFa_1 = usort([{{M,f,2},{M,g,3}}]),
DFa_2 = usort(DFa_1++[{{M,f,1},{M,f,2}},{{M,t,0},{M,f,1}}]),
- DFa_3 = usort(DFa_2++[{{M,bar,2},{M,t,0}},{{M,g,3},{M,bar,2}}]),
+ DFa_3 = usort(DFa_2++[{{M,bar,2},{M,t,0}},{{M,g,3},{M,bar,2}},
+ {{M,string,0},{M,string,0}}]),
DFa = DFa_3,
{ok,DFa} = xref:analyze(s, deprecated_function_calls),
diff --git a/lib/wx/Makefile b/lib/wx/Makefile
index 2397950925..002887d9da 100644
--- a/lib/wx/Makefile
+++ b/lib/wx/Makefile
@@ -40,3 +40,5 @@ CLEANDIRS = $(SUBDIRS) api_gen
SUB_DIRECTORIES=$(SUBDIRS)
include $(ERL_TOP)/make/otp_subdir.mk
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index 797533309b..d029ae77cb 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -159,7 +159,13 @@ gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
w("-export_type([~s/0]).~n", [Name]),
case lists:filter(fun({_F,Depr}) -> Depr end, ExportList) of
[] -> ok;
- Depr -> w("-deprecated([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", Depr, 60)])
+ Depr ->
+ DepStr = "not available in wxWidgets-2.9 and later",
+ w("-deprecated([~s]).~n~n",
+ [args(fun({EF,_}) ->
+ [DFun,DArgs] = string:split(EF, "/"),
+ io_lib:format("{~s,~s,\"~s\"}", [DFun,DArgs,DepStr])
+ end, ",\n ", Depr, 60)])
end,
case lists:filter(fun({_,_,Depr}) -> Depr end, InExported) of
[] -> ok;
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index f35e6cdbd0..5a69738544 100644
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -82,7 +82,7 @@ LM_WINDOWS_ENVIRONMENT
USER_CFLAGS=$CFLAGS
-if test X"$MIXED_CYGWIN_VC" = X"yes" -o X"$MIXED_MSYS_VC" = X"yes"; then
+if test X"$MIXED_VC" = X"yes" ; then
CFLAGS="-Owx"
fi
@@ -319,7 +319,7 @@ dnl
if test "$cross_compiling" = "yes"; then
echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" >> ./CONF_INFO
WXERL_CAN_BUILD_DRIVER=false
-elif test X"$MIXED_CYGWIN_VC" = X"no" -a X"$MIXED_MSYS_VC" = X"no"; then
+elif test X"$MIXED_VC" = X"no"; then
WX_VERSION=`wx-config --version`
case $WX_VERSION in
2.8.*)
@@ -392,7 +392,7 @@ define(wx_warn_text,[
fi
else
AC_MSG_CHECKING(for wxWidgets in standard locations)
-
+ echo
# Check whether --with-wxdir was given.
AC_MSG_NOTICE(OptionCheck: [$with_wxdir $with_wx_prefix])
@@ -403,45 +403,40 @@ else
if test "${with_wx_prefix+set}" = set; then :
withval=$with_wx_prefix; CWXWIN0=$withval
else
- echo Setting it empty
CWXWIN0=""
fi
fi
- if test "x$MIXED_MSYS" = "xyes"; then
- CWXWIN_CONFIG=`win2msys_path.sh $wx_config_name 2>/dev/null`
- else
- CWXWIN_CONFIG=`cygpath $wx_config_name 2>/dev/null`
- fi
+ CWXWIN_CONFIG=`win32_path.sh -u $wx_config_name 2>/dev/null`
CWXWIN1=`dirname $CWXWIN_CONFIG 2>/dev/null`
CWXWIN2=`dirname $CWXWIN1 2>/dev/null`
if test -z "$PROGRAMFILES" ; then
- PROGRAMFILES=c:/Program Files
+ PROGRAMFILES="c:/Program Files"
fi
-
- if test "x$MIXED_MSYS" = "xyes"; then
- CWXWIN_PROG=`win2msys_path.sh "$PROGRAMFILES" 2>/dev/null`
- else
- CWXWIN_PROG=`cygpath -d "$PROGRAMFILES" | cygpath -f - 2>/dev/null`
- fi
+ CWXWIN_PROG=`win32_path.sh -u "$PROGRAMFILES" 2>/dev/null`
CWXWIN3="$CWXWIN_PROG/wxWidgets-3.*.* $CWXWIN_PROG/wxWidgets-2.*.*"
CWXWIN4="$CWXWIN_PROG/wxMSW-3.*.* $CWXWIN_PROG/wxMSW-2.*.*"
- DOC_OPT=/opt/local/pgm
- CWX_DOCUMENTED="$DOC_OPT/wxWidgets-2.*.* $DOC_OPT/wxMSW-2.*.*"
- CWX_DOCUMENTED="$DOC_OPT/wxWidgets-3.*.* $DOC_OPT/wxMSW-3.*.* $CWX_DOCUMENTED"
+ DOC_OPT1=/opt/local/pgm
+ DOC_OPT2=/mnt/c/opt/local/pgm
+ CWX_DOCUMENTED="$DOC_OPT1/wxWidgets-3.*.* $DOC_OPT1/wxMSW-3.*.*"
+ CWX_DOCUMENTED="$DOC_OPT2/wxWidgets-3.*.* $DOC_OPT2/wxMSW-3.*.* $CWX_DOCUMENTED"
case $ac_cv_sizeof_void_p in
8)
- DOC_OPT64=/opt/local64/pgm
- CWX_DOCUMENTED="$DOC_OPT64/wxWidgets-2.*.* $DOC_OPT64/wxMSW-2.*.* $CWX_DOCUMENTED"
- CWX_DOCUMENTED="$DOC_OPT64/wxWidgets-3.*.* $DOC_OPT64/wxMSW-3.*.* $CWX_DOCUMENTED"
+ DOC_OPT64_1=/opt/local64/pgm
+ DOC_OPT64_2=/mnt/c/opt/local64/pgm
+ CWX_DOCUMENTED="$DOC_OPT64_1/wxWidgets-3.*.* $DOC_OPT64_1/wxMSW-3.*.* $CWX_DOCUMENTED"
+ CWX_DOCUMENTED="$DOC_OPT64_2/wxWidgets-3.*.* $DOC_OPT64_2/wxMSW-3.*.* $CWX_DOCUMENTED"
;;
*)
- true
+ DOC_OPT3=/opt/local32/pgm
+ DOC_OPT4=/mnt/c/opt/local32/pgm
+ CWX_DOCUMENTED="$DOC_OPT3/wxWidgets-3.*.* $DOC_OPT3/wxMSW-3.*.* $CWX_DOCUMENTED"
+ CWX_DOCUMENTED="$DOC_OPT4/wxWidgets-3.*.* $DOC_OPT4/wxMSW-3.*.* $CWX_DOCUMENTED"
;;
esac
@@ -460,7 +455,7 @@ else
WX_RESCOMP="rc.sh -I$WXINCLUDE_PLAIN -D __WIN32__"
RC_FILE_TYPE=res
for lib in $WX_LIBDIR $WX_LIBDIR64; do
- maybe=`ls $lib/wxbase*.lib | egrep 'wxbase[[0-9]]*u\.lib'`
+ maybe=`ls $lib/wxbase*.lib 2> /dev/null | 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
@@ -502,7 +497,36 @@ if test "$WXERL_CAN_BUILD_DRIVER" != "false"; then
AC_SUBST(WX_HAVE_STATIC_LIBS)
AC_SUBST(RC_FILE_TYPE)
-AC_MSG_CHECKING(if wxwidgets have opengl support)
+AC_MSG_CHECKING(for wxwidgets 2.8 compatibility )
+AC_LANG_PUSH(C++)
+saved_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS"
+
+AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #ifdef WIN32
+ # include <windows.h>
+ #endif
+ #include "wx/wx.h"
+ ]], [[
+ #if wxMAJOR_VERSION > 2 && WXWIN_COMPATIBILITY_2_8 == 1
+ ;
+ #else
+ #error barf
+ #endif
+ ]])],
+ HAVE_COMPAT28_SUPPORT=yes, HAVE_COMPAT28_SUPPORT=no)
+
+CXXFLAGS=$saved_CXXFLAGS
+AC_LANG_POP(C++)
+AC_MSG_RESULT($HAVE_COMPAT28_SUPPORT)
+
+if test X"$HAVE_COMPAT28_SUPPORT" != X"yes" ; then
+ echo "wxWidgets was not compiled with --enable-compat28, wx will NOT be useable" >> ./CONF_INFO
+ WXERL_CAN_BUILD_DRIVER=false
+fi
+
+AC_MSG_CHECKING(for wxwidgets opengl support)
AC_LANG_PUSH(C++)
saved_CXXFLAGS=$CXXFLAGS
CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS"
diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile
index f66d63f63b..1d2b9824f4 100644
--- a/lib/wx/doc/src/Makefile
+++ b/lib/wx/doc/src/Makefile
@@ -37,10 +37,11 @@ ModsNoExt = $(ErlMods:%.erl=%) $(GenMods:%.erl=%)
# Release directory specification
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = $(ErlMods:%.erl=%.xml) $(GenMods:%.erl=%.xml)
+EDOC_REF3_FILES = $(ErlMods:%.erl=%.xml)
+XML_REF3_FILES = $(GenMods:%.erl=%.xml)
XML_PART_FILES = part.xml
-XML_CHAPTER_FILES = chapter.xml
+EDOC_CHAPTER_FILE = chapter.xml
XML_NOTES_FILES = notes.xml
BOOK_FILES = book.xml
@@ -50,101 +51,27 @@ XML_FILES = \
$(XML_PART_FILES) $(XML_NOTES_FILES)
XML_GEN_FILES = \
- $(XML_CHAPTER_FILES:%=$(XMLDIR)/%) \
- $(XML_REF3_FILES:%=$(XMLDIR)/%) \
- $(XML_APPLICATION_FILES:%=$(XMLDIR)/%)
+ $(XML_APPLICATION_FILES:%=$(XMLDIR)/%) \
+ $(XML_REF3_FILES:%=$(XMLDIR)/%)
-# ----------------------------------------------------
-INFO_FILE = ../../info
-
-HTML_FILES = \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_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
-
-SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+EDOC_FLAGS=-i ../../src -preprocess true -sort_functions false
TOP_SPECS_FILE = specs.xml
# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-SPECS_FLAGS = -I../../include -I../../src
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
+include $(ERL_TOP)/make/doc.mk
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
+$(XMLDIR)/%.xml: $(APP_SRC_DIR)/gen/%.erl
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
+ -def vsn $(VSN) $(EDOC_FLAGS) -dir $(XMLDIR) $<
-ref_man.xml: ref_man.xml.src
+ref_man.xml: ref_man.xml.src Makefile
@echo Preparing $@
@cat ref_man.xml.src > $@
@for d in $(ModsNoExt); do \
echo " <xi:include href=\"$$d.xml\"/>" >> $@ ; \
done
@echo "</application>" >> $@
-
-$(ErlMods:%.erl=$(XMLDIR)/%.xml):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
- -def vsn $(VSN) -preprocess true -sort_functions false -dir $(XMLDIR) \
- ../../src/$(@:$(XMLDIR)/%.xml=%.erl)
-
-$(GenMods:%.erl=$(XMLDIR)/%.xml):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
- -def vsn $(VSN) -i ../../src -preprocess true -sort_functions false -dir $(XMLDIR) \
- ../../src/gen/$(@:$(XMLDIR)/%.xml=%.erl)
-
-$(XML_CHAPTER_FILES:%=$(XMLDIR)/%): ../overview.edoc
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
- -def vsn $(VSN) -chapter -dir $(XMLDIR) $<
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(SPECDIR)/*
- rm -f errs core *~ ../html/edoc-info
- rm -f $(XML_GEN_FILES) *.html
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
-release_tests_spec:
diff --git a/lib/wx/src/gen/wxCalendarCtrl.erl b/lib/wx/src/gen/wxCalendarCtrl.erl
index 51116f601f..221de7bf69 100644
--- a/lib/wx/src/gen/wxCalendarCtrl.erl
+++ b/lib/wx/src/gen/wxCalendarCtrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2019. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -78,7 +78,8 @@
update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxCalendarCtrl/0]).
--deprecated([enableYearChange/1,enableYearChange/2]).
+-deprecated([{enableYearChange,1,"not available in wxWidgets-2.9 and later"},
+ {enableYearChange,2,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(wxControl) -> true;
diff --git a/lib/wx/src/gen/wxClientDC.erl b/lib/wx/src/gen/wxClientDC.erl
index 4965536690..a29ab8cab8 100644
--- a/lib/wx/src/gen/wxClientDC.erl
+++ b/lib/wx/src/gen/wxClientDC.erl
@@ -55,7 +55,7 @@
startPage/1]).
-export_type([wxClientDC/0]).
--deprecated([new/0]).
+-deprecated([{new,0,"not available in wxWidgets-2.9 and later"}]).
-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
diff --git a/lib/wx/src/gen/wxCursor.erl b/lib/wx/src/gen/wxCursor.erl
index 689935576a..7a9033dbee 100644
--- a/lib/wx/src/gen/wxCursor.erl
+++ b/lib/wx/src/gen/wxCursor.erl
@@ -36,7 +36,8 @@
saveFile/4,setDepth/2,setHeight/2,setMask/2,setPalette/2,setWidth/2]).
-export_type([wxCursor/0]).
--deprecated([new/3,new/4]).
+-deprecated([{new,3,"not available in wxWidgets-2.9 and later"},
+ {new,4,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(wxBitmap) -> true;
diff --git a/lib/wx/src/gen/wxDC.erl b/lib/wx/src/gen/wxDC.erl
index 16bfcc3463..322f80e4f0 100644
--- a/lib/wx/src/gen/wxDC.erl
+++ b/lib/wx/src/gen/wxDC.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@
-export([parent_class/1]).
-export_type([wxDC/0]).
--deprecated([computeScaleAndOrigin/1]).
+-deprecated([{computeScaleAndOrigin,1,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGraphicsRenderer.erl b/lib/wx/src/gen/wxGraphicsRenderer.erl
index a1b67476e3..2e3e21bf50 100644
--- a/lib/wx/src/gen/wxGraphicsRenderer.erl
+++ b/lib/wx/src/gen/wxGraphicsRenderer.erl
@@ -33,7 +33,8 @@
-export([parent_class/1]).
-export_type([wxGraphicsRenderer/0]).
--deprecated([createLinearGradientBrush/7,createRadialGradientBrush/8]).
+-deprecated([{createLinearGradientBrush,7,"not available in wxWidgets-2.9 and later"},
+ {createRadialGradientBrush,8,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellEditor.erl b/lib/wx/src/gen/wxGridCellEditor.erl
index d14b1c994c..c4a730ed8b 100644
--- a/lib/wx/src/gen/wxGridCellEditor.erl
+++ b/lib/wx/src/gen/wxGridCellEditor.erl
@@ -32,7 +32,8 @@
-export([parent_class/1]).
-export_type([wxGridCellEditor/0]).
--deprecated([endEdit/4,paintBackground/3]).
+-deprecated([{endEdit,4,"not available in wxWidgets-2.9 and later"},
+ {paintBackground,3,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxIdleEvent.erl b/lib/wx/src/gen/wxIdleEvent.erl
index 1cb3e34ef6..d23943805e 100644
--- a/lib/wx/src/gen/wxIdleEvent.erl
+++ b/lib/wx/src/gen/wxIdleEvent.erl
@@ -39,7 +39,7 @@
resumePropagation/2,shouldPropagate/1,skip/1,skip/2,stopPropagation/1]).
-export_type([wxIdleEvent/0]).
--deprecated([canSend/1]).
+-deprecated([{canSend,1,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(wxEvent) -> true;
diff --git a/lib/wx/src/gen/wxMDIClientWindow.erl b/lib/wx/src/gen/wxMDIClientWindow.erl
index 7eabc7d99a..cd603aa08b 100644
--- a/lib/wx/src/gen/wxMDIClientWindow.erl
+++ b/lib/wx/src/gen/wxMDIClientWindow.erl
@@ -72,7 +72,8 @@
update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxMDIClientWindow/0]).
--deprecated([new/1,new/2]).
+-deprecated([{new,1,"not available in wxWidgets-2.9 and later"},
+ {new,2,"not available in wxWidgets-2.9 and later"}]).
%% @hidden
parent_class(wxWindow) -> true;
diff --git a/lib/wx/src/gen/wxPaintDC.erl b/lib/wx/src/gen/wxPaintDC.erl
index b571219020..bb61511302 100644
--- a/lib/wx/src/gen/wxPaintDC.erl
+++ b/lib/wx/src/gen/wxPaintDC.erl
@@ -55,7 +55,7 @@
startPage/1]).
-export_type([wxPaintDC/0]).
--deprecated([new/0]).
+-deprecated([{new,0,"not available in wxWidgets-2.9 and later"}]).
-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
diff --git a/lib/wx/src/gen/wxPostScriptDC.erl b/lib/wx/src/gen/wxPostScriptDC.erl
index 07764bca74..eb315341f1 100644
--- a/lib/wx/src/gen/wxPostScriptDC.erl
+++ b/lib/wx/src/gen/wxPostScriptDC.erl
@@ -54,7 +54,8 @@
startPage/1]).
-export_type([wxPostScriptDC/0]).
--deprecated([getResolution/0,setResolution/1]).
+-deprecated([{getResolution,0,"not available in wxWidgets-2.9 and later"},
+ {setResolution,1,"not available in wxWidgets-2.9 and later"}]).
-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
diff --git a/lib/wx/src/gen/wxWindowDC.erl b/lib/wx/src/gen/wxWindowDC.erl
index 9b1045b84d..04856786a8 100644
--- a/lib/wx/src/gen/wxWindowDC.erl
+++ b/lib/wx/src/gen/wxWindowDC.erl
@@ -54,7 +54,7 @@
startPage/1]).
-export_type([wxWindowDC/0]).
--deprecated([new/0]).
+-deprecated([{new,0,"not available in wxWidgets-2.9 and later"}]).
-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]).
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 181206c762..81d188b26a 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -117,12 +117,16 @@
start_link/3, start_link/4,
stop/1, stop/3,
call/2, call/3,
+ send_request/2, wait_response/1, wait_response/2, check_response/2,
cast/2,
reply/2,
get_pid/1,
set_pid/2
]).
+-type request_id() :: term().
+-type server_ref() :: Obj::wx:wx_object()|atom()|pid().
+
%% -export([behaviour_info/1]).
-callback init(Args :: term()) ->
{#wx_ref{}, State :: term()} | {#wx_ref{}, State :: term(), timeout() | 'hibernate'} |
@@ -317,6 +321,34 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
+%% @doc Make an send_request to a generic server.
+%% and return a RequestId which can/should be used with wait_response/[1|2].
+%% Invokes handle_call(Request, From, State) in server.
+-spec send_request(Obj, Request::term()) -> request_id() when
+ Obj::wx:wx_object()|atom()|pid().
+send_request(#wx_ref{state=Pid}, Request) ->
+ gen:send_request(Pid, '$gen_call', Request);
+send_request(Pid, Request) when is_atom(Pid) orelse is_pid(Pid) ->
+ gen:send_request(Pid, '$gen_call', Request).
+
+%% @doc Wait infinitely for a reply from a generic server.
+-spec wait_response(RequestId::request_id()) ->
+ {reply, Reply::term()} | {error, {term(), server_ref()}}.
+wait_response(RequestId) ->
+ gen:wait_response(RequestId, infinity).
+
+%% @doc Wait 'timeout' for a reply from a generic server.
+-spec wait_response(Key::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+ gen:wait_response(RequestId, Timeout).
+
+%% @doc Check if a received message was a reply to a RequestId
+-spec check_response(Msg::term(), Key::request_id()) ->
+ {reply, Reply::term()} | 'false' | {error, {term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+ gen:check_response(Msg, RequestId).
+
%% @doc Make a cast to a wx_object server.
%% Invokes handle_cast(Request, State) in the server
-spec cast(Obj, Request) -> ok when
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index ad03a378de..433b705673 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -394,10 +394,19 @@ wx_object(Config) ->
{call, {Frame,Panel}, _} = wx_object:call(Frame, fun(US) -> US end),
?m(false, wxWindow:getParent(Panel) =:= Frame),
?m(true, wx:equal(wxWindow:getParent(Panel),Frame)),
+ flush(),
+ ReqId = wx_object:send_request(Frame, fun(_US) -> timer:sleep(10), yes end),
+ timeout = wx_object:wait_response(ReqId, 0),
+ {reply, {call, yes, {Me,{_,ReqId}}}} = wx_object:wait_response(ReqId, 1000),
+ ReqId2 = wx_object:send_request(Frame, yes),
+ [Msg] = flush(),
+ no_reply = wx_object:check_response(Msg, ReqId),
+ {reply, {call, yes, {Me,{_,ReqId2}}}} = wx_object:check_response(Msg, ReqId2),
+
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()),
diff --git a/lib/xmerl/Makefile b/lib/xmerl/Makefile
index a584aacbac..65a0af9e4a 100644
--- a/lib/xmerl/Makefile
+++ b/lib/xmerl/Makefile
@@ -50,12 +50,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
@@ -97,3 +92,4 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile
index 0def492246..5effc831e2 100644
--- a/lib/xmerl/doc/src/Makefile
+++ b/lib/xmerl/doc/src/Makefile
@@ -28,33 +28,19 @@ VSN=$(XMERL_VSN)
APPLICATION=xmerl
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Help application directory specification
-# ----------------------------------------------------
-
-EDOC_DIR = $(ERL_TOP)/lib/edoc
-SYNTAX_TOOLS_DIR = $(ERL_TOP)/lib/syntax_tools
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XMERL_DIR = $(ERL_TOP)/lib/$(APPLICATION)/src
-XMERL_MODULES = \
- xmerl_scan \
- xmerl \
- xmerl_xs \
- xmerl_eventp \
- xmerl_xpath \
- xmerl_xsd
-
+EDOC_REF3_FILES = \
+ xmerl_scan.xml \
+ xmerl.xml \
+ xmerl_xs.xml \
+ xmerl_eventp.xml \
+ xmerl_xpath.xml \
+ xmerl_xsd.xml
XML_APPLICATION_FILES = ref_man.xml
-XMERL_XML_FILES = $(XMERL_MODULES:%=$(XMLDIR)/%.xml)
XML_REF3_FILES = xmerl_sax_parser.xml
@@ -85,97 +71,15 @@ EXAMPLE_FILES = people2.txt people.txt motorcycles.txt motorcycles_dtd.txt \
new_motorcycles.txt new_motorcycles2.txt result_export.html \
motorcycles2.txt result_xs.html motorcycles2html.erl
+HTML_EXTRA_FILES = $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES)
+
XML_FILES= \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-XML_GEN_FILES = $(XMERL_XML_FILES) $(XML_CHAPTER_GEN_FILES)
-
-# ----------------------------------------------------
-INFO_FILE = ../../info
-
-HTML_FILES = $(XML_REF_MAN:%.xml=$(HTMLDIR)/%.html) \
- $(XML_HTML_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) $(XMERL_MODULES:%=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_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)
-
-$(XMERL_XML_FILES):
- $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -dir $(XMLDIR) $(XMERL_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-xml: $(XMERL_XML_FILES)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- rm -f $(MAN3DIR)/*
- rm -f $(MAN6DIR)/*
- rm -f $(XMERL_XML_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-info:
- @echo "XML_PART_FILES: $(XML_PART_FILES)"
- @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)"
- @echo "XMERL_XML_FILES: $(XMERL_XML_FILES)"
- @echo "XMERL_MODULES: $(XMERL_MODULES)"
- @echo "HTML_FILES: $(HTML_FILES)"
- @echo "HTMLDIR: $(HTMLDIR)"
- @echo "DEFAULT_GIF_FILES: $(DEFAULT_GIF_FILES)"
- @echo "DEFAULT_HTML_FILES: $(DEFAULT_HTML_FILES)"
+XML_GEN_FILES = $(XML_CHAPTER_GEN_FILES)
# ----------------------------------------------------
# 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) $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
-
-
-release_tests_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/xmerl/doc/src/xmerl_sax_parser.xml b/lib/xmerl/doc/src/xmerl_sax_parser.xml
index 2390779028..0c5ecb5c2e 100644
--- a/lib/xmerl/doc/src/xmerl_sax_parser.xml
+++ b/lib/xmerl/doc/src/xmerl_sax_parser.xml
@@ -56,7 +56,7 @@
<taglist>
<tag><c>{continuation_fun, ContinuationFun}</c></tag>
<item>
- <seealso marker="#ContinuationFun/1">ContinuationFun</seealso> is a call back function to decide what to do if
+ <seealso marker="#Module:ContinuationFun/1">ContinuationFun</seealso> is a call back function to decide what to do if
the parser runs into EOF before the document is complete.
</item>
<tag><c>{continuation_state, term()}</c></tag>
@@ -65,7 +65,7 @@
</item>
<tag><c>{event_fun, EventFun}</c></tag>
<item>
- <seealso marker="#EventFun/3">EventFun</seealso> is the call back function for parser events.
+ <seealso marker="#Module:EventFun/3">EventFun</seealso> is the call back function for parser events.
</item>
<tag><c>{event_state, term()}</c></tag>
<item>
@@ -381,7 +381,7 @@
<funcs>
<func>
- <name since="">ContinuationFun(State) -> {NewBytes, NewState}</name>
+ <name since="">Module:ContinuationFun(State) -> {NewBytes, NewState}</name>
<fsummary>Continuation call back function.</fsummary>
<type>
<v>State = NewState = term()</v>
@@ -402,7 +402,7 @@
</func>
<func>
- <name since="">EventFun(Event, Location, State) -> NewState</name>
+ <name since="">Module:EventFun(Event, Location, State) -> NewState</name>
<fsummary>Event call back function.</fsummary>
<type>
<v>Event = event()</v>
diff --git a/make/app_targets.mk b/make/app_targets.mk
new file mode 100644
index 0000000000..e9aaa4193c
--- /dev/null
+++ b/make/app_targets.mk
@@ -0,0 +1,66 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2019. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+APPLICATION ?= $(basename $(notdir $(PWD)))
+
+.PHONY: test info gclean dialyzer dialyzer_plt dclean
+
+test:
+ $(ERL_TOP)/make/test_target_script.sh $(ERL_TOP)
+
+info:
+ @echo "$(APPLICATION)_VSN: $(VSN)"
+ @echo "APP_VSN: $(APP_VSN)"
+ @echo ""
+ @echo "DIA_PLT: $(DIA_PLT)"
+ @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
+ @echo ""
+
+gclean:
+ git clean -fXd
+
+
+DIA_DEFAULT_PLT_APPS = erts kernel stdlib $(APPLICATION)
+DIA_PLT_DIR = ./priv/plt
+DIA_PLT = $(DIA_PLT_DIR)/$(APPLICATION).plt
+DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+
+dialyzer_plt: $(DIA_PLT)
+
+$(DIA_PLT_DIR):
+ @mkdir -p $@
+
+$(DIA_PLT): $(DIA_PLT_DIR)
+ @echo "Building $(APPLICATION) plt file"
+ @$(ERL_TOP)/bin/dialyzer --build_plt \
+ --output_plt $@ \
+ --apps $(sort $(DIA_PLT_APPS) $(DIA_DEFAULT_PLT_APPS)) \
+ --output $(DIA_ANALYSIS) \
+ --verbose
+
+dialyzer: $(DIA_PLT)
+ @echo "Running dialyzer on $(APPLICATION)"
+ @dialyzer --plt $< \
+ ../$(APPLICATION)/ebin \
+ --verbose
+
+dclean:
+ rm -f $(DIA_PLT)
+ rm -f $(DIA_ANALYSIS)
diff --git a/make/configure.in b/make/configure.in
index c4b89c4f45..64680d55ca 100644
--- a/make/configure.in
+++ b/make/configure.in
@@ -339,6 +339,10 @@ AS_HELP_STRING([--enable-m32-build],
esac
],enable_m32_build=no)
+AC_ARG_ENABLE(pie,
+AS_HELP_STRING([--enable-pie], [build position independent executables])
+AS_HELP_STRING([--disable-pie], [do no build position independent executables]))
+
AC_ARG_WITH(libatomic_ops,
AS_HELP_STRING([--with-libatomic_ops=PATH],
[specify and prefer usage of libatomic_ops in the ethread library]))
diff --git a/make/doc.mk b/make/doc.mk
new file mode 100644
index 0000000000..dedb5d7e8e
--- /dev/null
+++ b/make/doc.mk
@@ -0,0 +1,215 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2019. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+ifeq ($(APPLICATION),erts)
+RELSYSDIR = $(RELEASE_PATH)/$(APPLICATION)-$(VSN)
+else
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+endif
+RELCHUNKSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+APP_DIR = $(ERL_TOP)/lib/$(APPLICATION)
+APP_SRC_DIR = $(APP_DIR)/src
+APP_EBIN_DIR = $(APP_DIR)/src
+
+# ----------------------------------------------------
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_HTML_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+XML_ALL_REF3_FILES = $(XML_REF3_FILES) $(EDOC_REF3_FILES)
+XML_CHAPTER_FILES += $(EDOC_CHAPTER_FILE)
+XML_GEN_FILES += $(EDOC_REF3_FILES:%=$(XMLDIR)/%) $(EDOC_CHAPTER_FILE:%=$(XMLDIR)/%)
+
+INFO_FILE = ../../info
+
+MAN1_FILES = $(XML_REF1_FILES:%_cmd.xml=$(MAN1DIR)/%.1)
+MAN2_FILES = $(XML_REF2_FILES:%.xml=$(MAN1DIR)/%.2)
+MAN3_FILES = $(XML_ALL_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
+MAN5_FILES = $(XML_REF5_FILES:%.xml=$(MAN4DIR)/%.5)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+MAN7_FILES = $(MIB_REF7_FILES:$(MIBSDIR)/%.mib=$(MAN7DIR)/%.7)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+ifneq ($(TOP_SPECS_FILE),)
+SPECS_FILES = $(XML_ALL_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+endif
+
+ifneq ($(strip $(CHUNKSDIR)),)
+_create_chunksdir_dirs := $(shell mkdir -p $(CHUNKSDIR))
+endif
+CHUNK_REF3_FILES = $(filter-out $(NO_CHUNKS), $(XML_ALL_REF3_FILES))
+CHUNK_FILES = $(CHUNK_REF3_FILES:%.xml=$(CHUNKSDIR)/%.chunk)
+
+ERL_CHUNK_FILES = $(patsubst $(APP_EBIN_DIR)/%.BEAM,$(CHUNKSDIR)/%.chunk,$(wildcard $(APP_EBIN_DIR)/*.beam))
+EMPTY_CHUNK_FILES = $(filter-out $(NO_CHUNKS:%.xml=$(CHUNKSDIR)/%.chunk) $(CHUNK_FILES), $(ERL_CHUNK_FILES))
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+SPECS_FLAGS = -I$(ERL_TOP)/lib -I$(ERL_TOP)/lib/*/include -I$(ERL_TOP)/lib/*/src
+
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+$(HTMLDIR)/%.png: %.png
+ $(INSTALL_DATA) $< $@
+$(HTMLDIR)/%.jpg: %.jpg
+ $(INSTALL_DATA) $< $@
+
+DOC_TARGETS?=man pdf html chunks
+
+docs: $(INFO_FILE) $(DOC_TARGETS)
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: images $(HTML_REF_MAN_FILE) $(HTMLDIR)/$(APPLICATION).eix
+
+man: $(MAN1_FILES) $(MAN2_FILES) $(MAN3_FILES) $(MAN4_FILES) $(MAN5_FILES) $(MAN6_FILES) $(MAN7_FILES)
+
+chunks: $(CHUNK_FILES) $(EMPTY_CHUNK_FILES)
+
+images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
+
+$(EDOC_REF3_FILES:%=$(XMLDIR)/%): $(APP_SRC_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript \
+ -def vsn $(VSN) $(EDOC_FLAGS) -dir $(XMLDIR) $(APP_SRC_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
+$(XMLDIR)/$(EDOC_CHAPTER_FILE): ../overview.edoc
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) \
+ -chapter -dir $(XMLDIR) $<
+
+info:
+ @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)"
+ @echo "XML_REF1_FILES: $(XML_REF1_FILES)"
+ @echo "XML_REF2_FILES: $(XML_REF2_FILES)"
+ @echo "XML_REF3_FILES: $(XML_ALL_REF3_FILES)"
+ @echo "XML_REF4_FILES: $(XML_REF4_FILES)"
+ @echo "XML_REF5_FILES: $(XML_REF5_FILES)"
+ @echo "XML_REF6_FILES: $(XML_REF6_FILES)"
+ @echo "XML_REF7_FILES: $(XML_REF7_FILES)"
+ @echo "XML_PART_FILES: $(XML_PART_FILES)"
+ @echo "XML_CHAPTER_FILES: $(XML_CHAPTER_FILES)"
+ @echo "BOOK_FILES: $(BOOK_FILES)"
+
+debug opt lcnt:
+
+clean clean_docs: clean_xml clean_pdf clean_html clean_man clean_chunks
+ rm -rf $(EXTRA_FILES)
+ rm -f errs core *~ *.eps
+
+clean_pdf:
+ rm -f $(PDFDIR)/*
+
+clean_man:
+ rm -f $(MAN1DIR)/* $(MAN3DIR)/* $(MAN4DIR)/* $(MAN6DIR)/*
+
+clean_xml:
+ rm -f $(SPECDIR)/*
+ rm -rf $(XMLDIR)
+
+clean_html:
+ rm -rf $(HTMLDIR)/*
+
+clean_chunks:
+ rm -f $(CHUNKSDIR)/*
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+$(RELSYSDIR):
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+
+release_pdf_spec: pdf
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DIR_DATA) $(HTMLDIR) "$(RELSYSDIR)/doc/html"
+ifneq ($(HTML_EXTRA_FILES),)
+ $(INSTALL_DATA) $(HTML_EXTRA_FILES) "$(RELSYSDIR)/doc/html"
+endif
+
+release_chunks_spec: chunks
+ifneq ($(CHUNK_FILES),)
+ $(INSTALL_DIR) "$(RELCHUNKSDIR)/doc/chunks"
+ $(INSTALL_DATA) $(CHUNKSDIR)/* "$(RELCHUNKSDIR)/doc/chunks"
+endif
+
+release_man_spec: man
+ifneq ($(MAN1_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
+ $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1"
+endif
+ifneq ($(MAN2_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man2"
+ $(INSTALL_DATA) $(MAN2DIR)/* "$(RELEASE_PATH)/man/man2"
+endif
+ifneq ($(MAN3_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+endif
+ifneq ($(MAN4_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
+ $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4"
+endif
+ifneq ($(MAN5_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man5"
+ $(INSTALL_DATA) $(MAN5_FILES) "$(RELEASE_PATH)/man/man5"
+endif
+ifneq ($(MAN6_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
+ $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+endif
+ifneq ($(MAN7_FILES),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man7"
+ $(INSTALL_DATA) $(MAN7_FILES) "$(RELEASE_PATH)/man/man7"
+endif
+
+release_docs_spec: $(RELSYSDIR) $(INFO_FILE) $(DOC_TARGETS:%=release_%_spec)
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ifneq ($(STANDARDS),)
+ $(INSTALL_DIR) "$(RELEASE_PATH)/doc/standard"
+ $(INSTALL_DATA) $(STANDARDS) "$(RELEASE_PATH)/doc/standard"
+endif
+
+release_spec:
+
+.PHONY: clean clean_xml clean_html clean_man clean_pdf \
+ debug opt info \
+ docs images html man pdf chunks \
+ release_docs_spec release_spec
diff --git a/make/otp.mk.in b/make/otp.mk.in
index 63748c2f2b..5c1032c894 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -90,12 +90,8 @@ AR = @AR@
PERL = @PERL@
LLVM_PROFDATA = @LLVM_PROFDATA@
-MIXED_CYGWIN_VC = @MIXED_CYGWIN_VC@
-MIXED_MSYS_VC = @MIXED_MSYS_VC@
MIXED_VC = @MIXED_VC@
-MIXED_CYGWIN_MINGW = @MIXED_CYGWIN_MINGW@
-MIXED_CYGWIN = @MIXED_CYGWIN@
-MIXED_MSYS = @MIXED_MSYS@
+MIXED_MINGW = @MIXED_MINGW@
BITS64 = @BITS64@
@@ -230,13 +226,16 @@ MAN1DIR = $(DOCDIR)/man1
MAN2DIR = $(DOCDIR)/man2
MAN3DIR = $(DOCDIR)/man3
MAN4DIR = $(DOCDIR)/man4
+MAN5DIR = $(DOCDIR)/man5
MAN6DIR = $(DOCDIR)/man6
+MAN7DIR = $(DOCDIR)/man7
MAN9DIR = $(DOCDIR)/man9
TEXDIR = .
SPECDIR = $(DOCDIR)/specs
XMLDIR = $(DOCDIR)/xml
+CHUNKSDIR = $(DOCDIR)/chunks
ifeq ($(CSS_FILE),)
CSS_FILE = otp_doc.css
@@ -272,7 +271,7 @@ DEFAULT_HTML_FILES = \
$(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.kwc) \
$(HTMLDIR)/index.html
-DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
+DEFAULT_GIF_FILES =
#
# Flags & Commands
@@ -291,14 +290,18 @@ SPECS_ESRC = ../../src
endif
SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript
# Extract specifications and types from Erlang source files (-spec, -type)
-$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl
+$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl $(TOP_SPECS_FILE)
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $<
-$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/gen/%.erl
+$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/gen/%.erl $(TOP_SPECS_FILE)
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $<
MANXSLTARGS=--stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_man_entities -path .
-$(MAN1DIR)/%.1 $(MAN2DIR)/%.2 $(MAN4DIR)/%.4 $(MAN4DIR)/%.5 $(MAN9DIR)/%.9: $(XMLDIR)/%.xml
+$(MAN1DIR)/%.1: $(XMLDIR)/%_cmd.xml
+ $(gen_verbose)date=`date +"%B %e, %Y"`; \
+ xsltproc --output "$@" $(MANXSLTARGS) $(DOCGEN)/priv/xsl/db_man.xsl $<
+
+$(MAN2DIR)/%.2 $(MAN4DIR)/%.4 $(MAN4DIR)/%.5 $(MAN9DIR)/%.9: $(XMLDIR)/%.xml
$(gen_verbose)date=`date +"%B %e, %Y"`; \
xsltproc --output "$@" $(MANXSLTARGS) $(DOCGEN)/priv/xsl/db_man.xsl $<
@@ -323,3 +326,12 @@ $(XMLDIR)/%.xml: $(XMLDIR)/%.xmlsrc
$(PDFDIR)/%.pdf: %.fo
$(FOP) -c $(FOP_CONFIG) -cache $(ERL_TOP)/make/$(TARGET)/fop-fonts.cache -fo $< -pdf $@
+
+$(CHUNKSDIR)/%.chunk: $(XMLDIR)/%.xml ../../ebin/%.beam $(DOCGEN)/priv/bin/chunk.escript
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/chunk.escript $^ $@
+
+$(CHUNKSDIR)/%.chunk: $(XMLDIR)/%.xml ../../preloaded/ebin/%.beam $(DOCGEN)/priv/bin/chunk.escript
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/chunk.escript $^ $@
+
+$(CHUNKSDIR)/%.chunk: ../../ebin/%.beam $(DOCGEN)/priv/bin/chunk.escript
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/chunk.escript $^ $@
diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk
index c629a7d143..fe1f5103ca 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -60,7 +60,7 @@ endif
ifeq ($(TOPDOC),)
-$(HTMLDIR)/index.html: $(XML_GEN_FILES) $(SPECS_FILES)
+$(HTMLDIR)/index.html: $(XML_GEN_FILES) $(SPECS_FILES) $(TOP_SPECS_FILE)
$(gen_verbose)date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --noout \
--stringparam outdir $(HTMLDIR) \
@@ -83,7 +83,7 @@ $(HTMLDIR)/index.html: $(XML_GEN_FILES) $(SPECS_FILES)
endif
-$(HTMLDIR)/users_guide.html: $(XML_GEN_FILES)
+$(HTMLDIR)/users_guide.html: $(XML_GEN_FILES) $(TOP_SPECS_FILE)
$(gen_verbose)date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --noout \
--stringparam outdir $(HTMLDIR) \
@@ -104,7 +104,7 @@ $(HTMLDIR)/users_guide.html: $(XML_GEN_FILES)
-path $(DOCGEN)/priv/dtd_html_entities \
$(DOCGEN)/priv/xsl/db_html.xsl $(XMLDIR)/book.xml
-%.fo: $(XML_GEN_FILES) $(SPECS_FILES)
+%.fo: $(XML_GEN_FILES) $(SPECS_FILES) $(TOP_SPECS_FILE)
$(gen_verbose)date=`date +"%B %e, %Y"`; \
$(XSLTPROC) \
--stringparam docgen "$(DOCGEN)" \
@@ -142,8 +142,6 @@ $(HTMLDIR)/$(APPLICATION).eix: $(XML_GEN_FILES) $(SPECS_FILES)
-path $(DOCGEN)/priv/dtd_html_entities \
$(DOCGEN)/priv/xsl/db_eix.xsl $(XMLDIR)/book.xml > $@
-docs: $(HTMLDIR)/$(APPLICATION).eix
-
## Here awk is used to find all xi:include files in $(BOOK_FILES)
## Then we look into all those files check for xi:includes
BOOK_XI_INC_FILES:=$(foreach file,$(BOOK_FILES),$(shell awk -F\" '/xi:include/ {print $$2}' $(file))) $(BOOK_FILES)
@@ -207,15 +205,21 @@ endif
# Standard release target
# ----------------------------------------------------
+pdf man chunks html: $(XML_GEN_FILES) $(SPECS_FILES) $(TOP_SPECS_FILE)
+release_man_spec: man
+release_pdf_spec: pdf
+release_chunks_spec: chunks
+release_html_spec: html
+
ifeq ($(TESTROOT),)
-release release_docs release_tests release_html:
+release release_docs release_tests:
$(MAKE) $(MFLAGS) RELEASE_PATH=$(OTP_DEFAULT_RELEASE_PATH) \
$(TARGET_MAKEFILE) $@_spec
else
-release release_docs release_tests release_html:
+release release_docs release_tests:
$(MAKE) $(MFLAGS) RELEASE_PATH="$(TESTROOT)" $(TARGET_MAKEFILE) $@_spec
endif
diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge
index 7039625aa5..e69de29bb2 100644
--- a/make/otp_version_tickets_in_merge
+++ b/make/otp_version_tickets_in_merge
@@ -1,3 +0,0 @@
-OTP-16457
-OTP-16459
-OTP-16486
diff --git a/make/target.mk b/make/target.mk
index 8917e1ae44..3bcb6e91d3 100644
--- a/make/target.mk
+++ b/make/target.mk
@@ -55,14 +55,11 @@ endif
ifneq ($(TARGET),)
ifneq ($(TARGET),win32)
-ifneq ($(findstring vxworks,$(TARGET)),vxworks)
override TARGET := $(shell $(ERL_TOP)/erts/autoconf/config.sub $(TARGET))
else
endif
else
endif
-else
-endif
ifeq ($(TARGET),)
$(error Neither TARGET nor OVERRIDE_TARGET can be determined!)
diff --git a/make/test_target_script.sh b/make/test_target_script.sh
new file mode 100755
index 0000000000..f605efa120
--- /dev/null
+++ b/make/test_target_script.sh
@@ -0,0 +1,254 @@
+#!/bin/sh
+
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2019. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+LIGHT_CYAN='\033[1;36m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+
+print_highlighted_msg_with_printer () {
+ COLOR=$1
+ MSG_PRINTER=$2
+ printf "\n${COLOR}======================================================================${NC}\n"
+ echo
+ $MSG_PRINTER
+ echo
+ printf "${COLOR}======================================================================${NC}\n"
+}
+
+print_highlighted_msg () {
+ COLOR=$1
+ MSG=$2
+ print_msg () {
+ echo "$MSG"
+ }
+ print_highlighted_msg_with_printer $COLOR print_msg
+}
+
+print_all_tests_takes_long_time_warning () {
+ print_msg () {
+ cat << EOM
+
+WARNING
+
+All tests will require several hours to run. You may want to check the
+following text file that describes how to run tests for a specific
+application.
+
+EOM
+ echo $ERL_TOP/HOWTO/TESTING.md
+ }
+ print_highlighted_msg_with_printer $YELLOW print_msg
+}
+
+print_all_tests_for_application_notes () {
+ print_msg () {
+ cat << EOM
+
+NOTE 1
+
+ct_run will now attempt to execute tests in the test directory, which
+may take a long time to do. One can pass arguments to ct_run by
+setting the ARGS variable when invoking "make test".
+
+Example:
+
+make ARGS="-suite asn1_SUITE -case ticket_7407" test
+
+NOTE 2
+
+You may want to look at the more established way of running tests that
+is described in the following text file if you encounter strange
+errors:
+
+EOM
+ echo "$ERL_TOP/HOWTO/TESTING.md"
+ }
+ print_highlighted_msg_with_printer $LIGHT_CYAN print_msg
+}
+
+print_c_files_warning () {
+ print_msg () {
+ cat << EOM
+
+WARNING
+
+The test directory contains .c files which means that some test cases
+will probably not work correctly when run through "make test". The
+text file at the following location describes how one can compile and
+run all test cases:
+
+
+EOM
+ echo $ERL_TOP/HOWTO/TESTING.md
+ }
+ print_highlighted_msg_with_printer $YELLOW print_msg
+}
+
+
+print_on_error_note () {
+ print_msg () {
+ cat << EOM
+NOTE:
+
+Some test cases do not work correctly when run through "make test" as
+they are designed to be run through the method that is described in
+the "$ERL_TOP/HOWTO/TESTING.md" text file. You may want to check this
+text file if you encounter strange errors. Note also that you can
+rerun a specific test case by passing parameters to ct_run as in the
+example below:
+
+make ARGS="-suite asn1_SUITE -case ticket_7407" test
+
+EOM
+ }
+ print_highlighted_msg_with_printer $NC print_msg
+}
+
+# Check ERL_TOP
+
+if [ -d "$1" ]
+then
+ ERL_TOP="$1"
+ shift
+fi
+
+if [ -z $ERL_TOP ]
+then
+ ERL_TOP=`git rev-parse --show-toplevel`
+ if [ $? = 0 ]
+ then
+ print_highlighted_msg $LIGHT_CYAN "The environment variable ERL_TOP has been set to the git root"
+ else
+ echo "The ERL_TOP environment variable need to be set before this script is executed."
+ exit 1
+ fi
+fi
+
+export ERL_TOP=$ERL_TOP
+
+
+if [ -z "${ARGS}" ]
+then
+ ARGS="$@"
+fi
+
+# make test in root
+DIR=`pwd`
+if [ "$DIR" -ef "$ERL_TOP" ]
+then
+ TARGET_SYS=`$ERL_TOP/erts/autoconf/config.guess`
+ REL_DIR="$ERL_TOP/release/$TARGET_SYS"
+ cd "$REL_DIR"
+ ./Install -minimal "`pwd`"
+ export PATH="$REL_DIR/bin:$PATH"
+ cd "$ERL_TOP/release/tests/test_server"
+ print_all_tests_takes_long_time_warning
+ echo "The tests will start in a few seconds..."
+ sleep 45
+ cd "$ERL_TOP/release/tests/test_server"
+ erl -eval "ts:install(),erlang:halt()"
+ erl -noinput -eval "ts:run([all_tests,batch]),erlang:halt()"
+ exit $?
+fi
+
+# check that there is a test directory
+if [ ! -d test ]
+then
+ print_highlighted_msg $RED "This target only works in directories containing a test directory or\nin the root directory."
+ exit 1
+fi
+
+
+APPLICATION="`basename $DIR`"
+CT_RUN="$ERL_TOP/bin/ct_run"
+MAKE_TEST_DIR="`pwd`/make_test_dir"
+MAKE_TEST_REL_DIR="$MAKE_TEST_DIR/${APPLICATION}_test"
+MAKE_TEST_CT_LOGS="$MAKE_TEST_DIR/ct_logs"
+RELEASE_TEST_SPEC_LOG="$MAKE_TEST_CT_LOGS/release_tests_spec_log"
+
+cd test
+echo "The tests in test directory for $APPLICATION will be executed with ct_run"
+if [ -z "${ARGS}" ]
+then
+ if [ ! -d "$MAKE_TEST_DIR" ]
+ then
+ print_all_tests_for_application_notes
+ fi
+ if find . -type f -name '*.c' | grep -q "."
+ then
+ print_c_files_warning
+ fi
+fi
+
+mkdir -p "$MAKE_TEST_DIR"
+mkdir -p "$MAKE_TEST_REL_DIR"
+mkdir -p "$MAKE_TEST_CT_LOGS"
+make RELSYSDIR=$MAKE_TEST_REL_DIR release_tests_spec > $RELEASE_TEST_SPEC_LOG 2>&1
+
+if [ $? != 0 ]
+then
+ cat $RELEASE_TEST_SPEC_LOG
+ print_highlighted_msg $RED "\"make RELSYSDIR="$MAKE_TEST_REL_DIR" release_tests_spec\" failed."
+ exit 1
+fi
+SPEC_FLAG=""
+SPEC_FILE=""
+if [ -z "${ARGS}" ]
+then
+ SPEC_FLAG="-spec"
+ SPEC_FILE="$MAKE_TEST_REL_DIR/$APPLICATION.spec"
+ ARGS="$SPEC_FLAG $SPEC_FILE"
+fi
+# Compile test server
+(cd "$ERL_TOP/lib/common_test/test_server" && make)
+# Run ct_run
+cd $MAKE_TEST_REL_DIR
+$CT_RUN -logdir $MAKE_TEST_CT_LOGS\
+ -pa "$ERL_TOP/lib/common_test/test_server"\
+ ${ARGS}\
+ -erl_args\
+ -env "$PATH"\
+ -env ERL_CRASH_DUMP "$MAKE_TEST_DIR/${APPLICATION}_erl_crash.dump"\
+ -boot start_sasl\
+ -sasl errlog_type error\
+ -pz "$ERL_TOP/lib/common_test/test_server"\
+ -pz "."\
+ -ct_test_vars "{net_dir,\"\"}"\
+ -noshell\
+ -sname test_server\
+ -rsh ssh\
+ ${ERL_ARGS}
+CT_RUN_STATUS=$?
+if [ $CT_RUN_STATUS = "0" ]
+then
+ print_highlighted_msg $GREEN "The test(s) ran successfully (ct_run returned a success code)\nTest logs: file://$MAKE_TEST_CT_LOGS/index.html"
+ exit 0
+else
+ print_on_error_note
+ print_highlighted_msg $RED "ct_run returned the error code $CT_RUN_STATUS\nTest logs: file://$MAKE_TEST_CT_LOGS/index.html"
+ exit $CT_RUN_STATUS
+fi
diff --git a/otp_build b/otp_build
index 244f0c3e25..c9bd577fe5 100755
--- a/otp_build
+++ b/otp_build
@@ -65,18 +65,10 @@ usage ()
echo ""
echo "Before trying to build on windows, consider the following option"
echo " env_win32 [<arch>] - echo environment settings for win32 with visual C++, use with eval"
- echo " The optional <arch> can be x64 for 64bit Windows 7"
- echo " or x86 for 32bit Windows XP+"
+ echo " The optional <arch> can be x64 for 64bit Windows"
+ echo " or x86 for 32bit Windows"
echo " env_win64 - echo environment settings for win32 with visual C++, use with eval"
- echo " Note that env_win32 x64 gives the same result, Windows 7 64bit"
- echo " env_mingw32 - echo environment settings for win32 with MinGW, use with eval"
- echo " - experimental!"
- echo " env_msys64 - echo environment settings for win32 with visual C++ running "
- echo " msys and mingw, use with eval"
- echo " - experimental!"
- echo ""
- echo "Before trying to build for vxworks, consider the following option"
- echo " env_vxworks <cpu> - echo environment settings for vxworks, use with eval"
+ echo " Note that env_win32 x64 gives the same result, Windows 64bit"
echo ""
case $version_controller in
none)
@@ -84,6 +76,7 @@ usage ()
git)
echo "update_primary [--no-commit] - build and maybe commit a new primary bootstrap"
echo "update_preloaded [--no-commit] - build and maybe commit the preloaded modules"
+ echo "update_deprecations [--no-commit] - build and maybe commit deprecations"
;;
esac
}
@@ -231,19 +224,6 @@ distribute_config_helpers ()
do_autoconf ()
{
distribute_config_helpers
-
- if [ ! -z "$OVERRIDE_CONFIGURE" ]; then
- echo "Autoconf disabled on target $TARGET, but is performed on host" >&2
- # We still use erts configure for erl_interface and VxWorks
- case "$TARGET" in
- *vxworks*)
- AUTOCONF_SUBDIRS=`echo $AUTOCONF_SUBDIRS | \
- sed -e 's,lib/erl_interface,,' \
- -e 's,lib/gs,,' \
- -e 's,lib/megaco,,'`
- ;;
- esac
- fi
hide_vars OVERRIDE_TARGET TARGET
TARGET=$BUILDSYS
@@ -452,33 +432,12 @@ do_configure ()
# Get `erl_build_tool_vars'
. "$ERL_TOP/erl-build-tool-vars.sh" || exit 1
- if [ ! -z "$OVERRIDE_CONFIGURE" ]; then
- case $TARGET in
- vxworks_*)
- ( cd erts/autoconf && \
- "$ERL_TOP/erts/autoconf/configure.vxworks" $TARGET )
- echo "Configuring for build system too..." >&2
- hide_vars OVERRIDE_TARGET TARGET
- TARGET=$BUILDSYS
- export TARGET
- set_config_flags "$@"
- run_configure "$@"
- restore_vars OVERRIDE_TARGET TARGET;;
- *)
- echo "Unexpected target when ordinary configure is" \
- "overridden" >&2
- echo 'check if $OVERRIDE_CONFIGURE and $OVERRIDE_TAGET' \
- 'environments are correct.' >&2
- exit 1;;
- esac
- else
- maybe_copy_static_cache
- try_cross_configure "$@"
- if [ $cross_configure = no ]; then
- CONFIG_FLAGS=
- set_config_flags "$@"
- run_configure "$@"
- fi
+ maybe_copy_static_cache
+ try_cross_configure "$@"
+ if [ $cross_configure = no ]; then
+ CONFIG_FLAGS=
+ set_config_flags "$@"
+ run_configure "$@"
fi
}
@@ -537,74 +496,9 @@ echo_envinfo ()
fi
}
-echo_env_vxworks ()
-{
- if [ -z "$1" ]; then
- echo "env_vxworks requires CPU architecture as parameter (ppc603, ppc860 etc)." >&2
- exit 1
- fi
- echo_env_erltop
- echo_setenv OVERRIDE_CONFIGURE true ';'
- echo_setenv OVERRIDE_TARGET vxworks_$1
- echo_envinfo
-}
-
-echo_env_win32 ()
-{
- #echo_envinfo
- if [ X"$SHELL" = X"" ]; then
- echo "You need to export the shell variable first," \
- "for bourne-like shells, type:" >&2
- echo 'export SHELL' >&2
- echo "and for csh-like shells, type:" >&2
- echo 'setenv SHELL $SHELL' >&2
- echo " - then try again." >&2
- exit 1
- fi
- echo_env_erltop
- # Try to cope with paths containing unexpected things like stray
- # mixed paths (c:/something/bin) and quotes. Only C and D drive
- # handled.
- CCYGPATH=`cygpath c:\\`
- DCYGPATH=`cygpath d:\\`
- P2=`echo :$PATH | \
- sed "s,\",,g;s,:[cC]:,:$CCYGPATH,g;s,:[dD]:,:$DCYGPATH,g;s,^:,,"`
- P3=""
- save_ifs=$IFS
- IFS=:
- for p in $P2; do
- if [ -d "$p" ]; then
- C1="`(cygpath -d $p 2>/dev/null || cygpath -w $p)`" 2> /dev/null
- C2=`cygpath "$C1" 2> /dev/null` 2> /dev/null
- else
- C2=""
- fi
- if [ ! -z "$C2" ]; then
- if [ -z "$P3" ];then
- P3="$C2"
- else
- P3="$P3:$C2"
- fi
- fi
- done
- IFS=$save_ifs
- WIN32_WRAPPER_PATH="$ERL_TOP/erts/etc/win32/cygwin_tools/vc:$ERL_TOP/erts/etc/win32/cygwin_tools"
-
-
- echo_setenv OVERRIDE_TARGET win32 ';'
- echo_setenv CC cc.sh ';'
- echo_setenv CXX cc.sh ';'
- echo_setenv AR ar.sh ';'
- echo_setenv RANLIB true ';'
- if [ -f "$ERL_TOP/erts/autoconf/win32.config.cache.static" ]; then
- echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win32.config.cache.static" ';'
- fi
- echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win32.config.cache" ';'
- echo_setenv WIN32_WRAPPER_PATH "$WIN32_WRAPPER_PATH" ';'
- echo_setenv PATH "$WIN32_WRAPPER_PATH:$P3" ';'
- echo_envinfo
-}
-
+#
+# Cygwin build without microsoft visual C++ (dead?)
+#
echo_env_mingw32 ()
{
#echo_envinfo
@@ -681,8 +575,9 @@ echo_env_mingw32 ()
# N.B. In Erlang, and the build system, win32 means windows, so we keep
# everything as terget win32, but add the CONFIG_SUBTYPE win64, which can
# be handled by configure, setting WINDOWS_64BIT in headers and such
-echo_env_win64 ()
+echo_env_cygwin ()
{
+ X64=$1
#echo_envinfo
if [ X"$SHELL" = X"" ]; then
echo "You need to export the shell variable first," \
@@ -722,24 +617,33 @@ echo_env_win64 ()
IFS=$save_ifs
WIN32_WRAPPER_PATH="$ERL_TOP/erts/etc/win32/cygwin_tools/vc:$ERL_TOP/erts/etc/win32/cygwin_tools"
-
echo_setenv OVERRIDE_TARGET win32 ';'
- echo_setenv CONFIG_SUBTYPE win64 ';'
+ if [ X"$X64" = X"true" ]; then
+ echo_setenv CONFIG_SUBTYPE win64 ';'
+ fi
echo_setenv CC cc.sh ';'
echo_setenv CXX cc.sh ';'
echo_setenv AR ar.sh ';'
echo_setenv RANLIB true ';'
- if [ -f "$ERL_TOP/erts/autoconf/win64.config.cache.static" ]; then
- echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win64.config.cache.static" ';'
+ if [ X"$X64" = X"true" ]; then
+ if [ -f "$ERL_TOP/erts/autoconf/win64.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win64.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win64.config.cache" ';'
+ else
+ if [ -f "$ERL_TOP/erts/autoconf/win32.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win32.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win32.config.cache" ';'
fi
- echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win64.config.cache" ';'
echo_setenv WIN32_WRAPPER_PATH "$WIN32_WRAPPER_PATH" ';'
echo_setenv PATH "$WIN32_WRAPPER_PATH:$P3" ';'
echo_envinfo
}
-echo_env_msys32 ()
+echo_env_msys ()
{
+ X64=$1
#echo_envinfo
if [ X"$SHELL" = X"" ]; then
echo "You need to export the shell variable first," \
@@ -779,23 +683,32 @@ echo_env_msys32 ()
WIN32_WRAPPER_PATH="$ERL_TOP/erts/etc/win32/msys_tools/vc:$ERL_TOP/erts/etc/win32/msys_tools"
echo_setenv OVERRIDE_TARGET win32 ';'
+ if [ X"$X64" = X"true" ]; then
+ echo_setenv CONFIG_SUBTYPE win64 ';'
+ fi
echo_setenv CC cc.sh ';'
echo_setenv CXX cc.sh ';'
echo_setenv AR ar.sh ';'
echo_setenv RANLIB true ';'
- if [ -f "$ERL_TOP/erts/autoconf/win32.config.cache.static" ]; then
- echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win32.config.cache.static" ';'
+ if [ X"$X64" = X"true" ]; then
+ if [ -f "$ERL_TOP/erts/autoconf/win64.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win64.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win64.config.cache" ';'
+ else
+ if [ -f "$ERL_TOP/erts/autoconf/win32.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win32.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win32.config.cache" ';'
fi
-
- echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win32.config.cache" ';'
echo_setenv WIN32_WRAPPER_PATH "$WIN32_WRAPPER_PATH" ';'
echo_setenv PATH "$WIN32_WRAPPER_PATH:$P3" ';'
echo_envinfo
}
-
-echo_env_msys64 ()
+echo_env_wsl ()
{
+ X64=$1
#echo_envinfo
if [ X"$SHELL" = X"" ]; then
echo "You need to export the shell variable first," \
@@ -807,50 +720,69 @@ echo_env_msys64 ()
exit 1
fi
echo_env_erltop
- # Try to cope with paths containing unexpected things like stray
- # mixed paths (c:/something/bin) and quotes. Only C and D drive
- # handled.
- P2=`echo :$PATH | \
- sed "s,\",,g;s,:\([a-zA-Z]\):,:/\L\1,g;s,^:,,"`
- P3=""
- save_pwd=`pwd`
- save_ifs=$IFS
- IFS=:
- for p in $P2; do
- if [ -d "$p" ]; then
- C1=`(cd "$p" && cmd //C "for %i in (".") do @echo %~fsi")`
- C2=`echo "$C1" | sed 's,^\([a-zA-Z]\):\\\\,/\L\1/,;s,\\\\,/,g'`
- else
- C2=""
- fi
- if [ ! -z "$C2" ]; then
- if [ -z "$P3" ];then
- P3="$C2"
- else
- P3="$P3:$C2"
- fi
- fi
- done
- IFS=$save_ifs
- WIN32_WRAPPER_PATH="$ERL_TOP/erts/etc/win32/msys_tools/vc:$ERL_TOP/erts/etc/win32/msys_tools"
+ WIN32_WRAPPER_PATH="$ERL_TOP/erts/etc/win32/wsl_tools/vc:$ERL_TOP/erts/etc/win32/wsl_tools"
+
+ if [ ! -n "`lookup_prog_in_path cl`" ]; then
+ if [ X"$X64" = X"true" ]; then
+ setup_win32_cl_env "x64" `wslpath -a -m erts/etc/win32/wsl_tools/SetupWSLcross.bat`
+ else
+ setup_win32_cl_env "x86" `wslpath -a -m erts/etc/win32/wsl_tools/SetupWSLcross.bat`
+ fi
+ fi
echo_setenv OVERRIDE_TARGET win32 ';'
- echo_setenv CONFIG_SUBTYPE win64 ';'
+ if [ X"$X64" = X"true" ]; then
+ echo_setenv CONFIG_SUBTYPE win64 ';'
+ fi
+ echo_setenv WSLcross true ';'
echo_setenv CC cc.sh ';'
echo_setenv CXX cc.sh ';'
echo_setenv AR ar.sh ';'
echo_setenv RANLIB true ';'
- if [ -f "$ERL_TOP/erts/autoconf/win64.config.cache.static" ]; then
- echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win64.config.cache.static" ';'
+ if [ X"$X64" = X"true" ]; then
+ if [ -f "$ERL_TOP/erts/autoconf/win64.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win64.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win64.config.cache" ';'
+ else
+ if [ -f "$ERL_TOP/erts/autoconf/win32.config.cache.static" ]; then
+ echo_setenv OVERRIDE_CONFIG_CACHE_STATIC "$ERL_TOP/erts/autoconf/win32.config.cache.static" ';'
+ fi
+ echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win32.config.cache" ';'
fi
-
- echo_setenv OVERRIDE_CONFIG_CACHE "$ERL_TOP/erts/autoconf/win64.config.cache" ';'
echo_setenv WIN32_WRAPPER_PATH "$WIN32_WRAPPER_PATH" ';'
- echo_setenv PATH "$WIN32_WRAPPER_PATH:$P3" ';'
+ echo_setenv PATH "$WIN32_WRAPPER_PATH:$PATH" ';'
echo_envinfo
}
+setup_win32_cl_env ()
+{
+ eval `cmd.exe /c $2 $1`
+ echo_setenv INCLUDE "$INCLUDE" ';'
+ echo_setenv LIB "$LIB" ';'
+ echo_setenv LIBPATH "$LIBPATH" ';'
+ echo_setenv VCToolsRedistDir "$VCToolsRedistDir" ';'
+ echo_setenv WSLENV "$WSLENV:WSLPATH/l:CLASSPATH/l" ';'
+
+ save_ifs=$IFS
+ IFS=:
+ WSLPATH=""
+ for p in $PATH; do
+ if [ -d "$p" ]; then
+ case "$p" in
+ /mnt/c/*)
+ WSLPATH=$WSLPATH:$p
+ ;;
+ *)
+ ;;
+ esac
+ fi
+ done
+ IFS=$save_ifs
+ echo_setenv WSLPATH "$WSLPATH" ';'
+}
+
lookup_prog_in_path ()
{
PROG=$1
@@ -902,6 +834,35 @@ get_do_commit ()
fi
}
+do_deprecations_git ()
+{
+ get_do_commit $1
+ setup_make
+
+ $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET \
+ deprecations || exit 1;
+
+ $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET \
+ primary_bootstrap || exit 1;
+
+ if [ $do_commit = true ]; then
+ git add -A bootstrap/lib/stdlib/ebin/otp_internal.beam
+ git add -A lib/stdlib/src/otp_internal.erl
+ git commit --no-verify -m 'Update deprecations'
+ echo ""
+ echo "Deprecations updated and committed."
+ echo ""
+ else
+ echo ""
+ echo "Deprecations updated. Use the following commands to stage "
+ echo "changed files:"
+ echo ""
+ echo "$ git add bootstrap/lib/stdlib/ebin/otp_internal.beam"
+ echo "$ git add lib/stdlib/src/otp_internal.erl"
+ echo ""
+ fi
+}
+
do_primary_git ()
{
get_do_commit $1
@@ -1141,8 +1102,13 @@ case $TARGET in
'the command'
exit 1
fi;;
- *)
- ;;
+ *)
+ if [ -x /bin/wslpath -a X"$OVERRIDE_TARGET" = X"" \
+ -a X"$1" != X"env_win32" -a X"$1" != X"env_msys32" -a X"$1" != X"env_msys64" ]; then
+ echo "Building linux binary; if you intended to cross build for win32 use" >&2
+ echo ' eval `./otp_build env_win32`\n' >&2
+ fi
+ ;;
esac
if [ ! -z "$OVERRIDE_TARGET" ]; then
@@ -1211,6 +1177,11 @@ case "$1" in
fi;
FLAVOR=$1
do_boot;;
+ update_deprecations)
+ case $version_controller in
+ git) do_deprecations_git "$2";;
+ none) git_required ;;
+ esac ;;
update_primary)
case $version_controller in
git) do_primary_git "$2";;
@@ -1257,26 +1228,29 @@ case "$1" in
do_debuginfo_win32 "$2";;
env_win32)
if [ x"$2" = x"x64" -o x"$2" = x"amd64" ]; then
- if [ -x /usr/bin/msys-?.0.dll ]; then
- echo_env_msys64
- else
- echo_env_win64
- fi
+ ISX64=true
+ fi
+ if [ -x /bin/wslpath ]; then
+ echo_env_wsl $ISX64
+ elif [ -x /usr/bin/msys-?.0.dll ]; then
+ echo_env_msys $ISX64
else
- if [ -x /usr/bin/msys-?.0.dll ]; then
- echo_env_msys32
- else
- echo_env_win32
- fi
+ echo_env_cygwin $ISX64
fi;;
env_mingw32)
echo_env_mingw32;;
env_win64)
- echo_env_win64;;
+ if [ -x /bin/wslpath ]; then
+ echo_env_wsl true
+ elif [ -x /usr/bin/msys-?.0.dll ]; then
+ echo_env_msys true
+ else
+ echo_env_cygwin true
+ fi;;
+ env_msys32)
+ echo_env_msys;;
env_msys64)
- echo_env_msys64;;
- env_vxworks)
- echo_env_vxworks "$2";;
+ echo_env_msys true;;
env_cross)
echo_env_cross "$2";;
env_bootstrap)
diff --git a/scripts/build-otp b/scripts/build-otp
index afab1f9136..582c209e4d 100755
--- a/scripts/build-otp
+++ b/scripts/build-otp
@@ -58,6 +58,8 @@ if [ "$1" = "docs" ]; then
DOC_TARGET=${TRAVIS_TAG:-$DOC_TARGET}
TESTROOT=$PWD/$DOC_TARGET do_and_log "Building documentation" make release_docs
do_and_log "Linting documentation" make xmllint
+ do_and_log "Test chunks" erlc lib/stdlib/test/shell_docs_SUITE.erl &&
+ ct_run -no_auto_compile -suite shell_docs_SUITE -case render
# The code below prepares this build to be used as a deploy to
# github pages for documentation.
if [ "$TRAVIS_PULL_REQUEST" = "false" -a "$TRAVIS_TAG" = "" -a "$TRAVIS_REPO_SLUG" = "erlang/otp" ]; then
diff --git a/scripts/pre-push b/scripts/pre-push
index 71e9fd1e75..7da1f575db 100755
--- a/scripts/pre-push
+++ b/scripts/pre-push
@@ -22,12 +22,15 @@
# <local ref> <local sha1> <remote ref> <remote sha1>
#
-NEW_RELEASES="21 20 19 18 17"
+# Bump this version to give users an update notification.
+PRE_PUSH_SCRIPT_VERSION=1
+
+NEW_RELEASES="22 21 20 19 18 17"
OLD_RELEASES="r16 r15 r14 r13"
RELEASES="$NEW_RELEASES $OLD_RELEASES"
# First commit on master, not allowed in other branches
-MASTER_ONLY=aea2a053e28a11497796879715be29ab0c3cd1a0
+MASTER_ONLY=f633fe962ea7078c32f8c81d34950c0ebce0f472
# Number of commits and files allowed in one push by this script
NCOMMITS_MAX=100
@@ -54,13 +57,23 @@ null=0000000000000000000000000000000000000000
#echo "pre-push hook: remote=$remote"
#echo "pre-push hook: url=$url"
+red_on() {
+ printf '%b' "\033[31m"
+}
+
+red_off() {
+ printf '%b' "\033[0m"
+}
+
if [ "$url" = 'https://github.com/erlang/otp.git' -o "$url" = 'git@github.com:erlang/otp.git' ]
then
if [ $remote = "$url" ]; then
+ red_on
echo "$0 says:"
echo "***"
echo "*** Push to $url without using a named remote is NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
fi
IFS=' '
@@ -73,18 +86,22 @@ then
if [ "$local_sha" = $null ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** DELETE push to '$remote' NOT ALLOWED!!!!!"
echo "***"
+ red_off
exit 1
fi
if [ "$local_ref" != "$remote_ref" ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** RENAME push: $local_ref pushed as $remote_ref to '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
fi
case "$remote_ref" in
@@ -92,46 +109,74 @@ then
branch=${remote_ref#refs/heads/}
if [ "$remote_sha" = $null ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** UNKNOWN BRANCH: '$branch' does not exist at '$remote'!!!!"
echo "***"
+ red_off
exit 1
fi
if ! git log -1 --oneline $remote_sha > /dev/null 2>&1
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** The top of '$branch' at '$remote' ($remote_sha)"
echo "*** does not exist locally!!!"
echo "*** You probably need to refresh local '$branch' and redo merge."
echo "***"
+ red_off
exit 1
fi
if ! git merge-base --is-ancestor $remote_sha $local_sha
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** FORCE push branch to '$remote' NOT ALLOWED!!!"
echo "***"
+ red_off
exit 1
fi
if [ $remote_ref != refs/heads/master -a "$MASTER_ONLY" ] && git merge-base --is-ancestor $MASTER_ONLY $local_sha
then
- echo "$0 says:"
- echo "***"
- echo "*** INVALID MERGE: Commit $MASTER_ONLY should not be reachable from '$branch'!!!!"
- echo "*** You have probably merged master into '$branch' by mistake"
- echo "***"
- exit 1
+ THIS_SCRIPT=`git rev-parse --git-path hooks/pre-push`
+ THIS_SCRIPT=`realpath $THIS_SCRIPT`
+ if git show refs/remotes/$remote/master:scripts/pre-push | diff -q --context=0 $THIS_SCRIPT - > /dev/null 2>&1
+ then
+ red_on
+ echo "$0 says:"
+ echo "***"
+ echo "*** INVALID MERGE: Commit $MASTER_ONLY should not be reachable from '$branch'!!!!"
+ echo "*** You have probably merged master into '$branch' by mistake"
+ echo "***"
+ red_off
+ exit 1
+ else
+ red_on
+ echo "$0 says:"
+ echo "***"
+ echo "*** The pre-push hook of this OTP repo needs updating."
+ echo "*** Do it by executing the following command:"
+ echo "***"
+ echo "*** git show refs/remotes/$remote/master:scripts/pre-push > $THIS_SCRIPT"
+ echo "***"
+ echo "*** And then retry the push."
+ echo "***"
+ red_off
+ exit 1
+ fi
fi
if [ ${remote_ref#refs/heads/maint-} != $remote_ref ] && git merge-base --is-ancestor refs/remotes/$remote/maint $local_sha
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** INVALID MERGE: Branch maint should not be reachable from '$branch'!!!!"
echo "*** You have probably merged maint into '$branch' by mistake."
echo "***"
+ red_off
exit 1
fi
if [ $remote_ref = refs/heads/maint -o $remote_ref = refs/heads/master ]; then
@@ -147,29 +192,35 @@ then
fi
if [ $remote_ref = refs/heads/master ] && ! git merge-base --is-ancestor refs/remotes/$remote/maint $local_sha
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** INVALID PUSH: Branch '$remote/maint' is not reachable from master!!!!"
echo "*** Someone needs to merge maint forward to master and push."
echo "***"
+ red_off
exit 1
fi
NCOMMITS=`git rev-list --count $remote_sha..$local_sha`
if [ $NCOMMITS -gt $NCOMMITS_MAX ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** HUGE push: $NCOMMITS commits (> $NCOMMITS_MAX) to '$branch' at '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
fi
NFILES=`git diff --name-only $remote_sha $local_sha | wc --lines`
if [ $NFILES -gt $NFILES_MAX ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** HUGE push: $NFILES changed files (> $NFILES_MAX) to '$branch' at '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
fi
;;
@@ -185,49 +236,74 @@ then
done
if [ $REL = "UNKNOWN" ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** Unknown OTP release number in tag '$tag'"
echo "***"
+ red_off
exit 1
fi
if [ "$remote_sha" != $null ]
then
+ red_on
echo "$0 says:"
echo "***"
echo "*** FORCE push tag to '$remote' NOT ALLOWED!!!"
echo "*** Tag '$tag' already exists at '$remote'."
echo "***"
+ red_off
exit 1
fi
;;
refs/heads/*)
branch=${remote_ref#refs/heads/}
+ red_on
echo "$0 says:"
echo "***"
echo "*** UNKNOWN branch name: '$branch' pushed to '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
;;
refs/tags/*)
tag=${remote_ref#refs/tags/}
+ red_on
echo "$0 says:"
echo "***"
echo "*** UNKNOWN tag name: '$tag' pushed to '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
;;
*)
+ red_on
echo "$0 says:"
echo "***"
echo "*** STRANGE ref: '$remote_ref' pushed to '$remote' NOT ALLOWED!!!!"
echo "***"
+ red_off
exit 1
;;
esac
done
+
+ echo "$0: OK"
+
+ THIS_SCRIPT=`git rev-parse --git-path hooks/pre-push`
+ THIS_SCRIPT=`realpath $THIS_SCRIPT`
+ if git show refs/remotes/$remote/master:scripts/pre-push | diff --context=0 $THIS_SCRIPT - | grep -q PRE_PUSH_SCRIPT_VERSION > /dev/null 2>&1
+ then
+ echo ""
+ echo "NOTE: There is a newer version of the pre-push hook in this OTP repo."
+ echo " You can install it by executing the following command:"
+ echo
+ echo " git show refs/remotes/$remote/master:scripts/pre-push > $THIS_SCRIPT"
+ echo
+ fi
else
echo "$0: No checks done for remote '$remote' at $url."
fi
exit 0
+
diff --git a/scripts/run-dialyzer b/scripts/run-dialyzer
index 34724a8ca0..dbc3c121d4 100755
--- a/scripts/run-dialyzer
+++ b/scripts/run-dialyzer
@@ -12,6 +12,7 @@ filter () {
}
ALL_APPLICATIONS=$(ls -d -1 lib/*/ | sed 's:^lib\|/::g')
+ALL_APPLICATIONS="erts $ALL_APPLICATIONS"
echo "All applications: $ALL_APPLICATIONS" |tr '\n' ' ' && echo ""
BASE_PLT="compiler crypto erts hipe kernel stdlib syntax_tools"
diff --git a/system/doc/definitions/term.defs.xml b/system/doc/definitions/term.defs.xml
deleted file mode 100644
index fdcda4eddb..0000000000
--- a/system/doc/definitions/term.defs.xml
+++ /dev/null
@@ -1,1525 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE terms SYSTEM "terms.dtd">
-
-<terms>
- <term>
- <id>agent</id>
- <shortdef>agent</shortdef>
- <def>
-An entity that terminates a management protocol in the Network Element. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>API</id>
- <shortdef>API</shortdef>
- <def>
-Application Programming Interface. The interface towards an application. Usually this is a set of functions available, but can also be a set of messages sent to or from an application. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>application</id>
- <shortdef>application</shortdef>
- <def>
-A collection of resources which is required to offer a specific service. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>appmon</id>
- <shortdef>Application Monitor</shortdef>
- <def>
-A graphical node and application process tree viewer. See also appmon. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Appmon</id>
- <shortdef>Appmon</shortdef>
- <def>
-Application name for the Application Monitor within Erlang/OTP. A graphical node and process viewer. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>app callback</id>
- <shortdef>application callback module</shortdef>
- <def>
-A module which is called when the application is started, and when it has stopped. Every application has one application callback module. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>AC</id>
- <shortdef>application controller</shortdef>
- <def>
-A process which coordinates all operations on applications. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>app master</id>
- <shortdef>application master</shortdef>
- <def>
-The application master is a process that monitors the application. It is provided by the Erlang run-time system. Every application has an application master process. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>.app file</id>
- <shortdef>application resource file</shortdef>
- <def>
-Specifies the resources required by the application and how the application should be started. Every application has one application resource file, called AppName.app. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>arity</id>
- <shortdef>arity</shortdef>
- <def>
-Denotes the number of arguments to a function. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>ASN.1</id>
- <shortdef>ASN.1</shortdef>
- <def>
-Abstract Syntax Notation One - an ITU-T and ISO standard notation for describing data formats used in communication protocols. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ASN.1 Compiler</id>
- <shortdef>ASN.1 Compiler</shortdef>
- <def>
-The Erlang/OTP ASN.1 Compiler translates an ASN.1 module into a corresponding Erlang module with encode and decode functions. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>atom</id>
- <shortdef>atom</shortdef>
- <def>
-An atom is a constant. Atoms always starts with a lower case letter (a-z) and are terminated by a non-alphanumeric character - otherwise they must be quoted (enclosed in ' '). An atom is a data type in Erlang, used to enhance the legibility of programs. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>atomicity</id>
- <shortdef>atomicity</shortdef>
- <def>
-Atomicity refers to the "all or nothing" property. If a transaction succeeds (i.e. commits), then all its effects on the data is captured in the database. If the transaction does not succeed (i.e. aborts), then none of its effect on the data is captured in the database. In other words, the transaction processing algorithm guarantees that the database will not reflect a partitial effect of a transaction. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>attach</id>
- <shortdef>attach</shortdef>
- <def>
-The debugger may attach to a process. When attached, the debugger may show process details, such as message queues and variable bindings. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>behaviour</id>
- <shortdef>behaviour</shortdef>
- <def>
-A "pattern of design" which can be used to build applications and processes in an applications. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>BIF</id>
- <shortdef>BIF</shortdef>
- <def>
-Built-In Functions which perform operations that are impossible or inefficient to program in Erlang itself. Are defined in the module Erlang in the application kernel </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>binary</id>
- <shortdef>binary</shortdef>
- <def>
-A data type in Erlang which is used to store an area of untyped memory. Binaries are used for efficiently handling large quantities of untyped data. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>boolean</id>
- <shortdef>boolean</shortdef>
- <def>
-A common data type in programming and specification languages. The value can be either true or false. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>boot file</id>
- <shortdef>boot file</shortdef>
- <def>
-A binary file with extension .boot which is read during start of an Erlang node. See SASL User's Guide for more info. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>break point</id>
- <shortdef>break point</shortdef>
- <def>
-By setting a break point using the debugger, the user specifies a position in the source code of a module where execution is to be suspended and control transferred to the debugger. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>CAshort</id>
- <shortdef>CA</shortdef>
- <def>
-See Certification Authority. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CA certificate</id>
- <shortdef>CA certificate</shortdef>
- <def>
-A certificate containing a CA's public key. Network entities use this public key to verify certificates signed with the CA's private key. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>callback function</id>
- <shortdef>callback function</shortdef>
- <def>
-A callback function is a function exported from a callback module, that a generic behaviour calls to perform a specific task. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>callback module</id>
- <shortdef>callback module</shortdef>
- <def>
-A callback module is a module that implements the specific parts of a generic behaviour. The generic behaviour specifies which callback functions must be exported from the module. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>certificate</id>
- <shortdef>certificate</shortdef>
- <def>
-A file used for authenticating network entities under the SSL protocol. A certificate contains information about its owner (called the subject) and issuer, plus the owner's public key and a signature made by a CA. Network entities verify these signatures using CA certificates. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CAlong</id>
- <shortdef>Certification Authority (CA)</shortdef>
- <def>
-A trusted third party whose purpose is to sign certificates for network entities it has authenticated using secure means. Other network entities can check the signature to verify that a CA has authenticated the bearer of a certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>CSRlong</id>
- <shortdef>Certificate Signing Request (CSR)</shortdef>
- <def>
-An unsigned certificate for submission to a Certification Authority, which signs it with its private key. Once the CSR is signed, it becomes a certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>child</id>
- <shortdef>child</shortdef>
- <def>
-A supervised process. See also permanent, transient, temporary child. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>cipher</id>
- <shortdef>cipher</shortdef>
- <def>
-A system of encryption. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>ClearCase</id>
- <shortdef>ClearCase</shortdef>
- <def>
-A configuration management system from Rational Software Corporation. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>client-server model</id>
- <shortdef>client-server model</shortdef>
- <def>
-A model where there is a server, which manages some resource, and a number of clients which send requests to the server to access the resource. The client-server model is one of the basic programming techniques for coordinating the activities of several parallel processes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>cover</id>
- <shortdef>Coverage Analyser</shortdef>
- <def>
-Module name for the coverage analyser tool, located in the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>CORBAlong</id>
- <shortdef>Common Object Request Broker Architecture (CORBA)</shortdef>
- <def>
-A specification of an architecture for a distributed object system </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>CORBA</id>
- <shortdef>Common Object Request Broker Architecture (CORBA)</shortdef>
- <def>
-A specification of an architecture for a distributed object system </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>compiler</id>
- <shortdef>compiler</shortdef>
- <def>
-A compiler is a translator. A common type of compilers are those who takes source code for a programming language and translates it into code that is executable on a specific platform. E.g. the Erlang compiler translates Erlang source code to an intermediary code that is executable by the Erlang Run Time System. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>consistency</id>
- <shortdef>consistency</shortdef>
- <def>
-Consistency refers to the requirement that, given a consistent initial database state, the state of the database after the successful execution of a transaction is also consistent; that is, a transaction transforms the database from a consistent state to another consistent state. Database consistency may be defined as a set of rules or constraints. If the execution of a transaction causes the consistency constraints to be violated, the transaction is not accepted (and thus aborted) by the system. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>cookie</id>
- <shortdef>cookie</shortdef>
- <def>
-A magic cookie is a secret atom assigned to each Erlang node. The Erlang nodes in a distributed system must know each others cookies in order to authorize each other for communication </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>CORBAshort</id>
- <shortdef>CORBA</shortdef>
- <def>
-See Common Object RequestBroker Architecture. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>Coverage Analyser</id>
- <shortdef>Coverage Analyser</shortdef>
- <def>
-A tool which provides a set of functions for coverage analysis of Erlang programs, i.e observing how many times each line or function are executed. See also cover. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>coverage analysis</id>
- <shortdef>coverage analysis</shortdef>
- <def>
-The task of determining which lines, or how many lines of code, has actually been executed. Useful for determining the completeness of test suites. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>cross reference tool</id>
- <shortdef>cross reference tool</shortdef>
- <def>
-A tool that can be used for finding dependencies between functions, modules, applications and releases. The Erlang/OTP cross reference tool is called xref and is part of the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>CSRshort</id>
- <shortdef>CSR</shortdef>
- <def>
-See Certificate Signing Request. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>data type</id>
- <shortdef>data type</shortdef>
- <def>
-The data types in Erlang are numbers, atoms, tuples, lists, pids, funs, records, ports, references and binaries. The values of Erlang data types can be stored in variables. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>DBMSlong</id>
- <shortdef>Database Management System (DBMS)</shortdef>
- <def>
-A database is a collection of data and a DBMS is a system which manages the database. Applications accesses the database through the database management system. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>DBMSshort</id>
- <shortdef>DBMS</shortdef>
- <def>
-See Database Management System. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Debugger</id>
- <shortdef>Debugger</shortdef>
- <def>
-An Erlang/OTP tool which provides mechanisms which makes it possible to see what happens during the execution of code in specified modules, or when processes crash. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>dets</id>
- <shortdef>dets</shortdef>
- <def>
-A module within the stdlib application, which provides a term storage, and which is used as the underlying file storage mechanism by the Mnesia DBMS. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>dirty operations</id>
- <shortdef>dirty operations</shortdef>
- <def>
-Functions which manipulate data in a DBMS, without using transactions. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>distributed application</id>
- <shortdef>distributed application</shortdef>
- <def>
-An application which runs on one of several nodes. May be restarted on another node. (See local application.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>DNSshort</id>
- <shortdef>DNS</shortdef>
- <def>
-See Domain Name System. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>Docbuilder</id>
- <shortdef>Docbuilder</shortdef>
- <def>
-The documentation system used in Erlang/OTP. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>DNSlong</id>
- <shortdef>Domain Name System (DNS)</shortdef>
- <def>
-DNS is a service that map names to internet addresses </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>DTD</id>
- <shortdef>DTD</shortdef>
- <def>
-Document Type Definition as defined in SGML. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>durability</id>
- <shortdef>durability</shortdef>
- <def>
-If a transaction succeeds, then its effect on the data is persistently captured, and will survive subsequent system failures resulting in loss of data in volatile memory. Durability is usually enforced by first writing modified data to some non-volatile memory (usually disc), before a transaction is allowed to commit. If there is a system failure, the state of the non-volatile memory must be recovered to reflect the effect of all and only committed transactions. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Emacs</id>
- <shortdef>Emacs</shortdef>
- <def>
-A widely used text editor which allows customization of its behaviour. An Erlang mode for Emacs is included in the Erlang deliverables. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Emacs for Erlang</id>
- <shortdef>Emacs for Erlang</shortdef>
- <def>
-A tool which provides a major mode for editing Erlang source files in Emacs. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>encaps</id>
- <shortdef>encapsulation</shortdef>
- <def>
-Data can be encapsulated into another data element. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>eprof</id>
- <shortdef>eprof</shortdef>
- <def>
-A module in the tools application. See Profiler. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>erl</id>
- <shortdef>erl</shortdef>
- <def>
-The command which starts an Erlang run-time system. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>erl_interface</id>
- <shortdef>erl_interface library</shortdef>
- <def>
-A thread safe library with C-functions which makes it possible to write a C-program which appears as one of the nodes in a system of distributed Erlang nodes. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang</id>
- <shortdef>Erlang</shortdef>
- <def>
-Erlang is a functional programming language intended for designing large industrial soft real time systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang emulator</id>
- <shortdef>Erlang emulator</shortdef>
- <def>
-Another word for Erlang Virtual Machine. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ERTSlong</id>
- <shortdef>Erlang Run Time System</shortdef>
- <def>
-A fundamental part of Erlang/OTP which contains the Erlang Virtual Machine, the kernel and stdlib applications. The Erlang Run Time System is a mandatory part which all other Erlang applications are dependent upon. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Erlang VM</id>
- <shortdef>Erlang Virtual Machine</shortdef>
- <def>
-The virtual machine, which makes Erlang/OTP work together with a specific OS/HW platform. The Erlang Virtual Machine is available on several different platforms. The Erlang Virtual Machine is the glue which makes it possible to run an Erlang application on any platform without change. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ERTSshort</id>
- <shortdef>ERTS</shortdef>
- <def>
-See Erlang Run Time System. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>ETS</id>
- <shortdef>ETS</shortdef>
- <def>
-Erlang Term Storage tables. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>EVAshort</id>
- <shortdef>EVA</shortdef>
- <def>
-See Event and Alarm handling application </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>EVAlong</id>
- <shortdef>Event and Alarm handling application (EVA)</shortdef>
- <def>
-An application that consists of Fault Management functionality, such as sending and logging of events and alarms. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>event handler</id>
- <shortdef>event handler</shortdef>
- <def>
-A module exporting functions which can process events sent to an event manager process. The event handler is a behaviour of type gen_event. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>event manager</id>
- <shortdef>event manager</shortdef>
- <def>
-A process to which events of a certain category is sent. gen_event handler can be installed in the event manager. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>exit signal</id>
- <shortdef>exit signal</shortdef>
- <def>
-A signal which is sent from a terminating process to the processes and ports it is linked to. An EXIT signal has the following format: {'EXIT', Exiting_Process_Id, Reason}. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>foo</id>
- <shortdef>foo</shortdef>
- <def>
-Algebraic place holder. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>FSM</id>
- <shortdef>FSM</shortdef>
- <def>
-Finite State Machine. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>fun</id>
- <shortdef>fun</shortdef>
- <def>
-A data type, introduced in Erlang 4.4, which represent functional objects. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>function</id>
- <shortdef>function</shortdef>
- <def>
-Erlang programs are written entirely in terms of modules with functions. A function can have arguments and does always return a result. A function can be exported which makes it available for calls from other modules. Non exported functions can only be called internally within the module. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Gateway</id>
- <shortdef>gateway</shortdef>
- <def>
-A server which acts as an intermediary for some other server. Unlike a proxy, a gateway receives requests as if it were the origin server for the requested resource; the requesting client may not be aware that it is communicating with a gateway. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>gen_event</id>
- <shortdef>gen_event</shortdef>
- <def>
-A behaviour used for programming event handling mechanisms, such as alarm handlers, error loggers, and plug-and-play handlers. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gen_fsm</id>
- <shortdef>gen_fsm</shortdef>
- <def>
-A behaviour used for programming finite state machines. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gen_server</id>
- <shortdef>gen_server</shortdef>
- <def>
-A behaviour used for programming client-server processes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>gterm</id>
- <shortdef>Global Glossary Database</shortdef>
- <def>
-A glossary database used to list common acronymns and defintions etc. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>xref</id>
- <shortdef>xref</shortdef>
- <def>
-A cross reference tool that can be used for finding dependencies between functions, modules, applications and releases. Part of the Tools application. </def>
- <resp>gunilla</resp>
- </term>
- <term>
- <id>GSlong</id>
- <shortdef>Graphics System</shortdef>
- <def>
-A library module which provides a graphics interface for Erlang. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>grid</id>
- <shortdef>grid</shortdef>
- <def>
-A multi-column object which is used to display tables. (Graphics System.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>GSshort</id>
- <shortdef>GS</shortdef>
- <def>
-See Graphics System. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>GS Contributions</id>
- <shortdef>GS Contributions</shortdef>
- <def>
-Unsupported user supplied tools which are included with the Erlang/OTP software release. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>GUI</id>
- <shortdef>GUI</shortdef>
- <def>
-Graphical User Interface </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Home Directory</id>
- <shortdef>Home Directory</shortdef>
- <def>
-The position of a user account in the file system. The Home Directory is automatically passed to the Erlang run-time system at startup. On Unix the contents of the environment variable "HOME" is passed. Om Win32 the concatenation of the environment variables "HOMEDRIVE" and "HOMEPATH" is passed, or if these variables are not set, the value returned by the Win32 API function "GetWindowsDirectory" is passed. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>host name</id>
- <shortdef>host name</shortdef>
- <def>
-The name of a machine on a network, e.g. erlang.ericsson.se. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>HTML</id>
- <shortdef>HTML</shortdef>
- <def>
-Hypertext Markup Language. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>HTTP</id>
- <shortdef>HTTP</shortdef>
- <def>
-Hypertext Transfer Protocol. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>HTTPS</id>
- <shortdef>HTTPS</shortdef>
- <def>
-The Hypertext Transport Protocol, Secure, the standard SSL communication mechanism of the World Wide Web. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>IDLshort</id>
- <shortdef>IDL</shortdef>
- <def>
-See Interface Description Language. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>IDLlong</id>
- <shortdef>Interface Description Language (IDL)</shortdef>
- <def>
-The interface specification language created by OMG. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>indexing</id>
- <shortdef>indexing</shortdef>
- <def>
-Fast lookup using an (usually enumerated) key. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>I1</id>
- <shortdef>INETS</shortdef>
- <def>
-The Internet Services application </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>initial call</id>
- <shortdef>initial call</shortdef>
- <def>
-The first call to an interpreted function (when using the Interpreter). </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>instrumentation function</id>
- <shortdef>instrumentation function</shortdef>
- <def>
-A function used to implement a Managed Object, i.e. give access to the real resources behind an MO. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>IDLlong</id>
- <shortdef>Interface Description Language (IDL)</shortdef>
- <def>
-The interface specification language created by OMG. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>interpreter</id>
- <shortdef>interpreter</shortdef>
- <def>
-An application which provides mechanisms which make it possible to see what happens during the execution of code in specified (interpreted) modules, or when processes crash. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>isolation</id>
- <shortdef>isolation</shortdef>
- <def>
-A transaction executes as if no other concurrent transactions are executing, and thus its execution results are equivalent to those obtained by executing database transactions serially. A system which maintains transaction isolation is also said to be enforcing serializability. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>kernel</id>
- <shortdef>kernel</shortdef>
- <def>
-An application which contains file servers, code servers and other code necessary for the Erlang run-time system. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>key</id>
- <shortdef>key</shortdef>
- <def>
-A file containing the value that must be fed into an algorithm in order to encrypt or decrypt a message. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>key pair</id>
- <shortdef>key pair</shortdef>
- <def>
-A set of two keys used in public key cryptography. One is the public key used to encrypt data, and the other is the private key necessary to decrypt the same data. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>list</id>
- <shortdef>list</shortdef>
- <def>
-Terms separated by commas and enclosed in square brackets [ ] are called lists. A list is a data type in Erlang, used for storing a variable number of terms. It is dynamically sized. The first element of the list is referred to as the head of the list, and the remainer of the list as the tail. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>list box</id>
- <shortdef>list box </shortdef>
- <def>
-A list of labels with optional scroll bars attached. (Graphics System.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>lc</id>
- <shortdef>list comprehension</shortdef>
- <def>
-A language construct in Erlang which are analogous to set comprehensions in Zermelo-Frankel set theory. Analogous to the 'setof' and 'findall' predicates in Prolog. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>local application</id>
- <shortdef>local application</shortdef>
- <def>
-An application which runs on one node and which are always started at the local node only. (See distributed application.) </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>manager</id>
- <shortdef>manager</shortdef>
- <def>
-An entity that terminates a management protocol in the Network Management Station. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Master Agent</id>
- <shortdef>Master Agent</shortdef>
- <def>
-The SNMP agent system consists of one Master Agent which terminates the SNMP protocol </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MIB</id>
- <shortdef>Management Information Base (MIB)</shortdef>
- <def>
-An abstract definition of the management information available through a management interface in a system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>matching</id>
- <shortdef>matching</shortdef>
- <def>
-See pattern matching. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>message queue</id>
- <shortdef>message queue</shortdef>
- <def>
-The queue of not yet received messages that are in the mailbox of a process. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Mnemosyne</id>
- <shortdef>Mnemosyne</shortdef>
- <def>
-Mnemosyne was the query language of Mnesia up to the R11B release. Supersed by QLC.</def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Mnesia</id>
- <shortdef>Mnesia</shortdef>
- <def>
-Mnesia is a distributed Database Management System, appropriate for telecommunications applications and other applications with need of continuous operation and soft real-time properties. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>MIBshort</id>
- <shortdef>MIB</shortdef>
- <def>
-See Management Information Base. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MIME</id>
- <shortdef>MIME</shortdef>
- <def>
-Multi-purpose Internet Mail Extensions. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>MOlong</id>
- <shortdef>Managed Object (MO)</shortdef>
- <def>
-The abstract management information defined in a MIB. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>MO</id>
- <shortdef>MO</shortdef>
- <def>
-Managed Object; The abstract management information defined in a MIB. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>MOshort</id>
- <shortdef>MO</shortdef>
- <def>
-See Managed Object. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>module</id>
- <shortdef>module</shortdef>
- <def>
-Module is the unit for compilation and for loading in Erlang. A Module contains a module declaration, export declarations and code representing the functions in the module. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>NCSA</id>
- <shortdef>NCSA</shortdef>
- <def>
-The National Center for Supercomputing Applications. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>NEshort</id>
- <shortdef>NE</shortdef>
- <def>
-See Network Element. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NElong</id>
- <shortdef>Network Element</shortdef>
- <def>
-In OTP, the Network Element is the entire distributed OTP system, meaning that the distributed OTP system is managed as one entity. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NE</id>
- <shortdef>NE</shortdef>
- <def>
-Network Element; In OTP, the Network Element is the entire distributed OTP system, meaning that the distributed OTP system is managed as one entity. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NMSlong</id>
- <shortdef>Network Management Station (NMS)</shortdef>
- <def>
-The place where the operator manages the network. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>NMS</id>
- <shortdef>NMS</shortdef>
- <def>
-Network Management Station; The place where the operator manages the network. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>NMSshort</id>
- <shortdef>NMS</shortdef>
- <def>
-See Network Management Station. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>node</id>
- <shortdef>node</shortdef>
- <def>
-An executing Erlang run-time system which can communicate with other Erlang run-time systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>node name</id>
- <shortdef>node name</shortdef>
- <def>
-A node name is an atom constructed as the concatenation of a name supplied by the user, an "@" character, and the name of the host where the node is executing. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>notation</id>
- <shortdef>notation</shortdef>
- <def>
-How things are written. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>notification</id>
- <shortdef>notification</shortdef>
- <def>
-Information of an event. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>NROFF</id>
- <shortdef>NROFF</shortdef>
- <def>
-A text formatting language for line printer quality output devices that runs on the UNIX operating system. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>number</id>
- <shortdef>number</shortdef>
- <def>
-A data type in Erlang. Are subdivided into integers, for storing natural numbers, or floats, for storing real numbers. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>OMGlong</id>
- <shortdef>Object Managment Group (OMG)</shortdef>
- <def>
-A standardisation group for all specifications in the area of CORBA. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>OMGshort</id>
- <shortdef>OMG</shortdef>
- <def>
-Object Managment Group. </def>
- <resp>lars</resp>
- </term>
- <term>
- <id>OTP</id>
- <shortdef>OTP</shortdef>
- <def>
-Open Telecom Platform </def>
- <resp>mike</resp>
- </term>
- <term>
- <id>os_mon</id>
- <shortdef>os_mon</shortdef>
- <def>
-An application which monitors the behaviour of the underlying operating system </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>parser generator</id>
- <shortdef>parser generator</shortdef>
- <def>
-A tool for making compilers which takes a grammar description as input and generates a complete program (a parser) which recognizes input which complies with the grammar. YECC is a parser generator included in the Erlang/OTP. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>pass phrase</id>
- <shortdef>pass phrase</shortdef>
- <def>
-The word or phrase which authenticates the user who is authorized to use private key file. The pass phrase prevents unauthorized users from starting, restarting, or reconfiguring the server. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>pattern matching</id>
- <shortdef>pattern matching</shortdef>
- <def>
-A basic mechanism in Erlang for assigning values to variables and for controlling the flow of a program. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>permanent child</id>
- <shortdef>permanent child</shortdef>
- <def>
-A supervised process which always is restarted when it dies. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>Pid</id>
- <shortdef>Pid</shortdef>
- <def>
-Process Identifier. A data type in Erlang for storing process references. The process identity of the process displayed in the line. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Pman</id>
- <shortdef>Pman</shortdef>
- <def>
-Module and application name for the Process Trace Tool. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>point</id>
- <shortdef>point</shortdef>
- <def>
-A unit used to indicate the size of a typeface. Equal to 1/72 inches. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>pointer</id>
- <shortdef>pointer</shortdef>
- <def>
-A pointer tells where data is stored. Memory pointers are not used in Erlang. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>port</id>
- <shortdef>port</shortdef>
- <def>
-A data type in Erlang. Ports provide the basic mechanism for communication with the external world. </def>
- <resp>peterl</resp>
- </term>
- <term>
- <id>port controller</id>
- <shortdef>port controller</shortdef>
- <def>
-An Erlang process which controls a port program. A port has exactly one port controller. </def>
- <resp>peterl</resp>
- </term>
- <term>
- <id>port program</id>
- <shortdef>port program</shortdef>
- <def>
-A program that runs as an external program in the operating system and which the Erlang run-time system can start and communicate with by means of the Erlang port mechanism. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>PostScript</id>
- <shortdef>PostScript</shortdef>
- <def>
-A language describing a fully laid-out page in terms of fonts, lines, grey scales, and so on, in a way that is interpretable by a printer. The language was developed by Adobe Systems. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>pretty-printed</id>
- <shortdef>pretty-printed</shortdef>
- <def>
-Nicely formatted code or data, e.g. C or Erlang, with indents and tabs etc. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>primitive</id>
- <shortdef>primitive</shortdef>
- <def>
-The basic elements in a programming language. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>private key</id>
- <shortdef>private key</shortdef>
- <def>
-The secret key in a pair, used to decrypt incoming messages and sign outgoing ones. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>process</id>
- <shortdef>process</shortdef>
- <def>
-A process is a self-contained separate unit of execution which exists concurrently with other processes in the system. The BIF "spawn/3" creates and starts the execution of a new process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>process dictionary</id>
- <shortdef>process dictionary</shortdef>
- <def>
-Each process has an associated dictionary which provides the process with simple destructive storage capabilities. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Process Manager</id>
- <shortdef>Process Manager</shortdef>
- <def>
-Obsolete name for the Process Trace Tool. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Process Trace Tool</id>
- <shortdef>Process Trace Tool</shortdef>
- <def>
-A tool which gives an overview of the processes in the Erlang run-time system. See also Pman. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>Profiler</id>
- <shortdef>Profiler</shortdef>
- <def>
-Another name for eprof, a tool used to profile a system in order to find out how much time is spent in various segments of a program. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>program</id>
- <shortdef>program</shortdef>
- <def>
-Routines which can be executed by a computer. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>Proxy</id>
- <shortdef>proxy</shortdef>
- <def>
-An intermediary program which acts as both a server and a client for the purpose of making requests on behalf of other clients. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>public key</id>
- <shortdef>public key</shortdef>
- <def>
-The publicly available key in a key pair, used to encrypt messages bound for its owner and to decrypt signatures made by its owner. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>query</id>
- <shortdef>query</shortdef>
- <def>
-Queries are used for accessing the data in a Database Management System. The query specify a maybe complicated relation that should hold for all of the selected data. This could involve several tables as well as conditions like for instance less then and greater then. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>query language</id>
- <shortdef>query language</shortdef>
- <def>
-A language which is specially designed to express database queries. Examples of query languages are QLC and SQL. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>receive</id>
- <shortdef>receive</shortdef>
- <def>
-A primitive for message processing in Erlang, receives a message from a process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>record</id>
- <shortdef>record</shortdef>
- <def>
-A data structure intended for storing a fixed number of related Erlang terms, it is similar to a "struct" in C or a "record" in Pascal. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>recursion</id>
- <shortdef>recursion</shortdef>
- <def>
-A function is recursive if it calls itself until the result desired is attained. Recursion is the heart of functional programming. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>reference</id>
- <shortdef>reference</shortdef>
- <def>
-A data type in Erlang for storing system unique references. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>release handler</id>
- <shortdef>release handler</shortdef>
- <def>
-A SASL process which handles software upgrade. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>relup</id>
- <shortdef>release upgrade script</shortdef>
- <def>
-A script with instructions to the release handler of how the release should be installed in the system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>RPC</id>
- <shortdef>Remote Proceedure Call</shortdef>
- <def>
-A technique for evaluating a function transparently on a remote node. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>resource</id>
- <shortdef>resource</shortdef>
- <def>
-The actual resource to be managed. A resource is represented by a Managed Object. Each resource is mapped to one or several resources. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>resources</id>
- <shortdef>resources</shortdef>
- <def>
-The actual resources to be managed. A resource is represented by a Managed Object. Each resource is mapped to one or several resources. </def>
- <resp>nibe</resp>
- </term>
- <term>
- <id>RFC</id>
- <shortdef>RFC</shortdef>
- <def>
-A "Request for Comments" used as a proposed standard by IETF. </def>
- <resp>jocke</resp>
- </term>
- <term>
- <id>SASLshort</id>
- <shortdef>SASL</shortdef>
- <def>
-See System Architecture Support Libraries. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>schema</id>
- <shortdef>schema</shortdef>
- <def>
-The schema contains the definitions and whereabouts for all tables. In Mnesia it is realized as a special table named "schema". </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>schema functions</id>
- <shortdef>schema functions</shortdef>
- <def>
-The functions which are available for managing schemas. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>SDL</id>
- <shortdef>SDL</shortdef>
- <def>
-Specification and Description Language. A ITU-T standard specification language which is used to specify the behaviour of switching systems. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>send</id>
- <shortdef>send</shortdef>
- <def>
-A primitive for message processing in Erlang, sends a message to a process. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>shell</id>
- <shortdef>shell</shortdef>
- <def>
-The shell is an interactive front-end to an Erlang node where Erlang expressions can be evaluated. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>shell prompt</id>
- <shortdef>shell prompt</shortdef>
- <def>
-The text or symbol shown on the screen when the shell is ready to receive commands. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>SNMPEAlong</id>
- <shortdef>Simple Network Management Protocol Extensible Agent (SNMPEA).</shortdef>
- <def>
-An Erlang/OTP application that includes a bilingual extensible SNMP agent. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>single assignment</id>
- <shortdef>single assignment</shortdef>
- <def>
-Means that once a variable has been assigned a value, the value can never be changed. Erlang is a single assignment language. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>single step</id>
- <shortdef>single step</shortdef>
- <def>
-Single stepping is a function provided by the debugger. By single stepping the developer may use the debugger to follow the execution of a process and see what actually happens at each function call. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>slave</id>
- <shortdef>slave</shortdef>
- <def>
-Not in control, can never take over by himself. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>SSLlong</id>
- <shortdef>Secure Sockets Layer (SSL)</shortdef>
- <def>
-A protocol created by Netscape Communications Corporation for authentication and encryption over TCP/IP networks, including Web. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>signature</id>
- <shortdef>signature</shortdef>
- <def>
-An encrypted text block that validates a certificate or other file. A Certification Authority (CA) creates a signature by generating a hash of the public key embedded in a certificate, then encrypting the hash with its own private key. Only the CA's public key can decrypt the signature, verifying that the CA has authenticated the network entity that owns the certificate. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SNMPshort</id>
- <shortdef>SNMP</shortdef>
- <def>
-Simple Network Management Protocol. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>SNMPshort</id>
- <shortdef>SNMPEA</shortdef>
- <def>
-See Simple Network Management Protocol Extensible Agent. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>spawn</id>
- <shortdef>spawn</shortdef>
- <def>
-A primitive for multiprocessing in Erlang, that starts a parallel computation (called a process). The creation of a new process </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>SSLshort</id>
- <shortdef>SSL</shortdef>
- <def>
-See Secure Sockets Layer. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SSLeay</id>
- <shortdef>SSLeay</shortdef>
- <def>
-An SSL library developed by Eric Yong (eay@mincom.oz.au). </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>SSLTOP</id>
- <shortdef>SSLTOP</shortdef>
- <def>
-The path to your SSL directory, a subdirectory of ServerRoot. </def>
- <resp>helen</resp>
- </term>
- <term>
- <id>start script</id>
- <shortdef>start script</shortdef>
- <def>
-A start script is a file with .script extension which is the source when a boot file is created. See SASL User's Guide for more info. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>stdlib</id>
- <shortdef>stdlib</shortdef>
- <def>
-An application within Erlang/OTP which contains modules for manipulating lists, strings, files, etc. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>sticky directory</id>
- <shortdef>sticky directory</shortdef>
- <def>
-A directory containing Erlang object code that is part of the runtime system. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>sticky lock</id>
- <shortdef>sticky lock</shortdef>
- <def>
-A lock which lingers at a node after the transaction which first acquired the lock has terminated. Once a process has obtained a sticky lock on a node, subsequent locks acquired by processes on the same node, can be set without need of involving remote nodes. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>string</id>
- <shortdef>string</shortdef>
- <def>
-The ASCII or ISO-8859-1 representation of the list of characters occurring within quotation marks in Erlang code. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>Subagent</id>
- <shortdef>Subagent</shortdef>
- <def>
-The SNMP agent system consists of one Master Agent (See Master Agent) and zero or more Subagents which can be used to distribute the SNMP agent system on several nodes. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>supervision tree</id>
- <shortdef>supervision tree</shortdef>
- <def>
-A hierarcial tree of processes used to program fault tolerant systems. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>supervisor</id>
- <shortdef>supervisor</shortdef>
- <def>
-A behaviour to stucture fault tolerant computations, and program supervision trees with. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>sup_bridge</id>
- <shortdef>supervisor bridge</shortdef>
- <def>
-A behaviour used to connect a process, or subsystem, to a supervisor tree. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>SASLlong</id>
- <shortdef>System Architecture Support Libraries (SASL)</shortdef>
- <def>
-An Erlang/OTP application which contains services for error logging, release handling and report browsing. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>.config</id>
- <shortdef>system configuration file</shortdef>
- <def>
-A file which specifies configuration parameters for the applications in the system. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>table lock</id>
- <shortdef>table lock</shortdef>
- <def>
-Table locks are locks which are set on whole tables. They may either be read locks or write locks. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>Table Visualizer</id>
- <shortdef>Table Visualizer</shortdef>
- <def>
-A tool which enables the user to examine ETS and Mnesia tables. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>temporary child</id>
- <shortdef>temporary child</shortdef>
- <def>
-A supervised process which is never restarted when it dies. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>term</id>
- <shortdef>term</shortdef>
- <def>
-The super type of all Erlang types. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>Toolbar</id>
- <shortdef>Toolbar</shortdef>
- <def>
-A tool that provides an simplistic interface to the other various Erlang/OTP tools </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>tools</id>
- <shortdef>tools</shortdef>
- <def>
-An application within Erlang/OTP which contains the tools which are not applications themselves. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>transaction</id>
- <shortdef>transaction</shortdef>
- <def>
-Transactions groups a set of database accesses into an atomic unit. All transactions has the ACID (atomicity, concistency, isolation and durability) properties. </def>
- <resp>hakan</resp>
- </term>
- <term>
- <id>transient child</id>
- <shortdef>transient child</shortdef>
- <def>
-A supervised process which is restarted if it dies non-normally. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>trigger</id>
- <shortdef>trigger</shortdef>
- <def>
-The Interpreter. A break point that is reached by a process triggers if it is active, and the execution of the process is stopped. </def>
- <resp>olin</resp>
- </term>
- <term>
- <id>tty</id>
- <shortdef>tty</shortdef>
- <def>
-tty is a simple command line interface program where keystrokes are collected and interpreted. Originally meant teletypewriter equipment. Now it usually means the user console/terminal/shell window. </def>
- <resp>kent</resp>
- </term>
- <term>
- <id>tuple</id>
- <shortdef>tuple</shortdef>
- <def>
-A tuple is a data type in Erlang. Tuples are used as place holders for complex data structures. Tuples may contain anything of any size, and are written as sequences of terms separated by commas, and enclosed in curly brackets { }. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>variable</id>
- <shortdef>variable</shortdef>
- <def>
-An alias for a memory position, in which a value can be put. Erlang variables always start with an upper case letter. </def>
- <resp>kenneth</resp>
- </term>
- <term>
- <id>workers</id>
- <shortdef>workers</shortdef>
- <def>
-The lower nodes in a supervision tree. These are the processes that actually performs some real work, e.g. servers. </def>
- <resp>mbj</resp>
- </term>
- <term>
- <id>YECC</id>
- <shortdef>YECC</shortdef>
- <def>
-A LALR-1 parser generator included in Erlang/OTP. It is written in Erlang and generates a parser as an Erlang module. </def>
- <resp>kenneth</resp>
- </term>
-</terms>
-
diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile
index 2fbd7d087f..d781809137 100644
--- a/system/doc/design_principles/Makefile
+++ b/system/doc/design_principles/Makefile
@@ -95,7 +95,9 @@ $(HTMLDIR)/%.gif: %.gif
$(HTMLDIR)/%.svg: %.svg
$(INSTALL_DATA) $< $@
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -116,12 +118,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(IMAGE_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(IMAGE_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
release_spec:
diff --git a/system/doc/design_principles/distributed_applications.xml b/system/doc/design_principles/distributed_applications.xml
index 62b7882f25..7eb0e0d3e0 100644
--- a/system/doc/design_principles/distributed_applications.xml
+++ b/system/doc/design_principles/distributed_applications.xml
@@ -45,7 +45,7 @@
addressing mechanism is required to ensure that it can be
addressed by other applications, regardless on which node it
currently executes. This issue is not addressed here, but the
- <c>global</c> or <c>pg2</c> modules in Kernel
+ <c>global</c> or <c>pg</c> modules in Kernel
can be used for this purpose.</p>
</section>
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index f657e8cb00..ddf2ce0bea 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -53,8 +53,8 @@
For an Event-Driven State Machine, the input is an event
that triggers a <em>state transition</em> and the output
is actions executed during the <em>state transition</em>.
- It can analogously to the mathematical model of a
- Finite State Machine be described as
+ Analogously to the mathematical model of a
+ Finite State Machine, it can be described as
a set of relations of the following form:
</p>
<pre>
@@ -62,8 +62,8 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<p>
These relations are interpreted as follows:
if we are in state <c>S</c> and event <c>E</c> occurs, we
- are to perform actions <c>A</c> and make a transition to
- state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c>
+ are to perform actions <c>A</c>, and make a transition to
+ state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c>,
and that <c>A</c> can be empty.
</p>
<p>
@@ -73,8 +73,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
the current state <c>S</c>, where "different" means
Erlang's strict inequality: <c>=/=</c>
also know as "does not match".
- During a <em>state changes</em>,
- <c>gen_statem</c> does more things
+ <c>gen_statem</c> does more things during <em>state changes</em>
than during other <em>state transitions</em>.
</p>
<p>
@@ -123,8 +122,8 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<seealso marker="#Inserted Events">
Inserted Events
</seealso>
- that is: events from the state machine to itself
- (in particular purely internal events)
+ (that is, events from the state machine to itself;
+ for purely internal events in particular)
</item>
<item>
<seealso marker="#State Enter Calls">
@@ -532,9 +531,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<title>Transition Actions</title>
<p>
In the first section
- <seealso marker="#Event-Driven State Machines">
- Event-Driven State Machines
- </seealso>
+ (<seealso marker="#Event-Driven State Machines">Event-Driven State Machines</seealso>),
actions were mentioned as a part of
the general state machine model. These general actions
are implemented with the code that <em>callback module</em>
diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile
index a2742a1354..285f8aad4b 100644
--- a/system/doc/efficiency_guide/Makefile
+++ b/system/doc/efficiency_guide/Makefile
@@ -88,7 +88,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -107,15 +109,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
release_spec:
-
-
-
diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile
index 1604075312..bb95c8370f 100644
--- a/system/doc/embedded/Makefile
+++ b/system/doc/embedded/Makefile
@@ -76,7 +76,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -95,13 +97,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/general_info/Makefile b/system/doc/general_info/Makefile
index 539075280e..7496e128b2 100644
--- a/system/doc/general_info/Makefile
+++ b/system/doc/general_info/Makefile
@@ -69,7 +69,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -88,12 +90,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/general_info/deprecations.xml b/system/doc/general_info/deprecations.xml
index c748e60059..7e6ed124e0 100644
--- a/system/doc/general_info/deprecations.xml
+++ b/system/doc/general_info/deprecations.xml
@@ -39,6 +39,57 @@
<seealso marker="../system_principles/misc#deprecation">Support, Compatibility,
Deprecations, and Removal</seealso>.</p>
</section>
+
+ <section>
+ <marker id="OTP-23"/>
+ <title>OTP 23</title>
+ <section>
+ <title>Crypto Old API</title>
+ <p>The <seealso marker="crypto:new_api#the-old-api">Old API</seealso> is now deprecated
+ and has also been
+ <seealso marker="scheduled_for_removal#OTP-24">scheduled for removal</seealso>.</p>
+ <p>For replacement functions see the
+ <seealso marker="crypto:new_api#the-new-api">New API</seealso>.</p>
+ </section>
+
+ <section>
+ <title>http_uri</title>
+ <p>Since OTP 21 the recommended module to handle URIs is
+ <seealso marker="stdlib:uri_string">uri_string</seealso>. The module
+ http_uri does not provide a implementation that satisfies the
+ RFC.
+ </p>
+ </section>
+
+ <section>
+ <title>pg2</title>
+ <p>
+ As of OTP 23, a new process group implementation
+ <seealso marker="kernel:pg">pg</seealso> is introduced.
+ <c>pg</c> is similar to <seealso marker="kernel:pg2">pg2</seealso>,
+ but with much better scalability properties. The API and behaviour
+ is however not compatible.
+ </p>
+ <p>
+ <c>pg2</c> is now deprecated and has also been
+ <seealso marker="scheduled_for_removal#OTP-24">scheduled for removal</seealso>
+ in OTP 24.
+ </p>
+ </section>
+
+ <section>
+ <title>Distributed Disk Logs</title>
+ <p>
+ As of OTP 23, the distributed
+ <seealso marker="kernel:disk_log"><c>disk_log</c></seealso>
+ feature has been deprecated and it has also been
+ <seealso marker="scheduled_for_removal#OTP-24">scheduled for removal</seealso>
+ in OTP 24.
+ </p>
+ </section>
+
+ </section>
+
<section>
<marker id="OTP-22"/>
<title>OTP 22</title>
@@ -83,6 +134,16 @@
</p>
</section>
</section>
+
+ <section>
+ <marker id="OTP-19"/>
+ <title>OTP 19</title>
+ <section>
+ <title>SSL/TLS</title>
+ <p>For security reasons SSL-3.0 is no longer supported by default, but can be configured.</p>
+ </section>
+ </section>
+
<section>
<marker id="OTP-18"/>
<title>OTP 18</title>
@@ -94,5 +155,28 @@
<seealso marker="erts:time_correction#Dos_and_Donts">Dos and Donts</seealso>
section on how to replace usage of <c>erlang:now()</c>.</p>
</section>
+
+ <section>
+ <title>httpd_conf module</title>
+ <p>
+ API functions in the module called httpd_conf was deprecated in favour
+ of standard modules such as lists, strings, fillib and erlang.
+ </p>
+ </section>
+
+ </section>
+
+ <section>
+ <marker id="OTP-12"/>
+ <title>OTP 12</title>
+ <section>
+ <title>inets - httpd Apache config files</title>
+ <p>
+ New config file format, instead of Apache compatible, is introduced.
+ </p>
+ </section>
+
</section>
+
+
</chapter>
diff --git a/system/doc/general_info/scheduled_for_removal.xml b/system/doc/general_info/scheduled_for_removal.xml
index a9421df385..b2efefba75 100644
--- a/system/doc/general_info/scheduled_for_removal.xml
+++ b/system/doc/general_info/scheduled_for_removal.xml
@@ -37,6 +37,49 @@
<seealso marker="../system_principles/misc#removal">Support, Compatibility,
Deprecations, and Removal</seealso>.</p>
</section>
+
+ <section>
+ <marker id="OTP-25"/>
+ <title>OTP 25</title>
+ <section>
+ <title>http_uri</title>
+ <p>Since OTP 21 the recommended module to handle URIs is
+ <seealso marker="stdlib:uri_string">uri_string</seealso>. The module
+ http_uri does not provide a implementation that satisfies the
+ RFC. Formally deprecated since OTP-23 </p>
+ </section>
+ </section>
+
+ <section>
+ <marker id="OTP-24"/>
+ <title>OTP 24</title>
+ <section>
+ <title>Crypto Old API</title>
+ <p>The <seealso marker="crypto:new_api#the-old-api">Old API</seealso> will be
+ removed as of OTP 24. The support was formally deprecated as of OTP 23.</p>
+ <p>For replacement functions see the
+ <seealso marker="crypto:new_api#the-new-api">New API</seealso>.</p>
+ </section>
+ <section>
+ <title>pg2</title>
+ <p>
+ <seealso marker="kernel:pg2">pg2</seealso> is as of
+ <seealso marker="deprecations#OTP-23">OTP 23 deprecated</seealso>
+ and will be removed in OTP 24.
+ </p>
+ </section>
+ <section>
+ <title>Distributed Disk Logs</title>
+ <p>
+ The distributed
+ <seealso marker="kernel:disk_log"><c>disk_log</c></seealso>
+ feature is as of
+ <seealso marker="deprecations#OTP-23">OTP 23 deprecated</seealso>
+ and will be removed in OTP 24.
+ </p>
+ </section>
+ </section>
+
<section>
<marker id="OTP-23"/>
<title>OTP 23</title>
@@ -57,5 +100,29 @@
of the <c>ei</c> library which also is part of the <c>erl_interface</c>
application.</p>
</section>
+
+ <section>
+ <title>httpd_conf module</title>
+ <p>
+ API functions in the module called httpd_conf was deprecated in favour
+ of standard modules such as lists, strings, fillib and erlang. Formally
+ deprecated as of OTP 18.
+ </p>
+ </section>
+
+ <section>
+ <title>inets - httpd Apache config files</title>
+ <p>
+ API functions in the module called httpd_conf was deprecated in favour
+ of standard modules such as lists, strings, fillib and erlang. Formally
+ deprecated as of OTP 18.
+ </p>
+ </section>
+
+ <section>
+ <title>SSL/TLS</title>
+ <p>For security reasons SSL-3.0 is no longer supported at all.</p>
+ </section>
+
</section>
</chapter>
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index 1c917895d5..47442e85e3 100644
--- a/system/doc/getting_started/Makefile
+++ b/system/doc/getting_started/Makefile
@@ -75,7 +75,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -94,11 +96,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+
release_spec:
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index c5234c1c9a..c95a426653 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -100,7 +100,9 @@ $(REDIRECT_HTML_DIR)/%.html: Makefile
echo "This <a href=\"../"$(notdir $@)"\">link</a> will" >> $@
echo "take you there immediately.</p></body></html>" >> $@
-docs: $(XML_GEN_FILES) html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
local_docs: $(XML_GEN_FILES)
@@ -123,13 +125,12 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html $(RELSYSDIR)
$(INSTALL_DIR) $(REDIRECT_HTML_RELSYSDIR)
$(INSTALL_DATA) $(REDIRECT_HTML_FILES) $(REDIRECT_HTML_RELSYSDIR)
-release_spec:
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile
index 2eb429e04d..4e3848a91a 100644
--- a/system/doc/oam/Makefile
+++ b/system/doc/oam/Makefile
@@ -75,7 +75,9 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(CP) $< $@
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -96,11 +98,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index 9c67c24b64..7c1bdde698 100644
--- a/system/doc/programming_examples/Makefile
+++ b/system/doc/programming_examples/Makefile
@@ -75,7 +75,10 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
+
local_docs: PDFDIR=../../pdf
html: $(GIF_FILES) $(HTML_UG_FILE)
@@ -93,12 +96,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
-
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml
index a64be0c007..870e07e41a 100644
--- a/system/doc/programming_examples/bit_syntax.xml
+++ b/system/doc/programming_examples/bit_syntax.xml
@@ -292,8 +292,9 @@ X:4/little-signed-integer-unit:8</code>
<p>When matching <c>Value</c>, value must be either a variable or
an integer, or a floating point literal. Expressions are not
allowed.</p>
- <p><c>Size</c> must be an integer literal, or a previously bound
- variable. The following is not allowed:</p>
+ <p><c>Size</c> must be a
+ <seealso marker="doc/reference_manual:expressions#guard_expressions">guard expression</seealso>, which can use literals and previously bound variables.
+ The following is not allowed:</p>
<code type="none"><![CDATA[
foo(N, <<X:N,T/binary>>) ->
{X,T}.]]></code>
@@ -304,6 +305,33 @@ foo(N, <<X:N,T/binary>>) ->
foo(N, Bin) ->
<<X:N,T/binary>> = Bin,
{X,T}.]]></code>
+ <note><p>Before OTP 23, <c>Size</c> was restricted to be an
+ integer or a variable bound to an integer.</p></note>
+
+ <section>
+ <title>Binding and Using a Size Variable</title>
+ <p>There is one exception to the rule that a variable that is
+ as size must be previously bound. It is possible to match and
+ bind a variable, and use it as a size within the the same
+ binary pattern. For example:</p>
+
+ <code type="none"><![CDATA[
+bar(<<Sz:8,Payload:Sz/binary-unit:8,Rest/binary>>) ->
+ {Payload,Rest}.]]></code>
+
+ <p>Here <c>Sz</c> is bound to the value in the first byte of
+ the binary. <c>Sz</c> is then used at the number of bytes
+ to match out as a binary.</p>
+
+ <p>Starting in OTP 23, the size can be a guard expression:</p>
+ <code type="none"><![CDATA[
+bar(<<Sz:8,Payload:((Sz-1)*8)/binary,Rest/binary>>) ->
+ {Payload,Rest}.]]></code>
+
+ <p>Here <c>Sz</c> is the combined size of the header and the
+ payload, so we will need to subtract one byte to get the size
+ of the payload.</p>
+ </section>
<section>
<title>Getting the Rest of the Binary or Bitstring</title>
diff --git a/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile
index 809eb2c979..f7b93814ab 100644
--- a/system/doc/reference_manual/Makefile
+++ b/system/doc/reference_manual/Makefile
@@ -18,6 +18,7 @@
# %CopyrightEnd%
#
#
+
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -25,10 +26,10 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Application version
# ----------------------------------------------------
include $(ERL_TOP)/erts/vsn.mk
-#VSN=$(SYSTEM_VSN)
APPLICATION=otp-system-documentation
XMLDIR := $(XMLDIR)/reference_manual
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -40,6 +41,7 @@ RELSYSDIR = "$(RELEASE_PATH)/doc/reference_manual"
XML_PART_FILES = part.xml
include xmlfiles.mk
+
XML_CHAPTER_FILES=$(REF_MAN_CHAPTER_FILES)
TOPDOCDIR=..
@@ -48,7 +50,6 @@ BOOK_FILES = book.xml
GIF_FILES=
-
XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES)
@@ -65,14 +66,6 @@ HTML_FILES = \
HTMLDIR = ../html/reference_manual
-EXTRA_FILES = $(DEFAULT_GIF_FILES) \
- $(DEFAULT_HTML_FILES) \
- $(C_FILES) \
- $(ERL_FILES) \
- $(HRL_FILES) \
- $(MISC_FILES) \
- $(XML_CHAPTER_FILES:%.xml=%.html)
-
HTML_UG_FILE = $(HTMLDIR)/users_guide.html
# ----------------------------------------------------
@@ -85,7 +78,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -104,14 +99,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
-
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml
index 93c679357b..8e3e181303 100644
--- a/system/doc/reference_manual/data_types.xml
+++ b/system/doc/reference_manual/data_types.xml
@@ -52,24 +52,33 @@
Integer with the base <em><c>base</c></em>, that must be an
integer in the range 2..36.</item>
</list>
+ <p>Leading zeroes are ignored. Single underscore <c>_</c> can be inserted
+ between digits as a visual separator.</p>
<p><em>Examples:</em></p>
<pre>
1> <input>42.</input>
42
-2> <input>$A.</input>
+2> <input>-1_234_567_890.</input>
+-1234567890
+3> <input>$A.</input>
65
-3> <input>$\n.</input>
+4> <input>$\n.</input>
10
-4> <input>2#101.</input>
+5> <input>2#101.</input>
5
-5> <input>16#1f.</input>
+6> <input>16#1f.</input>
31
-6> <input>2.3.</input>
+7> <input>16#4865_316F_774F_6C64.</input>
+5216630098191412324
+8> <input>2.3.</input>
2.3
-7> <input>2.3e3.</input>
+9> <input>2.3e3.</input>
2.3e3
-8> <input>2.3e-3.</input>
-0.0023</pre>
+10> <input>2.3e-3.</input>
+0.0023
+11> <input>1_234.333_333</input>
+1234.333333
+</pre>
</section>
<section>
diff --git a/system/doc/reference_manual/errors.xml b/system/doc/reference_manual/errors.xml
index 16d3e7590e..68c96863f5 100644
--- a/system/doc/reference_manual/errors.xml
+++ b/system/doc/reference_manual/errors.xml
@@ -115,6 +115,7 @@
error.</p>
<section>
+ <marker id="stacktrace"></marker>
<title>The call-stack back trace (stacktrace)</title>
<p>The stack back-trace (<em>stacktrace</em>) is a list of
<c>{Module,Function,Arity,Location}</c>
@@ -237,7 +238,11 @@
</row>
<row>
<cell align="left" valign="middle"><c>noproc</c></cell>
- <cell align="left" valign="middle">Trying to link to a non-existing process.</cell>
+ <cell align="left" valign="middle">Trying to link or monitor to a non-existing process or port.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle"><c>noconnection</c></cell>
+ <cell align="left" valign="middle">A link or monitor to a remote process was broken because a connection between the nodes could not be established or was severed.</cell>
</row>
<row>
<cell align="left" valign="middle"><c>{nocatch,V}</c></cell>
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index 4277c21fdb..c6fb8dfabe 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -944,7 +944,7 @@ M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code>
Here <c>M0</c> is any map. It follows that <c>M1 .. M4</c> are maps as well.
</p>
<p>
- More <em>Examples:</em>
+ <em>More examples:</em>
</p>
<pre>
1> <input>M = #{1 => a}.</input>
@@ -973,15 +973,20 @@ M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code>
<code>#{ K := V } = M</code>
<p>
- Here <c>M</c> is any map. The key <c>K</c> must be an expression with bound
- variables or literals. <c>V</c> can be any pattern with either bound or
- unbound variables.
+ Here <c>M</c> is any map. The key <c>K</c> must be a
+ <seealso marker="#guard_expressions">guard
+ expression</seealso>, with all variables already bound.
+ <c>V</c> can be any pattern with either bound or unbound
+ variables.
</p>
<p>
If the variable <c>V</c> is unbound, it becomes bound to the value associated
with the key <c>K</c>, which must exist in the map <c>M</c>. If the variable
<c>V</c> is bound, it must match the value associated with <c>K</c> in <c>M</c>.
</p>
+ <note><p>Before OTP 23, the expression defining the key
+ <c>K</c> was restricted to be either a single variable or a
+ literal.</p></note>
<p><em>Example:</em></p>
<pre>
1> <input>M = #{"tuple" => {1,2}}.</input>
@@ -998,9 +1003,12 @@ M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code>
</p>
<code>#{ K1 := V1, .., Kn := Vn } = M</code>
<p>
- Here 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> is matched to the
- associated values of their respective keys.
+ Here keys <c>K1 .. Kn</c> are any expressions with
+ literals or bound variables. If all key expressions
+ evalute successfully and all keys exist in map
+ <c>M</c>, all variables in <c>V1 .. Vn</c> is
+ matched to the associated values of their respective
+ keys.
</p>
<p>
If the matching conditions are not met, the match fails, either with:
@@ -1034,6 +1042,9 @@ M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code>
This expression matches if the expression <c>Expr</c> is of type map, otherwise
it fails with an exception <c>badmatch</c>.
</p>
+ <p>Here the key to be retrieved is constructed from an expression:</p>
+ <code>#{{tag,length(List)} := V} = Map</code>
+ <p><c>List</c> must be an already bound variable.</p>
<section>
<title>Matching Syntax</title>
<p>
@@ -1057,7 +1068,7 @@ handle_call(change, From, #{ state := start } = S) ->
Maps are allowed in guards as long as all subexpressions are valid guard expressions.
</p>
<p>
- Two guard BIFs handle maps:
+ The following guard BIFs handle maps:
</p>
<list>
<item>
@@ -1065,6 +1076,10 @@ handle_call(change, From, #{ state := start } = S) ->
in the <c>erlang</c> module
</item>
<item>
+ <seealso marker="erts:erlang#map_get/2">map_get/2</seealso>
+ in the <c>erlang</c> module
+ </item>
+ <item>
<seealso marker="erts:erlang#map_size/1">map_size/1</seealso>
in the <c>erlang</c> module
</item>
@@ -1100,8 +1115,13 @@ Ei = Value |
<p>Used in a bit string construction, <c>Size</c> is an expression
that is to evaluate to an integer.</p>
- <p>Used in a bit string matching, <c>Size</c> must be an integer, or a
- variable bound to an integer.</p>
+ <p>Used in a bit string matching, <c>Size</c> must be a
+ <seealso marker="#guard_expressions">guard expression</seealso>
+ that evaluates to an integer. All variables in the guard expression
+ must be already bound.</p>
+
+ <note><p>Before OTP 23, <c>Size</c> was restricted to be an
+ integer or a variable bound to an integer.</p></note>
<p>The value of <c>Size</c> specifies the size of the segment in
units (see below). The default value depends on the type (see
@@ -1632,16 +1652,26 @@ end</pre>
by comma (,). The guard is true if all guard expressions
evaluate to <c>true</c>.</p>
<p><c>GuardExpr1,...,GuardExprN</c></p>
- <p>The set of valid <em>guard expressions</em> (sometimes called
- guard tests) is a subset of the set of valid Erlang expressions.
- The reason for restricting the set of valid expressions is that
- evaluation of a guard expression must be guaranteed to be free
- of side effects. Valid guard expressions are the following:</p>
+ </section>
+
+ <section>
+ <marker id="guard_expressions"></marker>
+ <title>Guard Expressions</title>
+ <p>The set of valid <em>guard expressions</em> is a subset of the
+ set of valid Erlang expressions. The reason for restricting the
+ set of valid expressions is that evaluation of a guard expression
+ must be guaranteed to be free of side effects. Valid guard
+ expressions are the following:</p>
<list type="bulleted">
- <item>The atom <c>true</c></item>
- <item>Other constants (terms and bound variables), all regarded
- as false</item>
- <item>Calls to the BIFs specified in table <c>Type Test BIFs</c></item>
+ <item>Variables</item>
+ <item>Constants (atoms, integer, floats, lists, tuples, records,
+ binaries, and maps)</item>
+ <item>Expressions that construct atoms, integer, floats, lists,
+ tuples, records, binaries, and maps</item>
+ <item>Expressions that update a map</item>
+ <item>The record epxressions <c>Expr#Name.Field</c> and <c>#Name.Field</c></item>
+ <item>Calls to the BIFs specified in tables <em>Type Test BIFs</em> and
+ <em>Other BIFs Allowed in Guard Expressions</em></item>
<item>Term comparisons</item>
<item>Arithmetic expressions</item>
<item>Boolean expressions</item>
@@ -1729,6 +1759,9 @@ end</pre>
<cell align="left" valign="middle"><c>length(List)</c></cell>
</row>
<row>
+ <cell align="left" valign="middle"><c>map_get(Key, Map)</c></cell>
+ </row>
+ <row>
<cell align="left" valign="middle"><c>map_size(Map)</c></cell>
</row>
<row>
diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile
index ea9ee85105..a7b00b4ec5 100644
--- a/system/doc/system_architecture_intro/Makefile
+++ b/system/doc/system_architecture_intro/Makefile
@@ -70,7 +70,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -89,13 +91,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
-
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile
index 5110b73373..44009b674f 100644
--- a/system/doc/system_principles/Makefile
+++ b/system/doc/system_principles/Makefile
@@ -71,7 +71,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -90,12 +92,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
-release_spec:
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+release_spec:
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index e3f9c4710a..2953491d71 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -249,8 +249,9 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
+DOC_TARGETS?=pdf html man
-docs: pdf html $(INFO_FILES)
+docs: $(DOC_TARGETS) $(INFO_FILES)
local_docs: PDFREFDIR=../pdf
@@ -258,10 +259,11 @@ $(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
-html: $(INDEX_FILES) \
- $(MAN_INDEX) $(JAVASCRIPT)
+html: $(INDEX_FILES) $(JAVASCRIPT)
-debug opt:
+man: $(MAN_INDEX)
+
+debug opt:
clean:
$(RM) ../html/js/*.js
@@ -280,21 +282,28 @@ include $(ERL_TOP)/make/otp_release_targets.mk
$(RELSYSDIR)/temporary:
$(INSTALL_DIR) $(RELSYSDIR)/temporary
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELEASE_PATH)"
- $(INSTALL_DATA) $(INFO_FILES) "$(RELEASE_PATH)"
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELSYSDIR)/pdf
- $(INSTALL_DATA) \
- $(TOP_PDF_FILE) $(RELSYSDIR)/pdf
+$(RELSYSDIR)/docbuild:
+ $(INSTALL_DIR) $(RELSYSDIR)/docbuild
+
+release_man_spec: man $(RELSYSDIR)/docbuild
+ $(INSTALL_DATA) $(MAN_INDEX) $(RELSYSDIR)
+ $(INSTALL_DATA) $(MAN_INDEX_SRC) $(MAN_INDEX_SCRIPT) $(RELSYSDIR)/docbuild
+
+release_html_spec: html $(RELSYSDIR)/docbuild
$(INSTALL_DIR) $(RELSYSDIR)/js
$(INSTALL_DATA) $(JAVASCRIPT) $(RELSYSDIR)/js
- $(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELSYSDIR)/docbuild
- $(INSTALL_DATA) $(INDEX_SCRIPT) $(MAN_INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \
- $(INDEX_SRC) $(MAN_INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \
+ $(INSTALL_DATA) $(INDEX_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \
+ $(INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \
$(TEMPLATES) $(RELSYSDIR)/docbuild
- $(RM) -r $(RELSYSDIR)/temporary
+release_pdf_spec: pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/pdf
+ $(INSTALL_DATA) \
+ $(TOP_PDF_FILE) $(RELSYSDIR)/pdf
+
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec) $(INFO_FILES)
+ $(INSTALL_DATA) $(INFO_FILES) "$(RELEASE_PATH)"
+ $(RM) -r $(RELSYSDIR)/temporary
release_spec:
diff --git a/system/doc/top/print.html b/system/doc/top/print.html
index b562d0e9bc..e65ac491a9 100644
--- a/system/doc/top/print.html
+++ b/system/doc/top/print.html
@@ -19,7 +19,7 @@
<center>
<a href="http://www.ericsson.com/technology/opensource/erlang">
- <img alt="Ericsson AB" BORDER=0 SRC="pics/min_head.gif">
+ <img alt="Ericsson AB" BORDER=0>
</a>
<br>
diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile
index 4c62deeffd..effc673af6 100644
--- a/system/doc/tutorial/Makefile
+++ b/system/doc/tutorial/Makefile
@@ -97,7 +97,9 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(CP) $< $@
-docs: html
+DOC_TARGETS?=html
+
+docs: $(DOC_TARGETS)
local_docs: PDFDIR=../../pdf
@@ -118,11 +120,11 @@ clean clean_docs:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-release_docs_spec: docs
-# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
-# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
- $(INSTALL_DIR) $(RELSYSDIR)
+release_html_spec: html
+ $(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \
$(RELSYSDIR)
+release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
+
release_spec:
diff --git a/system/doc/tutorial/cnode.xmlsrc b/system/doc/tutorial/cnode.xmlsrc
index f1f0e976ad..b53bbfe550 100644
--- a/system/doc/tutorial/cnode.xmlsrc
+++ b/system/doc/tutorial/cnode.xmlsrc
@@ -29,6 +29,23 @@
<rev></rev>
<file>cnode.xml</file>
</header>
+ <p>The reader is referred to
+ <seealso marker="erl_interface:ei_users_guide">
+ the erl_interface users guide</seealso> for information about how
+ to create C nodes.</p>
+ <!-- Commented out because it uses the removed erl_*
+ functions. Should be updated to use ei_* functions instead.
+
+
+
+ <note><p>This tutorial uses the <c>erl_*</c> functions which have
+ been removed as of OTP version 23. The <c>erl_*</c> functions are
+ replaced by the <c>ei_*</c> functions in the erl_interface
+ application. The reader is therefore referred to
+ <seealso marker="erl_interface:ei_users_guide">
+ the erl_interface users guide</seealso> for information about how
+ to create C nodes until this tutorial has got updated.</p></note>
+
<p>This section outlines an example of how to solve the example
problem in <seealso marker="example">Problem Example</seealso>
by using a C node. Notice that a C node is not typically
@@ -273,6 +290,7 @@ Eshell V4.9.1.2 (abort with ^G)
4
(e1@idril.du.uab.ericsson.se)2> <input>complex4:bar(5).</input>
10</pre>
- </section>
+</section>
+-->
</chapter>
diff --git a/system/doc/tutorial/distribution.xml b/system/doc/tutorial/distribution.xml
index b489410841..b337dc59b1 100644
--- a/system/doc/tutorial/distribution.xml
+++ b/system/doc/tutorial/distribution.xml
@@ -59,7 +59,7 @@
<item>global_group - Grouping nodes to global name registration groups.</item>
<item>net_adm - Various net administration routines.</item>
<item>net_kernel - Networking kernel.</item>
- <item>pg2 - Distributed named process groups.</item>
+ <item>pg - Distributed named process groups.</item>
<item>pool - Load distribution facility.</item>
<item>slave - Functions for starting and controlling slave nodes.</item>
</list>
diff --git a/system/doc/tutorial/ei.c b/system/doc/tutorial/ei.c
index c33e3fb78e..8db226b462 100644
--- a/system/doc/tutorial/ei.c
+++ b/system/doc/tutorial/ei.c
@@ -1,38 +1,57 @@
/* ei.c */
-#include "erl_interface.h"
#include "ei.h"
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
typedef unsigned char byte;
-int main() {
- ETERM *tuplep, *intp;
- ETERM *fnp, *argp;
- int res;
- byte buf[100];
- long allocated, freed;
-
- erl_init(NULL, 0);
+int read_cmd(byte *buf);
+int write_cmd(byte *buf, int len);
+int foo(int x);
+int bar(int y);
- while (read_cmd(buf) > 0) {
- tuplep = erl_decode(buf);
- fnp = erl_element(1, tuplep);
- argp = erl_element(2, tuplep);
-
- if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {
- res = foo(ERL_INT_VALUE(argp));
- } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {
- res = bar(ERL_INT_VALUE(argp));
- }
+static void fail(int place) {
+ fprintf(stderr, "Something went wrong %d\n", place);
+ exit(1);
+}
- intp = erl_mk_int(res);
- erl_encode(intp, buf);
- write_cmd(buf, erl_term_len(intp));
+int main() {
+ byte buf[100];
+ int index = 0;
+ int version = 0;
+ int arity = 0;
+ char atom[128];
+ long in = 0;
+ int res = 0;
+ ei_x_buff res_buf;
+ ei_init();
+ while (read_cmd(buf) > 0) {
+ if (ei_decode_version(buf, &index, &version) != 0)
+ fail(1);
+ if (ei_decode_tuple_header(buf, &index, &arity) != 0)
+ fail(2);
+ if (arity != 2)
+ fail(3);
+ if (ei_decode_atom(buf, &index, atom) != 0)
+ fail(4);
+ if (ei_decode_long(buf, &index, &in) != 0)
+ fail(5);
+ if (strncmp(atom, "foo", 3) == 0) {
+ res = foo((int)in);
+ } else if (strncmp(atom, "bar", 3) == 0) {
+ res = bar((int)in);
+ }
+ if (ei_x_new_with_version(&res_buf) != 0)
+ fail(6);
+ if (ei_x_encode_long(&res_buf, res) != 0)
+ fail(7);
+ write_cmd(res_buf.buff, res_buf.index);
- erl_free_compound(tuplep);
- erl_free_term(fnp);
- erl_free_term(argp);
- erl_free_term(intp);
- }
+ if (ei_x_free(&res_buf) != 0)
+ fail(8);
+ index = 0;
+ }
}
-
+
diff --git a/system/doc/tutorial/erl_comm.c b/system/doc/tutorial/erl_comm.c
index 303c6dc170..c4f8cb3117 100644
--- a/system/doc/tutorial/erl_comm.c
+++ b/system/doc/tutorial/erl_comm.c
@@ -1,44 +1,25 @@
/* erl_comm.c */
-typedef unsigned char byte;
-
-read_cmd(byte *buf)
-{
- int len;
-
- if (read_exact(buf, 2) != 2)
- return(-1);
- len = (buf[0] << 8) | buf[1];
- return read_exact(buf, len);
-}
-
-write_cmd(byte *buf, int len)
-{
- byte li;
+#include <stdio.h>
+#include <unistd.h>
- li = (len >> 8) & 0xff;
- write_exact(&li, 1);
-
- li = len & 0xff;
- write_exact(&li, 1);
-
- return write_exact(buf, len);
-}
+typedef unsigned char byte;
-read_exact(byte *buf, int len)
+int read_exact(byte *buf, int len)
{
int i, got=0;
do {
- if ((i = read(0, buf+got, len-got)) <= 0)
- return(i);
+ if ((i = read(0, buf+got, len-got)) <= 0){
+ return(i);
+ }
got += i;
} while (got<len);
return(len);
}
-write_exact(byte *buf, int len)
+int write_exact(byte *buf, int len)
{
int i, wrote = 0;
@@ -50,3 +31,27 @@ write_exact(byte *buf, int len)
return (len);
}
+
+int read_cmd(byte *buf)
+{
+ int len;
+
+ if (read_exact(buf, 2) != 2)
+ return(-1);
+ len = (buf[0] << 8) | buf[1];
+ return read_exact(buf, len);
+}
+
+int write_cmd(byte *buf, int len)
+{
+ byte li;
+
+ li = (len >> 8) & 0xff;
+ write_exact(&li, 1);
+
+ li = len & 0xff;
+ write_exact(&li, 1);
+
+ return write_exact(buf, len);
+}
+
diff --git a/system/doc/tutorial/erl_interface.xmlsrc b/system/doc/tutorial/erl_interface.xmlsrc
index bf9bd36597..ce2dfa9d18 100644
--- a/system/doc/tutorial/erl_interface.xmlsrc
+++ b/system/doc/tutorial/erl_interface.xmlsrc
@@ -83,88 +83,27 @@ end</pre>
<section>
<title>C Program</title>
<p>The following example shows a C program communicating with an
- Erlang program over a plain port with home made encoding:</p>
- <codeinclude file="port.c" type="c"/>
- <p>Compared to the C program in <seealso marker="c_port">
- Ports</seealso>, using only the plain port, the
- <c>while</c>-loop must be rewritten. Messages coming from the
- port is on the Erlang external term format. They must be
- converted into an <c>ETERM</c> struct, which is a C struct
- similar to an Erlang term. The result of calling <c>foo()</c> or
- <c>bar()</c> must be converted to the Erlang external term
- format before being sent back to the port. But before calling
- any other Erl_Interface function, the memory handling must be
- initiated:</p>
- <pre>
-erl_init(NULL, 0);</pre>
- <p>The following functions, <c>read_cmd()</c> and
+ Erlang program over a plain port with the Erlang external term format encoding:</p>
+ <codeinclude file="ei.c" type="c"/>
+ <p>The following functions, <c>read_cmd()</c> and
<c>write_cmd()</c>, from the <c>erl_comm.c</c> example in
<seealso marker="c_port">Ports</seealso> can still be
used for reading from and writing to the port:
</p>
<codeinclude file="erl_comm.c" type="c"/>
- <p>The function <c>erl_decode()</c> from <c>erl_marshal</c>
- converts the binary into an <c>ETERM</c> struct:</p>
- <pre>
-int main() {
- ETERM *tuplep;
- while (read_cmd(buf) > 0) {
- tuplep = erl_decode(buf);</pre>
- <p>Here, <c>tuplep</c> points to an <c>ETERM</c> struct
- representing a tuple with two elements; the function name (atom)
- and the argument (integer). Using the function
- <c>erl_element()</c> from <c>erl_eterm</c>, these elements can
- be extracted, but they must also be declared as pointers to an
- <c>ETERM</c> struct:</p>
- <pre>
- fnp = erl_element(1, tuplep);
- argp = erl_element(2, tuplep);</pre>
- <p>The macros <c>ERL_ATOM_PTR</c> and <c>ERL_INT_VALUE</c> from
- <c>erl_eterm</c> can be used to obtain the actual values of the
- atom and the integer. The atom value is represented as a string.
- By comparing this value with the strings "foo" and "bar", it can
- be decided which function to call:</p>
- <pre>
- if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {
- res = foo(ERL_INT_VALUE(argp));
- } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {
- res = bar(ERL_INT_VALUE(argp));
- }</pre>
- <p>Now an <c>ETERM</c> struct that represents the integer result
- can be constructed using the function <c>erl_mk_int()</c> from
- <c>erl_eterm</c>. The function
- <c>erl_format()</c> from the module <c>erl_format</c> can also
- be used:</p>
- <pre>
- intp = erl_mk_int(res);</pre>
- <p>The resulting <c>ETERM</c> struct is converted into the Erlang
- external term format using the function <c>erl_encode()</c> from
- <c>erl_marshal</c> and sent to Erlang using
- <c>write_cmd()</c>:</p>
- <pre>
- erl_encode(intp, buf);
- write_cmd(buf, erl_eterm_len(intp));</pre>
- <p>Finally, the memory allocated by the <c>ETERM</c> creating
- functions must be freed:</p>
- <pre>
- erl_free_compound(tuplep);
- erl_free_term(fnp);
- erl_free_term(argp);
- erl_free_term(intp);</pre>
- <p>The resulting C program is as follows:</p>
- <codeinclude file="ei.c" type="c"/>
+
</section>
<section>
<title>Running the Example</title>
<p><em>Step 1.</em> Compile the C code. This provides the paths to
- the include files <c>erl_interface.h</c> and <c>ei.h</c>, and
- also to the libraries <c>erl_interface</c> and <c>ei</c>:</p>
+ the include file <c>ei.h</c>, and
+ also to the library <c>ei</c>:</p>
<pre>
-unix> <input>gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \\ </input>
-<input> -L/usr/local/otp/lib/erl_interface-3.9.2/lib \\ </input>
-<input> complex.c erl_comm.c ei.c -lerl_interface -lei -lpthread</input></pre>
+unix> <input>gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \ </input>
+<input> -L/usr/local/otp/lib/erl_interface-3.9.2/lib \ </input>
+<input> complex.c erl_comm.c ei.c -lei -lpthread</input></pre>
<p>In Erlang/OTP R5B and later versions of OTP, the <c>include</c>
and <c>lib</c> directories are situated under
<c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is
diff --git a/system/doc/tutorial/overview.xml b/system/doc/tutorial/overview.xml
index bd652b1e4b..a9331cb191 100644
--- a/system/doc/tutorial/overview.xml
+++ b/system/doc/tutorial/overview.xml
@@ -64,7 +64,7 @@
(describes the BIFs)</item>
<item><seealso marker="kernel:global">global</seealso> manual page in Kernel</item>
<item><seealso marker="kernel:net_adm">net_adm</seealso> manual page in Kernel</item>
- <item><seealso marker="kernel:pg2">pg2</seealso> manual page in Kernel</item>
+ <item><seealso marker="kernel:pg">pg</seealso> manual page in Kernel</item>
<item><seealso marker="kernel:rpc">rpc</seealso> manual page in Kernel</item>
<item><seealso marker="stdlib:pool">pool</seealso> manual page in STDLIB</item>
<item><seealso marker="stdlib:slave">slave</seealso> manual page in STDLIB</item>
@@ -126,25 +126,7 @@
<title>Erl_Interface</title>
<p>The program at the other side of a port is often a C program.
To help the C programmer, the Erl_Interface library
- has been developed, including the following five parts:</p>
- <list type="bulleted">
- <item>
- <c>erl_marshal</c>, <c>erl_eterm</c>, <c>erl_format</c>, and
- <c>erl_malloc</c>: Handling of the Erlang external term format</item>
- <item>
- <c>erl_connect</c>:
- Communication with distributed Erlang, see <seealso
- marker="#cnode">C nodes</seealso> below</item>
- <item>
- <c>erl_error</c>:
- Error print routines</item>
- <item>
- <c>erl_global</c>:
- Access globally registered names</item>
- <item>
- <c>Registry</c>:
- Store and backup of key-value pairs</item>
- </list>
+ has been developed</p>
<p>The Erlang external term format is a representation of an
Erlang term as a sequence of bytes, that is, a binary.
Conversion between the two representations is done using the
@@ -181,12 +163,13 @@ Term = binary_to_term(Binary)</pre>
processors (as opposed to control processors) where C is a
better choice than Erlang due to memory limitations or
application characteristics, or both.</p>
- <p><em>Where to read more:</em> See the <c>erl_connect</c> part
- of the Erl_Interface documentation. The programmer also needs
- to be familiar with TCP/IP sockets, see Sockets in <seealso
- marker="#sockets">Standard
- Protocols</seealso> and Distributed Erlang in <seealso
- marker="#dist">Built-In Mechanisms</seealso>.</p>
+ <p><em>Where to read more:</em> See the <c>ei_connect</c> part
+ of the <seealso marker="erl_interface">Erl_Interface</seealso>
+ documentation. The programmer also needs to be familiar with
+ TCP/IP sockets, see Sockets in <seealso
+ marker="#sockets">Standard Protocols</seealso> and Distributed
+ Erlang in <seealso marker="#dist">Built-In
+ Mechanisms</seealso>.</p>
<p><em>Example:</em> C node example in <seealso marker="cnode">
C Nodes</seealso>.</p>
</section>
diff --git a/xcomp/erl-xcomp-arm-android.conf b/xcomp/erl-xcomp-arm-android.conf
index 44ff7537d3..6d25d48f0a 100644
--- a/xcomp/erl-xcomp-arm-android.conf
+++ b/xcomp/erl-xcomp-arm-android.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2019. All Rights Reserved.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@
##
## %CopyrightEnd%
##
-## File: erl-xcomp.conf.template
-## Author:
+## File: erl-xcomp-arm-android.conf
+## Author: Dmitry Kolesnikov
##
## -----------------------------------------------------------------------------
## When cross compiling Erlang/OTP using `otp_build', copy this file and set
@@ -60,14 +60,21 @@ 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"
+# Set --enable-builtin-zlib to avoid a inflateGetDictionary missing symbol
+erl_xcomp_configure_flags="--disable-hipe --without-termcap --without-wx \
+ --enable-builtin-zlib"
## -- Cross Compiler and Other Tools -------------------------------------------
##
##
-NDK_SYSROOT=$NDK_ROOT/platforms/$NDK_PLAT/arch-arm
+# Previously with older Android NDK versions before NDK r14, each Android
+# API level had a different set of headers, each in its own directory.
+#NDK_SYSROOT=$NDK_ROOT/platforms/$NDK_PLAT/arch-arm
+# Starting with Android NDK r14, a single set of headers (called Unified Headers)
+# is provided in one location for every Android API level.
+NDK_SYSROOT=$NDK_ROOT/sysroot
## 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
@@ -76,10 +83,15 @@ NDK_SYSROOT=$NDK_ROOT/platforms/$NDK_PLAT/arch-arm
## All variables in this section can also be used when native compiling.
# * `CC' - C compiler.
-CC="arm-linux-androideabi-gcc --sysroot=$NDK_SYSROOT"
+#
+# For older Android NDK versions still supporting GCC.
+#CC="arm-linux-androideabi-gcc --sysroot=$NDK_SYSROOT"
+# For more recent Android NDK versions only supporting Clang/LLVM.
+#NDK_ABI_PLAT=androideabi16 # when targeting Android 4.1 Jelly Bean
+CC="armv7a-linux-$NDK_ABI_PLAT-clang"
# * `CFLAGS' - C compiler flags.
-CFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+CFLAGS="-g -O2 -march=armv7-a -mfloat-abi=softfp -mthumb"
# * `STATIC_CFLAGS' - Static C compiler flags.
#STATIC_CFLAGS=
@@ -90,22 +102,38 @@ CFLAGS="-static -march=armv7-a -msoft-float -mthumb"
#CFLAG_RUNTIME_LIBRARY_PATH=
# * `CPP' - C pre-processor.
-CPP="arm-linux-androideabi-cpp --sysroot=$NDK_SYSROOT"
+#
+# For older Android NDK versions still supporting GCC.
+#CPP="arm-linux-androideabi-cpp --sysroot=$NDK_SYSROOT"
+# Not needed when using Clang/LLVM.
+#CPP=
# * `CPPFLAGS' - C pre-processor flags.
-CPPFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+#
+# For older Android NDK versions still supporting GCC.
+#CPPFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+# Not needed when using Clang/LLVM.
+#CPPFLAGS=
# * `CXX' - C++ compiler.
-CXX="arm-linux-androideabi-c++ --sysroot=$NDK_SYSROOT"
+#
+# For older Android NDK versions still supporting GCC.
+#CXX="arm-linux-androideabi-c++ --sysroot=$NDK_SYSROOT
+# For more recent Android NDK versions only supporting Clang/LLVM.
+CXX="armv7a-linux-$NDK_ABI_PLAT-clang++"
# * `CXXFLAGS' - C++ compiler flags.
-CXXFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+CXXFLAGS="-march=armv7-a -mfloat-abi=softfp -mthumb"
# * `LD' - Linker.
+#
+# Not needed when using GCC.
#LD=
+# For more recent Android NDK versions only supporting Clang/LLVM.
+LD="arm-linux-androideabi-ld"
# * `LDFLAGS' - Linker flags.
-LDFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+LDFLAGS="-march=armv7-a -mfloat-abi=softfp -mthumb"
# * `LIBS' - Libraries.
#LIBS=
diff --git a/xcomp/erl-xcomp-vxworks_ppc32.conf b/xcomp/erl-xcomp-arm64-android.conf
index f1655caa4e..e423651fed 100644
--- a/xcomp/erl-xcomp-vxworks_ppc32.conf
+++ b/xcomp/erl-xcomp-arm64-android.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2012. All Rights Reserved.
+## Copyright Ericsson AB 2019. All Rights Reserved.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@
##
## %CopyrightEnd%
##
-## File: erl-xcomp-vxworks_ppc32.conf
-## Author: Rickard Green/Patrik Nyblom
+## File: erl-xcomp-arm64-android.conf
+## Author: Jérôme de Bretagne
##
## -----------------------------------------------------------------------------
## When cross compiling Erlang/OTP using `otp_build', copy this file and set
@@ -56,14 +56,26 @@ erl_xcomp_build=guess
# 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=powerpc-wrs-vxworks
+erl_xcomp_host=aarch64-linux-android
# * `erl_xcomp_configure_flags' - Extra configure flags to pass to the
# `configure' script.
-erl_xcomp_configure_flags="--disable-threads --without-termcap"
+# Set --enable-builtin-zlib to avoid a inflateGetDictionary missing symbol
+erl_xcomp_configure_flags="--disable-hipe --without-termcap --without-wx \
+ --enable-builtin-zlib"
+
## -- Cross Compiler and Other Tools -------------------------------------------
+##
+##
+# Previously with older Android NDK versions before NDK r14, each Android
+# API level had a different set of headers, each in its own directory.
+#NDK_SYSROOT=$NDK_ROOT/platforms/$NDK_PLAT/arch-arm
+# Starting with Android NDK r14, a single set of headers (called Unified Headers)
+# is provided in one location for every Android API level.
+NDK_SYSROOT=$NDK_ROOT/sysroot
+
## 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').
@@ -71,10 +83,13 @@ erl_xcomp_configure_flags="--disable-threads --without-termcap"
## All variables in this section can also be used when native compiling.
# * `CC' - C compiler.
-CC="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/ccppc"
+#
+# For recent Android NDK versions only supporting Clang/LLVM.
+#NDK_ABI_PLAT=android21 # when targeting Android 5.0 Lollipop
+CC="aarch64-linux-$NDK_ABI_PLAT-clang"
# * `CFLAGS' - C compiler flags.
-CFLAGS="-DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I$WIND_BASE/vxworks-6.3/target/h -I$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/lib/gcc/powerpc-wrs-vxworks/3.4.4/include -I$WIND_BASE/vxworks-6.3/target/h/wrn/coreip -I$WIND_BASE/vxworks-6.3/target/h -mstrict-align -fvolatile -fno-builtin -mlongcall"
+CFLAGS="-g -O2"
# * `STATIC_CFLAGS' - Static C compiler flags.
#STATIC_CFLAGS=
@@ -85,22 +100,30 @@ CFLAGS="-DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS
#CFLAG_RUNTIME_LIBRARY_PATH=
# * `CPP' - C pre-processor.
-CPP="$CC $CFLAGS -E"
+#
+# Not needed when using Clang/LLVM.
+#CPP=
# * `CPPFLAGS' - C pre-processor flags.
+#
+# Not needed when using Clang/LLVM.
#CPPFLAGS=
# * `CXX' - C++ compiler.
-#CXX=
+#
+# For recent Android NDK versions only supporting Clang/LLVM.
+CXX="aarch64-linux-$NDK_ABI_PLAT-clang++"
# * `CXXFLAGS' - C++ compiler flags.
#CXXFLAGS=
# * `LD' - Linker.
-LD="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/ldppc"
+#
+# For recent Android NDK versions only supporting Clang/LLVM.
+LD="aarch64-linux-android-ld"
# * `LDFLAGS' - Linker flags.
-LDFLAGS="-mlongcall"
+#LDFLAGS=
# * `LIBS' - Libraries.
#LIBS=
@@ -135,10 +158,10 @@ LDFLAGS="-mlongcall"
## -- Other Tools --
# * `RANLIB' - `ranlib' archive index tool.
-RANLIB="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/ranlibppc"
+#RANLIB=
# * `AR' - `ar' archiving tool.
-AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc"
+#AR=
# * `GETCONF' - `getconf' system configuration inspection tool. `getconf' is
# currently used for finding out large file support flags to use, and
@@ -154,7 +177,8 @@ AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc"
# 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=
+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
@@ -186,16 +210,7 @@ AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc"
# 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=yes
-
-# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
-# target system must have doubles in "middle-endian" format. If
-# `no`, it has "regular" endianness. 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_double_middle_endian
+#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